كيف تراقب سير عمل n8n في الإنتاج؟

لقد كنت أفكر في مشكلة تصبح مؤلمة بمجرد أن يكون لديك عشرات سير العمل الإنتاجية:

كيف تعرف متى توقفت الأتمتة بصمت عن القيام بما يجب أن تفعله؟

أخطاء التنفيذ سهلة نسبياً في الكشف عنها.

الحالات الأصعب هي:

  • سير العمل ينفذ بنجاح لكنه ينتج مخرجات سيئة/فارغة
  • webhook يتوقف عن استقبال الأحداث
  • تغيير سلوك API الأعلى مستوى
  • سير العمل لم ينفذ منذ وقت طويل بشكل غير معتاد
  • النظام الأمامي يتوقف عن استقبال البيانات المتوقعة

فضولي كيف يتعامل الأشخاص الذين يشغلون n8n في الإنتاج مع هذا اليوم.

هل تعتمد على معالجة التنفيذ/الأخطاء المدمجة في n8n، أم على التنبيهات المخصصة، أم على المراقبة الخارجية، أم على شيء آخر؟

سؤال جيد — هذا هو بالضبط نمط الفشل الذي يؤلم أكثر لأن لا شيء يرمي خطأ.

إليك ما نجح عبر سير العمل التي أحافظ عليها:

الطبقة 1: سير عمل الخطأ (الخط الأساسي)

عيّن سير عمل خطأ عام في إعدادات n8n. كل تنفيذ فاشل غير معالج يصل إليه وينشره في Slack/Telegram فوراً. يغطي هذا الأخطاء الواضحة لكنه يفقد الأخطاء الصامتة.

الطبقة 2: عقد التحقق من المخرجات

بعد أي طلب HTTP إلى API خارجي، أضيف عقدة Filter أو IF التي تتحقق من نص الاستجابة عن إشارات النجاح — وليس فقط حالة HTTP. تعيد العديد من واجهات البرمجة 200 OK مع {"success": false} مدفونة في JSON. بدون هذا التحقق، يرى n8n تنفيذاً ناجحاً ويمضي قدماً.

الطبقة 3: كشف نبض القلب/الركود

بالنسبة لسير العمل المجدول الحرج، أكتب طابع زمني إلى ورقة Google أو Airtable بعد كل تشغيل ناجح. سير عمل مراقبة منفصل يعمل كل بضع ساعات ويتحقق: “هل عمل سير العمل هذا في آخر N ساعة؟” إن لم يحدث → تنبيه Slack. يمسك هذا مهام cron المتوقفة، ومصادر webhook التي خفتت، والتغييرات في API الأساسية التي تسبب خروج سير العمل مبكراً بدون خطأ.

الطبقة 4: تنبيهات الفرع المغلق

أي فرع IF/Switch يجب أن “لا ينطلق أبداً” يحصل على عقدة تنبيه Slack في النهاية بدلاً من مجرد الإنهاء. الخروج الصامت هو خطأ — تعامل معه بهذه الطريقة.

كتبت تفصيلاً أكثر تفصيلاً لهذه الأنماط (خاصة حالات النجاح الصامتة) هنا إذا كان مفيداً: The silent failure: when your n8n workflow succeeds but does nothing

فضولي ما يبدو عليه إعدادك الحالي — هل تقوم بتشغيل الاستضافة الذاتية أم السحابة؟

مرحبا @KSD

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

شيئان يغطيان ذلك:

  1. Dead man’s switch بدلاً من المراقبة الذاتية. اجعل كل سير عمل حرج يرسل ping لخدمة خارجية مثل Healthchecks.io أو Cronitor بعد تشغيل ناجح. إذا توقف الـ ping عن الوصول، سيأتي التنبيه من خارج مجموعتك، لذا يعمل حتى عندما يكون n8n معطلاً تماماً. نفس المبدأ الموجود في الطبقة 3 لكنه ينجو من الحالة حيث لا يمكن لسير العمل المراقب نفسه أن يعمل.
  2. لا تُنتج التنفيذات المتعطلة أي خطأ على الإطلاق. Error Trigger يُطلق فقط عندما تُرجع عقدة خطأ، لذا التنفيذ الذي يتم إيقافه في منتصف التشغيل لا يصل إليه أبداً ولا يظهر أي مدة في السجل. يستحق تصفية قائمة التنفيذات حسب حالة Crashed من حين لآخر، تلك غير مرئية للطبقات 1 و 4.

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

لقد كنت أعمل على هذه المشكلة. الإجابة الجزئية التي لدي: تتبع آخر طابع زمني للتنفيذ لكل سير عمل، وأصدر تنبيهًا إذا لم يعمل لفترة أطول من فترته المعتادة. إنه يكتشف مهام cron المتعطلة والويبهوكات الصامتة. لا يكتشف تغييرات سلوك API الأعلى — هذا يحتاج إلى التحقق من الإخراج كما ذكرت.

لا توجد حلاً كاملاً لحالة الصمت حتى الآن. فضولي يجعلني أتساءل إذا كان أحد هنا قد حلها."

شيئان لا يوجدان في الطبقات أعلاه، كلاهما موجه مباشرة نحو حالتك “يعمل بشكل جيد، الإخراج خاطئ”.

أصدر تنبيهاً عند الانحراف وليس عند الصفر. النتائج الصفرية هي النسخة السهلة وأي فحص غير فارغ يمسكها. ما يكلفك فعلاً هو التشغيل الذي يعيد بصمت 60 بالمائة مما يعيده عادة، لأن ذلك يمر بكل تأكيد خلو تكتبه. احتفظ بعدد الصفوف من آخر N تشغيل في مكان رخيص وقارن كل تشغيل مقابل الوسيط المتحرك بدلاً من حد ثابت. تصبح الحدود الثابتة قديمة في اللحظة التي يتغير فيها حجمك الفعلي، وبعد ذلك يبدأ الناس في تجاهل التنبيه، وهو أسوأ من عدم وجود واحد.

احتفظ بمدخل canary واحد. اختر سجلاً واحداً تعرف ناتجه الصحيح يدويًا والذي لا يجب أن يتغير، وشغّله على جدول زمني بجانب العمل الفعلي، وأكد على القيمة المتوقعة الدقيقة بدلاً من الشكل. عندما تعيد تسمية واجهة برمجية تطبيقات في المنبع حقل بصمت أو تبدأ في اختصار قائمة، يفشل الكناري على مدخل معروف جيد، مما يخبرك على الفور أنها هي وليست بيانات. بدونها ستنتهي بك الحال تحدق في إخراج غريب محاولاً معرفة ما إذا كان المصدر قد تغير أم أن هذا المدخل بالذات كان فقط غير عادي.

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

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

هذا هو مستوى المراقبة الذي أحاول جعله تلقائياً للوكالات التي تدير عملاء متعددين — بحيث لا يضطرون إلى بناء كل من هذه الطبقات لكل سير عمل يدوياً. هذا ما يفعله Okum: okum.cloud

النهج الطبقي منطقي جداً. أنا خاصة أقدّر التمييز بين التحقق من المخرجات واكتشاف النبضات/التقادم — فهي تتقاط فئات مختلفة جداً من الأعطال.

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

أنا حالياً أستكشف طبقة مراقبة خارجية خصيصاً لهذا — شيء يمكنه مراقبة سير العمل دون أن يتطلب منك تعديل كل سير عمل باستخدام عقد IF/Filter/heartbeat.

للسياق، أنا أقوم بتشغيل هذا مقابل n8n Cloud في الوقت الحالي، لكنني فضولي كم يتغير نهجك بين الخادم الذاتي والسحابة.

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

نعم، أعتقد أن تتبع فترة التنفيذ المتوقعة مقابل الفعلية هو على الأرجح الاتجاه الصحيح.

يبدو أن الجزء الصعب هو تحديد معنى “أطول من المعتاد”. عتبة ثابتة تعمل بشكل جيد لسير عمل cron بسيط، لكنها تصبح صاخبة عندما تختلف أنماط التنفيذ بشكل طبيعي.

أنا أجرب النظر في سجل التنفيذ الخاص بسير العمل نفسه بدلاً من الاعتماد فقط على عتبة مكونة يدويًا — في الأساس أسأل “هل يتصرف سير العمل هذا بشكل مختلف عن نمطه الطبيعي؟”

لكنني لا أزال أعمل على الحالات الحدية، خاصة بالنسبة لسير العمل الذي يتم تشغيله بواسطة الأحداث/webhook حيث “تكرار التنفيذ المتوقع” ليس واضحًا تمامًا.

نقطة الوسيط المتحرك مثيرة للاهتمام حقاً. أتفق معك بأن عتبة ثابتة من نوع „عدد النتائج أقل من X = فشل

نعم — هذا تمييز مهم. لسير عمل المراقبة الداخلي نفس مجال الفشل للشيء الذي يراقبه.

نهج “مفتاح الرجل الميت” هو على الأرجح الحل الأنظف للسير عمل الحرجة: يجب على n8n أن تثبت أنها على قيد الحياة بإرسال نبضة قلب إلى شيء خارجي.

حالة التنفيذ المتعطل مثيرة للاهتمام أيضًا. لم أكن قد اعتبرتها فئة منفصلة عن فشل التنفيذ العادي — خاصة لأنه لا يوجد عمليًا حدث “Error Trigger” للرد عليه.

لذا أبدأ في التفكير في هذا كثلاث طبقات منفصلة:

  1. شيء فشل أثناء التنفيذ

  2. شيء تم تنفيذه لكنه أنتج نتيجة غير طبيعية

  3. شيء كان من المفترض أن يتم تنفيذه لم يتم أبدًا

ثم هناك طبقة رابعة: n8n نفسها ليست صحية بما يكفي لتقرير أي من الأعلاه.

هذا على الأرجح مشكلة المراقبة الأصعب للحل بشكل نظيف.

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

1. محفز الجدول الزمني الذي يكون نشطًا ولا يتم تنفيذه أبدًا.
إذا كان محفز الجدول الزمني المعين على فترة weeks يفتقد weeksInterval في JSON لسير العمل، فلن يتم تنفيذه أبدًا — وليس متأخرًا، وليس مرة واحدة. أكدنا هذا بطريقتين على n8n 2.31.5: بقراءة فحص التكرار في المصدر، ونشر نسخة من الملف بالضبط كما تم شحنها ومراقبة عدم حدوث شيء. قيمة triggerAtMinute المفقودة من نفس الفئة — تصبح بهدوء دقيقة عشوائية مشتقة من hash بدلاً من الدقيقة التي قصدتها.

شيئان يجعل هذا صعب الاكتشاف قبل الشحن:

  • التنفيذ اليدوي يتخطى فحص التكرار تماما. “اختبرته وعمل بشكل جيد” لا يحمل أي معلومات حول ما إذا كان المحفز سيتم تنفيذه من تلقاء نفسه.
  • فتح وحفظ عقدة المحفز مرة واحدة في الواجهة يطبع JSON ويملأ الحقل المفقود. سير عمل معطوب كملف يصبح صحيحًا في اللحظة التي تفتشه فيها في المحرر، لذا لا يمكن للاختبار القائم على الواجهة أن يثبت أن الملف الذي شحنته أو استوردته معطوب.

لماذا تفتقد الطبقات الأعلى: الطبقة 1 تحتاج إلى تنفيذ فاشل، والطبقة 3 والمفتاح الخارجي للموت يحتاجان كلاهما إلى “فترة عادية” أو أول ping للمقارنة. “لم يتم التشغيل لفترة أطول من المعتاد” ليس له معتاد عندما تكون الإجابة الحقيقية أبدًا. عدم وجود أي تنفيذات منذ النشر يستحق أن يكون له إنذاره الخاص، منفصلاً عن التوقف عن التشغيل.

الفحص الذي نقوم به الآن: نشر سير العمل بالضبط كما هو موجود كملف، بدون فتح عقدة المحفز، ثم الانتظار لتنفيذ الإنتاج. في قائمة التنفيذات، لا يحتوي التشغيل المجدول على أيقونة دورق والتشغيل اليدوي يحتوي عليها — هذا هو الدليل القابل للتحقق من الآلة بأن الجدول الزمني تم تنفيذه بدلاً منك.

مجاور، نفس الصمت: يتم تفسير الساعة في محفز الجدول الزمني في المنطقة الزمنية للمثيل (إعدادات سير العمل → المنطقة الزمنية)، وليس لك. تعيين 15:38 على آلة US-Central التي كان المثيل الخاص بها مضبوطًا على America/New_York يعني 14:38 بالتوقيت المحلي، بالفعل في الماضي، لذا فإن تشغيل ذلك اليوم لم يحدث ببساطة.

2. العقدة المعطلة تمر عبر كل طبقة تحقق وتقلل بهدوء المخرجات.
تُستبعد العقدة المتروكة "disabled": true من فحص التفعيل — قرأنا هذا في ثلاثة أماكن في مثيل 2.31.5 الخاص بنا: خدمة التحقق من الخادم، مسار التفعيل الذي يستدعيها، وحزمة الواجهة الأمامية. في تشغيل الإنتاج، تيسر العقدة المعطلة أيضًا إدخالها مباشرة. لذا يُبلّغ التنفيذ عن النجاح، والمخرجات خاطئة بالضبط بالطريقة “تعمل بشكل جيد، المخرجات خاطئة” الموصوفة أعلاه، ولا شيء في أي مكان يشير إليها. يتم إدخال هذا في وقت التعديل بدلاً من التغيير في المنطقة الأعلى، لذا فإن الكناري يكتشفها فقط إذا كان الكناري يعمل من خلال نفس المسار.

تحذير النطاق: كل ما سبق يتم قياسه على 2.31.5 و 2.32.6 المستضاف ذاتيًا. ليس لدينا قياسات Cloud خاصة بنا.

بخصوص الجزء الذي سألت عنه فعلاً، مراقبة سير العمل دون تحرير كل منها، توفره واجهة برمجة التطبيقات العامة من الخارج. GET /api/v1/executions يُرجع workflowId و status و mode و startedAt و stoppedAt لكل تشغيل، لذا يمكن لمراقب خارجي واحد اشتقاق فحص الركود وخط أساس متدرج لكل سير عمل من عدد التشغيلات والمدة دون أي عقدة نبض في أي مكان. قم بالتصفية حسب وضع الإنتاج واستبعد التشغيلات المدمجة، وإلا فإن صفوف سير العمل الفرعي تضخم خط الأساس الذي تقارن به. بالنسبة لحالة الانحراف التدريجي، اطلب التنفيذ مع includeData وأكد على عدد العناصر في العقدة الأخيرة، وهذا ما يمسك بالتشغيل الذي يعود بنسبة 60 بالمائة وينجح في كل فحص الخلو أعلاه. السحابة والمضيف الذاتي يتصرفان بنفس الطريقة هنا، والفرق الوحيد هو مكان حصول مفتاح API، Settings > n8n API على المثيل.