Kotlin अनुप्रयोगों के लिए observability क्यों आवश्यक है
\n\nकल्पना करें: आपका Kotlin एप्लिकेशन प्रोडक्शन में चल रहा है, और अचानक उपयोगकर्ता शिकायत करते हैं कि अनुरोध 10 सेकंड में पूरे हो रहे हैं। आप लॉग में जाते हैं - सन्नाटा। मेट्रिक्स 100% CPU दिखाती हैं, लेकिन यह नहीं बताती कि कौन सा एंडपॉइंट धीमा है। परिचित लगता है?
\n\nउस दुनिया में आपका स्वागत है जहाँ observability (अवलोकनीयता) कोई विकल्प नहीं, बल्कि एक आवश्यकता है। पारंपरिक निगरानी के विपरीत, जो केवल "सिस्टम जीवित है या नहीं" प्रश्न का 'हाँ/नहीं' में उत्तर देती है, observability आपको उन प्रश्नों को पूछने की अनुमति देती है जिनके बारे में आपने पहले सोचा भी नहीं था। और Kotlin, अपनी संक्षिप्तता और शक्तिशाली इकोसिस्टम के साथ, इस संस्कृति को लागू करने के लिए आदर्श है।
\n\nइस लेख में, हम observability के तीन स्तंभों - ट्रेसिंग, मेट्रिक्स और लॉग - का विश्लेषण करेंगे और दिखाएंगे कि उन्हें Kotlin के साथ कैसे जोड़ा जाए। कोई अनावश्यक बात नहीं, केवल वास्तविक कोड उदाहरण।
\n\nट्रेसिंग: माइक्रोसर्विसेज के माध्यम से अनुरोध की यात्रा
\n\nवितरित ट्रेसिंग (tracing) आपके अनुरोध के लिए GPS की तरह है। आप देखते हैं कि अनुरोध किन सेवाओं से होकर गुज़रा, प्रत्येक चरण में कितना समय लगा और देरी कहाँ हुई। Kotlin के लिए इस काम में सबसे अच्छा साथी OpenTelemetry (OTel) है।
\n\nKotlin में OpenTelemetry सेटअप करना
\n\nbuild.gradle.kts में निर्भरताएँ जोड़ें:
dependencies {\n implementation("io.opentelemetry:opentelemetry-api:1.36.0")\n implementation("io.opentelemetry:opentelemetry-sdk:1.36.0")\n implementation("io.opentelemetry:opentelemetry-exporter-otlp:1.36.0")\n implementation("io.opentelemetry:opentelemetry-extension-kotlin:1.36.0")\n}\n\nअब एक सरल ट्रेसर बनाते हैं:
\n\nimport io.opentelemetry.api.GlobalOpenTelemetry\nimport io.opentelemetry.api.trace.Span\nimport io.opentelemetry.api.trace.Tracer\nimport io.opentelemetry.context.Scope\n\nclass OrderService(private val tracer: Tracer = GlobalOpenTelemetry.getTracer("order-service")) {\n\n fun processOrder(orderId: String) {\n val span: Span = tracer.spanBuilder("processOrder")\n .setAttribute("order.id", orderId)\n .startSpan()\n\n span.use { // use() स्वचालित रूप से span को बंद करेगा\n try {\n // कार्य का अनुकरण\n Thread.sleep(100)\n validateOrder(orderId)\n chargePayment(orderId)\n } catch (e: Exception) {\n span.recordException(e)\n span.setStatus(StatusCode.ERROR, "Order processing failed")\n throw e\n }\n }\n }\n\n private fun validateOrder(orderId: String) {\n val span: Span = tracer.spanBuilder("validateOrder")\n .setAttribute("order.id", orderId)\n .startSpan()\n\n span.use {\n Thread.sleep(50) // सत्यापन का अनुकरण\n }\n }\n\n private fun chargePayment(orderId: String) {\n val span: Span = tracer.spanBuilder("chargePayment")\n .setAttribute("order.id", orderId)\n .startSpan()\n\n span.use {\n Thread.sleep(150) // भुगतान का अनुकरण\n }\n }\n}\n\nspan.use पर ध्यान दें - यह opentelemetry-extension-kotlin से एक एक्सटेंशन फ़ंक्शन है, जो ब्लॉक के निष्पादन के बाद स्वचालित रूप से span को बंद कर देता है। कार्य में Kotlin की सुंदरता!
कोरूटीन के माध्यम से संदर्भ संचरण
\n\nKotlin में हम सक्रिय रूप से कोरूटीन का उपयोग करते हैं। OpenTelemetry Context तंत्र के माध्यम से उनके साथ अच्छी तरह से काम करता है:
\n\nimport kotlinx.coroutines.withContext\nimport io.opentelemetry.api.trace.Span\nimport io.opentelemetry.context.Context\n\nsuspend fun processOrderAsync(orderId: String) = withContext(Context.current()) {\n val span = tracer.spanBuilder("processOrderAsync").startSpan()\n span.use {\n // आपका अतुल्यकालिक तर्क\n }\n}\n\nट्रेसिंग पूरी तस्वीर प्रकट करती है: आप देखते हैं कि 80% समय भुगतान पर खर्च होता है