المقاطع والتتبعات ونشر السياق
المقاطع والتتبعات ونشر السياق
التتبع الموزع ليس سجلاً أحادياً ضخماً — بل هو رسم بياني حلقي موجّه للمقاطع، مترابط بمعرّفات تنتقل مع كل طلب عبر كل حد خدمة. لاستخدام التتبع الموزع بفاعلية في بيئة الإنتاج، تحتاج إلى فهم نموذج البيانات بدقة: ما الذي يحتويه المقطع، وكيف تشكّل المقاطع شجرة، وكيف يُنشر سياق التتبع عبر نقاط الشبكة حتى تتمكن المقاطع المنشأة في عمليات مختلفة تمامًا من التجمّع في صورة متماسكة.
تشريح المقطع (Span)
المقطع (Span) هو الوحدة الذرية في التتبع الموزع. يمثّل وحدة عمل واحدة: معالج HTTP وارد، استدعاء gRPC صادر، استعلام قاعدة بيانات، بحث في ذاكرة التخزين المؤقت، خطوة في مهمة خلفية. كل مقطع يحمل مجموعة ثابتة من الحقول يحددها مواصفات OpenTelemetry:
- Trace ID — معرّف فريد عالميًا بحجم 128 بت (16 بايت) لرحلة الطلب بأكملها. تشترك جميع المقاطع التابعة للطلب نفسه في هذا المعرّف. يُرمَّز عادةً كسلسلة سداسية عشرية من 32 حرفًا:
4bf92f3577b34da6a3ce929d0e0e4736. - Span ID — معرّف فريد بحجم 64 بت (8 بايت) لهذا المقطع تحديدًا ضمن تتبّعه. يُرمَّز كـ 16 حرفًا سداسيًا:
00f067aa0ba902b7. - Parent Span ID — معرّف المقطع الأصلي المباشر. المقطع الجذري (نقطة الدخول) ليس له أصل (أو معرّف أصل يساوي صفرًا). هذا الحقل هو ما ينشئ شجرة الأصل-الفرع.
- اسم العملية — اسم قابل للقراءة يصف العمل:
HTTP GET /api/orders،db.query SELECT orders،redis.get order:8821. - وقت البداية — طابع زمني عالي الدقة (نانوثانية منذ بداية Unix).
- المدة — الوقت المنقضي من البداية حتى النهاية بالنانوثانية.
- الحالة — إحدى ثلاث قيم:
UNSETأوOKأوERROR. تعيينERRORعلى مقطع هو ما يجعله قابلاً للاكتشاف في واجهات الخلفية وسياسات أخذ العينات الذيلية. - السمات (Attributes) — أزواج مفتاح-قيمة من البيانات الوصفية المنظمة. يُعرّف OTel اتفاقيات دلالية للسمات الشائعة:
http.method،http.status_code،db.system،db.statement. أضف سماتك الخاصة:order.id،user.tier. - الأحداث (Events) — تعليقات ذات طوابع زمنية داخل مدة المقطع: تتبع مكدس الاستثناءات، إخفاقات ذاكرة التخزين المؤقت، محاولات إعادة المحاولة. ليست سجلات منفصلة — تعيش داخل المقطع.
- الروابط (Links) — مراجع إلى مقاطع في تتبعات أخرى، تُستخدم لقوائم انتظار الرسائل وسير العمل غير المتزامن.
- النوع (Kind) — تصنيف الدور:
SERVER،CLIENT،PRODUCER/CONSUMER،INTERNAL.
العلاقات الأصل-الفرع وشجرة التتبع
تشكّل المقاطع شجرة تتجذّر في مقطع واحد لنقطة الدخول. كل مقطع باستثناء الجذر له أصل واحد بالضبط. هذا الهيكل يمنحك عرض الشلال الذي تراه في Jaeger وTempo: مخطط زمني مرئي يُظهر أي المقاطع عملت بالتسلسل وأيها بالتوازي، وكم من إجمالي كمون الطلب أسهم كل مقطع.
تخيّل طلب دفع يتدفق عبر أربع خدمات. يُنشئ بوابة API المقطع الجذري. تستدعي خدمتين تاليتين بشكل متزامن — order-service وinventory-service — كل منها ينشئ مقطعًا فرعيًا. تستدعي order-service بعد ذلك قاعدة بيانات المدفوعات، مُنشئةً مقطعًا حفيدًا. الشجرة الناتجة لها أربعة عقد، ومدة الطلب الكلية يحددها المسار الحرج: أطول سلسلة متتابعة من المقاطع من الجذر إلى الورقة.
W3C Trace Context: رأسية traceparent
لكي تعمل التتبعات عبر حدود الخدمات، يجب أن ينتقل سياق التتبع مع الطلب. إذا أنشأت الخدمة A مقطعًا جذريًا وأجرت استدعاءً HTTP للخدمة B، يجب أن تستقبل الخدمة B معرّف التتبع ومعرّف المقطع الأصلي حتى يُربط المقطع الذي تنشئه بمقطع الخدمة A في التتبع نفسه. بدون هذا النشر، تحصل على جزر منفصلة من المقاطع — عديمة الفائدة لتحليل السبب الجذري.
يحدد معيار W3C Trace Context (RFC نُشر عام 2021، مدعوم الآن بشكل شامل من OTel وJaeger وZipkin وDatadog وجميع موردي APM الرئيسيين) رأستَين HTTP لهذا الغرض:
traceparent— تحمل السياق الأساسي: الإصدار ومعرّف التتبع ومعرّف المقطع الأصلي وأعلام التتبع.tracestate— أزواج مفتاح-قيمة اختيارية خاصة بالمورد (أولوية أخذ عينات Datadog، أعلام B3، إلخ) تنتقل جنبًا إلى جنب دون تعارض مع المعيار.
رأسية traceparent لها صيغة محددة بدقة: version-traceId-parentSpanId-flags. عمليًا تبدو هكذا:
01 في traceparent يُشير إلى "أخذت عينة من هذا التتبع — الخدمات التالية، يرجى أيضًا أخذ العينات والإبلاغ عن المقاطع." لكن للخدمة التالية حرية تجاهله. في الممارسة الإنتاجية، تحترم الأنظمة العلم لضمان جمع جميع مقاطع التتبع المأخوذ منه عينة. عندما يكون العلم 00، لا تُبلّغ الخدمات التالية عادةً عن مقاطع، مما يُبقي الحمل العام قريبًا من الصفر للحركة غير المأخوذ منها عينات. يتعامل OTel SDK مع كل هذا تلقائيًا عند استخدام W3CPropagator.
نشر السياق في الممارسة
نشر السياق هو الآلية التي يُحقن بها سياق التتبع في الطلبات الصادرة ويُستخرج من الواردة. يوفر OTel SDK واجهة propagator تتعامل مع الحقن والاستخراج لتنسيقات نقل مختلفة. مُنشر W3C Trace Context هو الافتراضي لـ HTTP. لقوائم انتظار الرسائل (Kafka، RabbitMQ، SQS)، تُوضع المعرّفات نفسها في رأسيات الرسالة أو سماتها.
أحداث المقاطع وسماتها: أنماط الإنتاج
ميزتان في المقاطع تُستخدمان باستمرار بشكل أقل من اللازم لكنهما حيويتان في الإنتاج: أحداث المقطع والسمات المختارة بعناية.
حدث المقطع هو تعليق ذو طابع زمني مرفق بمقطع. بدلاً من إصدار سطر سجل منفصل لـ "إخفاق ذاكرة التخزين المؤقت، العودة إلى قاعدة البيانات"، سجّله كحدث على المقطع النشط. يُبقي ذلك البيانات في مكان واحد — عندما تنظر إلى مقطع بطيء في واجهة التتبع، ترى بالضبط ما حدث ومتى، دون الحاجة إلى التحوّل إلى مخزن السجلات والربط بالطابع الزمني.
يجب أن تلتقط السمات السياق التجاري الذي يحوّل "استعلام قاعدة البيانات هذا كان بطيئًا" إلى "استعلام قاعدة البيانات هذا كان بطيئًا لمستخدمي Premium في ألمانيا الذين يطلبون أكثر من 50 عنصرًا." أضف بحد أقصى 20-30 سمة لكل مقطع — كل سمة مُفهرَسة في الخلفية ولها تكلفة تخزين. تجنّب القيم عالية القدرة الاستيعابية (أجسام استعلامات SQL الخام، أجسام استجابة HTTP الكاملة) كسمات مقطع؛ قُصّها أو احذفها عند الحاجة.
context.Context (Go) أو Context (Java) أو contextvars.Context (Python) بشكل صريح عبر الحدود غير المتزامنة. إذا بدأت goroutine أو مهمة pool خيوط، التقط سياق المقطع الحالي قبل الحد غير المتزامن واستعده بالداخل. لا يستطيع OTel SDK فعل ذلك تلقائيًا — وهو أحد أكثر الأسباب شيوعًا للتتبعات المعطوبة في الإنتاج حيث تكون روابط الأصل-الفرع مفقودة.
ما يُعطّل التتبعات في الإنتاج
فهم أنماط الفشل بنفس أهمية فهم المسار السعيد. الأسباب الشائعة للتتبعات المعطوبة أو غير المكتملة:
- نشر مفقود عند قفزة واحدة: خدمة واحدة — غالبًا نظام قديم أو موزّع حمل أو بوابة API — تحذف
traceparentأو تتجاهلها. جميع المقاطع التالية لا تزال تحمل معرّف التتبع لكن رابط أصلها يشير إلى مقطع لم تستقبله الخلفية أبدًا، مُنشئةً شجرة فرعية منفصلة. الإصلاح: تدقيق كل حد خدمة وكل تكوين HTTP proxy. - فقدان سياق غير متزامن: يبدأ مقطع في خيط A، يُقابَل العمل في خيط B، ويُنشئ خيط B مقاطع فرعية — لكن بدون نقل السياق عبر الحد غير المتزامن، يُنشئ خيط B مقطعًا جذريًا جديدًا بدلاً من فرعي. ينقسم التتبع إلى شجرتين غير مترابطتين.
- انحراف الساعة: تأتي الطوابع الزمنية للمقطع من المضيف الذي يعمل عليه SDK. إذا كان للمضيفين انجراف ساعة (NTP غير مكوّن)، تبدو المقاطع وكأنها تبدأ قبل انتهاء أصلها — حالة مستحيلة فيزيائيًا. إصلاح الإنتاج: تشغيل
chronyأوntpdعلى جميع العقد. - عدم تطابق أخذ العينات: أخذ عينات رأسي بمعدلات مختلفة لكل خدمة يعني أن الخدمة A تأخذ 10% والخدمة B تأخذ 5%. قد لا يأخذ تتبع مأخوذ عند A عينات عند B، مُنشئًا تتبعًا غير مكتمل. استخدم أخذ عينات ذيلي في طبقة Collector لاتخاذ قرار الاحتفاظ/الحذف مرة واحدة، مركزيًا، لكامل التتبع.
- إسقاط دفعات المقاطع تحت الحمل: يُجمّع OTel SDK المقاطع في الذاكرة قبل التصدير. تحت ذروة الحركة، إذا امتلأت قائمة انتظار الدفعات أسرع مما يمكن للمُصدِّر تفريغها، تُسقَط المقاطع. راقب
otelcol_exporter_send_failed_spans_totalفي Collector وحجّم ذاكرة المعالج الدفعي (queue_size) لذروة حمولتك.
في الدرس التالي ننتقل إلى معيار OpenTelemetry نفسه — نموذج مكوناته (SDK، API، Collector، الاتفاقيات الدلالية)، وكيف حقق الحياد من البائع، وكيفية تقييمه مقابل الوكلاء الخاصة مثل Datadog tracer أو Dynatrace OneAgent لخدمة جديدة أو ترحيل.