Система журналирования Android стремится к достижению универсальной доступности и простоты использования, предполагая, что все данные журнала могут быть представлены в виде последовательности символов. Это предположение согласуется с большинством вариантов использования, особенно когда читаемость журнала критически важна без специализированных инструментов. Однако в средах, требующих высокой производительности журналирования и ограниченного размера журналов, текстовое журналирование не является оптимальным вариантом. Одним из таких сценариев является WindowManager, для которого требуется надежная система журналирования, обрабатывающая журналы переходов окон в реальном времени с минимальным влиянием на систему.
ProtoLog — это альтернатива для решения задач журналирования WindowManager и подобных сервисов. ProtoLog предлагает следующие преимущества по сравнению с logcat:
- Использует меньше ресурсов для ведения журнала.
- С точки зрения разработчика, он работает так же, как стандартная среда журналирования Android.
- Позволяет включать или отключать ведение журнала во время выполнения.
- Также можно записывать в logcat.
Для оптимизации использования памяти ProtoLog использует механизм интернирования строк. Этот механизм вычисляет и сохраняет скомпилированный хэш сообщения. Для повышения производительности ProtoLog выполняет интернирование строк во время компиляции для системных служб. Во время выполнения он записывает только идентификатор сообщения и аргументы. При создании трассировки ProtoLog или получении отчёта об ошибке ProtoLog автоматически включает словарь сообщений, созданный во время компиляции. Это позволяет декодировать сообщения из любой сборки.
ProtoLog хранит сообщения в двоичном формате (proto) в трассировке Perfetto. Декодирование сообщений происходит в trace_processor Perfetto. Процесс декодирует двоичные proto-сообщения, преобразует идентификаторы сообщений в строки, используя встроенный словарь сообщений, и форматирует строку, используя динамические аргументы.
ProtoLog поддерживает те же уровни журналирования, что и android.utils.Log , а именно d , v , i , w , e и wtf .
ProtoLog на стороне клиента
Изначально ProtoLog предназначался только для серверной части WindowManager, работая в рамках одного процесса и компонента. Позже он был расширен и теперь охватывает код оболочки WindowManager в процессе System UI. Однако использование ProtoLog требовало сложного шаблонного кода настройки. Кроме того, ведение журнала Proto было ограничено процессами системного сервера и System UI. Это затрудняло интеграцию в другие процессы и требовало отдельной настройки буфера памяти для каждого из них. Теперь ProtoLog доступен и для клиентского кода, что устраняет необходимость в дополнительном шаблонном коде.
В отличие от кода системных служб, клиентский код обычно пропускает интернирование строк на этапе компиляции. Вместо этого интернирование строк происходит динамически в фоновом потоке. В результате, хотя клиентский ProtoLog обеспечивает преимущества в использовании памяти, сравнимые с ProtoLog в системных службах, он влечет за собой несколько более высокие накладные расходы и не обладает преимуществом в сокращении потребления памяти, которое обеспечивается закреплённой памятью, как его серверный аналог.
Группы ProtoLog
Сообщения ProtoLog организованы в группы, называемые ProtoLogGroups , аналогично тому, как сообщения Logcat организованы с помощью TAG . Эти ProtoLogGroups служат кластерами сообщений, которые можно включать и отключать во время выполнения. Они также управляют тем, будут ли сообщения удаляться во время компиляции и где они будут регистрироваться (proto, logcat или оба). Каждая ProtoLogGroup включает следующие свойства:
-
enabled: если установлено значениеfalse, сообщения в этой группе исключаются во время компиляции и недоступны во время выполнения. -
logToProto: определяет, ведет ли эта группа журнал в двоичном формате. -
logToLogcat: определяет, записывает ли эта группа данные в logcat. -
tag: Имя источника зарегистрированного сообщения.
Для каждого процесса, использующего ProtoLog, должен быть настроен экземпляр ProtoLogGroup .
Поддерживаемые типы аргументов
ProtoLog внутренне форматирует строки с помощью android.text.TextUtils#formatSimple(String, Object...) , поэтому его синтаксис тот же.
ProtoLog поддерживает следующие типы аргументов:
-
%b- логическое значение -
%d,%x- целочисленный тип (короткий, целый или длинный) -
%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 , включающими различные типы журналирования переходов, причём первый тип включен по умолчанию.
Вы можете настроить ProtoLog с помощью Perfetto при запуске трассировки. Также можно настроить ProtoLog локально с помощью командной строки adb .
Команда 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
Вкладка просмотра ProtoLog в Winscope отображает трассировки ProtoLog в табличном формате. Вы можете фильтровать трассировки по уровню журнала, тегу, исходному файлу (где присутствует оператор ProtoLog) и содержанию сообщения. Все столбцы фильтруются. Щелчок по временной метке в первом столбце переносит временную шкалу к временной метке сообщения. Кроме того, нажатие кнопки «Перейти к текущему времени» прокручивает таблицу ProtoLog обратно к временной метке, выбранной на временной шкале:

Рисунок 1. Средство просмотра ProtoLog