Система логирования 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 в процессе системного пользовательского интерфейса. Однако использование ProtoLog требовало сложного шаблонного кода настройки. Кроме того, логирование Proto было ограничено серверными процессами и процессами системного пользовательского интерфейса. Это затрудняло интеграцию в другие процессы и требовало отдельной настройки буфера памяти для каждого из них. Теперь 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- тип данных с плавающей запятой (loat или 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