مقاييسنا كانت جزرًا معزولة وسجلاتنا صرخة في وادٍ: كيف أنقذنا OpenTelemetry من جحيم تتبع الأخطاء؟

“يا جماعة، السيرفر واقع!”… قصة من قلب المعركة

أذكرها وكأنها البارحة. كانت ليلة هادئة، وفجأة، بدأت التنبيهات تنهال علينا كالمطر. “Error rate is high on checkout service”، “Payment gateway latency > 2s”. ولّعت الدنيا، وقاعة “War Room” الافتراضية على “سلاك” امتلأت بالمهندسين. المشكلة كانت واضحة: المستخدمون لا يستطيعون إتمام عمليات الدفع.

لكن… أين بالضبط تكمن المشكلة؟

بدأت رحلة العذاب. فريق الواجهة الأمامية (Frontend) يقول: “الـ API الخاص بخدمة الطلبات (Orders Service) يرجع خطأ 500”. فريق خدمة الطلبات يفتح سجلاته (logs) ويقول: “لا يوجد خطأ عندنا، لكننا ننتظر ردًا طويلاً من خدمة الدفع (Payment Service)”. فريق خدمة الدفع، وهم في عالم آخر، يقولون: “مقاييسنا (metrics) تبدو طبيعية، لكن لحظة… نرى بعض الأخطاء المتفرقة في السجلات النصية التي لا نراقبها عادةً”.

قضينا ساعات، يا جماعة الخير، ونحن نقفز بين عشرات لوحات المراقبة (Dashboards)، ونبحث يدويًا في ملفات سجلات ذات تنسيقات مختلفة، محاولين ربط “معرّف طلب” (Request ID) معين عبر ثلاث خدمات مختلفة. كل خدمة كانت جزيرة منعزلة، لها لغتها ومقاييسها الخاصة. سجلاتنا كانت مجرد صرخات متفرقة في وادٍ سحيق، لا أحد يسمعها أو يربطها ببعضها. في تلك الليلة، أدركنا أن طريقتنا في المراقبة كانت هي المشكلة الحقيقية.

هذه الفوضى هي التي دفعتنا للبحث عن حل جذري، حل يوحد اللغة ويحول هذه الجزر المعزولة إلى قارة واحدة متصلة. هذا الحل كان اسمه: OpenTelemetry.

ما هي “القابلية للمراقبة” (Observability) ولماذا هي ليست مجرد “مراقبة” (Monitoring)؟

قبل أن نغوص في تفاصيل OpenTelemetry، دعونا نوضح مفهوماً أساسياً. كثيرون يخلطون بين المراقبة (Monitoring) والقابلية للمراقبة (Observability).

  • المراقبة (Monitoring): هي أن تراقب أشياء تعرف مسبقًا أنها قد تفشل. أنت تضع تنبيهات على استخدام المعالج (CPU)، استهلاك الذاكرة، ومعدل الأخطاء. إنها تجيب على سؤال: “هل النظام يعمل كما هو متوقع؟”.
  • القابلية للمراقبة (Observability): هي القدرة على فهم الحالة الداخلية لنظامك من خلال البيانات التي يصدرها، حتى لو لم تكن تتوقع المشكلة مسبقًا. إنها تجيب على سؤال: “لماذا لا يعمل النظام كما هو متوقع؟”.

تعتمد القابلية للمراقبة على ثلاثة أعمدة رئيسية:

  1. السجلات (Logs): سجلات زمنية للأحداث. تخبرك “ماذا حدث” في نقطة معينة.
  2. المقاييس (Metrics): بيانات رقمية مجمّعة على فترة زمنية. تخبرك “كيف هي صحة النظام” بشكل عام (مثل: عدد الطلبات في الدقيقة).
  3. التتبعات (Traces): تمثل رحلة طلب واحد عبر الخدمات المختلفة في نظامك. تخبرك “أين وكيف” حدثت المشكلة خلال هذه الرحلة.

في عالم الخدمات المصغرة (Microservices)، تتبع رحلة طلب واحد (Trace) هو أهم عمود، لأنه يربط كل شيء ببعضه. وهنا كانت مشكلتنا: كل خدمة تتحدث لغة مختلفة، مما جعل بناء “تتبع” متكامل شبه مستحيل.

المنقذ OpenTelemetry: توحيد اللغة وإنهاء الفوضى

OpenTelemetry، أو “OTel” كما نحب أن نسميه، ليس أداة أو منتجًا بحد ذاته. إنه معيار ومجموعة من الأدوات والـ SDKs لإنشاء وجمع وتصدير بيانات المراقبة (السجلات والمقاييس والتتبعات).

الفكرة عبقرية في بساطتها: بدلاً من أن يقوم كل تطبيق بإنشاء بيانات المراقبة بتنسيق خاص وإرسالها إلى أداة محددة (مثل Datadog أو Jaeger)، يقوم التطبيق باستخدام OTel SDK لإنشاء البيانات بتنسيق موحد ومفتوح. ثم يمكنك أن تقرر لاحقًا إلى أين تريد إرسال هذه البيانات.

نصيحة من أبو عمر: أجمل ما في OpenTelemetry هو أنه يحررك من قيود الشركات (Vendor Lock-in). يمكنك اليوم استخدام Jaeger كـ Backend للتتبعات، وغدًا تقرر الانتقال إلى New Relic أو بناء نظامك الخاص، وكل ذلك دون تغيير سطر كود واحد في تطبيقاتك الأساسية. هذه مرونة لا تقدر بثمن.

كيف يعمل OpenTelemetry؟

يتكون نظام OTel البيئي من عدة أجزاء رئيسية:

  • API: واجهة برمجية مجردة (Abstract) تستخدمها في الكود الخاص بك لإنشاء التتبعات والمقاييس.
  • SDK: التنفيذ الفعلي للـ API. هو الذي يهتم بتفاصيل إنشاء البيانات وتجميعها وإرسالها.
  • Exporter: قطعة برمجية مسؤولة عن تصدير البيانات من الـ SDK إلى نظام مراقبة معين (مثل Jaeger Exporter, Prometheus Exporter).
  • Collector (المجمّع): مكون اختياري ولكنه قوي جدًا. وهو عبارة عن بروكسي يمكنه استقبال البيانات من عدة مصادر، معالجتها (مثل إزالة معلومات حساسة)، ثم تصديرها إلى وجهات متعددة.

لنطبق عمليًا: رحلة مع OTel في عالم Node.js

الكلام النظري جميل، لكن دعونا نرى كيف يبدو هذا على أرض الواقع. سأستخدم مثالاً بسيطًا لتطبيق Express.js في Node.js.

الخطوة 1: الإعداد الأساسي و”السحر” التلقائي

أحد أروع جوانب OTel هو “Instrumentation التلقائي”. يمكنك إضافة مكتبات تقوم تلقائيًا بإنشاء تتبعات للمكتبات الشائعة مثل Express, HTTP, PostgreSQL وغيرها دون تدخل منك.

لنفترض أن لدينا ملف `instrumentation.js` بسيط:


// instrumentation.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { ConsoleSpanExporter } = require('@opentelemetry/sdk-trace-node');
const {
  getNodeAutoInstrumentations,
} = require('@opentelemetry/auto-instrumentations-node');
const {
  PeriodicExportingMetricReader,
  ConsoleMetricExporter,
} = require('@opentelemetry/sdk-metrics');

const sdk = new NodeSDK({
  traceExporter: new ConsoleSpanExporter(), // الآن سنطبع التتبعات على الكونسول فقط
  metricReader: new PeriodicExportingMetricReader({
    exporter: new ConsoleMetricExporter(), // ونطبع المقاييس أيضًا
  }),
  instrumentations: [getNodeAutoInstrumentations()], // هذا هو السحر!
});

sdk.start();

ثم نقوم بتشغيل تطبيق Express الخاص بنا مع هذا الملف:


node --require ./instrumentation.js app.js

الآن، وبدون إضافة أي كود تتبع داخل `app.js`، أي طلب HTTP يصل إلى تطبيقنا سيتم إنشاء “تتبع” (Trace) و”نطاق” (Span) له تلقائيًا! إذا كان تطبيقنا يتصل بقاعدة بيانات، فسيتم إنشاء نطاق فرعي لمكالمة قاعدة البيانات أيضًا. كل هذا بشكل تلقائي.

الخطوة 2: إضافة تتبعات يدوية لسياق أعمق

التتبع التلقائي رائع، لكن أحيانًا نحتاج إلى تفاصيل أكثر حول منطق العمل (Business Logic) الخاص بنا. هنا يأتي دور التتبع اليدوي.

لنفترض أن لدينا دالة مهمة تقوم بمعالجة معينة، ونريد أن نراها كجزء من التتبع العام.


const api = require('@opentelemetry/api');

// ... داخل إحدى دوال الـ Controller في Express
async function processOrder(req, res) {
  // الحصول على الـ tracer الحالي
  const tracer = api.trace.getTracer('my-app-tracer');

  // بدء نطاق جديد يدويًا
  await tracer.startActiveSpan('processOrderBusinessLogic', async (span) => {
    try {
      console.log('بدء معالجة منطق الطلب...');
      
      // إضافة معلومات إضافية مفيدة للنطاق
      span.setAttribute('order.id', req.body.orderId);
      span.setAttribute('user.id', req.user.id);

      // ... هنا يتم تنفيذ منطق العمل الحقيقي ...
      await someComplexFunction(req.body);

      span.addEvent('Business logic completed successfully');
      console.log('انتهت المعالجة بنجاح');

      res.status(200).send('Order processed!');
    } catch (err) {
      // تسجيل الخطأ في النطاق، هذا مهم جدًا!
      span.recordException(err);
      span.setStatus({ code: api.SpanStatusCode.ERROR, message: err.message });
      res.status(500).send('Something went wrong');
    } finally {
      // إنهاء النطاق
      span.end();
    }
  });
}

الآن، عندما تنظر إلى التتبع الكامل للطلب، لن ترى فقط “طلب HTTP” و “استعلام قاعدة البيانات”، بل سترى أيضًا نطاقًا واضحًا اسمه `processOrderBusinessLogic` مع كل المعلومات التي أضفناها، مما يمنحك فهمًا عميقًا لما حدث داخل تطبيقك.

البطل المجهول: المجمّع (The OTel Collector)

في البداية، قد تبدو فكرة إرسال البيانات من تطبيقك مباشرة إلى Jaeger أو Prometheus مغرية. لكن صدقني، هذه وصفة للمشاكل المستقبلية.

نصيحة عملية من خبرتي: استخدم الـ OTel Collector من اليوم الأول. اعتبره نقطة الدخول والخروج الوحيدة لجميع بيانات المراقبة الخاصة بك. إنه يفصل تطبيقاتك تمامًا عن أنظمة المراقبة النهائية.

الـ Collector يعمل كخط أنابيب (pipeline):

  • Receivers (المستقبلات): تستقبل البيانات بتنسيقات مختلفة (OTLP وهو البروتوكول الأصلي لـ OTel, Jaeger, Prometheus, …).
  • Processors (المعالجات): تعالج البيانات أثناء مرورها. يمكنك استخدامه لـ:
    • إضافة بيانات وصفية (metadata) مشتركة لكل التتبعات.
    • إزالة معلومات حساسة (PII) من النطاقات.
    • أخذ عينات (sampling) لتقليل حجم البيانات.
  • Exporters (المصدرات): ترسل البيانات المعالجة إلى وجهة أو أكثر (Jaeger, Prometheus, Loki, Datadog, …).

هذا مثال بسيط لملف إعدادات `config.yaml` لـ Collector يستقبل البيانات عبر بروتوكول OTLP ويرسلها إلى Jaeger وإلى الكونسول (للتصحيح):


receivers:
  otlp:
    protocols:
      grpc:
      http:

processors:
  batch:

exporters:
  jaeger:
    endpoint: jaeger-all-in-one:14250
    tls:
      insecure: true
  logging:
    loglevel: debug

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [jaeger, logging]

بهذه الطريقة، تطبيقاتك لا تعرف شيئًا عن Jaeger. كل ما تعرفه هو أنها ترسل البيانات إلى الـ Collector. إذا قررت غدًا إضافة Datadog، كل ما عليك فعله هو إضافة Datadog exporter إلى هذا الملف، دون لمس أي تطبيق!

الخلاصة: من جزر معزولة إلى قارة متصلة 🗺️

بالعودة إلى قصتنا في البداية، لو كان لدينا OpenTelemetry في تلك الليلة، لكان الوضع مختلفًا تمامًا. بمجرد وصول التنبيه، كنا سنفتح التتبع الخاص بأحد الطلبات الفاشلة ونرى القصة كاملة:

  1. الطلب بدأ في الواجهة الأمامية.
  2. وصل إلى خدمة الطلبات (Orders Service) واستغرق 20ms.
  3. خدمة الطلبات استدعت خدمة الدفع (Payment Service).
  4. خدمة الدفع استغرقت 2.5 ثانية، وداخل نطاقها نرى خطأً واضحًا مع رسالته ومكان حدوثه بالضبط في الكود.

كنا سنجد سبب المشكلة في دقائق، لا ساعات. هذا هو الفرق بين الصراخ في وادٍ وبين امتلاك خريطة واضحة لكل رحلة داخل نظامك.

الاستثمار في تطبيق OpenTelemetry ليس رفاهية، بل هو ضرورة حتمية لأي نظام حديث يعتمد على الخدمات المصغرة. إنه يمنحك الوضوح، المرونة، والقدرة على النوم بهدوء في الليل، مع العلم أنك قادر على فهم نظامك مهما كان معقدًا.

يلا يا جماعة، شدوا حيلكم وابدأوا رحلتكم مع OTel. الطريق قد يبدو طويلاً في البداية، لكن العائد يستحق كل دقيقة تقضونها في بنائه. 🚀

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

برمجة وقواعد بيانات

كانت تحديثات قاعدة البيانات كابوساً: كيف أنقذتنا أدوات الترحيل (Migrations) من جحيم التعديلات اليدوية؟

هل عانيت يوماً من تحديث مخطط قاعدة البيانات يدوياً بين فريقك؟ أبو عمر يشارككم قصة حقيقية حول كيف غيّرت أدوات الترحيل (Migrations) طريقة عمل فريقه،...

17 مايو، 2026 قراءة المزيد
الشبكات والـ APIs

كانت خوادمنا تستجدي التحديثات: كيف أنقذتنا ‘خطاطيف الويب’ (Webhooks) من جحيم الاستقصاء المستمر (Polling)؟

تخيل خوادمك تلهث من كثرة الطلبات غير الضرورية. في هذه المقالة، أشارككم قصة حقيقية من الميدان حول كيفية انتقالنا من جحيم الاستقصاء المستمر (Polling) إلى...

17 مايو، 2026 قراءة المزيد
الحوسبة السحابية

كانت بنيتنا التحتية قصراً من رمال: كيف أنقذتنا “البنية التحتية ككود” (IaC) من جحيم البيئات المتضاربة؟

أشارككم قصة حقيقية عن ليلة كادت أن تنهار فيها شركتنا بسبب الفوضى في البنية التحتية، وكيف كانت "البنية التحتية ككود" (IaC) طوق النجاة الذي انتشلنا...

17 مايو، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

كان ملفي الشخصي مقبرة لمشاريع الدورات: كيف أنقذتني ‘المساهمة في المصادر المفتوحة’ من جحيم الرفض التلقائي؟

هل تشعر أن ملفك الشخصي على GitHub لا يعكس قدراتك الحقيقية؟ في هذه المقالة، أشاركك يا صديقي تجربتي الشخصية، أنا أبو عمر، وكيف انتقلت من...

17 مايو، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

كانت قاعدة بياناتنا تتوسل الرحمة: كيف أنقذنا التخزين المؤقت (Caching) من جحيم الاستعلامات البطيئة

قصة حقيقية من واقع العمل عن كيفية انهيار نظامنا تحت ضغط الاستعلامات المتكررة، وكيف كان التخزين المؤقت (Caching) هو طوق النجاة. مقالة عملية للمطورين تشرح...

17 مايو، 2026 قراءة المزيد
التكنلوجيا المالية Fintech

كان التحقق من هوية عملائنا يستغرق أياماً: كيف أنقذنا الذكاء الاصطناعي (eKYC) من جحيم الإجراءات اليدوية؟

بصفتي مبرمجاً فلسطينياً، سأروي لكم حكايتنا مع كابوس التحقق اليدوي من هوية العملاء (KYC) وكيف كانت رحلة الانتقال إلى التحقق الإلكتروني (eKYC) باستخدام الذكاء الاصطناعي...

17 مايو، 2026 قراءة المزيد
البنية التحتية وإدارة السيرفرات

كانت أعطالنا تكتشف بعد فوات الأوان: كيف أنقذنا Prometheus من جحيم المراقبة التفاعلية؟

أشارككم قصة حقيقية من قلب المعركة التقنية، كيف انتقلنا من فوضى الأعطال المفاجئة إلى نظام مراقبة استباقي باستخدام Prometheus وGrafana. هذه المقالة ليست مجرد شرح...

17 مايو، 2026 قراءة المزيد
ادارة الفرق والتنمية البشرية

كانت فرقنا صامتة أمام الأخطاء: كيف أنقذتنا ‘السلامة النفسية’ من جحيم ثقافة اللوم؟

أشارككم قصة حقيقية من الميدان، عن اجتماع تحول إلى محكمة بسبب خطأ برمجي، وكيف كان هذا الموقف هو الشرارة التي قادتنا لاكتشاف "السلامة النفسية". هذه...

17 مايو، 2026 قراءة المزيد
البودكاست