Android 로깅 시스템은 모든 로그 데이터를 문자 시퀀스로 나타낼 수 있다고 가정하여 보편적인 접근성과 사용 편의성을 목표로 합니다. 이 가정은 특히 전문 도구 없이 로그 가독성이 중요한 대부분의 사용 사례에 적합합니다. 그러나 로깅 성능이 높고 로그 크기가 제한된 환경에서는 텍스트 기반 로깅이 최적의 방법이 아닐 수 있습니다. 이러한 시나리오 중 하나는 WindowManager입니다. WindowManager에는 최소한의 시스템 영향을 미치면서 실시간 창 전환 로그를 처리할 수 있는 강력한 로깅 시스템이 필요합니다.
ProtoLog는 WindowManager 및 유사 서비스의 로깅 요구사항을 해결하는 대안입니다. logcat 대비 ProtoLog의 주요 이점은 다음과 같습니다.
- 로깅에 사용되는 리소스 양이 적습니다.
- 개발자 관점에서는 기본 Android 로깅 프레임워크를 사용하는 것과 같습니다.
- 런타임에 로그 문을 사용 설정하거나 중지할 수 있도록 지원합니다.
- 필요한 경우 logcat에 계속 로깅할 수 있습니다.
메모리 사용량을 최적화하기 위해 ProtoLog는 메시지의 컴파일된 해시를 계산하고 저장하는 문자열 내부화 메커니즘을 사용합니다. 성능을 개선하기 위해 ProtoLog는 컴파일 중에 (시스템 서비스의 경우) 문자열 내부화를 실행하고 런타임 시 메시지 식별자와 인수만 기록합니다. 또한 ProtoLog 트레이스를 생성하거나 버그 신고를 가져올 때 ProtoLog는 컴파일 시 생성된 메시지 사전을 자동으로 통합하여 모든 빌드에서 메시지 디코딩을 사용 설정합니다.
ProtoLog를 사용하면 메시지가 Perfetto 트레이스 내에 바이너리 형식 (proto)으로 저장됩니다. 메시지 디코딩은 Perfetto의 trace_processor
내에서 실행됩니다. 이 프로세스는 바이너리 proto 메시지를 디코딩하고, 삽입된 메시지 사전을 사용하여 메시지 식별자를 문자열로 변환하고, 동적 인수를 사용하여 문자열 형식을 지정하는 것으로 구성됩니다.
ProtoLog는 android.utils.Log
와 동일한 로그 수준(d
, v
, i
, w
, e
, wtf
)을 지원합니다.
클라이언트 측 ProtoLog
처음에는 ProtoLog가 단일 프로세스 및 구성요소 내에서 작동하는 WindowManager의 서버 측용으로만 설계되었습니다. 이후 시스템 UI 프로세스의 WindowManager 셸 코드를 포함하도록 확장되었지만 ProtoLog를 사용하려면 복잡한 템플릿 설정 코드가 필요했습니다. 또한 Proto 로깅은 시스템 서버 및 시스템 UI 프로세스로 제한되어 다른 프로세스에 통합하기가 어렵고 각각에 대해 별도의 메모리 버퍼 설정이 필요했습니다. 하지만 이제 ProtoLog를 클라이언트 측 코드에 사용할 수 있으므로 더 이상 불필요한 템플릿 코드를 추가할 필요가 없습니다.
시스템 서비스 코드와 달리 클라이언트 측 코드는 일반적으로 컴파일 시간 문자열 내부화를 건너뜁니다. 대신 문자열 내부화는 백그라운드 스레드에서 동적으로 발생합니다. 따라서 클라이언트 측의 ProtoLog는 시스템 서비스의 ProtoLog와 비슷한 메모리 사용 이점을 제공하지만 성능 오버헤드는 약간 더 높고 서버 측 대응 항목의 고정된 메모리의 메모리 감소 이점은 없습니다.
ProtoLog 그룹
ProtoLog 메시지는 Logcat 메시지가 TAG
로 구성되는 것과 유사하게 ProtoLogGroups
라는 그룹으로 구성됩니다. 이러한 ProtoLogGroups
는 런타임에 일괄적으로 사용 설정 또는 중지할 수 있는 메시지 클러스터 역할을 합니다.
또한 컴파일 중에 메시지를 제거해야 하는지 여부와 메시지를 로깅할 위치 (proto, logcat 또는 둘 다)를 제어합니다. 각 ProtoLogGroup
는 다음 속성으로 구성됩니다.
enabled
:false
로 설정하면 이 그룹의 메시지가 컴파일 중에 제외되며 런타임에는 사용할 수 없습니다.logToProto
: 이 그룹이 바이너리 형식으로 로깅할지 여부를 정의합니다.logToLogcat
: 이 그룹이 Logcat에 로깅할지 여부를 정의합니다.tag
: 로깅된 메시지의 소스 이름입니다.
ProtoLog를 사용하는 각 프로세스에는 ProtoLogGroup
인스턴스가 구성되어 있어야 합니다.
지원되는 인수 유형
내부적으로 ProtoLog 형식 문자열은 android.text.TextUtils#formatSimple(String, Object...)
를 사용하므로 구문은 동일합니다.
ProtoLog는 다음 인수 유형을 지원합니다.
%b
- 불리언%d
,%x
- 정수 유형 (short, integer, long)%f
- 부동 소수점 유형 (float 또는 double)%s
- 문자열%%
- 리터럴 퍼센트 문자
%04d
및 %10b
와 같은 너비 및 정밀도 수정자는 지원되지만 argument_index
및 flags
는 지원되지 않습니다.
새 서비스에서 ProtoLog 사용
새 프로세스에서 ProtoLog를 사용하려면 다음 단계를 따르세요.
이 서비스의
ProtoLogGroup
정의를 만듭니다.정의가 처음 사용되기 전에 정의 초기화 (예: 프로세스 생성 시)
Protolog.init(ProtologGroup.values());
Protolog
를android.util.Log
와 동일한 방식으로 사용합니다.ProtoLog.v(WM_SHELL_STARTING_WINDOW, "create taskSnapshot surface for task: %d", taskId);
컴파일 시간 최적화 사용 설정
프로세스에서 컴파일 시간 ProtoLog를 사용 설정하려면 빌드 규칙을 변경하고 protologtool
바이너리를 호출해야 합니다.
ProtoLogTool
는 문자열 내부화를 실행하고 ProtoLog 호출을 업데이트하는 코드 변환 바이너리입니다. 이 바이너리는 다음 예와 같이 모든 ProtoLog
로깅 호출을 변환합니다.
ProtoLog.x(ProtoLogGroup.GROUP_NAME, "Format string %d %s", value1, value2);
다음으로 변환합니다.
if (ProtoLogImpl.isEnabled(GROUP_NAME)) {
int protoLogParam0 = value1;
String protoLogParam1 = String.valueOf(value2);
ProtoLogImpl.x(ProtoLogGroup.GROUP_NAME, 1234560b0100, protoLogParam0, protoLogParam1);
}
이 예에서 ProtoLog
, ProtoLogImpl
, ProtoLogGroup
는 인수로 제공된 클래스 (가져오거나 정적 가져오기 또는 전체 경로로 지정할 수 있으며, 와일드 카드 가져오기는 허용되지 않음)이고 x
는 로깅 메서드입니다.
변환은 소스 수준에서 실행됩니다. 해시는 형식 문자열, 로그 수준, 로그 그룹 이름에서 생성되고 ProtoLogGroup 인수 뒤에 삽입됩니다. 실제 생성된 코드는 인라인 처리되고 파일의 줄 번호를 유지하기 위해 여러 개의 새 줄 문자가 추가됩니다.
예:
genrule {
name: "wm_shell_protolog_src",
srcs: [
":protolog-impl", // protolog lib
":wm_shell_protolog-groups", // protolog groups declaration
":wm_shell-sources", // source code
],
tools: ["protologtool"],
cmd: "$(location protologtool) transform-protolog-calls " +
"--protolog-class com.android.internal.protolog.ProtoLog " +
"--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
"--loggroups-jar $(location :wm_shell_protolog-groups) " +
"--viewer-config-file-path /system_ext/etc/wmshell.protolog.pb " +
"--legacy-viewer-config-file-path /system_ext/etc/wmshell.protolog.json.gz " +
"--legacy-output-file-path /data/misc/wmtrace/shell_log.winscope " +
"--output-srcjar $(out) " +
"$(locations :wm_shell-sources)",
out: ["wm_shell_protolog.srcjar"],
}
명령줄 옵션
ProtoLog의 주요 이점 중 하나는 런타임에 사용 설정하거나 사용 중지할 수 있다는 것입니다. 예를 들어 빌드에서 기본적으로 사용 중지된 더 상세한 로깅을 사용하고 로컬 개발 중에 특정 문제를 디버그하기 위해 사용 설정할 수 있습니다. 이 패턴은 예를 들어 WindowManager에서 WM_DEBUG_WINDOW_TRANSITIONS
및 WM_DEBUG_WINDOW_TRANSITIONS_MIN
그룹을 사용하여 다양한 유형의 전환 로깅을 사용 설정하며, 기본적으로 전자는 사용 설정되어 있습니다.
트레이스를 시작할 때 Perfetto를 사용하여 ProtoLog를 구성할 수 있습니다.
adb
명령줄을 사용하여 로컬에서 ProtoLog를 구성할 수도 있습니다.
adb shell cmd protolog_configuration
명령어는 다음 인수를 지원합니다.
help
Print this help text.
groups (list | status)
list - lists all ProtoLog groups registered with ProtoLog service"
status <group> - print the status of a ProtoLog group"
logcat (enable | disable) <group>"
enable or disable ProtoLog to logcat
효과적인 사용을 위한 팁
ProtoLog는 메시지와 전달된 모든 문자열 인수에 문자열 내부화를 사용합니다. 즉, ProtoLog에서 더 많은 이점을 얻으려면 메시지가 반복되는 값을 변수로 분리해야 합니다.
예를 들어 다음 문을 살펴보겠습니다.
Protolog.v(MY_GROUP, "%s", "The argument value is " + argument);
컴파일 시 최적화하면 다음과 같이 변환됩니다.
ProtologImpl.v(MY_GROUP, 0x123, "The argument value is " + argument);
코드에서 ProtoLog가 A,B,C
인수와 함께 사용되는 경우:
Protolog.v(MY_GROUP, "%s", "The argument value is A");
Protolog.v(MY_GROUP, "%s", "The argument value is B");
Protolog.v(MY_GROUP, "%s", "The argument value is C");
Protolog.v(MY_GROUP, "%s", "The argument value is A");
메모리에 다음 메시지가 표시됩니다.
Dict:
0x123: "%s"
0x111: "The argument value is A"
0x222: "The argument value is B"
0x333: "The argument value is C"
Message1 (Hash: 0x123, Arg1: 0x111)
Message2 (Hash: 0x123, Arg2: 0x222)
Message3 (Hash: 0x123, Arg3: 0x333)
Message4 (Hash: 0x123, Arg1: 0x111)
대신 ProtoLog 문이 다음과 같이 작성된 경우
Protolog.v(MY_GROUP, "The argument value is %s", argument);
메모리 버퍼는 다음과 같이 종료됩니다.
Dict:
0x123: "The argument value is %s" (24 b)
0x111: "A" (1 b)
0x222: "B" (1 b)
0x333: "C" (1 b)
Message1 (Hash: 0x123, Arg1: 0x111)
Message2 (Hash: 0x123, Arg2: 0x222)
Message3 (Hash: 0x123, Arg3: 0x333)
Message4 (Hash: 0x123, Arg1: 0x111)
이 시퀀스를 사용하면 메모리 사용량이 35% 줄어듭니다.
Winscope 뷰어
Winscope의 ProtoLog 뷰어 탭에는 표 형식으로 구성된 ProtoLog 트레이스가 표시됩니다. 로그 수준, 태그, 소스 파일 (ProtoLog 문이 있는 위치), 메시지 콘텐츠를 기준으로 트레이스를 필터링할 수 있습니다. 모든 열을 필터링할 수 있습니다. 첫 번째 열의 타임스탬프를 클릭하면 타임라인이 메시지 타임스탬프로 이동합니다. 또한 현재 시간으로 이동을 클릭하면 ProtoLog 표가 타임라인에서 선택한 타임스탬프로 다시 스크롤됩니다.
그림 1. ProtoLog 뷰어