Система ведения журналов 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– целочисленный тип (short, integer или long);%f– тип с плавающей запятой (float или double).%s- string%%– символ процента.
Поддерживаются модификаторы ширины и точности, такие как %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 viewer
На вкладке ProtoLog viewer (Просмотрщик ProtoLog) в Winscope показаны трассировки ProtoLog, упорядоченные в табличном формате. Трассировки можно фильтровать по уровню журнала, тегу, исходному файлу (в котором присутствует оператор ProtoLog) и содержимому сообщения. Все столбцы можно фильтровать. Если нажать на временную метку в первом столбце, временная шкала перейдет к временной метке сообщения. Кроме того, при нажатии кнопки Перейти к текущему времени таблица ProtoLog прокручивается до временной метки, выбранной на временной шкале:
Рисунок 1. Просмотр ProtoLog