عندما يقول المستخدم إن الصفحة بطيئة، لا تكفي رسالة عامة أو معرفة أن CPU ارتفع. تحتاج أدوات التشغيل إلى الإجابة عن أسئلة حول الأعراض ومكان التأخير وتسلسل الخدمات وتأثير المشكلة. تجمع Observability بين السجلات والمقاييس والتتبع الموزع ضمن سياق يساعد على التشخيص والتحسين.
ما ستخرج به من المقال
- إشارات تغطي صحة النظام وتجربة المستخدم.
- ربط الطلب عبر الخدمات باستخدام trace context.
- حماية البيانات الحساسة والتنبيهات القابلة للتصرف.
Logs: ماذا حدث؟
السجلات تصف أحداثًا مثل فشل اتصال أو إتمام مهمة. يفضل أن تكون منظمة وتحتوي timestamp وlevel وservice وrequest ID أو trace ID عند توفره. السجل المتسق يسهل البحث والتجميع، بينما النص الحر غير المنظم أصعب للتحليل. لا تسجل كل شيء إلى الأبد؛ استخدم مستويات مناسبة وامتنع عن تسجيل كلمات المرور والرموز السرية والبيانات الشخصية غير اللازمة. احتفظ بسياق يساعد التحقيق دون تسريب أجسام طلبات كاملة.
Metrics: كيف تغيرت الحالة؟
المقاييس قياسات رقمية عبر الزمن مثل معدل الطلبات والأخطاء وزمن الاستجابة والذاكرة. تساعد مؤشرات Rate وErrors وDuration مع تشبع الموارد على معرفة ما إذا كانت المشكلة عامة أو محصورة بخدمة. تجنب وضع معرف مستخدم أو URL فريد في labels عالية الكاردينالية لأنه يرفع عدد السلاسل وكلفة التخزين؛ انقل التفاصيل الفردية إلى logs أو traces حيث يناسب.
Traces: أين قضى الطلب وقته؟
يساعد التتبع الموزع على متابعة طلب عبر الخدمات. يتكون trace من spans تمثل أجزاء مثل طلب API أو استعلام قاعدة بيانات أو اتصال خارجي. عندما تظهر span بطيئة تستطيع معرفة مكان التأخير. مرر trace context بين الخدمات عند دعمه، واختر attributes تفيد التشخيص دون كشف أسرار. قد يقلل sampling الكلفة لكنه يتطلب سياسة تحافظ على احتمال رؤية الأحداث المهمة.
تنبيهات قابلة للتصرف
التنبيه الجيد يشير إلى مشكلة تؤثر في المستخدم أو تهدد هدف خدمة. لا ترسل تنبيهًا عاجلًا لكل ارتفاع عابر في CPU، ولا تتجاهل ارتفاع الأخطاء. اربط التنبيه بلوحة معلومات وخطوات أولية للاستجابة. يمكن استخدام SLOs لزمن الاستجابة أو نسبة النجاح، بحيث ترتبط المقاييس بما يهم المستخدم. ابدأ ببنية صغيرة مثل logs منظمة ومعرف طلب، ثم أضف OpenTelemetry وtraces عند الحاجة، وراقب نظام المراقبة نفسه.
سجل منظم بلا أسرار
logger.info({
event: "order_created",
orderId: order.id,
requestId,
durationMs,
result: "success"
}, "Order created");
// لا تسجل كلمات مرور أو access tokens.
اربط السجل بالطلب والمقياس
السجل المفيد يسجل حدثًا ببنية ثابتة: نوع الحدث، ومعرف ارتباط، والمدة، والنتيجة، وربما معرف مورد غير حساس. هذه الحقول تسمح بالبحث والتجميع من دون تحليل جملة نصية حرة في كل مرة. عندما يسأل المستخدم عن طلب فاشل، يمكن استخدام requestId للوصول إلى مراحل الخدمة المرتبطة، بشرط تمريره بين الخدمات.
المقاييس تظهر إن كان الفشل حادثة فردية أو نمطًا مستمرًا، بينما trace يوضح أين قضى الطلب وقته عبر المكونات. لا يكفي وجود هذه الأنواع الثلاثة؛ يجب أن تتشارك معرّفات وسياقًا متسقًا، وإلا بقيت كل أداة منفصلة ولا تقدم صورة عملية عن المشكلة.
دراسة حالة مفصلة: من العرض إلى السبب الجذري
الحالة العملية هنا هي: يرتفع زمن الطلب عبر عدة خدمات ولا يستطيع الفريق تحديد موضع التأخير لغياب معرف ارتباط مشترك. لا يكفي أن نصلح العَرَض الظاهر؛ ينبغي رسم مسار البيانات أو الطلب من بدايته حتى النتيجة، ومعرفة أين نشأ الاختلاف. ابدأ بتسجيل ما حدث، وما المتوقع أن يحدث، والخطوة التي يمكن إثباتها بالقياس أو السجل أو الاختبار. بعد ذلك غيّر عاملًا واحدًا واضحًا، ثم أعد السيناريو نفسه للتحقق من أن السبب الجذري عولج. إذا تغيرت عدة مكونات دفعة واحدة، فسيصعب معرفة أيها أحدث الفرق.
اربط نتيجة الحالة بمبدأ المقال: Logs: ماذا حدث؟ وMetrics: كيف تغيرت الحالة؟. هذان المفهومان يحددان ما الذي يجب فحصه أولًا وما الذي يمكن تأجيله. سجّل أيضًا الحالات التي لم تختبرها، حتى لا يتحول نجاح سيناريو واحد إلى ادعاء بأن النظام كله خالٍ من المشكلة.
اختر مقاييس تقود إلى قرار
مقاييس الطلبات مثل المعدل والأخطاء والمدة (RED) مفيدة للخدمات التي تستقبل طلبات، بينما تساعد مؤشرات الاستخدام والضغط على الموارد (USE) في فهم CPU والذاكرة والقرص والشبكة. اختر المقاييس بناء على سؤال تشغيلي محدد، لا على كثرة الرسوم. المتوسط قد يخفي بطئًا في نسبة صغيرة من الطلبات، لذلك راقب المئينات المناسبة وعدد العينات.
لا تصنع metric label من معرف مستخدم أو طلب فريد بلا حدود؛ ذلك قد يسبب cardinality عالية وتكاليف تخزين كبيرة. استخدم أبعادًا محدودة مثل الخدمة والمنطقة ونوع النتيجة، واحتفظ بالمعرفات الفردية في logs أو traces عند الحاجة إلى التحقيق.
من المثال إلى التنفيذ في مشروع حقيقي
ابدأ من المثال البرمجي المعروض في المقال: «سجل منظم بلا أسرار». لا تنقله حرفيًا إلى الإنتاج قبل أن تحدد مدخلاته ومخرجاته وافتراضاته. اسأل ما الذي يحدث عند غياب قيمة، أو وصول بيانات غير متوقعة، أو تكرار الطلب، أو تأخر الاعتماد الخارجي. بعد ذلك نفّذ نسخة صغيرة داخل فرع مستقل وأضف اختبارًا للحالة الأساسية وآخر لحالة فشل.
استخدم هذه الخطوات كبداية: حدد أسئلة التشغيل التي تحتاج إجابة. ثم أضف logs منظمة ومعرفات طلب مترابطة. ثم راقب معدل الطلبات والأخطاء والزمن والتشبع.. اجعل كل خطوة قابلة للمراجعة، ولا تجمع إعدادات المشروع الحقيقية والأسرار داخل مثال تعليمي. إذا اختلفت بيئتك عن المثال، وثّق الاختلاف وعدّل التنفيذ بناءً على التوثيق الرسمي بدل نسخ الحل دون فهم.
انتقل من التنبيه إلى السبب الجذري
عندما ينبهك النظام إلى ارتفاع الأخطاء، ابدأ بتحديد نطاقها: أي خدمة، وأي منطقة، ومنذ متى، وهل تزامنت مع نشر أو زيادة حمل؟ استخدم المقياس لتحديد الفترة، والسجلات لفهم حالات معينة، وtrace لفحص الطلب الذي تأخر. لا تفترض أن أول span بطيء هو السبب إذا كان مجرد انتظار لخدمة أخرى.
صمم runbook لكل تنبيه مهم يذكر ما الذي يجب فحصه، وما الإجراء الآمن الأول، ومتى يجب التصعيد. التنبيه الذي لا يملك استجابة ممكنة يتحول إلى ضوضاء. راجع التنبيهات الكاذبة والمتكررة بعد الحوادث، وعدّل الحدود أو البيانات التي تعتمد عليها.
أسلوب تشخيص منهجي عند الفشل
عندما لا يعمل الحل، لا تبدأ بإضافة شرط آخر قبل تحديد دليل الفشل. دوّن المدخل، والنتيجة المتوقعة، والنتيجة الفعلية، والرسالة أو القياس الذي يدعم الاستنتاج. من الأخطاء المتكررة في هذا الموضوع: تسجيل أسرار أو بيانات حساسة.. تعامل مع هذه النقطة كفرضية للفحص، لا كتشخيص مسبق؛ فقد يكون سبب المشكلة في طبقة مجاورة أو في افتراض غير صحيح بالاختبار.
قسّم النظام إلى حدود واضحة: مدخلات، ومعالجة، واعتماد خارجي، ونتيجة. اختبر كل حد على حدة قدر الإمكان، ثم نفّذ سيناريو متكاملًا. إذا أصلحت المشكلة، أضف اختبارًا يمنع رجوعها أو ملاحظة تشغيلية تساعد على كشفها مبكرًا. الهدف أن يصبح التشخيص قابلًا للتكرار من عضو آخر بالفريق، لا أن يظل مرتبطًا بذاكرة الشخص الذي اكتشف العطل.
الخصوصية والكلفة والاحتفاظ
سجلات الإنتاج قد تجمع تفاصيل أكثر مما تحتاجه للتشخيص. لا تسجل كلمات مرور أو access tokens أو محتوى خاصًا كاملًا؛ استخدم إخفاءً أو تقليلًا للبيانات قبل الإرسال إلى منصة المراقبة. حدد مدة الاحتفاظ وأذونات الوصول والتشفير، خصوصًا عندما تحتوي السجلات على معرفات أشخاص أو أحداث حسابات.
لا ترفع sampling في traces إلى 100% طوال الوقت من دون حساب الكلفة. يمكن استخدام عينة أساسية مع الاحتفاظ الأعلى بالأخطاء أو الحالات النادرة حسب الأدوات المتاحة. يجب أن تعكس المراقبة أهم سلوكيات المنتج وأن تتحمل زيادة الحمل أثناء الحوادث دون أن تصبح هي نفسها سببًا في استهلاك الموارد.
اعتبارات متقدمة قبل اعتماد الحل
في المشروع الحقيقي، لا يُقيّم الحل على نجاح الحالة السعيدة فقط. راجع السلوك عند الحمل، وحالات الفشل، والتغييرات المستقبلية، والحدود الأمنية ذات الصلة. من المهم هنا أن اربط السجلات والمقاييس والتتبعات بسياق موحد، وقلل البيانات الحساسة والأبعاد عالية الكلفة. لا تُعامل هذه النقاط كملحق لاحق؛ فقرار التصميم قد يصعب تغييره عندما تتراكم البيانات أو يعتمد عليه عملاء متعددون.
حدّد مقاييس أو اختبارات قبول قبل الدمج، ودوّن حدود الحل بوضوح. قد يكون الحل مناسبًا لموقع صغير لكنه يحتاج طابورًا أو cache أو بنية أكثر صرامة عند نمو الاستخدام. لا تضف تعقيدًا مسبقًا بلا دليل، لكن لا تتجاهل علامة واضحة على أن المتطلبات تتجاوز النسخة البسيطة. القرار الجيد يوازن البساطة الحالية مع كلفة التغيير المتوقعة ويحافظ على وضوح النظام للفريق.
حوّل القياس إلى أهداف خدمة
حدد SLI قابلًا للقياس مثل نسبة الطلبات الناجحة أو زمن الاستجابة في المئين 95، ثم اختر SLO واقعيًا يمثل توقع المستخدم. لا تضع هدفًا لا تستطيع قياسه أو لا يملك الفريق قرارًا يؤثر فيه. اربط الاستهلاك الزائد لميزانية الخطأ بأولوية إصلاح الاستقرار مقابل إطلاق ميزات جديدة وفق اتفاق الفريق.
بعد حادثة، اكتب ملخصًا يصف الأثر والزمن والسبب والعوامل المساعدة والإجراءات الوقائية. ليس الغرض لوم شخص، بل تقليل احتمال التكرار وتحسين سرعة الكشف والاستجابة. راجع إن كانت المقاييس والتنبيهات ساعدت فعلًا على معرفة المشكلة أو احتاج الفريق وقتًا طويلًا لاكتشافها.
تطبيق عملي ومعايير قبول
جرّب هذا التمرين على مشروع تجريبي أو فرع منفصل: تتبع طلبًا واحدًا عبر ثلاث طبقات باستخدام requestId واكتشف نقطة التأخير. اكتب النتيجة التي تتوقعها قبل التنفيذ، وسجّل ما حدث بالفعل، ثم أصلح الفرق إن ظهر. جرّب كذلك حالة حدودية أو فشلًا مقصودًا بصورة آمنة. لا تستخدم بيانات حقيقية أو نظامًا إنتاجيًا لإحداث فشل تجريبي.
اعتبر المهمة مكتملة عندما تكون خطوات إعادة التجربة موثقة، والاختبار يثبت السلوك الأساسي وحالة الفشل، ولا توجد أسرار أو معلومات حساسة في السجل، ويستطيع مطور آخر فهم سبب الحل. إذا لم يتحقق هذا، فالمطلوب ليس إضافة مزيد من الكود مباشرة؛ أعد صياغة المشكلة ووضح الافتراض الذي لم يكن صحيحًا. هذه الطريقة تجعل المقال نقطة انطلاق لممارسة تقنية، لا مجرد قراءة نظرية.
أسئلة شائعة عن Observability
هل logs وحدها تكفي؟ غالبًا لا؛ هي تسجل أحداثًا لكن المقاييس تكشف الاتجاه وtraces تربط الرحلة عبر الخدمات.
هل أرسل كل البيانات بلا sampling؟ يعتمد على الحجم والكلفة والاحتياجات؛ يجب تحديد سياسة واعية.
ما التنبيه الجيد؟ تنبيه يرتبط بأثر على المستخدم أو خطر تشغيلي ويقترح خطوة يمكن تنفيذها.
خطة تطبيق عملية
- حدد أسئلة التشغيل التي تحتاج إجابة.
- أضف logs منظمة ومعرفات طلب مترابطة.
- راقب معدل الطلبات والأخطاء والزمن والتشبع.
- أضف traces للمسارات البطيئة.
- اكتب تنبيهات بدليل استجابة واختبر المراقبة.
أخطاء شائعة انتبه لها
- تسجيل أسرار أو بيانات حساسة.
- تنبيهات كثيرة لا تتطلب إجراءً واضحًا.
- وضع كل معرف فريد في labels للمقاييس.
الخلاصة
Observability الجيدة تربط الحدث بالمعدل وبمسار الطلب. اجمع إشارات منظمة، واحمِ البيانات، واجعل التنبيهات قابلة للتصرف. الهدف تقليل وقت اكتشاف المشكلة وفهمها وحلها، لا جمع أكبر كمية من السجلات.