المرة الأولى التي تحتاج فيها حقًا إلى إمكانية الملاحظة ليست عندما تنظر بهدوء إلى لوحة القيادة. يحدث ذلك عندما يكتب المستخدم "الخروج بطيء"، ويبدو الرسم البياني للخطأ طبيعيًا وفي السجلات لا تجد سوى صف من الرسائل غير المتصلة.
تم إنشاء OpenTelemetry لتجنب تلك اللحظة: ليس للحصول على المزيد من الرسومات، ولكن لربط القطع. يدخل الطلب إلى API، ويستدعي قاعدة بيانات، ويمر عبر موفر خارجي، وينشر مهمة في قائمة الانتظار، وربما يفشل ثلاث خدمات لاحقًا. وبدون التتبع الموزع، يمكنك إعادة بناء تلك القصة يدويًا. مع OpenTelemetry على الأقل لديك خريطة.
المغزى ليس trace، بل القصة
أ trace هو تسلسل من span. ضع مثل هذا يبدو باردا. عمليًا، كل span جزء من القصة: POST /checkout، SELECT inventory، call payment provider، publish order.created.
القيمة تأتي عندما تبدأ في الإجابة على الأسئلة الحقيقية:
- ما هي الخدمة الخارجية التي تتباطأ؟
- هل الأخطاء تأتي من إصدار معين؟
- هل المشكلة تؤثر على الجميع أم على مستأجر واحد فقط؟
- هل إعادة المحاولة تخفي المهلة؟
- المهمة غير المتزامنة تبدأ ثم تنتهي في مكان آخر؟
لا يمكن حل هذه الأسئلة بإلقاء console.log على عجل. في الواقع، غالبًا ما يساعدك السجل المُضاف في حالات الطوارئ اليوم ويصبح ضجيجًا غدًا.
كيف أضع هذا في التطبيق Node.js
الإعداد الأكثر صحة بسيط: ينتج التطبيق القياس عن بعد، ويقرر Collector مكان إرساله.
Node.js app -> OpenTelemetry Collector -> backend di observability
لماذا لا يتم التصدير مباشرة إلى البائع؟ لأنه في البداية يبدو الأمر أسرع، ثم تدرك أن كل خدمة لها تكوينات مختلفة، وعمليات إعادة محاولة مختلفة، ومرشحات مختلفة، ولا توجد نقطة مركزية لإزالة البيانات الحساسة أو تغيير الوجهة.
Collector ممل بكل الطرق الصحيحة. يتلقى OTLP، ويقوم بالدفع، ويمكنه التصفية، ويمكنه أخذ العينات، ويمكنه إضافة سمات مشتركة ويمكنه التصدير إلى أنظمة متعددة.
القياس الذاتي: جيد، لكنه ليس كافياً
في Node.js سأبدأ بالأجهزة التلقائية. فهو يمنحك رؤية فورية لـ HTTP والأطر المدعومة وقواعد البيانات والمكتبات العامة.
npm install @opentelemetry/sdk-node \ @opentelemetry/auto-instrumentations-node \ @opentelemetry/exporter-trace-otlp-http
ثم تقوم بتهيئة SDK قبل بقية التطبيق:
import { NodeSDK } from '@opentelemetry/sdk-node'; import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; const sdk = new NodeSDK({ traceExporter: new OTLPTraceExporter({ url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, }), instrumentations: [getNodeAutoInstrumentations()], }); sdk.start();
ومع ذلك، هذا يرى الإطار، وليس المنتج الخاص بك. إنه يعرف أنك قمت بإجراء استعلام، لكنه لا يعرف أن الاستعلام كان في "إنشاء أمر" أو "تجديد الاشتراك". لذلك تحتاج إلى span أدلة في النقاط التي تكون فيها الهيمنة مهمة.
const span = tracer.startSpan('checkout.create_order'); try { span.setAttribute('cart.items_count', input.items.length); const order = await createOrder(input); span.setAttribute('order.id', order.id); return order; } catch (error) { span.recordException(error as Error); throw error; } finally { span.end(); }
لن أضع أدلة span في أي مكان. أود أن أضعهم في المكان الذي أرغب في فهم ما حدث فيه، في الساعة الثالثة صباحًا، دون قراءة نصف قاعدة التعليمات البرمجية.
ثلاث قواعد تجنبك الكثير من الفوضى
القاعدة الأولى: يجب أن تحتوي كل خدمة على service.name والبيئة والإصدار. يبدو الأمر تافهًا، ولكن بدون هذه السمات، يكون trace أقل فائدة بكثير. عندما يعطل النشر شيئًا ما، فأنت تريد التصفية حسب الإصدار خلال ثانيتين.
القاعدة الثانية: لا تضع بيانات حساسة في السمات. يجب ألا تنتهي رسائل البريد الإلكتروني والرموز المميزة والحمولات الصحيحة والعناوين في واجهة خلفية قابلة للملاحظة عن طريق الصدفة. إذا كنت بحاجة إلى تحديد هوية مستخدم، ففكر في المعرفات الداخلية أو التجزئة أو الحقول الأقل حساسية.
القاعدة الثالثة: الانتباه إلى الأصل. user.id كخاصية لـ trace يمكن أن يكون له معنى. كتسمية مترية، يمكن أن تدمر تكاليفك وأدائك.
المقاييس: قليلة، لكنها جيدة
سأبدأ بمقاييس عملية للغاية:
- معدلات والأخطاء ومدة الطلبات؛
- الكمون من التبعيات الخارجية؛
- عدد المهلات وإعادة المحاولة؛
- عمق ذيول.
- مدة الوظيفة؛
- نسبة الأخطاء لكل إصدار.
ويتم إضافة الباقي عند الحاجة. لوحات المعلومات المليئة بالرسوم البيانية التي لا ينظر إليها أحد هي أثاث وليست إمكانية للملاحظة.
السجلات: لا تزال مفيدة، ولكنها مرتبطة
السجلات لا تختفي. إنها ببساطة تصبح أكثر فائدة عندما تحمل trace_id وspan_id. لذا يمكنك البدء من سجل الأخطاء وفتح trace، أو البدء من trace البطيء وقراءة السجلات المنتجة في هذا المسار فقط.
دون الارتباط، كنت تبحث عن الإبر. مع الارتباط، على الأقل أنت تعرف أي درج يجب أن تنظر إليه.
القائمة المرجعية التي سأستخدمها قبل أن أقول "لقد قمنا بالتغطية"
- إن trace يعبر في الواقع خدمات متعددة.
- تتضمن السجلات
trace_idوspan_id. - تم تكوين Collector بحدود الدفع والذاكرة.
- يتم تسجيل الأخطاء في span.
- هناك سياسة أخذ العينات.
- المقاييس تسيطر على العلاقة الأساسية.
- تتم تصفية البيانات الحساسة.
- التنبيهات تبدأ من أعراض المستخدم، وليس من الرسوم البيانية العشوائية.
الخلاصة
OpenTelemetry لا يحل مشاكل الإنتاج من تلقاء نفسه. لكن طريقة تعاملك معهم تتغير. بدلاً من إضافة السجلات بشكل أعمى، يمكنك البدء باتباع المسار الفعلي للطلب.
بالنسبة لي، علامة نجاح الأمر بسيطة: عندما يحدث شيء ما، يتوقف الفريق عن التساؤل "أين نبحث؟" ويبدأ بالتساؤل "لماذا هذه القطعة بطيئة؟". وهنا تصبح إمكانية الملاحظة أداة، وليست مجموعة من لوحات المعلومات.