ممارسات التسجيل المنظم
ممارسات التسجيل المنظم
السجلات هي أقدم إشارة للمراقبة، وهي أيضاً الأكثر إساءةً في الاستخدام. تنتج استراتيجية التسجيل السيئة تيرابايتات من الضوضاء، وفواتير Datadog بـ50 ألف دولار شهرياً، ومهندسين يبحثون بأمر grep في جدران من النص غير المنسق الساعة 3 صباحاً بينما يشتعل حادث في الإنتاج. أما الاستراتيجية الجيدة فتنتج سجلاً قابلاً للبحث والتربط وفعّالاً من حيث التكلفة لكل ما فعله نظامك — ولماذا.
يغطي هذا الدرس الممارسات الثلاث التي تفصل التسجيل الاحترافي عن الفوضى: السجلات المنظمة (JSON)، ومعرّفات الترابط، ومستويات السجل المنضبطة. هذه هي الإعدادات الافتراضية في كل شركة تقنية كبرى لسبب وجيه.
لماذا سجلات JSON أفضل من النص العادي
عالم التسجيل القديم يبدو هكذا: 2024-03-15 10:23:41 INFO User 4291 placed order 8820. هذا السطر مقروء للإنسان بشكل منفرد، لكنه شبه عديم الفائدة على نطاق واسع. تحليله يتطلب تعبيرات regex مخصصة لكل خدمة، وإضافة حقل جديد يعني كسر لوحات التحكم الموجودة.
التسجيل المنظم يعني إصدار كل إدخال سجل كوثيقة قابلة للقراءة آلياً — JSON في الغالب — حيث كل حقل زوج مفتاح-قيمة بنوع محدد. يصبح نفس الحدث:
الآن يمكن لأي منصة تجميع سجلات — Elasticsearch أو Loki أو Splunk أو CloudWatch — فهرسة كل حقل. يمكنك تصفية level:error AND service:order-svc AND amount_cents:>10000 في ملي ثانية دون كتابة أي regex. لوحات التحكم يمكنها تجميع avg(duration_ms) مجمّعاً حسب service. هذا ليس كمالياً على نطاق واسع؛ إنه متطلب أساسي للمراقبة.
الحقول المطلوبة: ما يجب أن تحمله كل سطر سجل
اتفق على مخطط موحد عبر جميع الخدمات، يُفرَّض على مستوى مكتبة التسجيل — وليس بالاعتماد على المطورين الأفراد لتذكره. كحد أدنى، يجب أن يحمل كل سطر سجل:
- timestamp — UTC، بصيغة ISO 8601 مع المللي ثانية. لا وقت محلي أبداً.
- level — نص بأحرف صغيرة:
debugأوinfoأوwarnأوerrorأوfatal. - service — اسم الخدمة المنطقية، وليس اسم المضيف.
- version — إصدار القطعة المنشورة أو SHA الالتزام. ضروري لربط خطأ بنشر.
- trace_id / correlation_id — تُغطى بالتفصيل أدناه.
- message — نص قصير ثابت. لا تبنِ الرسالة بدمج نصوص ديناميكية؛ ضع تلك البيانات في حقول مخصصة.
- env —
productionأوstagingأوdev.
"message": "order.placed" وضع البيانات الديناميكية في "order_id": 8820. إذا تغيرت سلسلة الرسالة مع كل استدعاء، لا يمكن لمجمّعات السجل تجميع تكرارات نفس الحدث — ستحصل على 10,000 سطر سجل فريد بدلاً من حدث واحد أُطلق 10,000 مرة.معرّفات الترابط: تتبع طلب عبر الخدمات
طلب مستخدم واحد في نظام الخدمات المصغرة يلمس 5 إلى 20 خدمة. عندما يفشل ذلك الطلب، تحتاج لمتابعة السلسلة من بوابة API عبر خدمة المصادقة وخدمة المنتجات وخدمة الدفع وخدمة الإشعارات. بدون معرّف مشترك أنت تطابق الطوابع الزمنية وتتمنى.
معرّف الترابط (يُسمى أيضاً trace ID أو request ID) هو معرّف فريد يُنشأ عند نقطة دخول النظام — بوابة API أو موزع الحمل — ويُنقل كرأس HTTP عبر كل استدعاء لاحق. كل خدمة تقرأ الرأس وتُرفق المعرّف بكل سطر سجل تُصدره لذلك الطلب.
آلية النقل القياسية هي رؤوس HTTP. معيار W3C Trace Context (traceparent / tracestate) هو المعيار الحديث المستخدم في OpenTelemetry وZipkin وJaeger. إذا لم تستخدم بعد مكدس التتبع الكامل، حتى رأس مخصص بسيط مثل X-Request-ID يُعدّ تحسناً هائلاً مقارنة بلا شيء.
traceparent. في Kubernetes غالباً تتم هذه العملية تلقائياً بواسطة شبكة الخدمات (Istio / Linkerd)، لكن لا تفترض ذلك أبداً — تحقق باختبار فعلي وأكّد وصول الرأس للخدمات الفرعية.مستويات السجل بشكل صحيح
مستويات السجل موجودة لتتيح لك ضبط التفصيل في وقت التشغيل دون إعادة نشر. عند الاستخدام الصحيح تتيح لك العمل بهدوء في الإنتاج، ورفع مستوى التفاصيل خلال الحوادث، وعدم الدفع أبداً مقابل سجلات لا تحتاجها. عند الاستخدام الخاطئ، كل سطر INFO، فاتورتك 40 ألف دولار شهرياً، والإشارة مدفونة في الضوضاء.
إليك الدلالات القياسية لكل مستوى كما تُستخدم في أنظمة الإنتاج واسعة النطاق:
- DEBUG — حالة داخلية تفصيلية، قيم المتغيرات، الخطوات الوسيطة. يجب أن يكون معطّلاً افتراضياً في الإنتاج. فعّله ديناميكياً لخدمة محددة خلال تحقيق نشط. التكلفة: حجم عالٍ وتكلفة عالية.
- INFO — أحداث العمل المهمة: اكتمل طلب بنجاح، بدأت مهمة، سجل مستخدم دخوله. يجب أن تكون ذات معنى لشخص غير مطوّر يقرأ السجلات. تجنب تسجيل كل استدعاء دالة كـINFO.
- WARN — حدث شيء غير متوقع لكن النظام تعافى. نجحت إعادة المحاولة. استُدعيت نقطة نهاية API مهجورة. فتح قاطع الدائرة وتراجع. يستحق التتبع كمؤشر أولي للأخطاء المستقبلية.
- ERROR — فشلت عملية ولم يستطع النظام التعافي منها لهذا الطلب. أدرج دائماً الاستثناء/تتبع المكدس. يجب إنذار المهندس المناوب إذا استمر. لا تستخدم ERROR للفشل المتوقع كإدخال المستخدم كلمة مرور خاطئة.
- FATAL — العملية على وشك الخروج بسبب حالة غير قابلة للاسترداد (OOM، تكوين فاسد، فقدان اتصال قاعدة البيانات عند البدء). نادر ودائماً مثير للقلق.
التكوين العملي: التسجيل المنظم مع Pino (Node.js)
معظم مكتبات التسجيل الحديثة تدعم الإخراج المنظم أصلاً. Pino لـNode.js وZap لـGo هي المعيار الذهبي للأداء. إليك إعداد Pino جاهز للإنتاج يغطي كل ما سبق:
نقاط رئيسية: LOG_LEVEL متغير بيئة حتى يتمكن الفريق التشغيلي من تغيير التفصيلية دون إعادة نشر. خيار redact يزيل الأسرار قبل أن تصل إلى تدفق السجل — هذا غير اختياري؛ معيارا PCI-DSS وSOC 2 يتطلبانه. يتحول النقل لطباعة جميلة محلياً بينما يُصدر JSON خاماً في الإنتاج.
أخذ العينات والتحكم في التكلفة
عند أحجام حركة مرور عالية، تسجيل كل طلب بمستوى INFO ينتج كميات بيانات هائلة. خدمة تتعامل مع 100,000 طلب في الثانية، كل منها يُصدر 5 أسطر سجل، هي 500,000 سطر في الثانية — عشرات الغيغابايت في الساعة لكل خدمة. تكلفة استيعاب السجلات في Datadog أو Splunk أو New Relic بهذا الحجم تتجاوز تكلفة الحوسبة للخدمة نفسها.
استراتيجيات الإنتاج للتحكم في التكلفة دون فقدان الإشارة:
- أخذ عينات من الرأس: سجّل 1% من الطلبات الناجحة بتفصيل كامل؛ سجّل دائماً 100% من الأخطاء والتحذيرات. طبّق على مستوى البوابة بـ
X-Sample-Rateحتى تحترم جميع الخدمات قرار أخذ العينات نفسه. - أخذ عينات من الذيل: خزّن السجلات في الذاكرة مؤقتاً، وأفرغها فقط إذا استغرق الطلب أكثر من 500ms أو أسفر عن خطأ. يتطلب وكيلاً ذكياً (OpenTelemetry Collector مع معالج أخذ عينات من الذيل).
- فهرسة حقول مختارة: معظم الخلفيات تتيح تخزين حمولة السجل الكاملة لكن فهرسة حقول محددة فقط. فهرس
levelوserviceوtrace_idوحقول الأحداث الرئيسية؛ خزّن الباقي كسمات باردة.
التسجيل المنظم هو الأساس الذي تُبنى عليه لوحات التحكم والتتبع الموزع والتنبيهات. أتقنه من اليوم الأول وكل استثمار آخر في المراقبة يتراكم فوقه. أضفه لاحقاً لنظام قائم وستدفع الثمن مرتين.