ओपनटेलीमेट्री ट्रेसिंग के मेरे डेमो में दो स्प्रिंग बूट घटक हैं। एक जावा एजेंट का उपयोग करता है, और जब मैंने हाल ही में इसे v1.x से v2.x में अपग्रेड किया तो मैंने एक अलग व्यवहार देखा। दूसरे में, मैं माइक्रोमीटर ट्रेसिंग का उपयोग कर रहा हूँ क्योंकि मैं GraalVM नेटिव में संकलित करता हूँ, और यह जावा एजेंटों को संसाधित नहीं कर सकता।
इस पोस्ट में, मैं इन तीन तरीकों की तुलना करना चाहता हूं: जावा एजेंट v1, जावा एजेंट v2, और माइक्रोमीटर ट्रेसिंग।
मैं उसी बेस एप्लीकेशन का उपयोग करूंगा: एक सरल स्प्रिंग बूट एप्लीकेशन, जिसे कोटलिन में कोड किया गया है। यह एक सिंगल एंडपॉइंट प्रदान करता है।
entry()
हैintermediate()
नामक एक अन्य फ़ंक्शन को कॉल करता हैRestTemplate
के प्रतिस्थापन, WebClient
इंस्टेंस का उपयोग करता हैentry()
फ़ंक्शन इसे ढूंढ लेता है, तो यह आगे नहीं बढ़ता है
इसका अनुवाद निम्नलिखित कोड में किया जाता है:
@SpringBootApplication class Agent1xApplication @RestController class MicrometerController { private val logger = LoggerFactory.getLogger(MicrometerController::class.java) @GetMapping("/{message}") fun entry(@PathVariable message: String, @RequestHeader("X-done") done: String?) { logger.info("entry: $message") if (done == null) intermediate() } fun intermediate() { logger.info("intermediate") RestClient.builder() .baseUrl("http://localhost:8080/done") .build() .get() .header("X-done", "true") .retrieve() .toBodilessEntity() } }
प्रत्येक सेटअप के लिए, मैं दो चरणों की जांच करूंगा: प्राथमिक चरण, जिसमें ओपनटेलीमेट्री सक्षम है, और अतिरिक्त आंतरिक स्पैन बनाने के लिए अनुकूलन चरण।
माइक्रोमीटर ट्रेसिंग की उत्पत्ति माइक्रोमीटर से हुई है, जो एक "विक्रेता-तटस्थ अनुप्रयोग अवलोकनीयता मुखौटा" है।
माइक्रोमीटर ट्रेसिंग सबसे लोकप्रिय ट्रेसर लाइब्रेरी के लिए एक सरल मुखौटा प्रदान करता है, जिससे आप अपने JVM-आधारित एप्लिकेशन कोड को विक्रेता लॉक-इन के बिना इंस्ट्रूमेंट कर सकते हैं। यह आपके ट्रेसिंग प्रयास की पोर्टेबिलिटी को अधिकतम करते हुए आपकी ट्रेसिंग संग्रह गतिविधि में बहुत कम या कोई ओवरहेड जोड़ने के लिए डिज़ाइन किया गया है।
माइक्रोमीटर ट्रेसिंग शुरू करने के लिए, कुछ निर्भरताएं जोड़ने की जरूरत है:
org.springframework.boot:spring-boot-starter-actuator
io.micrometer:micrometer-tracing
io.micrometer:micrometer-tracing-bridge-otel
io.opentelemetry:opentelemetry-exporter-otlp
हमें BOM की आवश्यकता नहीं है क्योंकि संस्करण पहले से ही स्प्रिंग बूट पैरेंट में परिभाषित हैं।
फिर भी, हमें दो रनटाइम कॉन्फ़िगरेशन पैरामीटर की आवश्यकता है: ट्रेस कहाँ भेजे जाने चाहिए, और घटक का नाम क्या है। वे MANAGEMENT_OTLP_TRACING_ENDPOINT
और SPRING_APPLICATION_NAME
चर द्वारा नियंत्रित होते हैं।
services: jaeger: image: jaegertracing/all-in-one:1.55 environment: - COLLECTOR_OTLP_ENABLED=true #1 ports: - "16686:16686" micrometer-tracing: build: dockerfile: Dockerfile-micrometer environment: MANAGEMENT_OTLP_TRACING_ENDPOINT: http://jaeger:4318/v1/traces #2 SPRING_APPLICATION_NAME: micrometer-tracing #3
परिणाम यह है:
बिना किसी अनुकूलन के, माइक्रोमीटर HTTP अनुरोध प्राप्त करने और भेजने के दौरान स्पैन बनाता है।
फ़्रेमवर्क को भेजने के लिए RestClient
में मैजिक इंजेक्ट करने की आवश्यकता होती है। इसके लिए हमें पहले वाले को बाद वाले को इंस्टेंटिएट करने देना चाहिए:
@SpringBootApplication class MicrometerTracingApplication { @Bean fun restClient(builder: RestClient.Builder) = builder.baseUrl("http://localhost:8080/done").build() }
हम कई तरीकों से मैन्युअल स्पैन बना सकते हैं, एक OpenTelemetry API के ज़रिए। हालाँकि, सेटअप के लिए बहुत सारे बॉयलरप्लेट कोड की आवश्यकता होती है। सबसे सीधा तरीका माइक्रोमीटर का ऑब्ज़र्वेशन API है। इसका मुख्य लाभ एक ही API का उपयोग करना है जो मेट्रिक्स और ट्रेस दोनों को प्रबंधित करता है।
अद्यतन कोड यहां है:
class MicrometerController( private val restClient: RestClient, private val registry: ObservationRegistry ) { @GetMapping("/{message}") fun entry(@PathVariable message: String, @RequestHeader("X-done") done: String?) { logger.info("entry: $message") val observation = Observation.start("entry", registry) if (done == null) intermediate(observation) observation.stop() } fun intermediate(parent: Observation) { logger.info("intermediate") val observation = Observation.createNotStarted("intermediate", registry) .parentObservation(parent) .start() restClient.get() .header("X-done", "true") .retrieve() .toBodilessEntity() observation.stop() } }
जोड़े गए अवलोकन कॉल उत्पन्न ट्रेसों पर प्रतिबिंबित होते हैं:
माइक्रोमीटर ट्रेसिंग का एक विकल्प जेनेरिक ओपनटेलीमेट्री जावा एजेंट है। इसका मुख्य लाभ यह है कि यह न तो कोड और न ही डेवलपर्स को प्रभावित करता है; एजेंट एक शुद्ध रनटाइम-स्कोप वाली चिंता है।
java -javaagent:opentelemetry-javaagent.jar agent-one-1.0-SNAPSHOT.jar
एजेंट पर्यावरण चर के साथ ओपनटेलीमेट्री के कॉन्फ़िगरेशन का पालन करता है:
services: agent-1x: build: dockerfile: Dockerfile-agent1 environment: OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4317 #1 OTEL_RESOURCE_ATTRIBUTES: service.name=agent-1x #2 OTEL_METRICS_EXPORTER: none #3 OTEL_LOGS_EXPORTER: none #4 ports: - "8081:8080"
/v1/traces
जोड़ती है
बिना किसी अतिरिक्त कॉन्फ़िगरेशन के, हमें निम्नलिखित निशान मिलते हैं:
एजेंट स्वचालित रूप से प्राप्त और भेजे गए अनुरोधों को ट्रैक करता है, साथ ही स्प्रिंग-संबंधित एनोटेशन के साथ चिह्नित फ़ंक्शन भी। कॉल स्टैक के अनुसार ट्रेस एक दूसरे के अंदर सही ढंग से नेस्ट किए गए हैं। अतिरिक्त फ़ंक्शन का पता लगाने के लिए, हमें अपने कोडबेस में एक निर्भरता जोड़ने की आवश्यकता है, io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations
। अब हम @WithSpan
एनोटेशन के साथ पहले से अनट्रेस किए गए फ़ंक्शन को एनोटेट कर सकते हैं।
value()
भाग ट्रेस के लेबल को नियंत्रित करता है, जबकि kind
span.kind
विशेषता के रूप में अनुवादित होता है। यदि मान को खाली स्ट्रिंग पर सेट किया जाता है, जो डिफ़ॉल्ट है, तो यह फ़ंक्शन का नाम आउटपुट करता है। मेरे उद्देश्यों के लिए, डिफ़ॉल्ट मान पर्याप्त हैं।
@WithSpan fun intermediate() { logger.info("intermediate") RestClient.builder() .baseUrl("http://localhost:8080/done") .build() .get() .header("X-done", "true") .retrieve() .toBodilessEntity() }
यह अपेक्षित नया intermediate()
ट्रेस उत्पन्न करता है:
ओपनटेलीमेट्री ने इस साल जनवरी में एजेंट का नया प्रमुख संस्करण जारी किया। मैंने इसके साथ अपना डेमो अपडेट किया; अब ट्रेस केवल तभी बनाए जाते हैं जब ऐप अनुरोध प्राप्त करता है और भेजता है।
पिछले संस्करण की तरह, हम @WithSpan
एनोटेशन के साथ ट्रेस जोड़ सकते हैं। एकमात्र अंतर यह है कि हमें entry()
फ़ंक्शन को भी एनोटेट करना होगा। यह डिफ़ॉल्ट रूप से ट्रेस नहीं किया जाता है।
स्प्रिंग दो कारणों से सफल हुआ: इसने जटिल समाधानों को सरल बनाया, यानी EJBs 2, और प्रतिस्पर्धी पुस्तकालयों पर एक अमूर्त परत प्रदान की। माइक्रोमीटर ट्रेसिंग की शुरुआत ज़िपकिन और जैगर पर एक अमूर्त परत के रूप में हुई, और यह पूरी तरह से समझ में आया। ओपनटेलीमेट्री को प्रोग्रामिंग भाषाओं और ट्रेस कलेक्टरों में अधिकांश पुस्तकालयों द्वारा समर्थित किए जाने के साथ यह तर्क बेमानी हो जाता है। अवलोकन API अभी भी माइक्रोमीटर ट्रेसिंग का एक बड़ा लाभ है, क्योंकि यह मेट्रिक्स और ट्रेस पर एकल API का उपयोग करता है।
जावा एजेंट की तरफ, ओपनटेलीमेट्री कॉन्फ़िगरेशन सभी तकनीकी स्टैक और लाइब्रेरीज़ - पर्यावरण चर में समान है। जब मैंने v1 से v2 में अपग्रेड किया तो मैं थोड़ा निराश था, क्योंकि नया एजेंट स्प्रिंग-अवेयर नहीं है: स्प्रिंग-एनोटेटेड फ़ंक्शन डिफ़ॉल्ट रूप से ट्रेस नहीं किए जाते हैं।
अंत में, यह एक बुद्धिमानी भरा निर्णय है। आप जो स्पैन चाहते हैं उसके बारे में स्पष्ट होना बेहतर है, बजाय इसके कि आप कुछ ऐसे स्पैन हटा दें जिन्हें आप नहीं देखना चाहते।
जोनाथन इवानोव को उनकी मदद और उनकी समीक्षा के लिए धन्यवाद ।
इस पोस्ट का पूरा स्रोत कोड GitHub पर पाया जा सकता है:
आगे जाने के लिए:
मूल रूप से 3 अगस्त, 2024 को A Java Geek पर प्रकाशित