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 के सर्वर साइड के लिए बनाया गया था. यह एक ही प्रोसेस और कॉम्पोनेंट में काम करता है. बाद में, इसे System UI प्रोसेस में 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 ग्रुप अलग-अलग तरह की ट्रांज़िशन लॉगिंग की सुविधा चालू करते हैं. इनमें से पहले ग्रुप की सुविधा डिफ़ॉल्ट रूप से चालू होती है.
ट्रेसिंग शुरू करते समय, 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 व्यूअर