أسس قابلية الرصد

قياس التطبيقات وتفعيل المراقبة

18 دقيقة الدرس 5 من 28

قياس التطبيقات وتفعيل المراقبة

لا تحدث القابلية للمراقبة تلقائياً. لا يُصدر أي عملية تشغيل أي شيء مفيد حتى يضيف المهندس عمداً كود القياس — وهو الكود الذي يولّد المقاييس والتتبعات والسجلات باعتبارها متطلباً أساسياً لا إضافةً لاحقة. في شركات مثل Google وMeta وStripe، تُعدّ أدوات القياس معياراً لجاهزية الإنتاج: لا يمكن شحن أي خدمة دون تحديد مقاييس SLI والمُصدِّرات المنظمة للسجلات وسياق التتبع المنتشر في جميع مسارات الطلبات.

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

مكتبات القياس: OpenTelemetry أولاً

تقاربت الصناعة على OpenTelemetry (OTel) بوصفه المعيار المحايد من الموردين لأدوات القياس. تشحن OTel حزم SDK لـ Go وJava وPython وNode.js و.NET وRuby وRust وPHP وغيرها. وتنتج الأنواع الثلاثة من الإشارات (المقاييس والتتبعات والسجلات) من خلال واجهة API موحدة، وتُصدّر إلى أي backend — Prometheus وJaeger وTempo وDatadog وHoneycomb — عبر OTel Collector دون تعديل كود التطبيق.

قبل OTel كان عليك الاختيار: StatsD للمقاييس، وOpenTracing للتتبعات، ومكتبة سجلات مخصصة. هذا التشتت يعني أن كل فريق يبني أدواته الوسيطة الخاصة. أما OTel فيُنهي ذلك. الخيار الافتراضي الصحيح في 2025 هو:

  • المقاييس — OTel SDK أو مكتبة عميل Prometheus (كلاهما جيد؛ مكتبة Prometheus أكثر رسوخاً للمقاييس الصرفة).
  • التتبعات — OTel SDK مع تصدير OTLP. لا تستخدم SDK الأصلي لـ Jaeger — فهو موقوف.
  • السجلات — OTel Logs SDK (لا يزال في طور النضج) أو مُسجِّل منظم (zerolog أو zap أو logrus) يُصدر JSON عبر OTel Collector.
القياس التلقائي مقابل اليدوي: توفر OTel عوامل القياس التلقائي (وكيل Java، وauto-instrument لـ Python، وخطاف require لـ Node.js) تُجري تصحيحات على أطر العمل الشائعة عند الإطلاق دون تغييرات في الكود. هذه نقطة بداية ممتازة. أما القياس اليدوي فيُضيف امتدادات ومسميات خاصة بالنطاق لا يمكن لأي وكيل استنتاجها — كتصنيف امتداد بـ db.tenant_id أو تسجيل مقياس أعمال مثل orders.checkout.total_usd.

يبدو خدمة Python بسيطة مُوصَّلة بـ OTel على النحو التالي. ثبّت SDK ومُصدِّر OTLP، ثم هيّئ قبل تشغيل أي كود لإطار العمل:

# تثبيت الاعتماديات pip install opentelemetry-sdk \ opentelemetry-exporter-otlp-proto-grpc \ opentelemetry-instrumentation-fastapi # app/telemetry.py — استدعاء setup_telemetry() عند بدء العملية from opentelemetry import trace, metrics from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter from opentelemetry.sdk.resources import Resource def setup_telemetry(service_name: str, service_version: str): resource = Resource.create({ "service.name": service_name, "service.version": service_version, "deployment.environment": "production", }) # --- التتبعات --- tracer_provider = TracerProvider(resource=resource) tracer_provider.add_span_processor( BatchSpanProcessor(OTLPSpanExporter(endpoint="http://otel-collector:4317")) ) trace.set_tracer_provider(tracer_provider) # --- المقاييس --- reader = PeriodicExportingMetricReader( OTLPMetricExporter(endpoint="http://otel-collector:4317"), export_interval_millis=30_000, ) metrics.set_meter_provider(MeterProvider(resource=resource, metric_readers=[reader])) # main.py from app.telemetry import setup_telemetry setup_telemetry("checkout-service", "2.4.1") from fastapi import FastAPI from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor app = FastAPI() FastAPIInstrumentor.instrument_app(app) # يُوصّل جميع المسارات تلقائياً

اصطلاحات التسمية

اسم المقياس عقدٌ ملزم. بمجرد أن تستند لوحات المعلومات والتنبيهات وكتب التشغيل إلى http_requests_total، لا يمكنك إعادة تسميته بصمت دون كسر نصف أدوات المناوبة. تفرض شركات التقنية الكبرى التسمية عبر قواعد المدقق في CI وسجلات المخطط. اتبع هذه الاصطلاحات منذ البداية:

  • اصطلاح Prometheus / OTel: {namespace}_{subsystem}_{name}_{unit}. تأتي الوحدات في النهاية بصيغة الجمع snake_case: _seconds و_bytes و_total (للعدادات). مثال: checkout_payment_requests_total وcheckout_payment_latency_seconds.
  • لا اختصارات في الاسم الأساسي. http_req_dur_s سيُربك المهندس المُستدعى في الثالثة صباحاً. اكتب http_server_request_duration_seconds.
  • قواعد اللاحقة: تنتهي العدادات بـ _total؛ تتجاهل المدرجات التكرارية والملخصات اللاحقة (Prometheus تُلحقها تلقائياً)؛ تصف المقاييس ما تقيسه (_bytes و_connections و_queue_depth).
  • أسماء الامتدادات (التتبعات): استخدم فعل اسمGET /orders/{id} وdb.query orders وkafka.publish payment-events. لا تُدرج قيم المتغيرات في الاسم؛ ضعها في سمات الامتداد.
استخدم اصطلاحات OTel الدلالية. تشحن مواصفة OTel مجموعة كبيرة من أسماء السمات المعيارية — http.request.method وdb.system وmessaging.destination.name وrpc.method. استخدامها يعني أن بياناتك متوافقة مع لوحات المعلومات الجاهزة والترابطات عبر أي backend يدعم OTel. أعطِ دائماً الأولوية لسمة الاصطلاح الدلالي على السمة المخصصة.

انضباط البعد الأساسي

البعد الأساسي (Cardinality) هو عدد السلاسل الزمنية الفريدة التي يولّدها مقياس ما. مقياس بتسميات method (5 قيم) × status_code (10 قيم) × route (200 قيم) ينتج 10,000 سلسلة. أضف user_id (مليون مستخدم) وستحصل على عشرة مليارات سلسلة — Prometheus ينهار، وDatadog يُرسل لك فاتورة بستة أرقام.

القيم ذات البعد المرتفع — معرّفات المستخدمين ومعرّفات التتبع وعناوين البريد الإلكتروني والسلاسل الحرة والـ UUID وعناوين IP — يجب ألا تظهر أبداً كقيم لتسميات المقاييس. مكانها في سمات امتدادات التتبع وحقول السجلات المنظمة، حيث يتعامل نموذج التخزين معها بكفاءة.

نمط قنبلة البعد الأساسي: حادثة إنتاج شائعة هي إضافة مطوّر لـ labels={"customer_id": customer_id} إلى عداد. كل عميل جديد يُنشئ سلسلة جديدة. بعد حملة تسويقية تجلب موجة تسجيلات، يتعطل Prometheus بسبب نفاد الذاكرة وتتوقف التنبيهات تماماً في اللحظة التي تحتاجها فيها أكثر ما يكون. أضف التحقق من تسميات المقاييس إلى قائمة مراجعة PR وثقافة مراجعة الكود.

الأسلوب الصحيح هو تقييد كل تسمية بمجموعة صغيرة ومحدودة من القيم. لمسارات HTTP، قم بتطبيع معلمات المسار: /orders/12345 تصبح قيمة التسمية /orders/{id}. لرموز الحالة، جمّعها في فئات (2xx و4xx و5xx) إذا كان لديك رموز متعددة. لأي شيء آخر، اسأل: "هل ستنمو مجموعة قيم هذه التسمية بلا حدود؟" إذا كانت الإجابة نعم، انقلها إلى التتبعات.

# صحيح: تسميات محدودة — method وstatus_class وقالب المسار from opentelemetry import metrics import re meter = metrics.get_meter("checkout.http", version="1.0") request_counter = meter.create_counter( name="http_server_requests_total", description="إجمالي طلبات HTTP المُعالَجة", unit="{request}", ) request_duration = meter.create_histogram( name="http_server_request_duration_seconds", description="مدة طلب HTTP بالثواني", unit="s", ) def normalize_route(path: str) -> str: """استبدال معرفات الأرقام بـ {id} لمنع انفجار البعد الأساسي.""" return re.sub(r"/\d+", "/{id}", path) def record_request(method: str, path: str, status: int, duration: float): route = normalize_route(path) status_class = f"{status // 100}xx" labels = {"method": method, "route": route, "status_class": status_class} request_counter.add(1, labels) request_duration.record(duration, labels) # خاطئ — لا تفعل هذا أبداً: # request_counter.add(1, {"user_id": user.id, "email": user.email})

موضع القياس: الإشارات الذهبية الأربع

قِس كل حد خدمة باستخدام الإشارات الذهبية الأربع التي روّج لها Google SRE: زمن الاستجابة وحركة المرور والأخطاء والإشباع. كحد أدنى، يجب أن يُصدر كل معالج HTTP/gRPC وكل استدعاء خارجي لقاعدة البيانات أو الكاش وكل مهمة خلفية هذه الأنواع الأربع من الإشارات. استخدم نمط Middleware أو Interceptor حتى يكون القياس تلقائياً ومتسقاً — لا تعتمد على تذكر المطورين لإضافته في كل نقطة نهاية.

تدفق القياس: من التطبيق إلى backend المراقبة Application HTTP / gRPC / Jobs OTel SDK OTLP/gRPC OTel Collector Receive · Process Filter · Export Prometheus المقاييس Tempo / Jaeger التتبعات Loki / S3 السجلات Middleware / Interceptor يُوصّل الحدود تلقائياً
يفصل OTel Collector التطبيقَ عن الـ backend — بدّل Prometheus بـ Datadog دون لمس كود التطبيق.

اختبار أدوات القياس

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

  • استخدم مُصدِّر الذاكرة الداخلية من OTel في اختبارات الوحدة للتحقق من أن امتدادات ومقاييس محددة تنتجها مسارات الكود.
  • شغّل OTel Collector محلياً مع مُصدِّر debug لطباعة جميع بيانات القياس المُستلمة في stdout أثناء التطوير.
  • أضف نقطة نهاية /metrics أو /debug/metrics وقم بـ curl عليها كاختبار دخاني في CI بعد النشر في staging.
  • اضبط تنبيهاً على absent(up{job="my-service"}) في Prometheus — إذا توقف الحصول على البيانات، ستعلم بذلك فوراً.
قِس أدوات القياس نفسها. يُصدر OTel Collector مقاييس عن خط أنابيبه الخاص — الامتدادات المُهدرة وأخطاء التصدير وعمق الطابور. راقب otelcol_exporter_send_failed_spans_total ونبّه عليه. كثير من الفرق تكتشف ثغرات في بيانات القياس فقط أثناء حادثة، حين تكون البيانات في أشد الحاجة إليها.

القياس هو الأساس الذي يرتكز عليه كل شيء. احصل على اختيار المكتبة والتسمية وحدود البعد الأساسي بشكل صحيح من البداية — إضافتها بأثر رجعي عبر بنية تحتية من 200 خدمة مشروع مُكلف يمتد لأرباع متعددة. ابنِ العادة الآن: كل خدمة جديدة تشحن مع أدوات القياس قبل أن تشحن بأي ميزة واحدة.