سرور از دسترس خارج شده، یک سرویس بدون دلیل روشن متوقف می‌شود یا کاربری گزارش می‌دهد که سایت هر چند ساعت یک بار برای مدت کوتاهی پاسخ نمی‌دهد. در همه این موارد پاسخ در لاگ‌ها است و در توزیع‌های مدرن لینوکس، ابزار اصلی خواندن لاگ‌ها journalctl است. برخلاف فایل‌های متنی پراکنده در /var/log، ژورنال systemd همه پیام‌های کرنل، سرویس‌ها و برنامه‌ها را در یک پایگاه داده ساخت‌یافته با فیلدهای قابل جستجو نگه می‌دارد. در این مقاله یاد می‌گیرید چگونه با فیلترهای زمان، سرویس، اولویت و فیلدهای سفارشی در کمتر از یک دقیقه به ریشه خطا برسید، حجم ژورنال را کنترل کنید و از اشتباهات رایج دور بمانید.

journald چگونه لاگ‌ها را ذخیره می‌کند؟

سرویس systemd-journald خروجی استاندارد و خطای همه سرویس‌ها، پیام‌های syslog، لاگ‌های کرنل و رویدادهای ورود کاربران را دریافت می‌کند و در فایل‌های باینری با پسوند .journal می‌نویسد. هر رکورد علاوه بر متن پیام، ده‌ها فیلد متادیتا مانند نام unit، شناسه پروسه، کاربر اجراکننده، نام میزبان و مهر زمانی دقیق دارد. همین فیلدها هستند که journalctl را از grep روی فایل‌های متنی قدرتمندتر می‌کنند: به جای حدس زدن الگوی متن، دقیقاً می‌گویید «همه پیام‌های سطح error از سرویس php-fpm از ساعت ۲ بامداد تا الان».

ژورنال فرّار در برابر ژورنال پایدار

در Debian و Ubuntu ژورنال به‌طور پیش‌فرض در /var/log/journal ذخیره می‌شود و پس از ریبوت باقی می‌ماند، اما در برخی توزیع‌ها یا تصاویر آماده سرور مجازی فقط در /run/log/journal (حافظه) نگه داشته می‌شود و با هر ریبوت از بین می‌رود. اگر دستور journalctl --list-boots فقط یک بوت نشان می‌دهد، احتمالاً ژورنال شما پایدار نیست. برای رفع آن مقدار Storage=persistent را در /etc/systemd/journald.conf تنظیم کنید، پوشه /var/log/journal را بسازید و سرویس را ری‌استارت کنید. بدون ژورنال پایدار، بررسی علت یک کرش یا ریبوت ناگهانی تقریباً ناممکن است.

رابطه journald با rsyslog

در بسیاری از سرورها هر دو سرویس فعال‌اند: journald پیام‌ها را دریافت می‌کند و به rsyslog می‌فرستد تا در فایل‌های آشنای /var/log/syslog یا /var/log/messages هم نوشته شوند. این دوباره‌نویسی مصرف دیسک را دو برابر می‌کند؛ اگر به فایل‌های متنی نیاز ندارید می‌توانید rsyslog را غیرفعال کنید و ابزارهایی مانند Promtail یا Vector مستقیماً همین ژورنال را به سرور لاگ مرکزی بفرستند.

فیلترهای اصلی journalctl: سرویس، بوت و زمان

اجرای journalctl بدون آرگومان، کل ژورنال را از قدیمی‌ترین رکورد در یک pager باز می‌کند که در عمل کاربردی ندارد. ترکیب چند فیلتر ساده، همان چیزی است که در اغلب موارد به آن نیاز دارید:

گزینهمعنامثال
-uمحدود به یک unitjournalctl -u nginx
-bفقط بوت فعلی (-b -1 بوت قبلی)journalctl -b -1 -u mariadb
--since / --untilبازه زمانیjournalctl --since "2026-11-01 02:00" --until "03:00"
-nتعداد خطوط آخرjournalctl -u php8.4-fpm -n 200
-fدنبال کردن زنده مانند tail -fjournalctl -u nginx -f
-rترتیب معکوس (جدیدترین اول)journalctl -r -p err
-kفقط پیام‌های کرنل (مانند dmesg)journalctl -k -b

نوشتن بازه زمانی به شکل انسانی

گزینه‌های --since و --until عبارت‌های نسبی را هم می‌پذیرند: "1 hour ago"، "yesterday"، "today" یا "-30min". برای بررسی یک قطعی که کاربر گزارش داده، ابتدا زمان دقیق را از مانیتورینگ یا لاگ وب‌سرور پیدا کنید و سپس با بازه‌ای حدود ده دقیقه قبل و بعد آن، همه سرویس‌ها را بدون فیلتر -u ببینید؛ اغلب خطا در سرویسی رخ داده که انتظارش را نداشتید.

بررسی ریبوت‌های غیرمنتظره

دستور journalctl --list-boots فهرست بوت‌ها با شماره و زمان را می‌دهد. برای فهمیدن علت یک ریبوت، آخرین خطوط بوت قبلی را با journalctl -b -1 -e ببینید: اگر لاگ ناگهان و بدون پیام‌های خاموشی قطع شده، سرور احتمالاً به دلیل قطع برق، کرش کرنل یا ریست از سمت هایپروایزر خاموش شده است؛ اگر پیام‌های «Stopping ...» دیده می‌شود، کسی یا چیزی دستور خاموشی عادی داده است.

فیلتر بر اساس اولویت و فیلدهای ساخت‌یافته

هر پیام ژورنال یک سطح اولویت syslog از 0 (emerg) تا 7 (debug) دارد. گزینه -p err پیام‌های سطح 3 و شدیدتر را نشان می‌دهد و -p warning..err فقط یک بازه مشخص را. ترکیب journalctl -p err -b معمولاً نخستین دستوری است که بعد از ورود به سروری با مشکل اجرا می‌کنید، چون در چند خط همه چیزهایی را که در این بوت واقعاً شکست خورده‌اند خلاصه می‌کند.

فیلدهایی که باید بشناسید

علاوه بر unit، می‌توانید مستقیماً روی فیلدها فیلتر کنید: _PID=1234 پیام‌های یک پروسه مشخص، _UID=33 همه پیام‌های کاربر www-data، _COMM=sshd پیام‌های یک باینری خاص صرف‌نظر از unit، و SYSLOG_IDENTIFIER=CRON پیام‌های برچسب‌خورده با یک شناسه. دستور journalctl -F _COMM همه مقادیر موجود یک فیلد را فهرست می‌کند. دو فیلتر از یک فیلد با OR و از فیلدهای مختلف با AND ترکیب می‌شوند.

journalctl -p err -b --no-pager
journalctl _COMM=sshd --since today | grep -i "failed password"
journalctl -u nginx -u php8.4-fpm --since "10 min ago"
journalctl _UID=33 -o short-precise -n 50

قالب‌های خروجی

گزینه -o قالب نمایش را تغییر می‌دهد: short-precise مهر زمانی با دقت میکروثانیه‌ای می‌دهد که برای همبسته کردن رویدادهای دو سرویس ضروری است؛ verbose همه فیلدهای هر رکورد را نشان می‌دهد؛ json و json-pretty برای پردازش با jq یا ارسال به سیستم‌های لاگ متمرکز مناسب‌اند؛ و cat فقط متن پیام را بدون متادیتا چاپ می‌کند. وقتی پیام‌های طولانی در pager بریده می‌شوند، از کلید جهت راست برای اسکرول افقی استفاده کنید.

سه سناریوی واقعی عیب‌یابی با journalctl

Nginx پس از تغییر پیکربندی بالا نمی‌آید

خروجی systemctl status فقط «failed (Result: exit-code)» می‌گوید. با journalctl -u nginx -b -n 30 پیام دقیق مانند «unexpected "}" in /etc/nginx/sites-enabled/site.conf:42» را می‌بینید و در همان لحظه خط مشکل‌دار مشخص می‌شود. اگر پیام «Address already in use» باشد، سرویس دیگری (مثلاً Apache باقی‌مانده از نصب قبلی) روی پورت 80 نشسته است و باید با ss -ltnp پیدا و متوقف شود.

دیتابیس هر چند روز یک بار ری‌استارت می‌شود

ابتدا با journalctl -u mariadb --since "7 days ago" و فیلتر grep روی عبارت‌های Started و Stopped زمان‌های دقیق ری‌استارت را استخراج کنید، سپس در همان زمان‌ها پیام‌های کرنل را با journalctl -k --since بررسی کنید. اگر عبارت «Out of memory: Killed process» را دیدید، مشکل از دیتابیس نیست بلکه حافظه سرور کم است یا innodb_buffer_pool_size بزرگ‌تر از حد مجاز تنظیم شده است.

تلاش‌های ورود مشکوک SSH

با journalctl _COMM=sshd --since yesterday و شمارش خطوط حاوی Failed password تعداد تلاش‌های ناموفق را به دست آورید و با افزودن awk آدرس‌های IP پرتکرار را استخراج کنید. اگر عدد غیرعادی است، نصب fail2ban یا محدود کردن SSH به کلید عمومی را در اولویت قرار دهید. مزیت ژورنال این است که فیلد _COMM مستقل از نام unit عمل می‌کند و نیازی نیست بدانید سرویس در توزیع شما ssh.service نام دارد یا sshd.service.

کنترل حجم ژورنال و جلوگیری از پر شدن دیسک

journald به‌طور پیش‌فرض حداکثر ۱۰ درصد از فایل‌سیستم (و نه بیشتر از 4 GB) را برای ژورنال پایدار استفاده می‌کند و به‌صورت خودکار فایل‌های قدیمی را حذف می‌کند، اما روی سرورهای مجازی با دیسک 20 یا 40 گیگابایتی همین مقدار هم قابل توجه است. ابتدا با journalctl --disk-usage حجم فعلی را ببینید. برای پاک‌سازی فوری از journalctl --vacuum-size=500M یا journalctl --vacuum-time=30d استفاده کنید؛ این دستورها فقط فایل‌های آرشیوشده را حذف می‌کنند و روی فایل فعال اثر ندارند، بنابراین اگر حجم کم نشد ابتدا journalctl --rotate را اجرا کنید.

# /etc/systemd/journald.conf
[Journal]
Storage=persistent
SystemMaxUse=1G
SystemMaxFileSize=100M
MaxRetentionSec=1month
Compress=yes

تنظیمات دائمی در journald.conf

SystemMaxUse سقف کل حجم، SystemMaxFileSize اندازه هر فایل قبل از چرخش و MaxRetentionSec حداکثر عمر رکوردها را تعیین می‌کند. پس از تغییر، سرویس را با systemctl restart systemd-journald ری‌استارت کنید؛ این کار لاگ‌های موجود را از بین نمی‌برد. همچنین RateLimitBurst و RateLimitIntervalSec مانع می‌شوند یک سرویس پرحرف با هزاران پیام در هر ثانیه، ژورنال را اشغال و پیام‌های مهم دیگر سرویس‌ها را از دید پنهان کند.

جمع‌بندی

journalctl ابزاری است که فاصله میان «سرویس خراب است» و «می‌دانم چرا خراب است» را به چند دستور کوتاه کاهش می‌دهد. ژورنال را پایدار کنید، عادت کنید هر عیب‌یابی را با journalctl -p err -b شروع کنید، با -u و --since دامنه جستجو را محدود کنید و برای پیدا کردن الگوهای تکرارشونده از فیلدهای _COMM و _UID کمک بگیرید. در کنار آن، سقف حجم ژورنال را متناسب با دیسک سرور تنظیم کنید تا ابزار عیب‌یابی خودش به عامل خرابی تبدیل نشود.

سوالات متداول

چرا journalctl لاگ‌های قبل از ریبوت را نشان نمی‌دهد؟

ژورنال شما احتمالاً در حافظه (/run/log/journal) ذخیره می‌شود و با هر ریبوت پاک می‌شود. مقدار Storage=persistent را در journald.conf تنظیم کنید، پوشه /var/log/journal را با mkdir بسازید و سرویس systemd-journald را ری‌استارت کنید تا از این پس لاگ‌ها روی دیسک بمانند.

چگونه فقط خطاهای یک سرویس خاص را ببینم؟

دو فیلتر را ترکیب کنید: journalctl -u nginx -p err -b فقط پیام‌های سطح error و شدیدتر از سرویس Nginx در بوت فعلی را نشان می‌دهد. اگر هشدارها هم مهم‌اند از -p warning استفاده کنید و برای دیدن زنده خطاها گزینه -f را اضافه کنید.

آیا می‌توان ژورنال را به‌صورت دستی پاک کرد؟

بله، اما فایل‌های .journal را مستقیماً rm نکنید. از journalctl --rotate و سپس journalctl --vacuum-time=2d یا --vacuum-size=200M استفاده کنید تا فایل‌های آرشیوی به‌صورت امن حذف شوند و ساختار ژورنال خراب نشود.

تفاوت journalctl و dmesg چیست؟

dmesg فقط بافر پیام‌های کرنل را نشان می‌دهد که پس از پر شدن بازنویسی می‌شود. journalctl -k همان پیام‌ها را از ژورنال می‌خواند، اما با مهر زمانی قابل خواندن، امکان فیلتر زمانی و دسترسی به بوت‌های قبلی. برای بررسی OOM Killer یا خطاهای دیسک، journalctl -k -b -1 دقیق‌تر است.

چرا پیام «Suppressed N messages» در لاگ ظاهر می‌شود؟

این پیام یعنی یک سرویس بیش از حد مجاز RateLimitBurst در بازه RateLimitIntervalSec پیام فرستاده و journald مابقی را حذف کرده است. اگر این پیام‌ها برای شما مهم‌اند، این دو مقدار را در journald.conf افزایش دهید یا ریشه پرحرفی سرویس را برطرف کنید.