Il sistema di logging di Android mira a un'accessibilità universale e a una facilità d'uso, assumendo che tutti i dati dei log possano essere rappresentati come una sequenza di caratteri. Questo presupposto è in linea con la maggior parte dei casi d'uso, in particolare quando la leggibilità dei log è fondamentale senza strumenti specializzati. Tuttavia, in ambienti che richiedono prestazioni elevate di registrazione e dimensioni limitate dei log, la registrazione basata su testo potrebbe non essere ottimale. Uno di questi scenari è WindowManager, che richiede un sistema di registrazione robusto in grado di gestire i log delle transizioni delle finestre in tempo reale con un impatto minimo sul sistema.
ProtoLog è l'alternativa per soddisfare le esigenze di logging di WindowManager e di servizi simili. I principali vantaggi di ProtoLog rispetto a logcat sono:
- La quantità di risorse utilizzate per la registrazione è inferiore.
- Dal punto di vista dello sviluppatore, è come utilizzare il framework di logging Android predefinito.
- Supporta le istruzioni di log da attivare o disattivare in fase di esecuzione.
- Se necessario, può comunque registrare in logcat.
Per ottimizzare l'utilizzo della memoria, ProtoLog utilizza un meccanismo di memorizzazione interna delle stringhe che comporta il calcolo e il salvataggio di un hash compilato del messaggio. Per migliorare le prestazioni, ProtoLog esegue l'interning delle stringhe durante la compilazione (per i servizi di sistema), registrando solo l'identificatore del messaggio e gli argomenti in fase di esecuzione. Inoltre, quando generi una traccia ProtoLog o ottieni una segnalazione di bug, ProtoLog incorpora automaticamente il dizionario dei messaggi creato in fase di compilazione, consentendo la decodifica dei messaggi da qualsiasi build.
Con ProtoLog, il messaggio viene archiviato in un formato binario (proto) all'interno di una traccia Perfetta. La decodifica del messaggio avviene all'interno di trace_processor
di Perfetto. Il processo consiste nel decodificare i messaggi proto binari, tradurre gli identificatori dei messaggi in stringhe utilizzando il dizionario dei messaggi incorporato e formattare la stringa utilizzando gli argomenti dinamici.
ProtoLog supporta gli stessi livelli di log di android.utils.Log
, ovvero: d
, v
,
i
, w
, e
, wtf
.
ProtoLog lato client
Inizialmente, ProtoLog era destinato esclusivamente al lato server di WindowManager, che opera all'interno di un singolo processo e componente. Successivamente, è stato ampliato per includere il codice shell di WindowManager nel processo dell'interfaccia utente di sistema, ma l'utilizzo di ProtoLog richiedeva un codice di configurazione boilerplate complesso. Inoltre, la registrazione di Proto era limitata ai processi del server di sistema e dell'interfaccia utente di sistema, il che rendeva laborioso incorporarla in altri processi e richiedeva la configurazione di un buffer di memoria separato per ciascuno. Tuttavia, ora ProtoLog è stato reso disponibile per il codice lato client, eliminando la necessità di codice boilerplate aggiuntivo.
A differenza del codice dei servizi di sistema, il codice lato client in genere salta l'internamento delle stringhe in fase di compilazione. Invece, l'internamento delle stringhe avviene dinamicamente in un THREAD in background. Di conseguenza, sebbene ProtoLog lato client offra vantaggi simili per l'utilizzo della memoria rispetto a ProtoLog sui servizi di sistema, comporta un overhead delle prestazioni leggermente superiore e non offre il vantaggio della riduzione della memoria della memoria bloccata della controparte lato server.
Gruppi di ProtoLog
I messaggi ProtoLog sono organizzati in gruppi chiamati ProtoLogGroups
, in modo simile a come i messaggi Logcat sono organizzati per TAG
. Questi ProtoLogGroups
fungono da cluster di messaggi che possono essere attivati o disattivati collettivamente in fase di esecuzione.
Inoltre, controllano se i messaggi devono essere rimossi durante la compilation e dove devono essere registrati (proto, logcat o entrambi). Ogni
ProtoLogGroup
comprende le seguenti proprietà:
enabled
: se impostato sufalse
, i messaggi in questo gruppo vengono esclusi durante la compilazione e non sono disponibili in fase di esecuzione.logToProto
: definisce se questo gruppo genera log in formato binario.logToLogcat
: definisce se questo gruppo registra i log in logcat.tag
: il nome dell'origine del messaggio registrato.
Per ogni processo che utilizza ProtoLog deve essere configurata un'istanza di ProtoLogGroup
.
Tipi di argomenti supportati
All'interno, le stringhe di formato ProtoLog utilizzano
android.text.TextUtils#formatSimple(String, Object...)
,
quindi la sintassi è la stessa.
ProtoLog supporta i seguenti tipi di argomenti:
%b
- booleano%d
,%x
: tipo integrale (short, integer o long)%f
- tipo a virgola mobile (float o double)%s
- stringa%%
: un carattere percentuale letterale
I modificatori di larghezza e precisione come %04d
e %10b
sono supportati, ma
argument_index
e flags
non lo sono.
Utilizzare ProtoLog in un nuovo servizio
Per utilizzare ProtoLog in una nuova procedura:
Crea una definizione di
ProtoLogGroup
per questo servizio.Inizializza la definizione prima del primo utilizzo (ad esempio, alla creazione del processo):
Protolog.init(ProtologGroup.values());
Utilizza
Protolog
nello stesso modo in cui utilizziandroid.util.Log
:ProtoLog.v(WM_SHELL_STARTING_WINDOW, "create taskSnapshot surface for task: %d", taskId);
Attivare l'ottimizzazione in fase di compilazione
Per abilitare ProtoLog in fase di compilazione in un processo, devi modificare le relative regole di compilazione e richiamare il file binario protologtool
.
ProtoLogTool
è un programma binario di trasformazione del codice che esegue l'interning delle stringhe
e aggiorna l'invocazione di ProtoLog. Questo file binario trasforma ogni chiamata di logging ProtoLog
come mostrato in questo esempio:
ProtoLog.x(ProtoLogGroup.GROUP_NAME, "Format string %d %s", value1, value2);
in:
if (ProtoLogImpl.isEnabled(GROUP_NAME)) {
int protoLogParam0 = value1;
String protoLogParam1 = String.valueOf(value2);
ProtoLogImpl.x(ProtoLogGroup.GROUP_NAME, 1234560b0100, protoLogParam0, protoLogParam1);
}
In questo esempio, ProtoLog
, ProtoLogImpl
e ProtoLogGroup
sono le classi fornite come argomenti (possono essere importate, importate in modo statico o con percorso completo; le importazioni con caratteri jolly non sono consentite) e x
è il metodo di registrazione.
La trasformazione viene eseguita a livello di origine. Viene generato un hash dalla stringa di formato, dal livello di log e dal nome del gruppo di log e inserito dopo l'argomento ProtoLogGroup. Il codice generato effettivo viene inserito in linea e viene aggiunto un numero di nuovi caratteri di riga per preservare la numerazione delle righe nel file.
Esempio:
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"],
}
Opzioni della riga di comando
Uno dei principali vantaggi di ProtoLog è che puoi attivarlo o disattivarlo in fase di esecuzione. Ad esempio, puoi avere un logging più dettagliato in una build, disabilitato per impostazione predefinita, e attivarlo durante lo sviluppo locale per eseguire il debug di un problema specifico. Questo pattern viene utilizzato, ad esempio, in WindowManager con i gruppi WM_DEBUG_WINDOW_TRANSITIONS
e WM_DEBUG_WINDOW_TRANSITIONS_MIN
che attivano diversi tipi di registrazione delle transizioni, il primo dei quali è attivato per impostazione predefinita.
Puoi configurare ProtoLog utilizzando Perfetto quando avvii una traccia.
Puoi anche configurare ProtoLog localmente utilizzando la riga di comando adb
.
Il comando adb shell cmd protolog_configuration
supporta i seguenti parametri:
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
Suggerimenti per un utilizzo efficace
ProtoLog utilizza l'interning delle stringhe sia per il messaggio sia per eventuali argomenti stringa tramessi. Ciò significa che, per ottenere un maggiore vantaggio da ProtoLog, i messaggi devono isolare i valori ripetuti in variabili.
Ad esempio, considera la seguente affermazione:
Protolog.v(MY_GROUP, "%s", "The argument value is " + argument);
Se ottimizzato in fase di compilazione, si traduce in:
ProtologImpl.v(MY_GROUP, 0x123, "The argument value is " + argument);
Se ProtoLog viene utilizzato nel codice con gli argomenti 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");
In memoria vengono visualizzati i seguenti messaggi:
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)
Se invece l'istruzione ProtoLog è stata scritta come segue:
Protolog.v(MY_GROUP, "The argument value is %s", argument);
Il buffer in memoria avrà il seguente aspetto:
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)
Questa sequenza comporta un'impronta di memoria inferiore del 35%.
Visualizzatore Winscope
La scheda del visualizzatore di ProtoLog di Winscope mostra le tracce di ProtoLog organizzate in un formato tabellare. Puoi filtrare le tracce in base al livello di log, al tag, al file di origine (dove è presente l'istruzione ProtoLog) e ai contenuti del messaggio. Tutte le colonne sono filtrabili. Se fai clic sul timestamp nella prima colonna, la sequenza temporale viene spostata al timestamp del messaggio. Inoltre, facendo clic su Vai all'ora corrente, la tabella ProtoLog torna al timestamp selezionato nella sequenza temporale:
Figura 1. Visualizzatore ProtoLog