cloud-devops

دليل تعليمي OpenTelemetry Collector + Node.js

١٣ يونيو ٢٠٢٦

OpenTelemetry Collector + Node.js Tracing Tutorial (2026)

ملخص

ستقوم بتهيئة تطبيق Node.js + Express باستخدام OpenTelemetry، وإرسال التتبعات (traces) عبر OTLP إلى OpenTelemetry Collector، ثم توزيعها على Jaeger v2، وقراءة التتبعات الموزعة الناتجة في واجهة مستخدم Jaeger. تعمل المجموعة بالكامل محليًا باستخدام أمر واحد: Docker compose up. تم تثبيت والتحقق من كل إصدارات الحزم وعلامات الصور (image tags) أدناه في 2026-06-13، وتم تنفيذ كود التهيئة للتأكد من الـ spans التي ينتجها. الميزانية الزمنية حوالي 30 دقيقة.

ما ستتعلمه

  • كيفية إرسال تتبعات Node.js OpenTelemetry إلى الـ Collector ثم إلى Jaeger، ولماذا يتوسط الـ Collector بينهما
  • كيفية التهيئة التلقائية (auto-instrument) لتطبيق Express بحيث تظهر الـ spans الخاصة بـ HTTP والمسارات (routes) بدون كتابة كود داخل كل معالج (handler)
  • كيفية كتابة إعدادات Collector جاهزة للإنتاج باستخدام مستقبل OTLP، ومعالجات memory_limiter + batch، ومصدرات otlp + debug
  • كيفية تشغيل Jaeger v2 في Docker وعرض التتبعات الموزعة على http://localhost:16686
  • كيفية إضافة span مخصص وسمات موارد (resource attributes) باستخدام SDK 2.x الحالي (resourceFromAttributes)
  • كيفية تصحيح الخطأ الكلاسيكي "التتبعات الخاصة بي لا تظهر أبدًا في Jaeger"

كيف تترابط الأجزاء معًا

إجابة مباشرة أولاً: يقوم تطبيقك بتصدير الـ spans عبر OTLP (بروتوكول OpenTelemetry السلكي) إلى Collector؛ يقوم الـ Collector بتجميعها وإرسالها إلى Jaeger v2، الذي يقوم بتخزينها وعرضها. الـ Collector اختياري في مرحلة التجربة البسيطة ولكنه قياسي في بيئة الإنتاج لأنه يفصل تطبيقك عن الواجهة الخلفية (backend) — يمكنك إضافة أخذ العينات (sampling)، والتجميع (batching)، وإعادة المحاولة، ووجهات إضافية دون إعادة نشر التطبيق.

إليك تدفق البيانات الذي ستنشئه:

flowchart LR
  A["Node.js app<br/>(OTLP/HTTP :4318)"] -->|OTLP| C["OTel Collector<br/>memory_limiter, batch"]
  C -->|OTLP gRPC :4317| J["Jaeger v2<br/>storage + query"]
  C -->|debug exporter| L["Collector logs<br/>(stdout)"]
  J --> U["Jaeger UI<br/>:16686"]

ينتقل OTLP عبر وسيلتي نقل: gRPC على المنفذ 4317 و HTTP على المنفذ 4318.1 يستخدم التطبيق في هذا الدليل OTLP/HTTP للوصول إلى الـ Collector؛ ويستخدم الـ Collector بروتوكول OTLP/gRPC للوصول إلى Jaeger. كلاهما من الدرجة الأولى — الاختيار يتعلق بالتبعيات وجدران الحماية، وليس بالصحة التقنية.

لماذا نهتم بوجود Collector أصلاً بينما يمكن لـ Jaeger v2 استقبال OTLP مباشرة؟ لأن الـ Collector هو المكان الذي تعيش فيه الاهتمامات التشغيلية. تشغيل واحد كـ وكيل (agent) بجانب كل تطبيق (أو كـ بوابة (gateway) مشتركة لمجموعة من التطبيقات) يعني أنه يمكنك تغيير معدلات أخذ العينات، أو إضافة واجهة خلفية ثانية، أو تنظيف السمات الحساسة، أو استيعاب انقطاع الواجهة الخلفية مع إعادة المحاولة والانتظار في طوابير — كل ذلك دون إعادة نشر تطبيق واحد. يظل التطبيق بسيطًا: يرسل OTLP وينسى. هذا الفصل هو السبب الكامل لوجود الـ Collector، وهو السبب في أن إعدادات الإنتاج تضعه في المسار حتى لو كان البروتوكول يسمح لك بتخطيه.2

المتطلبات الأساسية

  • Node.js 24.16.0 (خط LTS النشط الحالي) أو أحدث. علامة المحمل node --import التي يعتمد عليها هذا الدليل مستقرة منذ Node 20.6.3
  • Docker مع إضافة Compose (أمر Docker compose، وليس Docker-compose القديم).
  • طرفية (terminal) ومتصفح. لا يفترض وجود معرفة مسبقة بـ Jaeger أو الـ Collector.

يتم تثبيت كل شيء آخر أدناه. الإصدارات المثبتة المستخدمة في الدليل:

المكونالإصدارالمصدر
@opentelemetry/sdk-node0.219.0npm latest4
@opentelemetry/auto-instrumentations-node0.77.0npm latest4
@opentelemetry/exporter-trace-otlp-proto0.219.0npm latest4
@opentelemetry/resources2.8.0npm latest4
@opentelemetry/semantic-conventions1.41.1npm latest4
@opentelemetry/API1.9.1npm latest4
express5.2.1npm latest
OTel Collector (contrib)0.153.0collector-releases5
Jaeger2.19.0jaegertracing.io/download6

هناك ملحوظة تستحق المعرفة مسبقًا: تشحن OpenTelemetry JS حزمها المستقرة على خطوط الإصدار 1.x/2.x (مثل API 1.9.1، و resources 2.8.0) وحزمها التي لا تزال تجريبية على خط 0.x مشترك (sdk-node 0.219.0، والمصدرات، والتهيئة التلقائية).4 عدم وجود إصدار مثل "sdk-node 2.x" ليس خطأ — 0.219.0 هو الإصدار التجريبي الحالي، وهذا أمر متوقع.

الخطوة 1 — إنشاء هيكل المشروع وتثبيت OpenTelemetry

أنشئ دليلًا للمشروع وقم بتثبيت SDK، وحزمة التهيئة التلقائية، ومصدر OTLP، و Express:

mkdir orders-API && cd orders-API
npm init -y
npm pkg set type=module
npm install \
  @opentelemetry/API@1.9.1 \
  @opentelemetry/sdk-node@0.219.0 \
  @opentelemetry/auto-instrumentations-node@0.77.0 \
  @opentelemetry/exporter-trace-otlp-proto@0.219.0 \
  @opentelemetry/resources@2.8.0 \
  @opentelemetry/semantic-conventions@1.41.1 \
  express@5.2.1

أمر npm pkg set type=module يجعل المشروع يستخدم وحدات ES، وهو ما يفترضه ملف التشغيل أدناه. بعد اكتمال التثبيت، يجب أن يسرد npm ls --depth=0 كل حزمة بالإصدار الذي طلبته دون أي تحذيرات ERESOLVE.

الخطوة 2 — كتابة ملف تهيئة التشغيل

يجب تهيئة OpenTelemetry قبل استيراد (import) أي مكتبة مهيأة (مثل http، express)، لذا فهي توضع في ملف خاص بها يتم تحميله أولاً. أنشئ ملف instrumentation.js:

// instrumentation.js
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { resourceFromAttributes } from '@opentelemetry/resources';
import {
  ATTR_SERVICE_NAME,
  ATTR_SERVICE_VERSION,
  ATTR_DEPLOYMENT_ENVIRONMENT_NAME,
} from '@opentelemetry/semantic-conventions';

const sdk = new NodeSDK({
  resource: resourceFromAttributes({
    [ATTR_SERVICE_NAME]: 'orders-API',
    [ATTR_SERVICE_VERSION]: '1.0.0',
    [ATTR_DEPLOYMENT_ENVIRONMENT_NAME]: 'development',
  }),
  traceExporter: new OTLPTraceExporter({
    url: 'http://localhost:4318/v1/traces',
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();

process.on('SIGTERM', () => {
  sdk.shutdown().finally(() => process.exit(0));
});

هناك ثلاثة تفاصيل مهمة هنا، وكل منها مكان تخطئ فيه الدروس القديمة:

  • resourceFromAttributes هو الـ API الحالي. تم حذف منشئ new Resource() في خط SDK 2.x الخاص بـ @opentelemetry/resources؛ استدعاؤه الآن سيؤدي لخطأ. الـ service.name الذي تحدده هنا هو ما يميز خدمتك في واجهة مستخدم Jaeger.4
  • مفاتيح الاصطلاحات الدلالية (semantic-convention) هي ثوابت مستوردة، وليست سلاسل نصية خام. ATTR_SERVICE_NAME تؤول إلى 'service.name'، و ATTR_DEPLOYMENT_ENVIRONMENT_NAME تؤول إلى 'deployment.environment.name'.7 استيرادها يبقيك متوافقًا مع المواصفات بدلاً من كتابة أسماء السمات يدويًا.
  • sdk.start() متزامن؛ بينما sdk.shutdown() يرجع Promise. معالج الـ spans الافتراضي يقوم بتجميعها (batching)، لذا فإن أي عملية تخرج فجأة قد تفقد ما لم يتم إرساله (flushed). ربط shutdown() بـ SIGTERM يضمن إرسال الدفعة المتبقية عند التوقف النظيف.

الخطوة 3 — كتابة تطبيق Express

الآن التطبيق نفسه. أنشئ ملف server.js بمسار واحد يقوم ببعض العمل الداخلي، بالإضافة إلى span يدوي حتى تتمكن من رؤية الأدوات المخصصة (custom instrumentation) بجانب النوع التلقائي:

// server.js
import express from 'express';
import { trace } from '@opentelemetry/API';

const app = express();
const tracer = trace.getTracer('orders-API');

app.get('/orders/:id', async (req, res) => {
  const span = tracer.startSpan('load-order-from-db');
  await new Promise((resolve) => setTimeout(resolve, 25)); // pretend DB call
  span.setAttribute('order.id', req.params.id);
  span.end();
  res.json({ id: req.params.id, status: 'shipped' });
});

app.listen(3000, () => console.log('orders-API listening on :3000'));

لم تضطر لاستيراد أي شيء خاص بـ OpenTelemetry للحصول على الـ spans الخاصة بـ HTTP والمسارات (routes) — لأن getNodeAutoInstrumentations() تقوم بتعديل طبقات http و Express تلقائياً من أجلك. القياس اليدوي الوحيد هو span الـ load-order-from-db، والذي يتداخل داخل span الطلب التلقائي لأنه تم إنشاؤه بينما كان ذلك الطلب في السياق النشط (active context).

الخطوة 4 — تكوين الـ Collector و Jaeger

أنشئ ملف otel-collector.yaml. هذا مسار تتبع (trace pipeline) مصمم للإنتاج: يستقبل OTLP، يحمي الذاكرة، يجمع البيانات في دفعات، ثم يصدرها إلى Jaeger وإلى سجلات الـ Collector الخاصة.

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

processors:
  memory_limiter:
    check_interval: 1s
    limit_mib: 512
    spike_limit_mib: 128
  batch:
    timeout: 5s
    send_batch_size: 1024

exporters:
  otlp/jaeger:
    endpoint: jaeger:4317
    tls:
      insecure: true
  debug:
    verbosity: detailed

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlp/jaeger, debug]

لماذا يوجد كل مكون هنا:

  • memory_limiter مدرج أولاً. يقوم بفحص استخدام الـ heap كل check_interval ويرفض البيانات عندما تقترب العملية من limit_mib، بحيث تؤدي زيادة حركة المرور المفاجئة إلى التخلص من الحمل الزائد بدلاً من قتل الـ Collector بسبب نفاد الذاكرة (OOM). التوجيه الرسمي هو وضعه قبل أي معالج آخر لحماية المسار بالكامل.8
  • batch يجمع الـ spans قبل التصدير، ويرسلها بعد timeout أو بمجرد تراكم send_batch_size من الـ spans، أيهما يحدث أولاً. التجميع يقلل عدد الطلبات الصادرة بشكل كبير.2
  • otlp/jaeger يصدر عبر OTLP gRPC إلى jaeger:4317. خيار tls.insecure: true يعطل TLS للشبكة المحلية — وهو مناسب داخل شبكة Compose خاصة، وليس للإنترنت العام.2 صيغة type/name (مثل otlp/jaeger) تسمح لك بتعريف أكثر من مصدر من نفس النوع لاحقاً.
  • debug يطبع الـ spans إلى stdout الخاص بالـ Collector بمستوى verbosity: detailed، وهو أمر لا يقدر بثمن أثناء عملية الربط. لاحظ الاسم: كان هذا المصدر يسمى logging قبل إصدار Collector v0.86.0 وتم تغيير اسمه إلى debug. الدروس التعليمية التي لا تزال تستخدم logging ستفشل مع إصدار Collector الحالي.2

الآن ملف Compose الذي يشغل كلتا الخدمتين. أنشئ Docker-compose.yaml:

# Docker-compose.yaml
services:
  jaeger:
    image: cr.jaegertracing.io/jaegertracing/jaeger:2.19.0
    container_name: jaeger
    ports:
      - "16686:16686"
    networks: [otel]

otel-collector:
    image: otel/opentelemetry-collector-contrib:0.153.0
    container_name: otel-collector
    command: ["--config=/etc/otelcol-contrib/config.yaml"]
    volumes:
      - ./otel-collector.yaml:/etc/otelcol-contrib/config.yaml
    ports:
      - "4317:4317"
      - "4318:4318"
    depends_on: [jaeger]
    networks: [otel]

networks:
  otel:

هناك بعض الأشياء التي يطبقها هذا الملف بشكل صحيح وتفتقر إليها الأدلة القديمة:

  • استخدام Jaeger v2، وليس v1. وصل Jaeger v1 إلى نهاية عمره الافتراضي في 2025-12-31؛ صورة jaeger (الإصدار الرئيسي v2) هي الخط المدعوم وهي نفسها مبنية على إطار عمل OpenTelemetry Collector.6 يقبل Jaeger v2 بروتوكول OTLP تلقائياً — لم تعد هناك حاجة لضبط علامة COLLECTOR_OTLP_ENABLED=true، لأن OTLP مفعل افتراضياً.6
  • يقوم الـ Collector بالتصدير إلى Jaeger باستخدام مصدر otlp، وليس مصدر jaeger. تمت إزالة مصدر jaeger الأصلي من توزيعات الـ Collector بعد الإصدار v0.85.0؛ توجيهات الهجرة الخاصة بالمشروع هي إرسال OTLP، والذي يستقبله Jaeger الآن مباشرة.9
  • الـ Collector فقط هو من ينشر المنافذ 4317/4318 لجهازك المضيف. يعرض Jaeger فقط منفذ واجهة المستخدم 16686؛ يتصل الـ Collector بـ Jaeger عبر شبكة otel الخاصة، لذا يتحدث تطبيقك مع localhost:4318 ولا يتصل بـ Jaeger مباشرة أبداً.

الخطوة 5 — تشغيل المكدس وإنشاء تتبع

قم بتشغيل Jaeger والـ Collector:

Docker compose up -d
Docker compose ps

يجب أن تظهر كلتا الحاويتين في حالة تشغيل (running). في نافذة terminal ثانية، ابدأ التطبيق مع تحميل ملف القياس قبل الخادم عبر --import:

node --import ./instrumentation.js server.js

المخرجات المتوقعة:

orders-API listening on :3000

الآن أرسل طلباً حتى يكون هناك شيء لتتبعه:

curl http://localhost:3000/orders/42

الاستجابة المتوقعة:

{"id":"42","status":"shipped"}

خلال ثانية أو ثانيتين، سيقوم مصدر debug الخاص بالـ Collector بتسجيل الـ spans المستلمة، وستصل نفس الـ spans إلى Jaeger. يمكنك مراقبة معالجة الـ Collector لها باستخدام Docker compose logs -f otel-collector.

إذا كنت تريد إجراء فحص سلامة (sanity check) بدون واجهة خلفية أولاً، فاستبدل مصدر OTLP مؤقتاً بمصدر console. تشغيل نفس التطبيق مع ConsoleSpanExporter يطبع كل span مكتمل؛ طلب curl واحد إلى /orders/42 ينتج تتبعاً متصلاً واحداً تشترك الـ spans الخاصة به في traceId:

{
  name: 'GET',
  kind: 1,                       // SERVER span (the HTTP request)
  attributes: { 'http.method': 'GET', 'http.status_code': 200 },
  resource: { 'service.name': 'orders-API' }
}
{
  name: 'request handler - /orders/:id',
  attributes: { 'http.route': '/orders/:id' }   // the Express route span
}
{
  name: 'load-order-from-db',
  kind: 0,                       // your custom span, nested in the request
  attributes: { 'order.id': '42' }
}

المخرجات أعلاه مختصرة — مصدر الـ console الحقيقي يطبع معرفات الـ trace/span، والطوابع الزمنية، والمزيد من الخصائص — ولكن أسماء الـ span، و نوع الـ SERVER، و http.route الخاص بالمسار، و order.id الخاص بك هي بالضبط ما يصدره القياس.

الخطوة 6 — قراءة التتبع في Jaeger

افتح http://localhost:16686. في قائمة Service المنسدلة، اختر orders-API (الـ service.name من موردك)، واترك العملية كـ "all"، ثم انقر على Find Traces.

سترى تتبعاً واحداً لكل طلب. افتحه وستحصل على شلال (waterfall): الـ root span الخاص بالخادم GET /orders/:id، و span فرعي لمعالج مسار Express، و span الـ load-order-from-db الخاص بك بالأسفل، بعرض 25 مللي ثانية تقريباً — وهو التأخير الاصطناعي الذي أضفته. انقر فوق أي span لفحص علاماته (tags)، بما في ذلك service.name و http.route و order.id.

اقرأ الشلال بنفس الطريقة التي تقرأ بها الرسم البياني اللهبي (flame graph). عرض كل شريط يمثل الوقت الفعلي، لذا فإن span الـ load-order-from-db الذي يهيمن بوضوح على الطلب هو إشارتك إلى أن خطوة "قاعدة البيانات" — وليس عبء إطار العمل — هي المسؤولة عن التأخير (latency). عندما يكون الطلب بطيئاً في بيئة الإنتاج، تجيب هذه الرؤية على سؤال "أين البطء" في ثوانٍ بدلاً من إجبارك على استنتاج ذلك من سجلات متفرقة. الفجوة بين نهاية الـ span ونهاية الـ parent الخاص به هي وقت غير محسوب، والذي يشير عادةً إلى عمل متزامن لم يتم قياسه بعد.

هذا هو الهدف الكامل من التتبع الموزع: يصبح الطلب الواحد شجرة من العمليات الموقوتة التي يمكنك التعمق فيها. أضف خدمة ثانية تستدعيها هذه الخدمة عبر HTTP، وقم بنقل السياق (القياس التلقائي يفعل ذلك من أجلك)، وسيقوم Jaeger بربط كلتا الخدمتين في تتبع واحد.

الخطوة 7 — تعزيز بيئة الإنتاج

المسار الذي بنيته مهيأ بالفعل للإنتاج، ولكن هناك ثلاثة إعدادات تستحق الضبط المتعمد قبل الإطلاق.

قم بأخذ عينات (Sample)، ولا تخزن كل شيء. في حالات حركة المرور الحقيقية، يكون تخزين 100% من التتبعات مكلفاً ونادراً ما يكون مفيداً. اضبط أداة أخذ العينات (sampler) الخاصة بـ SDK باستخدام متغير بيئة بحيث تحتفظ، على سبيل المثال، بـ 10% من التتبعات بناءً على البداية (head-based) — دون الحاجة لتغيير الكود:

OTEL_TRACES_SAMPLER=traceidratio \
OTEL_TRACES_SAMPLER_ARG=0.1 \
node --import ./instrumentation.js server.js

traceidratio يتخذ قرار الإبقاء/الإسقاط بشكل حتمي بناءً على معرف التتبع (trace ID)، لذا — طالما أن كل خدمة تستخدم نفس النسبة — فإما أن يتم الإبقاء على التتبع بالكامل أو إسقاطه بالكامل عبر جميع الخدمات، ولن تحصل أبداً على نصف تتبع.4

وجه المصدر (exporter) باستخدام متغيرات البيئة، وليس عناوين URL مكتوبة برمجياً. في مرحلة التشغيل التجريبي (bootstrap)، قمت بضبط عنوان URL داخلياً للتوضيح، ولكن متغير OTEL_EXPORTER_OTLP_ENDPOINT القياسي يسمح لفرق العمليات بإعادة توجيه التطبيق حسب البيئة دون لمس الكود. تقرأ مصادر OTLP هذا المتغير كنقطة نهاية أساسية وتضيف مسار الإشارة (/v1/traces):4

OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318 \
node --import ./instrumentation.js server.js

انتبه لحدود الدفعات (batch) والذاكرة. قيم batch و memory_limiter في ملف otel-collector.yaml هي نقاط انطلاق وليست قواعد ثابتة. ارفع قيمة send_batch_size للحصول على إنتاجية أعلى، واضبط limit_mib على حوالي 80% من الذاكرة التي تمنحها فعلياً لحاوية Collector حتى يبدأ المحدد في العمل قبل أن يتدخل نظام التشغيل.8

التحقق

فحص المسار الناجح من البداية للنهاية:

# 1. البيئة تعمل
Docker compose ps          # jaeger + otel-collector كلاهما يعملان

# 2. التطبيق يستجيب
curl -s http://localhost:3000/orders/42
# => {"id":"42","status":"shipped"}

# 3. الـ Collector استلم الـ spans (debug exporter)
Docker compose logs otel-collector | grep -i "TracesExporter\|spans"

# 4. Jaeger يحتوي على التتبع
#    المتصفح: http://localhost:16686 -> الخدمة: orders-API -> Find Traces

إذا أظهرت الخطوة 3 أن Collector يسجل الـ spans المستلمة وأدرجت الخطوة 4 تتبعاً تحت orders-API، فإن المسار يعمل بشكل صحيح.

الأخطاء الشائعة

لا توجد spans في Jaeger، لكن التطبيق يعمل بشكل جيد. السبب الأكثر شيوعاً هو انتهاء العملية قبل إرسال الدفعة (batch flush). يقوم معالج BatchSpanProcessor الافتراضي بالاحتفاظ بالـ spans في الذاكرة وتصديرها بناءً على مؤقت؛ لذا فإن السكربتات قصيرة العمر أو الضغط على Ctrl+C بدون إغلاق نظيف يؤدي إلى فقدانها. تأكد من تشغيل sdk.shutdown() عند الخروج (كما في الخطوة 2)، أو انتقل مؤقتاً إلى console exporter لإثبات أن الـ spans يتم إنتاجها من الأساس.

Cannot find module أو يبدأ التطبيق بدون أي أدوات قياس (instrumentation). يجب تحميل ملف أدوات القياس قبل كود تطبيقك. استخدم node --import ./instrumentation.js server.js؛ إذا كنت تستخدم CommonJS بدلاً من ES modules، فإن العلم المكافئ هو --require ./instrumentation.js. تحميله بعد عمل import لـ Express يعني أن المكتبات قد تم تحميلها بالفعل دون تعديل (unpatched).

الـ Collector لا يبدأ: unknown type: "logging". أنت تستخدم نسخة حديثة من Collector مع إعدادات قديمة. تم تغيير اسم المصدر logging إلى debug في الإصدار v0.86.0. قم بتغيير الاسم في المصدر وفي مرجع المسار (pipeline).2

الـ Collector لا يبدأ: unknown type: "jaeger" في المصادر. نفس مشكلة القدم في جانب التصدير: تمت إزالة مصدر jaeger الأصلي بعد الإصدار v0.85.0. أرسل OTLP إلى jaeger:4317 باستخدام مصدر otlp بدلاً من ذلك، تماماً كما في الخطوة 4.9

فشل الاتصال (Connection refused) من التطبيق إلى localhost:4318. حاوية Collector لا تنشر منفذ OTLP HTTP، أو أنها توقفت بسبب إعدادات خاطئة. تحقق من Docker compose ps و Docker compose logs otel-collector؛ أي خطأ مطبعي في الإعدادات يجعل Collector يخرج فوراً بدلاً من العمل بالإعدادات الافتراضية.

bind: address already in use على المنفذ 4317 أو 4318. هناك شيء آخر على جهازك يمتلك منافذ OTLP بالفعل — غالباً Collector سابق لم يتم إغلاقه أو وكيل قياس آخر. ابحث عنه باستخدام lsof -i :4318، أوقفه، ثم قم بتشغيل Docker compose up -d مرة أخرى. تغيير منفذ المضيف المنشور (على سبيل المثال "14318:4318") سيعمل أيضاً، طالما وجهت مصدر التطبيق إلى منفذ المضيف الجديد.

كل طلب يظهر كأنه تتبعين مكررين. هذا يعني أن SDK بدأ مرتين — عادةً لأن instrumentation.js تم تمريره إلى --import وتم استيراده مرة أخرى من داخل server.js. قم بتحميله مرة واحدة فقط، من خلال علم المحمل (loader flag)، وقم بإزالة أي import './instrumentation.js' من كود تطبيقك.

الخطوات التالية وقراءات إضافية

  • بمجرد تدفق التتبعات، تصبح الأسئلة الأصعب تنظيمية: الاستبقاء (retention)، والعددية (cardinality)، والتكلفة. تغطي المبادئ الواردة في دليلنا حول تصميم منصة مراقبة حديثة المقايضات التي لا يمكن لدرس تعليمي واحد تغطيتها.
  • التتبع هو إشارة واحدة؛ إذا كانت أعباء عملك تعمل على Kubernetes، فقم بدمج هذا مع عمليات نشر Kubernetes بدون وقت توقف حتى لا يؤدي النشر أبداً إلى فقدان الـ spans في منتصف العملية.
  • للاطلاع على نفس المشكلة في بيئة تشغيل الطرفية (edge-runtime)، تعرف على كيفية ربط مراقبة Cloudflare Workers مع Workers Logs و Sentry.

المراجع المعتمدة:

Footnotes

  1. مواصفات OTLP — المنافذ الافتراضية 4317 (gRPC) و 4318 (HTTP): https://opentelemetry.io/docs/specs/otlp/

  2. OpenTelemetry Collector — الإعدادات (المستقبلات، المعالجات، المصادر بما في ذلك مصدر debug والملاحظة بأنه كان logging قبل الإصدار v0.86.0؛ آخر تعديل 29-05-2026): https://opentelemetry.io/docs/collector/configuration/ 2 3 4 5

  3. إصدارات Node.js (24 Active LTS) وعلم --import (مستقر منذ 20.6): https://nodejs.org/en/about/previous-releases

  4. OpenTelemetry JavaScript — دليل البداية (Node.js)، و SDK، والمصدرون (exporters)، وإعدادات أخذ العينات (sampler): https://opentelemetry.io/docs/languages/js/getting-started/nodejs/ و https://opentelemetry.io/docs/languages/sdk-configuration/general/ 2 3 4 5 6 7 8 9 10

  5. إصدارات OpenTelemetry Collector (تاجات صور Collector؛ قم بتثبيت إصدار 0.15x الحالي): https://GitHub.com/open-telemetry/opentelemetry-collector-releases/releases

  6. تحميلات Jaeger وملاحظات الإصدار الثاني v2 (الأحدث 2.19.0؛ نهاية دعم الإصدار الأول v1 في 2025-12-31؛ الإصدار الثاني v2 مبني على OTel Collector مع تفعيل OTLP افتراضياً): https://www.jaegertracing.io/download/ 2 3

  7. اتفاقيات التسمية الدلالية (semantic conventions) لـ OpenTelemetry في JS (ATTR_SERVICE_NAME، ATTR_SERVICE_VERSION، ATTR_DEPLOYMENT_ENVIRONMENT_NAME): https://opentelemetry.io/docs/specs/semconv/

  8. ملف README لمعالج محدد الذاكرة (Memory Limiter Processor) (check_interval، limit_mib، spike_limit_mib؛ ضعه أولاً في خط الأنابيب): https://GitHub.com/open-telemetry/opentelemetry-collector/blob/main/processor/memorylimiterprocessor/README.md 2

  9. مدونة OpenTelemetry — الانتقال بعيداً عن مصدر Jaeger (Jaeger exporter) في Collector (تمت إزالة مصدر jaeger الأصلي؛ أرسل OTLP بدلاً منه): https://opentelemetry.io/blog/2023/jaeger-exporter-collector-migration/ 2