Sistem logging Android bertujuan untuk mencapai aksesibilitas universal dan kemudahan penggunaan, dengan asumsi semua data log dapat direpresentasikan sebagai urutan karakter. Asumsi ini selaras dengan sebagian besar kasus penggunaan, terutama saat keterbacaan log sangat penting tanpa alat khusus. Namun, di lingkungan yang memerlukan performa logging tinggi dan ukuran log terbatas, logging berbasis teks tidak optimal. Salah satu skenario tersebut adalah WindowManager, yang memerlukan sistem logging yang kuat yang menangani log transisi jendela real-time dengan dampak sistem minimal.
ProtoLog adalah alternatif untuk mengatasi kebutuhan logging WindowManager dan layanan serupa. ProtoLog menawarkan manfaat berikut dibandingkan logcat:
- Menggunakan lebih sedikit resource untuk logging.
- Dari perspektif developer, cara kerjanya sama dengan framework logging Android default.
- Memungkinkan Anda mengaktifkan atau menonaktifkan pernyataan log saat runtime.
- Juga dapat mencatat log ke logcat.
Untuk mengoptimalkan penggunaan memori, ProtoLog menggunakan mekanisme interning string. Mekanisme ini menghitung dan menyimpan hash pesan yang dikompilasi. Untuk meningkatkan performa, ProtoLog melakukan interning string selama kompilasi untuk layanan sistem. Hanya ID pesan dan argumen yang dicatat saat runtime. Saat Anda membuat pelacakan ProtoLog atau mendapatkan laporan bug, ProtoLog akan otomatis menggabungkan kamus pesan yang dibuat pada waktu kompilasi. Hal ini memungkinkan decoding pesan dari build apa pun.
ProtoLog menyimpan pesan dalam format biner (proto) dalam pelacakan Perfetto.
Decoding pesan terjadi dalam trace_processor Perfetto. Proses ini mendekode pesan proto biner, menerjemahkan ID pesan ke dalam string menggunakan kamus pesan yang disematkan, dan memformat string menggunakan argumen dinamis.
ProtoLog mendukung tingkat log yang sama dengan android.utils.Log, yaitu d,
v, i, w, e, dan wtf.
ProtoLog sisi klien
Awalnya, ProtoLog hanya ditujukan untuk sisi server WindowManager, yang beroperasi dalam satu proses dan komponen. Kemudian, ProtoLog diperluas untuk mencakup kode shell WindowManager dalam proses UI Sistem. Namun, penggunaan ProtoLog memerlukan kode penyiapan boilerplate yang rumit. Selain itu, logging Proto dibatasi untuk server sistem dan proses UI Sistem. Hal ini menyulitkan untuk menggabungkannya ke dalam proses lain dan memerlukan penyiapan buffer memori terpisah untuk setiap proses. ProtoLog kini tersedia untuk kode sisi klien, sehingga tidak perlu kode boilerplate tambahan.
Tidak seperti kode layanan sistem, kode sisi klien biasanya melewati interning string waktu kompilasi. Sebagai gantinya, interning string terjadi secara dinamis dalam thread latar belakang. Akibatnya, meskipun ProtoLog sisi klien memberikan keuntungan penggunaan memori yang sebanding dengan ProtoLog pada layanan sistem, ProtoLog sisi klien menimbulkan overhead performa yang sedikit lebih tinggi dan tidak memiliki keuntungan pengurangan memori dari memori yang disematkan yang ditawarkan oleh counterpart sisi server.
Grup ProtoLog
Pesan ProtoLog diatur ke dalam grup yang disebut ProtoLogGroups, mirip dengan
cara Logcat pesan diatur berdasarkan TAG. These ProtoLogGroups berfungsi sebagai
cluster pesan yang dapat Anda aktifkan atau nonaktifkan saat runtime. Grup ini juga mengontrol
apakah pesan dihapus selama kompilasi dan tempat pesan dicatat
(proto, logcat, atau keduanya). Setiap ProtoLogGroup mencakup 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.
Setiap proses yang menggunakan ProtoLog harus memiliki instance ProtoLogGroup yang dikonfigurasi.
Jenis argumen yang didukung
ProtoLog secara internal memformat string menggunakan android.text.TextUtils#formatSimple(String, Object...),
sehingga sintaksnya 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.
Namun, argument_index dan flags tidak didukung.
Menggunakan ProtoLog di layanan baru
Untuk menggunakan ProtoLog di layanan baru, ikuti langkah-langkah berikut:
- Buat definisi
ProtoLogGroupuntuk layanan ini. Lakukan inisialisasi definisi sebelum penggunaan pertamanya. Misalnya, lakukan inisialisasi selama pembuatan proses:
ProtoLog.init(ProtoLogGroup.values());Gunakan
ProtoLogdengan cara yang sama seperti Anda menggunakanandroid.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);
menjadi:
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 diberikan sebagai argumen (dapat diimpor, diimpor statis, atau jalur lengkap,
impor karakter pengganti tidak diizinkan) dan, x adalah metode logging.
Transformasi dilakukan di tingkat sumber. Hash dibuat dari string format, tingkat log, dan nama grup log, lalu dimasukkan setelah argumen ProtoLogGroup. Kode yang dihasilkan sebenarnya di-inline dan sejumlah karakter baris baru 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 keunggulan utama ProtoLog adalah Anda dapat mengaktifkan atau menonaktifkannya saat runtime. Misalnya, Anda dapat memiliki logging yang lebih verbose dalam build, yang 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 pelacakan. Anda juga dapat
mengonfigurasi ProtoLog secara lokal menggunakan adb command line.
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, pernyataan tersebut 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");
Hal ini akan menghasilkan pesan berikut dalam 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, sebagai gantinya, pernyataan ProtoLog ditulis sebagai:
Protolog.v(MY_GROUP, "The argument value is %s", argument);
Buffer dalam memori akan menjadi:
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 jejak memori yang 35% lebih kecil.
Penampil Winscope
Tab penampil ProtoLog Winscope menampilkan pelacakan ProtoLog yang disusun dalam format tabel. Anda dapat memfilter pelacakan berdasarkan tingkat log, tag, file sumber (tempat pernyataan ProtoLog berada), dan konten pesan. Semua kolom dapat difilter. Mengklik stempel waktu di kolom pertama akan memindahkan linimasa ke stempel waktu pesan. Selain itu, mengklik Go to Current Time akan men-scroll kembali tabel ProtoLog ke stempel waktu yang dipilih di linimasa:
Gambar 1. Penampil ProtoLog