Sistem logging Android bertujuan untuk aksesibilitas universal dan kemudahan penggunaan, dengan asumsi bahwa semua data log dapat direpresentasikan sebagai urutan karakter. Asumsi ini selaras dengan sebagian besar kasus penggunaan, terutama jika keterbacaan log sangat penting tanpa alat khusus. Namun, di lingkungan yang menuntut performa logging tinggi dan ukuran log yang dibatasi, logging berbasis teks mungkin tidak optimal. Salah satu skenario tersebut adalah WindowManager, yang memerlukan sistem logging andal yang mampu menangani log transisi jendela real-time dengan dampak sistem minimal.
ProtoLog adalah alternatif untuk memenuhi kebutuhan logging WindowManager dan layanan serupa. Manfaat utama ProtoLog dibandingkan logcat adalah:
- Jumlah resource yang digunakan untuk logging lebih kecil.
- Dari sudut pandang developer, hal ini sama dengan menggunakan framework logging Android default.
- Mendukung pernyataan log untuk diaktifkan atau dinonaktifkan saat runtime.
- Masih dapat mencatat ke logcat jika diperlukan.
Untuk mengoptimalkan penggunaan memori, ProtoLog menggunakan mekanisme interning string yang mencakup penghitungan dan penyimpanan hash pesan yang dikompilasi. Untuk meningkatkan performa, ProtoLog melakukan interning string selama kompilasi (untuk layanan sistem), hanya merekam ID pesan dan argumen saat runtime. Selain itu, saat membuat rekaman aktivitas ProtoLog atau mendapatkan laporan bug, ProtoLog akan otomatis menggabungkan kamus pesan yang dibuat pada waktu kompilasi, sehingga memungkinkan decoding pesan dari build apa pun.
Dengan menggunakan ProtoLog, pesan disimpan dalam format biner (proto) dalam
rekaman aktivitas Perfetto. Dekode pesan terjadi di dalam
trace_processor
Perfetto. Proses ini terdiri dari mendekode pesan proto biner,
menerjemahkan ID pesan menjadi string menggunakan kamus pesan
yang disematkan, dan memformat string menggunakan argumen dinamis.
ProtoLog mendukung level log yang sama dengan android.utils.Log
, yaitu: d
, v
,
i
, w
, e
, wtf
.
ProtoLog sisi klien
Awalnya, ProtoLog hanya ditujukan untuk sisi server WindowManager, yang beroperasi dalam satu proses dan komponen. Selanjutnya, kode ini diperluas untuk mencakup kode shell WindowManager dalam proses UI Sistem, tetapi penggunaan ProtoLog memerlukan kode penyiapan boilerplate yang rumit. Selain itu, logging Proto dibatasi untuk server sistem dan proses UI Sistem, sehingga sulit untuk disertakan ke dalam proses lain dan memerlukan penyiapan buffering memori terpisah untuk setiap proses. Namun, ProtoLog kini telah tersedia untuk kode sisi klien, sehingga Anda tidak memerlukan kode boilerplate tambahan.
Tidak seperti kode layanan sistem, kode sisi klien biasanya melewati interning string waktu kompilasi. Sebagai gantinya, interning string terjadi secara dinamis di thread latar belakang. Akibatnya, meskipun ProtoLog di sisi klien memberikan keunggulan penggunaan memori yang sebanding dengan ProtoLog di layanan sistem, ProtoLog ini menimbulkan overhead performa yang sedikit lebih tinggi dan tidak memiliki keunggulan pengurangan memori dari memori yang disematkan dari sisi server.
Grup ProtoLog
Pesan ProtoLog diatur ke dalam grup yang disebut ProtoLogGroups
, mirip dengan
cara pesan Logcat diatur
oleh TAG
. ProtoLogGroups
ini berfungsi sebagai cluster pesan yang dapat
diaktifkan atau dinonaktifkan secara kolektif saat runtime.
Selain itu, flag ini mengontrol apakah pesan harus dihapus selama
kompilasi dan tempat pesan tersebut harus dicatat dalam log (proto, logcat, atau keduanya). Setiap
ProtoLogGroup
terdiri dari properti berikut:
enabled
: Jika ditetapkan kefalse
, pesan dalam grup ini akan dikecualikan selama kompilasi dan tidak tersedia saat runtime.logToProto
: Menentukan apakah grup ini mencatat log dengan format biner.logToLogcat
: Menentukan apakah grup ini mencatat log ke logcat.tag
: Nama sumber pesan yang dicatat ke dalam log.
Setiap proses yang menggunakan ProtoLog harus memiliki instance ProtoLogGroup
yang dikonfigurasi.
Jenis argumen yang didukung
Secara internal, string format ProtoLog menggunakan
android.text.TextUtils#formatSimple(String, Object...)
,
sehingga sintaksisnya sama.
ProtoLog mendukung jenis argumen berikut:
%b
- boolean%d
,%x
- jenis integral (short, integer, atau long)%f
- jenis floating point (float atau double)%s
- string%%
- karakter persen literal
Pengubah lebar dan presisi seperti %04d
dan %10b
didukung, tetapi
argument_index
dan flags
tidak didukung.
Menggunakan ProtoLog di layanan baru
Untuk menggunakan ProtoLog dalam proses baru:
Buat definisi
ProtoLogGroup
untuk layanan ini.Lakukan inisialisasi definisi sebelum penggunaan pertamanya (misalnya, saat pembuatan proses):
Protolog.init(ProtologGroup.values());
Gunakan
Protolog
dengan cara yang sama sepertiandroid.util.Log
:ProtoLog.v(WM_SHELL_STARTING_WINDOW, "create taskSnapshot surface for task: %d", taskId);
Mengaktifkan pengoptimalan waktu kompilasi
Untuk mengaktifkan ProtoLog waktu kompilasi dalam suatu proses, Anda harus mengubah aturan
build-nya dan memanggil biner protologtool
.
ProtoLogTool
adalah biner transformasi kode yang melakukan interning string
dan memperbarui pemanggilan ProtoLog. Biner ini mengubah setiap panggilan logging
ProtoLog
seperti yang ditunjukkan dalam contoh ini:
ProtoLog.x(ProtoLogGroup.GROUP_NAME, "Format string %d %s", value1, value2);
into:
if (ProtoLogImpl.isEnabled(GROUP_NAME)) {
int protoLogParam0 = value1;
String protoLogParam1 = String.valueOf(value2);
ProtoLogImpl.x(ProtoLogGroup.GROUP_NAME, 1234560b0100, protoLogParam0, protoLogParam1);
}
Dalam contoh ini, ProtoLog
, ProtoLogImpl
, dan ProtoLogGroup
adalah class
yang disediakan sebagai argumen (dapat diimpor, diimpor secara statis, atau jalur lengkap, impor
karakter pengganti tidak diizinkan) dan, x
adalah metode logging.
Transformasi dilakukan di tingkat sumber. Hash dihasilkan dari string format, level log, dan nama grup log, serta disisipkan setelah argumen ProtoLogGroup. Kode yang benar-benar dihasilkan akan disisipkan dan sejumlah karakter baris baru akan ditambahkan untuk mempertahankan penomoran baris dalam file.
Contoh:
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"],
}
Opsi command line
Salah satu keuntungan utama ProtoLog adalah Anda dapat mengaktifkan atau menonaktifkannya saat
runtime. Misalnya, Anda dapat memiliki logging yang lebih panjang dalam build,
dinonaktifkan secara default, dan mengaktifkannya selama pengembangan lokal, untuk men-debug
masalah tertentu. Pola ini digunakan, misalnya, di WindowManager dengan
grup WM_DEBUG_WINDOW_TRANSITIONS
dan WM_DEBUG_WINDOW_TRANSITIONS_MIN
yang mengaktifkan berbagai jenis logging transisi, dengan yang pertama diaktifkan secara
default.
Anda dapat mengonfigurasi ProtoLog menggunakan Perfetto, saat memulai rekaman aktivitas.
Anda juga dapat mengonfigurasi ProtoLog secara lokal menggunakan command line adb
.
Perintah adb shell cmd protolog_configuration
mendukung argumen
berikut:
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
Tips untuk penggunaan yang efektif
ProtoLog menggunakan interning string untuk pesan dan argumen string yang diteruskan. Artinya, untuk mendapatkan lebih banyak manfaat dari ProtoLog, pesan harus mengisolasi nilai berulang ke dalam variabel.
Misalnya, perhatikan pernyataan berikut:
Protolog.v(MY_GROUP, "%s", "The argument value is " + argument);
Jika dioptimalkan pada waktu kompilasi, kode ini akan diterjemahkan menjadi:
ProtologImpl.v(MY_GROUP, 0x123, "The argument value is " + argument);
Jika ProtoLog digunakan dalam kode dengan argumen 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");
Tindakan ini akan menghasilkan pesan berikut di memori:
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)
Jika pernyataan ProtoLog ditulis sebagai:
Protolog.v(MY_GROUP, "The argument value is %s", argument);
Buffer dalam memori akan berakhir sebagai:
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)
Urutan ini menghasilkan footprint memori yang 35% lebih kecil.
Penampil Winscope
Tab penampil ProtoLog Winscope menampilkan rekaman aktivitas ProtoLog yang diatur dalam format tabel. Anda dapat memfilter rekaman aktivitas menurut tingkat log, tag, file sumber (tempat pernyataan ProtoLog ada), dan konten pesan. Semua kolom dapat difilter. Mengklik stempel waktu di kolom pertama akan memindahkan linimasa ke stempel waktu pesan. Selain itu, mengklik Buka Waktu Saat Ini akan men-scroll tabel ProtoLog kembali ke stempel waktu yang dipilih di linimasa:
Gambar 1. Penampil ProtoLog