أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

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

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول

Distributed Tracing بـ OpenTelemetry: ليه طلب API بياخد 3 ثواني وإنت مش عارف السبب

📅 ٢٤ أبريل ٢٠٢٦⏱ 6 دقائق قراءة
Distributed Tracing بـ OpenTelemetry: ليه طلب API بياخد 3 ثواني وإنت مش عارف السبب

Distributed Tracing بـ OpenTelemetry: خريطة الطلب من أوله لآخره

لو عندك 4 أو 5 microservices، وطلب واحد من الـ frontend بياخد 2800ms بدل 200ms، ومفيش طريقة تعرف بالظبط أنهي service هو السبب — المقال ده هيوفّرلك أسبوع كامل من التخمين. Distributed Tracing بـ OpenTelemetry بيديك خريطة دقيقة للطلب من لحظة دخوله لحد رجوع الرد، ومعاه زمن كل خطوة.

لوحة تحليلات تعرض رسوم بيانية لقياس زمن الاستجابة بين خدمات موزعة مثل Grafana Tempo

المشكلة باختصار

في monolith، لو طلب بطيء، بتفتح APM واحد وبتشوف أنهي function بتستهلك الوقت. في microservices، الطلب الواحد بيعدّي على auth-service ثم orders-service ثم payment-service ثم notifications-service. logs كل service موجودة في مكان مختلف، ومفيش حاجة تربطهم. بتقعد ساعتين بتدوّر في kibana و request-id يدوي علشان تعرف إن البطء في استعلام واحد في payment-service بياخد 1600ms.

Distributed Tracing بيحل المشكلة دي جذريًا: كل طلب بياخد trace_id واحد بيتنقل مع الهيدر بين كل الـ services، وكل عملية جوّا service بتتسجل كـ span تحت نفس الـ trace. النتيجة: شجرة كاملة بتوريك مين استنى مين.

المفهوم بمثال بسيط قبل أي كود

تخيّل شركة توصيل طلبات. العميل طلب أوردر في 9:00 صباحًا. الأوردر عدّى على 4 موظفين: استقبال، محاسبة، مخزن، ثم سائق. العميل استلم الأوردر 9:45. أنت كمدير عايز تعرف مين فيهم أخد وقت. لو كل موظف كتب في كراسته وقت استلامه ووقت تسليمه، تقدر تطلع جدول زي ده:

  • الاستقبال: 9:00 → 9:05 (5 دقائق)
  • المحاسبة: 9:05 → 9:08 (3 دقائق)
  • المخزن: 9:08 → 9:35 (27 دقيقة) ← هنا المشكلة
  • السائق: 9:35 → 9:45 (10 دقائق)

دلوقني عرفت إن المخزن هو المشكلة، من غير ما تقف على كل موظف وتسأله. ده بالظبط اللي Distributed Tracing بيعمله: كل "موظف" هو service، كل "كراسة" هي span، ورقم الأوردر المشترك هو trace_id.

المصطلحات العلمية بدقة

بعد ما فهمت المثال، دي التعريفات الرسمية من وثائق OpenTelemetry:

  • Trace: تمثيل لرحلة طلب واحد عبر النظام بالكامل. كل trace ليه trace_id فريد بـ 128 bit.
  • Span: عملية واحدة لها اسم، وقت بداية، وقت نهاية، وattributes. الـ span ممكن يكون له parent span ومنها بنبني الشجرة.
  • Context Propagation: آلية نقل الـ trace_id و span_id بين الـ services. المعيار الرسمي هو W3C Trace Context، وبيتبعث في هيدر اسمه traceparent.
  • Instrumentation: الكود اللي بيولّد الـ spans. ممكن يكون auto-instrumentation (بيشتغل من غير ما تعدّل كودك) أو manual (بتكتب spans مخصوصة).
  • Exporter: المكوّن اللي بيبعت الـ spans لمكان تخزين (Jaeger, Tempo, Datadog, Honeycomb...).

إعداد Node.js في 3 دقائق (auto-instrumentation)

أسرع طريقة تبدأ بيها هي الـ auto-instrumentation. في Node.js، الحزمة @opentelemetry/auto-instrumentations-node بتـ patch تلقائيًا مكتبات زي Express و http و pg و ioredis، وبتولّد spans من غير ما تعدّل سطر في الكود الأساسي.

Bash
npm install \
  @opentelemetry/sdk-node \
  @opentelemetry/auto-instrumentations-node \
  @opentelemetry/exporter-trace-otlp-http \
  @opentelemetry/resources \
  @opentelemetry/semantic-conventions

اعمل ملف tracing.js وحمّله قبل أي حاجة في التطبيق:

JavaScript
// tracing.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const { resourceFromAttributes } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');

const sdk = new NodeSDK({
  resource: resourceFromAttributes({
    [SemanticResourceAttributes.SERVICE_NAME]: 'orders-service',
    [SemanticResourceAttributes.SERVICE_VERSION]: '1.2.0',
    [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: 'production'
  }),
  traceExporter: new OTLPTraceExporter({
    url: 'http://otel-collector:4318/v1/traces'
  }),
  instrumentations: [getNodeAutoInstrumentations()]
});

sdk.start();

وفي الـ start script: node --require ./tracing.js server.js. خلاص. كل طلب HTTP، كل استعلام Postgres، كل call على Redis، كلهم هيتسجّلوا كـ spans تلقائيًا.

Python بنفس المنطق

Bash
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install

OTEL_SERVICE_NAME=payments-service \
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318 \
OTEL_TRACES_EXPORTER=otlp \
opentelemetry-instrument python app.py

الأمر opentelemetry-bootstrap بيكتشف المكتبات المثبّتة (Django, Flask, FastAPI, psycopg2...) ويثبّت الـ instrumentation المناسبة. بعدها opentelemetry-instrument بيغلّف تطبيقك ويبدأ يبعت spans.

الـ Collector: محطة وسطى مش رفاهية

شبكة من العقد المترابطة تمثل انتشار السياق trace context بين خدمات موزعة عبر W3C Trace Context

ممكن الـ SDK في التطبيق يبعت مباشرة لـ Jaeger أو Tempo، لكن ده مش مُوصى به في production. الأفضل يكون فيه OpenTelemetry Collector في النص — gateway بتستقبل الـ spans، تعمل buffering و batching و sampling، وبعدين تبعت لمكان التخزين.

YAML
# otel-collector-config.yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch:
    timeout: 5s
    send_batch_size: 1024
  tail_sampling:
    decision_wait: 10s
    policies:
      - name: errors-policy
        type: status_code
        status_code: { status_codes: [ERROR] }
      - name: slow-policy
        type: latency
        latency: { threshold_ms: 500 }
      - name: probabilistic-policy
        type: probabilistic
        probabilistic: { sampling_percentage: 10 }

exporters:
  otlp/tempo:
    endpoint: tempo:4317
    tls:
      insecure: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch, tail_sampling]
      exporters: [otlp/tempo]

الـ tail sampling هنا مهم: بيحتفظ بـ 100% من الـ traces اللي فيها error أو latency أعلى من 500ms، و 10% عشوائي من الباقي. ده بيقلّل التكلفة بنسبة تقارب 85% مع الحفاظ على الـ traces المهمة.

الأرقام اللي هتهمك فعلًا

  • Overhead على التطبيق: SDK الرسمي بيضيف 1-3% CPU و ~50MB RAM في متوسط حالات production. مقبول.
  • حجم البيانات: trace متوسط في نظام فيه 8 services بياخد 3-8 KB. مع sampling 10%، تطبيق بـ 2000 req/s بينتج حوالي 48 GB/يوم من الـ traces.
  • تكلفة Tempo على S3: بتتكلف في حدود 15-30 دولار/شهر لحجم 500GB بـ retention 14 يوم. أرخص بكتير من Datadog APM.
  • زمن استكشاف مشكلة: قبل tracing، تشخيص latency spike بياخد من 30 دقيقة لساعتين من الـ log diving. بعد tracing، بتوصل للـ service المسؤول في أقل من 3 دقائق.

Trade-offs لازم تكون صريح معاها

بتكسب: رؤية كاملة للـ request flow، سرعة تشخيص عالية، ربط tightl بين traces و logs و metrics. بتخسر:

  • تكلفة تخزين: الـ spans بتتكدّس بسرعة. بدون sampling ذكي، هتدفع أكثر من اللازم.
  • تعقيد في الإعداد: Collector، backend، Grafana، كلهم مكوّنات جديدة محتاجة صيانة.
  • Overhead على طلبات معينة: endpoints بتعمل آلاف الـ DB calls في الطلب الواحد ممكن تعاني. لازم تستبعدها أو تقلّل sampling عليها.
  • خصوصية: attributes ممكن تحتوي على بيانات حسّاسة (emails, tokens). لازم تعدّل الـ Collector يعمل redaction.

متى لا تستخدم Distributed Tracing

لا تبدأ بـ tracing لو:

  • عندك service واحد (monolith). APM تقليدي أبسط وأرخص.
  • حجم الطلبات أقل من 50 req/minute. مش هتاخد قيمة توازي الإعداد.
  • فريقك لسه مش عامل centralized logging. Logs أولًا، تراسنج ثانيًا.
  • الـ services بتاعتك بتتكلم بـ protocols غريبة مش مدعومة (بعض الـ legacy RPC).

الافتراضات اللي بنيت عليها الأرقام

الأرقام في المقال مبنية على تطبيق Node.js/Python متوسط الحجم، يستقبل بين 500 و 5000 طلب/ثانية، ويشتغل على Kubernetes بنظام 3-8 microservices. لو عندك أكتر من 20K طلب/ثانية أو حجم بيانات أكبر، احتاجت ضبط أدق للـ sampling rates والـ collector replicas.

الخطوة التالية

افتح أكبر service عندك، ثبّت SDK أو auto-instrumentation بتاع اللغة بتاعتك، وابعت traces لـ Jaeger محلي بـ Docker لمدة يوم واحد. docker run -d -p 16686:16686 -p 4318:4318 jaegertracing/all-in-one:latest هيديك UI كامل على localhost:16686. بعد ما تشوف أول trace شجري، هتفهم لوحدك أنهي service هو الأبطأ. لو الـ SDK مش بيبعت spans، 90% من الوقت المشكلة في endpoint أو authentication على الـ Collector — ابعتلي رسالة الخطأ.

المصادر

  • OpenTelemetry Docs — Observability Primer و Traces concepts (opentelemetry.io/docs/concepts/signals/traces/).
  • W3C Trace Context Recommendation — المعيار الرسمي لـ traceparent header (w3.org/TR/trace-context/).
  • OpenTelemetry Python Auto-Instrumentation Example (opentelemetry.io/docs/zero-code/python/example/).
  • OpenTelemetry Kubernetes Operator — Injecting Auto-instrumentation (opentelemetry.io/docs/platforms/kubernetes/operator/automatic/).
  • Grafana Tempo Docs — high-scale distributed tracing backend.
  • Uptrace — What is Distributed Tracing? How It Works with OpenTelemetry.
  • Red Hat Developer (فبراير 2026) — How to use auto-instrumentation with OpenTelemetry.
  • Dash0 Knowledge Base — How OpenTelemetry Distributed Tracing Works (with Examples).

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة