السلسلة: هذا الدرس جزء من برنامج «أساسيات Linux 2026». للحصول على نظرة شاملة، اقرأ الدليل الرئيسي أولاً.
مقدمة
قبل ظهور systemd، كانت قراءة سجلات Linux تعني التنقل بين نصف دزينة من الملفات النصية في /var/log/، بأسماء وصيغ تختلف من توزيعة إلى أخرى. منذ الاعتماد الواسع لـ systemd، أصبح السجل مركزياً، مهيكلاً، مفهرساً، ويُستجوَب بأمر واحد: journalctl. إتقان هذه الأداة من أعلى الاستثمارات مردوديةً لمسؤول النظام. في 80% من حوادث الإنتاج، السبب الدقيق للعطل مكتوب في مكان ما داخل السجل — يبقى أن تعرف كيف تُصفّي للوصول إليه. هذا الدرس ينقلك من الصفر إلى استعلامات journalctl دقيقة في ثماني خطوات تطبيقية.
المتطلبات
- جهاز Linux مع systemd (أي توزيعة حديثة: Ubuntu 24.04/26.04 LTS، Debian 13، AlmaLinux 9، Rocky Linux 9).
- حساب مع صلاحيات sudo (قراءة السجل الكامل تتطلب امتيازات).
- معرفة أساسية بخدمات systemd — راجع درس systemd إن لزم.
- المستوى: مبتدئ متوسط.
- المدة المقدّرة: 75 دقيقة.
الخطوة 1 — فهم أين يعيش السجل
تلتقط خدمة systemd-journald ثلاثة تدفقات: المخرجات والأخطاء القياسية لكل خدمات systemd، رسائل النواة (dmesg)، والرسائل التاريخية لـ syslog. يُخزَّن كل ذلك في ملفات ثنائية تحت /var/log/journal/ (دائم) أو /run/log/journal/ (متطاير، يضيع عند إعادة التشغيل).
# التحقق من مكان تخزين السجل
sudo ls -la /var/log/journal/ 2>/dev/null && echo "Journal persistant" || echo "Journal volatil"
# المساحة المستخدمة
journalctl --disk-usage
# الإعدادات السارية
cat /etc/systemd/journald.conf
إذا لم يوجد /var/log/journal/، فالسجل متطاير — يختفي عند كل إعادة تشغيل، ما يجعل التشخيص بعد الحادث مستحيلاً. لجعله دائماً، أنشئ المجلد بـ sudo mkdir -p /var/log/journal && sudo systemd-tmpfiles --create --prefix /var/log/journal ثم sudo systemctl restart systemd-journald. على VPS صغيرة (1 جيغا RAM، 25 جيغا قرص)، فكّر في وضع سقف للحجم بـ SystemMaxUse=200M في /etc/systemd/journald.conf — بدون ذلك، قد ينمو السجل ليحتل 10% من قسم الجذر ويسبب أعطال «قرص ممتلئ».
الخطوة 2 — الاستعلامات الأساسية
خمسة أوامر تغطي 90% من الاستخدامات اليومية. تعلّمها عن ظهر قلب.
journalctl -u nginx # كامل سجل خدمة nginx
journalctl -u nginx -n 50 --no-pager # آخر 50 سطر، بدون pager
journalctl -u nginx -f # متابعة مباشرة (Ctrl-C للخروج)
journalctl -u nginx --since "1 hour ago" # منذ ساعة
journalctl -u nginx -p err # الأخطاء فقط (priorité ≤ err)
الراية -u تُصفّي حسب وحدة systemd؛ -n N تحدّ بآخر N سطر؛ -f تتابع السجل مباشرة (مكافئ tail -f)؛ --since و--until تحدّان نافذة زمنية؛ -p تُصفّي حسب الأولوية (من emerg إلى debug). الخيار --no-pager يرسل المخرجات مباشرة إلى الطرفية بدل فتح less، ضروري في السكربتات. بدون -n، يبدأ journalctl بعرض الأقدم؛ لرؤية الأحدث أولاً، أضف -r (reverse).
الخطوة 3 — التصفية بالنافذة الزمنية
عند وقوع حادث في ساعة محددة، تريد قصر القراءة على تلك النافذة، لا ابتلاع سجل ثلاثة أيام.
journalctl --since "2026-05-04 14:00:00" --until "2026-05-04 15:00:00"
journalctl --since today
journalctl --since yesterday --until today
journalctl --since "30 min ago"
journalctl --since "2 days ago" -p warning..err
تعبير "30 min ago" يُفسّر بنفس قواعد OnCalendar= في مؤقتات systemd. -p warning..err مجال أولويات. المستويات المتاحة: emerg (0)، alert (1)، crit (2)، err (3)، warning (4)، notice (5)، info (6)، debug (7). القاعدة العملية: على خادم إنتاجي، ابدأ بـ -p err لرصد العاجل الفعلي، انزل إلى -p warning عند الحاجة، ولا تعرض كل شيء (-p info) إلا كحل أخير للتشخيص الدقيق.
الخطوة 4 — التصفية حسب الإقلاع والربط
عندما يُعاد تشغيل خادم في اليوم نفسه، يغطي السجل عدة «إقلاعات» متتالية. يُميّزها journalctl بمعرّف فريد يُسمى boot ID.
journalctl --list-boots # قائمة الإقلاعات المفهرسة
# 0 9b3a7c8d... May 5 09:00 — May 5 14:30
# -1 4e2f1a5b... May 4 22:15 — May 5 08:55
journalctl -b # سجل الإقلاع الحالي
journalctl -b -1 # سجل الإقلاع السابق
journalctl -b 9b3a7c8d... -p err # أخطاء إقلاع محدد
journalctl -k # رسائل النواة للإقلاع الحالي فقط
journalctl -k -b -1 # رسائل النواة للإقلاع السابق
الراية -k هي البديل الحديث لأمر dmesg: تستخرج من السجل الأسطر المنبعثة من النواة — تلك المتعلقة بالتعريفات، العتاد، OOM Killer أو أخطاء الأقراص. لفهم لماذا قتل الخادم عمليتك في الثالثة فجراً، journalctl -k -b | grep -i "out of memory" يعطي الجواب غالباً في سطر واحد، مع PID، اسم العملية، وكمية الذاكرة المستهلكة لحظة القتل.
الخطوة 5 — مرشحات الحقول للدقة
كل مدخل في السجل يحمل قرابة عشرين حقلاً مهيكلاً. journalctl يسمح باستخدامها كمرشحات مباشرة بصيغة CHAMP=valeur.
# التصفية حسب PID
journalctl _PID=1234
# التصفية حسب المستخدم
journalctl _UID=1000
# التصفية حسب الملف التنفيذي
journalctl _EXE=/usr/bin/python3
# التصفية حسب معرّف معاملة systemd
journalctl _SYSTEMD_INVOCATION_ID=abc123...
# الجمع (مرشحات على نفس الحقل = OR، على حقول مختلفة = AND)
journalctl _SYSTEMD_UNIT=nginx.service _SYSTEMD_UNIT=php-fpm.service
journalctl _SYSTEMD_UNIT=nginx.service _PID=1234
# رؤية كل الحقول المتاحة في مدخل واحد
journalctl -u nginx -n 1 --output=verbose
الوضع --output=verbose يعرض لكل مدخل مجموعة الحقول كاملة — وهو ما يكافئ تشريح الرسالة. على نظام حديث، تجد فيه الطابع الزمني بالميكروثانية (__REALTIME_TIMESTAMP)، هرمية cgroup (_SYSTEMD_CGROUP)، معرّف الإقلاع (_BOOT_ID)، اسم المضيف (_HOSTNAME) وكثير غيرها. هذه البنية تجعل السجل قابلاً للاستغلال من مُجمّعات مثل Loki أو Vector أو Elasticsearch بدون الحاجة لتحليل سلاسل نصية هشّة.
الخطوة 6 — إخراج مهيكل وسلاسل أنابيب
لإدماج السجل في خط رصد، يمكن لـ journalctl إصدار JSON سطراً سطراً، صيغة قابلة للاستهلاك مباشرة من jq وLoki وأي مُجمّع حديث.
# JSON خام، سطر لكل مدخل
journalctl -u nginx --since "1 hour ago" -o json
# JSON منسّق ومقروء
journalctl -u nginx -n 5 -o json-pretty
# عدّ الأخطاء حسب الرسالة
journalctl -p err --since today -o json | jq -r .MESSAGE | sort | uniq -c | sort -rn | head
# استخراج عناوين IP التي سببت خطأ 5xx
journalctl -u nginx --since "10 min ago" -o json | jq -r 'select(.MESSAGE | test("HTTP/1.1\" 5[0-9][0-9]")) | .MESSAGE'
ميزة JSON على الإخراج النصي التقليدي: لا انكسار إذا احتوت الرسالة على فراغات أو أسطر أو محارف غير ASCII، وكل حقل يبقى متاحاً منفصلاً. للتكامل مع Loki، يعرف العميل promtail قراءة سجل systemd مباشرة؛ للتكامل مع Elasticsearch، Filebeat معدّ على وحدة journald يفي بالغرض. كلاهما يحتمل آلاف الرسائل في الثانية دون عناء.
الخطوة 7 — التدوير وسياسة الاحتفاظ
السجل ينمو. بدون سياسة احتفاظ صريحة، ينتهي به الأمر إلى إشباع القرص — حادث كلاسيكي على VPS متواضعة. ثلاث روافع تضع سقفاً لحجمه.
# رؤية الاستخدام الحالي
journalctl --disk-usage
# الإبقاء فقط على آخر 7 أيام
sudo journalctl --vacuum-time=7d
# الإبقاء فقط على آخر 500 ميغا
sudo journalctl --vacuum-size=500M
# الإبقاء فقط على آخر 5 ملفات
sudo journalctl --vacuum-files=5
لسقف دائم، عدّل /etc/systemd/journald.conf وفعّل التوجيهات التالية: SystemMaxUse=500M (الحجم الكلي الأقصى)، SystemKeepFree=1G (مساحة دنيا تُترك حرة على القسم)، SystemMaxFileSize=50M (حجم كل ملف قبل التدوير)، MaxRetentionSec=30day (مدة احتفاظ قصوى). بعد التعديل، sudo systemctl restart systemd-journald يطبّق القواعد. للملفات النصية خارج systemd التي تبقى في /var/log/ (مثل auth.log على Debian)، التدوير يديره logrotate، مُعَدّ في /etc/logrotate.d/ ومُشغَّل يومياً عبر مؤقت systemd.
الخطوة 8 — التحقق والعادات الجيدة
للتثبّت من إتقانك، شغّل تسلسل التشخيص أدناه على خادم اختبار. يستنسخ ما يكتبه مسؤول خبير بشكل غريزي عند وصوله إلى آلة لا يعرفها.
# 1. نظرة عامة: الخدمات الفاشلة والأخطاء الأخيرة
systemctl --failed
journalctl -p err -b --no-pager | tail -30
# 2. ضغط الذاكرة: هل ضرب OOM killer؟
journalctl -k -b | grep -i "out of memory" | tail -10
# 3. اتصالات SSH المشبوهة
journalctl -u ssh --since "24 hours ago" | grep -i "Failed\|Accepted" | tail -20
# 4. تدقيق استخدام السجل للقرص
journalctl --disk-usage
# 5. التحقق من إعدادات الاحتفاظ
grep -E "^(SystemMaxUse|MaxRetention|SystemKeepFree)" /etc/systemd/journald.conf
المخرجات المنتظَرة تشبه: صفر خدمة فاشلة، بعض الأخطاء المتكررة لكن المُتحكم بها (لتصحَّح)، لا OOM حديث، اتصالات SSH متوافقة مع نشاطك، سجل بين 100 ميغا و1 جيغا حسب الآلة، وعلى الأقل توجيه سقف نشط. هذه الروتين من خمس دقائق يعطيك عند الوصول إلى خادم صورة وفية لصحته — غالباً أنفع من النظرة العامة لأدوات الرصد الموجهة بالمقاييس.
الأخطاء الشائعة
| الخطأ | السبب | الحل |
|---|---|---|
| journalctl يعرض فقط الإقلاع الحالي | سجل متطاير (لا يوجد /var/log/journal/) | أنشئ المجلد وأعد تشغيل journald، انظر الخطوة 1 |
| إخراج مبتور افتراضياً | pager less يقطع الأسطر الطويلة | أضف --no-pager أو اطبع -S داخل less |
| Permission denied على بعض الرسائل | قراءة سجلات خدمات أخرى تتطلب sudo أو مجموعة systemd-journal | sudo usermod -aG systemd-journal $USER ثم جلسة جديدة |
| قرص ممتلئ بسبب السجل | لا حد معرّف | اضبط SystemMaxUse= في journald.conf |
| سجلات تطبيقية مفقودة رغم اشتغال الخدمة | التطبيق يكتب في ملفه الخاص بدل stdout | اضبط StandardOutput=journal في ملف .service |
| بحث نصي بطيء | journalctl ليس له فهرس نصي كامل | صفِّ حسب حقل مهيكل (_SYSTEMD_UNIT, _PID) قبل grep |
دروس ذات صلة
الأسئلة الشائعة
هل نحتفظ بـ syslog مع journald؟
على معظم خوادم 2026، journald وحده يكفي. rsyslog يبقى مهماً في حالتين: إذا كنت تشحن السجلات إلى خادم مركزي عبر بروتوكول syslog (منفذ 514)، أو إذا كتب تطبيق طرف ثالث صراحة عبر logger أو دالة C syslog(). وإلا، تعطيل rsyslog يُخفّف النظام من خدمة زائدة.
كيف نشحن السجل إلى خادم مركزي؟
ثلاث خيارات حسب السياق. systemd-journal-remote يرسل عبر HTTPS إلى journald بعيد آخر — أصلي، بلا تبعيات، لكن تصفية محدودة. promtail من Grafana يقرأ السجل ويرسله إلى Loki — خفيف، متعدد المستأجرين، قابل للاستعلام بـ LogQL. Filebeat أو Vector يقرآن السجل ويرسلانه إلى Elasticsearch أو أي وجهة — أثقل لكن مرن جداً. لأسطول أقل من 50 آلة، Loki عادةً أفضل توافق.
كيف نرى سجلات جلسة SSH سابقة؟
جلسات SSH تظهر في سجل خدمة ssh.service (Debian/Ubuntu) أو sshd.service (Alma/Rocky). journalctl -u ssh --since "yesterday" | grep -E "Accepted|Failed" يدرج محاولات الاتصال. للأثر التفصيلي لشل تفاعلي، يجب تفعيل auditd بقواعد محددة؛ سجل systemd لا يلتقط ما يحدث داخل shell.
كيف نشخّص خدمة لا تظهر في السجل؟
ثلاثة أسباب محتملة. الأكثر تواتراً: الخدمة تكتب في ملفها الخاص (/var/log/mon-app/app.log) لأن تهيئتها التطبيقية تعيد توجيه stdout. الحل تعديل التهيئة لتكتب على stdout وترك systemd يلتقط. السبب الثاني: ملف .service يحتوي StandardOutput=null أو StandardOutput=file:، يُصحَّح إلى StandardOutput=journal. السبب الثالث: الخدمة تتعطل قبل كتابة أي شيء؛ systemctl status mon-app يعرض حينها رمز العودة والإشارات الأولى.
أي أولوية أستخدم عندما يُسجّل تطبيقي نفسه؟
الاصطلاح: error لما يقطع عملية يتوقعها المستخدم، warning لما هو شاذ لكن أُنقذ، notice للأحداث المهمة العادية (تشغيل، إيقاف، نشر)، info لمتابعة الأعمال، debug للتفاصيل الداخلية. في الإنتاج، info هو السقف السليم؛ debug يُفعَّل مؤقتاً عند الطلب لكن لا دائماً، تحت طائلة إشباع القرص.
كيف نضع علامة يدوية على حدث لاسترجاعه لاحقاً؟
الأمر logger -t deploy "Déploiement v2.4 démarré" يُدخل قيداً في السجل بوسم deploy. تجده بعد ذلك بـ journalctl -t deploy. هذه الممارسة المعيارية لتوسيم تدخل يدوي، نشر أو حادث — الأثر يبقى مرتبطاً ببقية السجل ويسمح بإعادة بناء التسلسل الزمني.
كيف نعرف الخدمات الأكثر كتابة في السجل؟
الأمر journalctl --since today -o json | jq -r ._SYSTEMD_UNIT | sort | uniq -c | sort -rn | head يعدّ مدخلات اليوم حسب الوحدة ويعرض القمة. على خادم سليم، الأوائل هم systemd نفسه، خدمة الشبكة، وعميل الرصد المحتمل. إذا ظهر تطبيق أعمال في الصدارة بعشرات الآلاف من المدخلات يومياً، فهذا غالباً مؤشر على مستوى تسجيل تطبيقي ثرثار جداً وجب تصحيحه.
كيف نتحقق من سلامة السجل؟
الصيغة الثنائية لـ journald تتضمن مجاميع تحقق داخلية. sudo journalctl --verify يفحص كل الملفات ويبلّغ عن أي تلف — مفيد بعد انقطاع كهرباء مفاجئ أو إذا عرف نظام الملفات أخطاء. عند رصد تلف، الممارسة الموصى بها وضع الملفات المعنية جانباً للتحليل، بدل محاولة إصلاحها في مكانها.
كيف نُدمج journalctl في سكربت نشر؟
ثلاثة أنماط متكررة. أولاً، التقاط آخر N أسطر من خدمة بعد إعادة تشغيل وإدراجها في تقرير النشر: journalctl -u mon-app -n 50 --no-pager --since "1 minute ago". ثانياً، انتظار أن تشير الخدمة إلى استعدادها قبل المتابعة: journalctl -u mon-app -f -o cat | grep -m 1 "ready to accept connections". ثالثاً، التحقق من عدم ظهور أخطاء منذ التشغيل: journalctl -u mon-app -p err --since "$(systemctl show -p ActiveEnterTimestamp mon-app | cut -d= -f2)". هذه القصاصات الثلاث، مدمجة في خط CI/CD، تحول عمليات نشر عمياء إلى عمليات مرصودة.