लॉगिंग बुरी तरह टूटी हुई है
(loggingsucks.com)- आधुनिक distributed systems में पारंपरिक logging तरीकों की ऐसी संरचनात्मक सीमाएँ हैं कि वे सच्चाई नहीं बता पाते
- logs अब भी 2005-स्टाइल single-server environment को मानकर डिज़ाइन किए गए हैं, इसलिए कई services, databases और cache से गुजरने वाले request का context खो जाता है
- साधारण string search structure, relation, correlation को नहीं समझती, जिससे समस्या की जड़ तक पहुँचना मुश्किल हो जाता है
- इसका समाधान है कि हर request के लिए सभी context वाला एक single ‘Wide Event(या Canonical Log Line)’ छोड़ा जाए
- इससे logs सिर्फ text नहीं रहते, बल्कि analyzable data asset में बदल जाते हैं
लॉगिंग की मूल समस्या
- मौजूदा logs monolithic server युग को ध्यान में रखकर बनाए गए थे, इसलिए वे आज की distributed service architecture को reflect नहीं करते
- एक request कई services, DB, cache और queue से होकर गुजरता है, लेकिन logs अब भी single server के हिसाब से लिखे जाते हैं
- उदाहरण logs में एक request पर 13 lines बनती हैं, इसलिए 10,000 concurrent users पर प्रति सेकंड 130,000 lines निकलती हैं, लेकिन इनमें से अधिकतर अर्थहीन जानकारी होती है
- समस्या आने पर सबसे ज़रूरी चीज़ context होती है, लेकिन मौजूदा logs में वही नहीं होता
string search की सीमाएँ
- जब कोई user रिपोर्ट करता है कि “payment नहीं हो रहा”, तब email या user_id से logs खोजने पर भी कोई consistent structure न होने के कारण उपयोगी नतीजे मिलना मुश्किल होता है
- एक ही user ID
user-123,user_id=user-123,{"userId":"user-123"}जैसी दर्जनों अलग forms में रिकॉर्ड हो सकती है
- एक ही user ID
- services के बीच log format अलग-अलग होने से related events को trace करना असंभव हो जाता है
- मूल समस्या यह है कि logs को write के लिए डिज़ाइन किया गया है, query के लिए optimize नहीं किया गया
मुख्य अवधारणाओं की परिभाषा
- Structured Logging: strings की जगह key-value (JSON) format में रिकॉर्ड करने का तरीका
- Cardinality: किसी field के unique values की संख्या; उदाहरण के लिए
user_idबहुत high होता है - Dimensionality: किसी log event में fields की संख्या; जितनी अधिक होगी, analysis की संभावना उतनी अधिक होगी
- Wide Event / Canonical Log Line: प्रति request एक context-rich single log event
- अधिकांश logging systems high-cardinality data को cost कारणों से सीमित करते हैं, जबकि वास्तव में debugging के लिए वही सबसे उपयोगी होता है
OpenTelemetry की सीमाएँ
- OpenTelemetry(OTel) एक protocol और SDKs का set है, जो सिर्फ data collection और transport का standard देता है
- लेकिन OTel यह काम नहीं करता
- क्या log करना है, यह तय नहीं करता
- business context (जैसे subscription tier, cart amount आदि) अपने आप नहीं जोड़ता
- developer की logging mindset नहीं बदलता
- एक ही library इस्तेमाल करने पर भी, जानबूझकर context जोड़कर की गई instrumentation और साधारण instrumentation के debugging experience में बहुत बड़ा फर्क होता है
- OTel सिर्फ plumbing है; उसमें क्या बहाना है, यह developer को तय करना होता है
Wide Event / Canonical Log Line तरीका
- पारंपरिक “code क्या कर रहा है” वाली logging से हटकर, “request के साथ क्या हुआ” यह रिकॉर्ड करना चाहिए
- हर request के लिए service स्तर पर एक व्यापक event बनाया जाए
- इसमें request, user, payment, error, environment आदि के 50 से अधिक fields शामिल हो सकते हैं
- उदाहरण JSON में
user_id,subscription_tier,service_version,error_codeजैसे debugging के लिए ज़रूरी सभी context शामिल होते हैं - इससे एक ही search में “premium users की payment failure का कारण” जैसी चीज़ों का तुरंत analysis संभव हो जाता है
Wide Event की query में उपयोगिता
- Wide Event को साधारण text search की तरह नहीं, बल्कि structured data query की तरह handle किया जाता है
- high-cardinality और high-dimensional data के आधार पर real-time analysis स्तर की debugging संभव होती है
- उदाहरण: “पिछले 1 घंटे में premium users की payment failure rate को error code के हिसाब से aggregate करो” जैसी query तुरंत चलाई जा सकती है
implementation pattern
- पूरे request lifecycle के दौरान event तैयार किया जाए, और अंत में सिर्फ एक बार output किया जाए
- middleware में
request_id,timestamp,method,pathजैसे base fields initialize करें - handler में user, cart, payment, error जानकारी को धीरे-धीरे जोड़ें
- middleware में
- आखिर में
logger.info(event)से एक single JSON event रिकॉर्ड करें
sampling से cost control
- प्रति request 50 से अधिक fields रिकॉर्ड करने पर cost तेज़ी से बढ़ सकती है, इसलिए sampling ज़रूरी है
- साधारण random sampling में errors छूट जाने का जोखिम रहता है
- Tail Sampling strategy का सुझाव
- errors (जैसे 500) हमेशा store करें
- slow requests (p99 से ऊपर) हमेशा store करें
- VIP users या specific flag sessions हमेशा store करें
- बाकी में सिर्फ 1~5% को random sample करें
- इससे cost reduction और critical events preservation दोनों साथ हासिल किए जा सकते हैं
आम गलतफहमियाँ
- Structured Logging ≠ Wide Event: सिर्फ JSON format काफी नहीं है, context ही असली चीज़ है
- OpenTelemetry का उपयोग ≠ complete observability: यह सिर्फ collection को standardize करता है, क्या रिकॉर्ड करना है यह developer पर निर्भर है
- यह Tracing जैसा नहीं है: tracing services के बीच flow दिखाता है, जबकि Wide Event service के अंदर का context देता है
- logs debugging के लिए, metrics dashboard के लिए जैसी विभाजन रेखा अनावश्यक है — Wide Event दोनों काम कर सकता है
- high-cardinality data महँगा होता है वाली सोच पुरानी हो चुकी है; ClickHouse, BigQuery जैसे modern DBs इसे efficiently handle कर सकते हैं
Wide Event अपनाने का असर
- debugging archaeology से analytics में बदल जाती है
- “user payment failure” खोजने के लिए 50 services के logs पर grep चलाने के बजाय,
“premium users की payment failure rate को error code के हिसाब से देखो” जैसे single-query based analysis पर शिफ्ट हो जाता है - नतीजतन logs झूठ बोलने वाले tool से, सच बताने वाले data asset में बदल जाते हैं
1 टिप्पणियां
Hacker News की राय
लेख पढ़ने में कठिन था और उसमें AI की मदद से लिखा गया सा एहसास था। फिर भी संदेश मूल्यवान था, बस यह थोड़ा और संक्षिप्त होता तो बेहतर रहता।
हाल में मेरे मन में आए कुछ बिंदु ये हैं।
इस विषय में Charity Majors का ज़िक्र किए बिना बात नहीं हो सकती। वह 10 साल से अधिक समय से “wide events” और “observability” की अवधारणाएँ फैलाती रही हैं, और Honeycomb.io को भी उसी दर्शन पर बनाया गया।
आजकल कई तरह के tools से इस तरीके को लागू किया जा सकता है। structured logs या traces का उपयोग करके wide events capture करना, और time-series, histogram जैसी समृद्ध visualization वाले tools का उपयोग करना महत्वपूर्ण है।
मैं लेख के कुछ दावों से सहमत हूँ, लेकिन सिर्फ एक single wide event छोड़ने के तरीके में जोखिम है। अगर request के बीच में exception या timeout हो जाए, तो कुछ भी रिकॉर्ड नहीं बचेगा।
भाषा के default logging framework या dependency logs भी छूट सकते हैं।
इसलिए इसे मौजूदा logs के ऊपर चढ़ाई जाने वाली एक अतिरिक्त layer के रूप में इस्तेमाल करना बेहतर है। request/session स्तर का ID रखकर ClickHouse जैसी जगह में aggregation किया जा सकता है।
log.error(data)औरwide_event.attach(error, data)मूल रूप से एक ही बात हैं।presentation और interactive examples शानदार थे। लेकिन आखिरकार बात “logs में structured tags जोड़ो” तक सिमट जाती है।
wide log में जटिलता और पठनीयता में गिरावट के मुकाबले फ़ायदा बहुत बड़ा नहीं लगा।
जब साधारण
grep "uid=user-123" application.logसे काम चल जाता है, तो क्या सचमुच उपयोगकर्ता की delivery method जैसी चीज़ भी जोड़ने की ज़रूरत है?(वैसे Android Brave browser में checkbox काम नहीं कर रहे थे)
grep '"uid": "user-123"'से खोजा जा सकता है।--contextoption से आसपास की lines भी देखी जा सकती हैं।semiconductor manufacturing environment में मैंने ऐसा system संभाला है जिसमें हज़ारों message bus participants थे। प्रति घंटा 300~400MB logs निकलते थे, लेकिन सिर्फ grep और CLI tools से भी सब संभालना काफ़ी था।
logs बस events की time-series थे, और विस्तृत analysis Oracle queries से किया जाता था। logs का काम घटनाओं के कारण-परिणाम संबंध को समझने का साधन होना है।
logs बताते हैं “कब, क्या हुआ”, जबकि “क्यों” का जवाब code, data और events के संयोजन से मिलता है।
व्यक्तिगत रूप से मुझे ELK stack जैसे interfaces सहज exploration के लिए असुविधाजनक लगते हैं। logs को सहज रूप से आगे बढ़ते हुए पढ़ना महत्वपूर्ण है।
लेख के अंत में दिया गया “हर error, exception, और slow request को log करो” वाला सुझाव खतरनाक सोच है।
उदाहरण के लिए, अगर कोई dependency धीमी हो जाए तो log volume 100 गुना तक बढ़ सकता है।
outage के समय service को कम काम करना चाहिए ताकि recovery आसान हो, लेकिन logs का विस्फोट उल्टा श्रृंखलाबद्ध विफलता पैदा कर सकता है।
log volume जितना बढ़ता है, sampling ratio अपने-आप समायोजित हो जाता है ताकि system overload न हो।
आधुनिक software में एक single log से “क्या हुआ” पूरी तरह समझाना मुश्किल है।
इसलिए vertical correlation और horizontal correlation दोनों की ज़रूरत होती है।
stack की ऊपर-नीचे वाली layers के बीच एक ही correlation value साझा होनी चाहिए, और systems के बीच communication में peers के बीच correlation चाहिए।
API या protocol में ऐसी values जोड़ना कठिन है, लेकिन अगर transaction ID पहले से design कर लिया जाए तो end-to-end tracing संभव हो जाती है।
सिर्फ एक लेख के लिए अलग domain register करना मुझे टिकाऊ नहीं लगता।
हर साल renewal fee देनी पड़ती है, इसलिए व्यक्तिगत blog या subdomain इस्तेमाल करना बेहतर लगता है।
उदाहरण के लिए logging-sucks.boristane.com जैसा रूप अच्छा है।
“logs monolithic युग की विरासत हैं” वाले दावे पर मेरा मानना है कि local logs अब भी उपयोगी हैं।
उनका मूल काम local process की बातचीत को रिकॉर्ड करना है, और दूसरे servers की स्थिति समझने के लिए transaction tracing चाहिए।
सही जगह के logs भर देखने से भी root cause तक पहुँचा जा सकता है।
समृद्ध context वाले logs analysis engine के साथ मिलकर product improvement में भी काम आ सकते हैं।
“code क्या कर रहा है” की बजाय “request के साथ क्या हुआ” इसे log करो — इस बात से मैं सहमत हूँ, लेकिन लेखक थोड़ा अनुभवहीन लगा।
मैं इसे “bug parts logging” कहता हूँ, और मेरा मानना है कि इसमें processing path, count, time जैसी पूर्व-संकेत वाली बातें शामिल होनी चाहिए।
logging, metrics या audit से अलग है। logging fail हो जाए तो processing जारी रहनी चाहिए, लेकिन audit fail होना गंभीर है।
SCADA systems के “historian” concept की तरह, observables और evaluatives में अंतर करना चाहिए।
उदाहरण के लिए fuel sensor के सूक्ष्म events diagnosis के लिए उपयोगी हो सकते हैं, लेकिन “क्या गंतव्य तक पहुँचा जा सकता है?” जैसे प्रश्न के लिए वे अनावश्यक हैं।
आखिरकार महत्वपूर्ण बात यह स्पष्ट करना है कि क्या observe करना है, और क्या evaluate करना है।
भले ही storage, transformation, और querying के तरीके अलग हों, consumption points और mechanisms को एक जैसा design किया जा सकता है।
इससे system design सरल हो जाता है, और लंबे समय तक रखे गए logs को बाद में फिर से process भी किया जा सकता है।