Node.js बैकएंड विकास बूटकैंप · पाठ

सहसंबंध आईडी के साथ संरचित लॉगिंग

मशीन द्वारा पढ़े जा सकने वाले JSON लॉग जारी कीजिए और अनुरोध ट्रेसिंग के लिए async संदर्भ में सहसंबंध आईडी आगे भेजिए।

पाठ 1, कुल 4 में से13 चरण

सहसंबंध आईडी के साथ संरचित लॉगिंग, CoddyKit पर Node.js बैकएंड विकास बूटकैंप का एक निःशुल्क पाठ है। यह 4 में से 1वाँ पाठ है। आप नीचे पूरा पाठ निःशुल्क पढ़ सकते हैं—फिर अंतर्निहित कोड संपादक और 24/7 एआई ट्यूटर के साथ ब्राउज़र में इसका व्यावहारिक अभ्यास कर सकते हैं। यह Node.js बैकएंड विकास बूटकैंप सीखने के मार्ग का हिस्सा है और आपकी प्रगति वेब तथा CoddyKit ऐप पर सिंक होती रहती है। Node.js बैकएंड विकास बूटकैंप पाठ्यक्रम में कुल 4 पाठ शामिल हैं।

संरचित लॉगिंग क्यों

उत्पादन में लॉग मनुष्यों से पहले मशीनें पढ़ती हैं। console.log('User ' + id + ' failed login') जैसी पंक्ति मुक्त-रूप स्ट्रिंग होती है: बाद में उसे ढूँढने के लिए आपको नाज़ुक रेगेक्स लिखना पड़ता है और आप विश्वसनीय ढंग से उसका समूह या फ़िल्टर नहीं बना सकते।

संरचित लॉगिंग हर लॉग को एकसमान फ़ील्ड वाले JSON ऑब्जेक्ट के रूप में जारी करती है। लॉग एग्रीगेटर (Loki, Elasticsearch, Datadog, CloudWatch) इन फ़ील्ड को अनुक्रमित करते हैं, इसलिए आप level="error" AND userId=42 को तुरंत क्वेरी कर सकते हैं।

  • मुक्त-रूप: लिखना आसान, क्वेरी करना कठिन।
  • संरचित: हर पंक्ति में एक JSON ऑब्जेक्ट (NDJSON), जिसे आसानी से पार्स किया जा सकता है।

न्यूनतम JSON लॉगर

मूल विचार छोटा है: एक साधारण ऑब्जेक्ट बनाएँ, उसमें स्तर और टाइमस्टैम्प अंकित करें और प्रति पंक्ति एक JSON स्ट्रिंग stdout पर लिखें। रनटाइम या कंटेनर stdout एकत्र करके उसे आपके एग्रीगेटर तक भेजता है।

हर लॉग वैध JSON की एकल पंक्ति होता है। इस प्रारूप को NDJSON (नई पंक्ति से अलग किया गया JSON) कहते हैं और हर आधुनिक लॉग बैकएंड इसे समझता है।

function log(level, message, fields = {}) {
  const entry = {
    level,
    time: new Date().toISOString(),
    message,
    ...fields,
  };
  process.stdout.write(JSON.stringify(entry) + '\n');
}

log('info', 'server started', { port: 3000 });
log('error', 'login failed', { userId: 42, reason: 'bad_password' });

वास्तविक लॉगर का उपयोग करें: pino

सीखने के लिए स्वयं बनाना ठीक है, लेकिन उत्पादन ऐप्स युद्ध-परीक्षित लॉगर का उपयोग करते हैं। pino Node.js के लिए वास्तविक मानक विकल्प है: यह बहुत तेज़ है, क्योंकि यह वर्कर-अनुकूल तरीके से JSON को क्रमबद्ध करता है और अतुल्यकालिक रूप से लिखता है।

  • logger.info(obj, msg) — पहला तर्क फ़ील्ड वाला ऑब्जेक्ट है और दूसरा संदेश स्ट्रिंग।
  • स्तर: trace, debug, info, warn, error, fatal।
  • उत्पादन में stdout को pino-pretty तक केवल विकास के दौरान पाइप करें; कच्चा JSON उत्पादन में जाए।
const pino = require('pino');
const logger = pino({ level: 'info' });

logger.info({ port: 3000 }, 'server started');
logger.error({ userId: 42, reason: 'bad_password' }, 'login failed');

// Child loggers bind fields onto every subsequent log:
const reqLog = logger.child({ requestId: 'abc-123' });
reqLog.info('handling request');

समस्या: एक अनुरोध का पता लगाना

लोड के दौरान सैकड़ों अनुरोधों के लॉग एक-दूसरे में मिल जाते हैं। जब उपयोगकर्ता 42 किसी त्रुटि की सूचना देता है, तो आपको मिडलवेयर, सेवाओं और डेटाबेस कॉल के बीच उसके अनुरोध से संबंधित हर लॉग पंक्ति देखनी होती है।

समाधान है correlation ID (जिसे request ID या trace ID भी कहते हैं): हर अनुरोध के लिए एक बार बनाया गया विशिष्ट पहचानकर्ता, जो उस अनुरोध को सँभालते समय जारी किए गए हर लॉग से जुड़ा होता है।

  • correlationId="7f3a..." को क्वेरी करने पर एग्रीगेटर पूरे अनुरोध की समयरेखा फिर बना देता है।
  • यदि ID incoming हेडर में आती है, तो आप सेवाओं के बीच लॉग का संबंध जोड़ सकते हैं।

ID बनाना और स्वीकार करना

अपने सिस्टम के किनारे पर, यदि कॉल करने वाले (गेटवे या अपस्ट्रीम सेवा) ने पहले से कोई सहसंबंध हेडर सेट किया है, तो उसे पढ़ें; अन्यथा नया UUID बनाएँ। उसे प्रतिक्रिया में हमेशा वापस भेजें, ताकि क्लाइंट और प्रॉक्सी भी उसे दर्ज कर सकें।

मानक हेडर नाम x-request-id या x-correlation-id हैं। इनपुट से मिले ID का दोबारा उपयोग करना ही सेवा सीमाओं के पार अनुरेखण को संभव बनाता है।

const { randomUUID } = require('crypto');

function correlationMiddleware(req, res, next) {
  const incoming = req.headers['x-correlation-id'];
  const correlationId = incoming || randomUUID();
  req.correlationId = correlationId;
  res.setHeader('x-correlation-id', correlationId);
  next();
}

module.exports = { correlationMiddleware };

सरल तरीका और उसकी समस्याएँ

सबसे स्पष्ट तरीका है कि req.correlationId को हर फ़ंक्शन और हर लॉग कॉल में तर्क के रूप में पास किया जाए। यह काम तो करता है, लेकिन बड़े सिस्टम में प्रभावी नहीं रहता: गहरी कॉल शृंखला (कंट्रोलर → सेवा → रिपॉज़िटरी → सहायक) आपको ID को उन फ़ंक्शनों के माध्यम से आगे बढ़ाने के लिए मजबूर करती है, जिन्हें अन्यथा इसके बारे में जानने की कोई आवश्यकता नहीं होती।

बैकएंड के लिए इसे प्रॉप ड्रिलिंग कहा जा सकता है। आप चाहते हैं कि अनुरोध के दौरान चल रहे किसी भी कोड को ID अप्रत्यक्ष रूप से उपलब्ध हो—हर फ़ंक्शन के हस्ताक्षर को बदले बिना।

AsyncLocalStorage से समाधान

Node.js में AsyncLocalStorage (async_hooks मॉड्यूल में) पहले से उपलब्ध है। यह ऐसा स्टोर बनाता है जो वर्तमान अतुल्यकालिक निष्पादन संदर्भ से जुड़ा रहता है—await, कॉलबैक, टाइमर और प्रॉमिस के पार भी बना रहता है—और समवर्ती अनुरोधों के बीच साझा नहीं होता।

इसे अनुरोध-सीमित थ्रेड-लोकल स्टोरेज की तरह समझें। आप प्रत्येक अनुरोध के लिए एक बार als.run(store, callback) कॉल करते हैं; उस कॉलबैक के भीतर कहीं भी (चाहे कितनी भी गहराई पर या कितने ही await के बाद) als.getStore() वही स्टोर लौटाता है।

const { AsyncLocalStorage } = require('async_hooks');
const als = new AsyncLocalStorage();

async function deep() {
  await new Promise((r) => setTimeout(r, 10));
  // Same store, even after awaits and timers:
  return als.getStore().correlationId;
}

async function main() {
  await als.run({ correlationId: 'req-1' }, async () => {
    console.log('inside:', await deep());
  });
  console.log('outside:', als.getStore());
}

main();

अनुरोध में AsyncLocalStorage जोड़ना

सरल मिडलवेयर को बदलें: ID को req में जोड़ने के बजाय, ID को स्टोर में रखकर बाकी अनुरोध को als.run() के अंदर चलाएँ। अब इस अनुरोध के लिए निष्पादित कोड की हर पंक्ति—कंट्रोलर, सेवाएँ और डेटाबेस कॉलबैक—getStore() के माध्यम से ID तक पहुँच सकती है।

महत्वपूर्ण बात यह है कि आपको next() को run कॉलबैक के अंदर कॉल करना होगा, ताकि आगे के हैंडलर इस संदर्भ को अपना सकें।

const { AsyncLocalStorage } = require('async_hooks');
const { randomUUID } = require('crypto');

const als = new AsyncLocalStorage();

function context() {
  return als.getStore() || {};
}

function correlationMiddleware(req, res, next) {
  const correlationId = req.headers['x-correlation-id'] || randomUUID();
  res.setHeader('x-correlation-id', correlationId);
  als.run({ correlationId }, () => next());
}

module.exports = { als, context, correlationMiddleware };

हर लॉग में ID अपने-आप जोड़ना

इसका लाभ यह है कि आप अपने लॉगर को इस तरह रैप कर सकते हैं कि वह AsyncLocalStorage से सहसंबंध ID अपने-आप पढ़ ले। एप्लिकेशन कोड log.info('saved order') को बिना किसी ID तर्क के कॉल करता है, फिर भी निकलने वाली हर पंक्ति में सही correlationId शामिल होता है।

pino के साथ आप इसे mixin विकल्प का उपयोग करके घोषणात्मक रूप से व्यक्त करते हैं। यह लिखे जाने के समय अतिरिक्त फ़ील्ड को हर लॉग रिकॉर्ड में मिला देता है।

const pino = require('pino');
const { als } = require('./context');

const logger = pino({
  level: 'info',
  mixin() {
    const store = als.getStore();
    return store ? { correlationId: store.correlationId } : {};
  },
});

// Anywhere deep in the request, no ID passed explicitly:
function saveOrder(order) {
  logger.info({ orderId: order.id }, 'order saved');
}

module.exports = { logger, saveOrder };

डाउनस्ट्रीम कॉल तक ID पहुँचाना

सहसंबंध तभी सेवाओं के बीच फैलता है, जब आप आउटबाउंड अनुरोधों में ID आगे भेजते हैं। जब आपकी सेवा किसी अन्य HTTP API को कॉल करे, तो संदर्भ से ID पढ़कर उसे हेडर के रूप में सेट करें। डाउनस्ट्रीम सेवा का मिडलवेयर उसका दोबारा उपयोग करेगा, इसलिए दोनों सेवाओं के लॉग में एक ही ID होगी।

यही सिद्धांत संदेश कतारों पर भी लागू होता है (ID को संदेश मेटाडेटा में रखें) और बैकग्राउंड कार्यों पर भी (इसे कार्य पेलोड में संग्रहीत करें)।

const { context } = require('./context');

async function callInventoryService(sku) {
  const { correlationId } = context();
  const res = await fetch('https://inventory.internal/check', {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      'x-correlation-id': correlationId,
    },
    body: JSON.stringify({ sku }),
  });
  return res.json();
}

module.exports = { callInventoryService };

उत्पादन परिवेश की स्वच्छता

कुछ नियम आपके संरचित लॉग को साफ़ और सुरक्षित बनाए रखते हैं:

  • गोपनीय जानकारी या PII को कभी लॉग न करें — पासवर्ड, टोकन और पूरे कार्ड नंबर। pino में redact पथ कॉन्फ़िगर करें (जैसे ['req.headers.authorization', '*.password'])।
  • उचित स्तर पर लॉग करें — व्यावसायिक घटनाओं के लिए info, सुधारी जा सकने वाली समस्याओं के लिए warn, और विफलताओं के लिए क्रमबद्ध त्रुटि ऑब्जेक्ट के साथ error।
  • फ़ील्ड नाम स्थिर रखें — correlationId का अर्थ हमेशा एक ही होना चाहिए; डैशबोर्ड और अलर्ट इसी पर निर्भर करते हैं।
  • हर पंक्ति में एक JSON ऑब्जेक्ट रखें — उत्पादन में सुंदर फ़ॉर्मैट में प्रिंट न करें; इससे NDJSON का विश्लेषण टूट जाता है।
const pino = require('pino');

const logger = pino({
  level: process.env.LOG_LEVEL || 'info',
  redact: ['req.headers.authorization', 'password', '*.password', 'creditCard'],
});

logger.info({ user: { id: 7, password: 'hunter2' } }, 'login');
// -> password is replaced with [Redacted] in the output

त्वरित जाँच

आपको अनुरोध के दौरान गहरी कॉल शृंखला के हर फ़ंक्शन के लिए सहसंबंध ID उपलब्ध चाहिए, इसे तर्क के रूप में पास किए बिना और समवर्ती अनुरोधों के बीच लीक किए बिना। Node.js में सही तरीका क्या है?

पुनरावलोकन

आपने सीखा कि लॉग को मशीन द्वारा विश्लेषण योग्य और अनुरेखण योग्य कैसे बनाया जाता है:

  • संरचित लॉगिंग हर पंक्ति में एक JSON ऑब्जेक्ट (NDJSON) लिखती है, ताकि एग्रीगेटर फ़ील्ड को अनुक्रमित और क्वेरी कर सकें।
  • pino Node.js का मानक तेज़ लॉगर है; फ़ील्ड ऑब्जेक्ट पहले और संदेश दूसरे स्थान पर आता है।
  • सहसंबंध ID प्रत्येक अनुरोध के लिए इनपुट हेडर से बनाई या दोबारा उपयोग की जाती है और प्रतिक्रिया में वापस भेजी जाती है।
  • AsyncLocalStorage फ़ंक्शन हस्ताक्षर बदले बिना उस ID को पूरी अतुल्यकालिक कॉल शृंखला में पहुँचाता है और समवर्ती अनुरोधों को अलग रखता है।
  • pino का mixin ID को हर लॉग में अपने-आप जोड़ता है; इसे आउटबाउंड हेडर के रूप में आगे भेजने से सेवाओं के बीच अनुरेखण बढ़ता है।
  • अच्छी स्वच्छता अपनाएँ: गोपनीय जानकारी को redact करें, सही स्तरों का उपयोग करें, फ़ील्ड नाम स्थिर रखें और उत्पादन में कभी सुंदर फ़ॉर्मैट में प्रिंट न करें।
शुरुआत निःशुल्क

एआई शिक्षक के साथ JavaScript सीखें — निःशुल्क

अपने ब्राउज़र में वास्तविक कोड लिखें और चलाएँ, चौबीसों घंटे एआई शिक्षक से तुरंत सहायता पाएँ, और वेब या ऐप पर वहीं से शुरू करें जहाँ आपने छोड़ा था।

पाठ्यक्रम
22
पाठ
92

अक्सर पूछे जाने वाले प्रश्न

क्या “सहसंबंध आईडी के साथ संरचित लॉगिंग” पाठ निःशुल्क है?

हाँ—“सहसंबंध आईडी के साथ संरचित लॉगिंग” का पूरा पाठ यहाँ वेब पर निःशुल्क पढ़ा जा सकता है। इंटरैक्टिव अभ्यास (अंतर्निहित कोड संपादक और 24/7 एआई ट्यूटर) करने और Node.js बैकएंड विकास बूटकैंप पाठ्यक्रम का बाकी हिस्सा अनलॉक करने के लिए CoddyKit PRO लें। Node.js बैकएंड विकास बूटकैंप पाठ्यक्रम में कुल 4 पाठ शामिल हैं।

“सहसंबंध आईडी के साथ संरचित लॉगिंग” में मैं क्या सीखूँगा?

मशीन द्वारा पढ़े जा सकने वाले JSON लॉग जारी कीजिए और अनुरोध ट्रेसिंग के लिए async संदर्भ में सहसंबंध आईडी आगे भेजिए। आप ब्राउज़र में सीधे चलाए जाने वाले व्यावहारिक कोड के साथ Node.js बैकएंड विकास बूटकैंप का अभ्यास करते हैं, और पाठ पूरा करते समय 24/7 एआई ट्यूटर आपके प्रश्नों के उत्तर देता है।

क्या Node.js बैकएंड विकास बूटकैंप शुरू करने के लिए मुझे किसी अनुभव की आवश्यकता है?

पहले के अनुभव की आवश्यकता नहीं है। CoddyKit पर Node.js बैकएंड विकास बूटकैंप शुरुआती से लेकर उन्नत शिक्षार्थियों तक सभी के लिए व्यवस्थित किया गया है, इसलिए आप यहीं से या शुरुआत से सीखना शुरू कर सकते हैं और अपनी गति से आगे बढ़ सकते हैं। यह 4 में से 1वाँ पाठ है।

“सहसंबंध आईडी के साथ संरचित लॉगिंग” पाठ पूरा करने में कितना समय लगता है?

CoddyKit का अधिकांश पाठ लगभग 5–10 मिनट में पूरा हो जाता है। हर पाठ छोटा और संवादात्मक है, इसलिए आप लगातार प्रगति करते हैं और वेब या ऐप पर वहीं से सीखना जारी रख सकते हैं जहाँ आपने छोड़ा था।

क्या मैं इस Node.js बैकएंड विकास बूटकैंप पाठ में कोड लिख और चला सकता हूँ?

हाँ। हर Node.js बैकएंड विकास बूटकैंप पाठ में एक अंतर्निर्मित कोड संपादक शामिल है, जिससे आप सीधे अपने ब्राउज़र में वास्तविक कोड लिख और चला सकते हैं और तुरंत एआई प्रतिक्रिया पा सकते हैं—स्थानीय सेटअप की आवश्यकता नहीं है।

इस पाठ्यक्रम के सभी पाठ

  1. सहसंबंध आईडी के साथ संरचित लॉगिंग
  2. OpenTelemetry स्पैन से वितरित ट्रेसिंग
  3. ऐप्लिकेशन मेट्रिक्स उजागर करना और RED पद्धति
  4. AsyncLocalStorage से संदर्भ प्रसार
← Node.js बैकएंड विकास बूटकैंप पर वापस जाएँ