Android का लॉगिंग सिस्टम, सभी के लिए उपलब्ध होने और इस्तेमाल में आसान होने के मकसद से बनाया गया है. इसमें यह माना जाता है कि सभी लॉग डेटा को वर्णों के क्रम के तौर पर दिखाया जा सकता है. यह अनुमान, इस्तेमाल के ज़्यादातर उदाहरणों के मुताबिक है. खास तौर पर, जब खास टूल के बिना लॉग को आसानी से पढ़ा जा सकता हो. हालांकि, ऐसे एनवायरमेंट में जहां लॉगिंग की परफ़ॉर्मेंस बेहतर होनी चाहिए और लॉग का साइज़ सीमित होना चाहिए वहां टेक्स्ट पर आधारित लॉगिंग सबसे सही विकल्प नहीं हो सकता. WindowManager ऐसा ही एक उदाहरण है. इसके लिए, एक मज़बूत लॉगिंग सिस्टम की ज़रूरत होती है. यह सिस्टम, रीयल-टाइम में विंडो ट्रांज़िशन लॉग को कम से कम सिस्टम पर असर डालते हुए मैनेज कर सकता है.
ProtoLog, WindowManager और इसी तरह की अन्य सेवाओं की लॉगिंग की ज़रूरतों को पूरा करने का एक विकल्प है. ProtoLog के logcat से ज़्यादा फ़ायदे ये हैं:
- लॉगिंग के लिए इस्तेमाल किए गए संसाधनों की संख्या कम है.
- डेवलपर के नज़रिए से, यह डिफ़ॉल्ट Android लॉगिंग फ़्रेमवर्क का इस्तेमाल करने जैसा ही है.
- यह सुविधा, लॉग स्टेटमेंट को रनटाइम पर चालू या बंद करने की अनुमति देती है.
- ज़रूरत पड़ने पर, अब भी logcat में लॉग कर सकता है.
मेमोरी के इस्तेमाल को ऑप्टिमाइज़ करने के लिए, ProtoLog एक स्ट्रिंग इंटर्निंग मेकेनिज़्म का इस्तेमाल करता है. इसमें मैसेज के कंपाइल किए गए हैश की गिनती करना और उसे सेव करना शामिल है. बेहतर परफ़ॉर्मेंस के लिए, ProtoLog कंपाइलेशन के दौरान स्ट्रिंग इंटरनिंग करता है. यह सिस्टम सेवाओं के लिए होता है. साथ ही, यह सिर्फ़ मैसेज आइडेंटिफ़ायर और आर्ग्युमेंट को रनटाइम में रिकॉर्ड करता है. इसके अलावा, ProtoLog ट्रेस जनरेट करते समय या गड़बड़ी की रिपोर्ट पाते समय, ProtoLog अपने-आप कंपाइल टाइम पर बनाई गई मैसेज डिक्शनरी को शामिल कर लेता है. इससे किसी भी बिल्ड से मैसेज को डिकोड किया जा सकता है.
ProtoLog का इस्तेमाल करके, मैसेज को Perfetto ट्रेस में बाइनरी फ़ॉर्मैट (प्रोटो) में सेव किया जाता है. मैसेज डिकोड करने की प्रोसेस, Perfetto के trace_processor
में होती है. इस प्रोसेस में, बाइनरी प्रोटो मैसेज को डिकोड करना, एम्बेड किए गए मैसेज डिक्शनरी का इस्तेमाल करके मैसेज आइडेंटिफ़ायर को स्ट्रिंग में बदलना, और डाइनैमिक आर्ग्युमेंट का इस्तेमाल करके स्ट्रिंग को फ़ॉर्मैट करना शामिल है.
ProtoLog, android.utils.Log
के जैसे ही लॉग लेवल के साथ काम करता है. जैसे: d
, v
,
i
, w
, e
, wtf
.
क्लाइंट-साइड ProtoLog
शुरुआत में, ProtoLog को सिर्फ़ WindowManager के सर्वर साइड के लिए बनाया गया था. यह एक ही प्रोसेस और कॉम्पोनेंट में काम करता था. इसके बाद, इसे सिस्टम यूज़र इंटरफ़ेस (यूआई) प्रोसेस में WindowManager शेल कोड को शामिल करने के लिए बढ़ाया गया. हालांकि, ProtoLog का इस्तेमाल करने के लिए, एक जटिल बॉयलरप्लेट सेटअप कोड की ज़रूरत होती है. इसके अलावा, प्रोटो लॉगिंग को सिस्टम सर्वर और सिस्टम यूज़र इंटरफ़ेस (यूआई) प्रोसेस तक सीमित कर दिया गया था. इस वजह से, इसे अन्य प्रोसेस में शामिल करना मुश्किल हो गया था. साथ ही, हर प्रोसेस के लिए अलग मेमोरी बफ़र सेटअप करना ज़रूरी हो गया था. हालांकि, अब ProtoLog को क्लाइंट-साइड कोड के लिए उपलब्ध करा दिया गया है. इससे अतिरिक्त बॉयलरप्लेट कोड की ज़रूरत नहीं पड़ती.
सिस्टम सेवाओं के कोड के उलट, क्लाइंट-साइड कोड आम तौर पर कंपाइल-टाइम स्ट्रिंग इंटर्निंग को छोड़ देता है. इसके बजाय, स्ट्रिंग इंटर्निंग, बैकग्राउंड थ्रेड में डाइनैमिक तौर पर होती है. इस वजह से, क्लाइंट-साइड पर ProtoLog, सिस्टम सेवाओं पर ProtoLog की तुलना में मेमोरी के इस्तेमाल के मामले में बेहतर है. हालांकि, इसकी परफ़ॉर्मेंस में थोड़ा अंतर आता है. साथ ही, इसमें सर्वर-साइड पर मौजूद ProtoLog की तरह, पिन की गई मेमोरी को कम करने का फ़ायदा नहीं मिलता.
ProtoLog ग्रुप
ProtoLog मैसेज को ProtoLogGroups
नाम के ग्रुप में व्यवस्थित किया जाता है. यह ठीक उसी तरह होता है जिस तरह Logcat मैसेज को TAG
के हिसाब से व्यवस्थित किया जाता है. ये ProtoLogGroups
, मैसेज के क्लस्टर के तौर पर काम करते हैं. इन्हें रनटाइम में एक साथ चालू या बंद किया जा सकता है.
इसके अलावा, वे यह भी कंट्रोल करते हैं कि कंपाइल करने के दौरान मैसेज हटाए जाने चाहिए या नहीं. साथ ही, वे यह भी कंट्रोल करते हैं कि मैसेज को कहां लॉग किया जाना चाहिए (प्रोटो, लॉगकैट या दोनों). हर ProtoLogGroup
में ये प्रॉपर्टी शामिल होती हैं:
enabled
:false
पर सेट होने पर, इस ग्रुप के मैसेज को कंपाइल करने के दौरान शामिल नहीं किया जाता है. साथ ही, ये रनटाइम के दौरान उपलब्ध नहीं होते हैं.logToProto
: इससे यह तय होता है कि यह ग्रुप, बाइनरी फ़ॉर्मैट में लॉग करता है या नहीं.logToLogcat
: इससे यह तय होता है कि यह ग्रुप, logcat में लॉग करता है या नहीं.tag
: लॉग किए गए मैसेज के सोर्स का नाम.
ProtoLog का इस्तेमाल करने वाली हर प्रोसेस के लिए, ProtoLogGroup
इंस्टेंस कॉन्फ़िगर किया जाना चाहिए.
आर्ग्युमेंट के वे टाइप जो इसके साथ काम करते हैं
ProtoLog फ़ॉर्मैट की स्ट्रिंग, इंटरनल तौर पर android.text.TextUtils#formatSimple(String, Object...)
का इस्तेमाल करती हैं. इसलिए, इसका सिंटैक्स एक जैसा होता है.
ProtoLog में, इस तरह के आर्ग्युमेंट इस्तेमाल किए जा सकते हैं:
%b
- बूलियन%d
,%x
- इंटिग्रल टाइप (शॉर्ट, पूर्णांक या लंबा)%f
- फ़्लोटिंग पॉइंट टाइप (फ़्लोट या डबल)%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
ग्रुप होते हैं. इनकी मदद से, अलग-अलग तरह की ट्रांज़िशन लॉगिंग की जा सकती है. इनमें से WM_DEBUG_WINDOW_TRANSITIONS
ग्रुप डिफ़ॉल्ट रूप से चालू होता है.
ट्रेसिंग शुरू करते समय, Perfetto का इस्तेमाल करके ProtoLog को कॉन्फ़िगर किया जा सकता है.
adb
कमांड लाइन का इस्तेमाल करके, ProtoLog को स्थानीय तौर पर भी कॉन्फ़िगर किया जा सकता है.
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 व्यूअर
Winscope के ProtoLog व्यूअर टैब में, ProtoLog ट्रेस को टेबल के फ़ॉर्मैट में व्यवस्थित करके दिखाया जाता है. लॉग लेवल, टैग, सोर्स फ़ाइल (जहां ProtoLog स्टेटमेंट मौजूद है), और मैसेज के कॉन्टेंट के हिसाब से ट्रेस फ़िल्टर किए जा सकते हैं. सभी कॉलम को फ़िल्टर किया जा सकता है. पहले कॉलम में मौजूद टाइमस्टैंप पर क्लिक करने से, टाइमलाइन को मैसेज के टाइमस्टैंप पर ले जाया जाता है. इसके अलावा, मौजूदा समय पर जाएं पर क्लिक करने से, ProtoLog टेबल वापस उस टाइमस्टैंप पर स्क्रोल हो जाती है जिसे टाइमलाइन में चुना गया था:
पहली इमेज. ProtoLog व्यूअर