لو الـ POST /checkout بقى 1.8 ثانية بدل 400ms فجأة وعندك 8 microservices، انت قدام أصعب نوع debugging. الـ logs مفرّقة على 8 سيرفرات، كل log فيه trace_id مختلف، وفريقك بياخد 142 دقيقة في المتوسط علشان يلاقي الـ bottleneck. OpenTelemetry بـ 12 سطر إعداد بيوريك الطلب كامل كـ waterfall في Jaeger، وبيخلّي وقت التشخيص ينزّل لـ 9 دقائق على نفس الحادثة.
المشكلة باختصار: ليه logs مش كفاية مع microservices
لما كان عندك monolith، الـ stack trace كان بيقولك بالظبط فين الـ slowdown. مع microservices، الطلب الواحد بيمر على 5 إلى 12 خدمة. كل خدمة بتكتب logs محلية، وكل log بيحط timestamp بتوقيت السيرفر بتاعه (اللي ممكن يكون فارق عن غيره بـ 200ms بسبب NTP drift).
المثال الواقعي قبل ما ندخل في الشرح
تخيل شركة شحن. الزبون بيطلب طرد. الموظف بياخد الطلب، يدّيه للسائق، السائق يوصّله لمحطة التوزيع، محطة التوزيع تبعته لمحطة تانية، وهكذا لحد ما يوصل البيت. لو الطرد اتأخر 3 ساعات، انت محتاج تعرف عند مين بالظبط اتعطّل. لو كل موظف بس بيكتب في دفتره الخاص "استلمت 10:14 وسلّمت 10:19"، انت محتاج تجمع 8 دفاتر وتقارن. ده بيستهلك وقت طويل.
Distributed Tracing هو إن كل موظف يكتب في نفس الورقة (نفس الـ trace_id) مع وقته الخاص. في الآخر بتبص على الورقة الواحدة دي وتشوف الرحلة كاملة في 30 ثانية.
التعريف العلمي بدقة
Distributed Tracing هو نظام بيتبع وحدة واحدة من العمل (request, message, job) عبر عدة خدمات. مبني على ورقة Google Dapper سنة 2010 (Sigelman et al.) اللي قدّمت مفهومين أساسيين:
- Trace: العملية الكاملة من الأول للآخر. لها
trace_idفريد. - Span: وحدة عمل واحدة جوّا الـ trace (مثلاً: استدعاء قاعدة بيانات، طلب HTTP، حساب). كل span له
span_idوparent_span_id.
الـ trace_id بينتقل بين الخدمات عبر HTTP header اسمه traceparent (W3C Trace Context Standard 2021). أي خدمة تستلم الـ header دي بتعرف إنها جزء من نفس الـ trace، وبتبني span جوّاها بـ parent بيشاور على الـ span اللي قبلها.
الحل: إعداد OpenTelemetry في 12 سطر
OpenTelemetry (OTel) هو معيار CNCF موحّد للـ telemetry data (traces, metrics, logs). دلوقتي هو الـ standard الفعلي بعد ما اتدمج مع OpenTracing و OpenCensus سنة 2019.