پرش به محتویات

۹. لاگ‌ها و عیب‌یابی عمومی

لاگ شاهد صحنهٔ جرم است: اول بخوانید، بعد تغییر دهید. اکثر مشکلات با «چه چیزی، کِی، و بلافاصله قبلش چه تغییری شد؟» حل می‌شوند.

راهنمای سریع

فرمان کاربرد ریسک
journalctl -u S -b / -f / -p err لاگ سرویس / زنده / فقط خطاها بدون تغییر
journalctl -k / dmesg -T پیام‌های کرنل بدون تغییر
tail -F / grep / zgrep / awk فایل‌های لاگ بدون تغییر
last -x / lastb ورود، بوت، خاموشی بدون تغییر
logger نوشتن پیام در لاگ تغییر
journalctl --vacuum-* پاک‌سازی journal مخرب (شواهد از بین می‌رود)
logrotate -f چرخش اجباری تغییر

journalctl

journalctl -b
journalctl -b -1                         # بوت قبلی (فقط اگر journal پایدار باشد)
journalctl --list-boots
journalctl -p warning
journalctl -p err..emerg -b
journalctl -u nginx --since today
journalctl -u ssh --since '30 minutes ago'
journalctl -u nginx --since '2026-10-30 09:00' --until '2026-10-30 09:30'
journalctl -f
journalctl -u nginx -f -n 50
journalctl -k
journalctl -g 'timeout|refused' -u myapp -b      # grep داخلی (regex)
journalctl -u myapp -o short-iso --no-pager
journalctl -u myapp -o json-pretty -n 1
journalctl _PID=1234
journalctl -t sshd
  • -b: بوت جاری؛ -b -1: بوت قبلی، اگر لاگ پایدار ذخیره شده باشد.
  • -p warning: هشدارها و موارد با شدت بالاتر.
  • -u: فیلتر بر اساس سرویس؛ -t: بر اساس برچسب (syslog identifier).
  • -k: پیام‌های کرنل.
  • -xe: توضیح خطا (در اغلب موارد journalctl -xeu SERVICE مفیدتر است).

پایدارکردن journal و کنترل حجم

در بعضی توزیع‌ها journal فقط در RAM است و بعد از reboot از بین می‌رود، یعنی دلیل کرش/ریبوت را نمی‌توان دید:

journalctl --disk-usage
ls -ld /var/log/journal              # اگر نیست، journal فقط در حافظه است
sudo mkdir -p /etc/systemd/journald.conf.d
printf '[Journal]\nStorage=persistent\nSystemMaxUse=1G\nMaxRetentionSec=1month\n' | sudo tee /etc/systemd/journald.conf.d/10-persistent.conf
sudo systemctl restart systemd-journald
sudo journalctl --vacuum-size=500M    # دستی: فقط برای فضای اضطراری
sudo journalctl --verify

فایل‌های لاگ متداول

ls -lah /var/log
tail -F /var/log/syslog        # Ubuntu/Debian، اگر فعال باشد
tail -F /var/log/auth.log      # Ubuntu/Debian، اگر فعال باشد
tail -F /var/log/messages      # RHEL و بعضی توزیع‌ها

نام و محل لاگ به توزیع و سرویس بستگی دارد؛ برای سرویس‌های systemd ابتدا journalctl -u SERVICE را امتحان کنید.

موضوع Debian / Ubuntu RHEL / Rocky
ورود و sudo /var/log/auth.log /var/log/secure
عمومی /var/log/syslog /var/log/messages
بسته‌ها /var/log/apt/history.log ، /var/log/dpkg.log dnf history ، /var/log/dnf.log
cron journalctl -u cron / syslog journalctl -u crond / /var/log/cron
nginx /var/log/nginx/{access,error}.log همان
Apache /var/log/apache2/ /var/log/httpd/
کرنل dmesg -T ، /var/log/kern.log dmesg -T
ممیزی /var/log/audit/audit.log همان (auditd)

فیلتر و شمارش

grep -Ein 'error|failed|timeout' /var/log/myapp/app.log
grep -c 'HTTP 500' /var/log/nginx/access.log
zgrep -c ' 500 ' /var/log/nginx/access.log.*.gz        # لاگ‌های چرخش‌یافته
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head   # پرتکرارترین IP ها
awk '$9 ~ /^5/ {print $9}' /var/log/nginx/access.log | sort | uniq -c           # خطاهای 5xx
awk '{print $4}' /var/log/nginx/access.log | cut -c2-15 | uniq -c | tail         # درخواست در ساعت/دقیقه (قالب پیش‌فرض)

قالب لاگ‌ها متفاوت است؛ پیش از تحلیل با awk، چند خط نمونه را بخوانید تا ستون‌ها را درست تشخیص دهید. در پیش‌فرض nginx/Apache (combined): $1=IP، $7=مسیر، $9=کد وضعیت. اگر پشت proxy هستید $1 آدرس proxy است، نه کلاینت.

رخدادهای کرنل و بوت

dmesg -T | tail -n 100
dmesg -T --level=err,warn | tail
journalctl -k --since today
systemctl --failed
last -x | head -n 30                # reboot/shutdown/runlevel
journalctl -b -1 -e                 # انتهای لاگ بوت قبلی (چرا ریبوت شد؟)
coredumpctl list | tail             # کرش برنامه‌ها (اگر systemd-coredump نصب است)

dmesg بدون -T زمان را از بوت (ثانیه) می‌نویسد؛ -T ساعت دیواری می‌دهد (روی سیستم‌های suspend/resume ممکن است اندکی نادقیق باشد).

نوشتن در لاگ و اسکریپت‌ها

logger -t backup -p user.notice "backup finished rc=0"
journalctl -t backup --since today

اسکریپت‌های عملیاتی را با logger یا systemd-cat به journal وصل کنید تا خروجی گم نشود.

گردش لاگ

sudo logrotate -d /etc/logrotate.conf
sudo logrotate -f /etc/logrotate.conf

-d حالت اشکال‌زدایی است؛ -f گردش اجباری انجام می‌دهد و نباید بی‌دلیل روی همه لاگ‌ها اجرا شود. نمونه تنظیم برای برنامهٔ خودتان:

# /etc/logrotate.d/myapp   (نمونه)
/var/log/myapp/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 640 svc-myapp svc-myapp
    sharedscripts
    postrotate
        systemctl kill -s HUP myapp.service >/dev/null 2>&1 || true
    endscript
}

بعد از افزودن: sudo logrotate -d /etc/logrotate.d/myapp. اگر برنامه فایل را باز نگه دارد و سیگنال بازگشایی نگیرد، فضا آزاد نمی‌شود (یا copytruncate که ممکن است چند خط را گم کند).

دیکشنری امضای لاگ (عبارت ← معنی ← قدم بعد)

در عیب‌یابی سریع، اول این عبارت‌ها را جست‌وجو کنید: journalctl -k -b | grep -Ei 'oom|segfault|i/o error|lockup|conntrack|throttl'

عبارت در لاگ معنی قدم بعد
Out of memory: Killed process OOM killer پردازشی را کشت کارایی ، رخداد OOM
segfault at ... in libXYZ کرش برنامه یا کتابخانه coredumpctl list ، نسخهٔ بسته را بررسی کنید
EXT4-fs error / XFS: ... Corruption / Remounting filesystem read-only خطای فایل‌سیستم فایل‌سیستم فقط‌خواندنی ، بکاپ قبل از fsck
blk_update_request: I/O error / Buffer I/O error دیسک، کابل یا SAN مشکل دارد smartctl -a ، cat /proc/mdstat
nf_conntrack: table full, dropping packet بسته‌ها به‌خاطر پر بودن conntrack دور ریخته می‌شوند شبکه
possible SYN flooding on port صف SYN پر شد (حمله یا برنامهٔ کند) ss -lnt ، nstat -az \| grep -i listen
Failed password / Invalid user تلاش ورود ناموفق امنیت
Start request repeated too quickly / start-limit-hit سرویس در حلقهٔ crash است و systemd متوقفش کرد علت را در journalctl -u S پیدا کنید، بعد systemctl reset-failed S
soft lockup / rcu_sched self-detected stall CPU مدتی پاسخ نداد (درایور، VM پرفشار) st در top ، لاگ میزبان مجازی
NETDEV WATCHDOG: ... transmit queue timed out NIC یا درایور هنگ کرد ethtool -S ، نسخهٔ درایور/firmware
mce: / Hardware Error / EDAC خطای سخت‌افزاری حافظه یا CPU ثبت کامل پیام و تماس با پشتیبانی سخت‌افزار
cpu clock throttled محدودیت حرارتی دما، فن، گرد و غبار
Too many open files سقف فایل باز برای پردازش LimitNOFILE (کارایی)
No space left on device دیسک یا inode پر است df -hT ، df -ih
Time has been changed / پرش ساعت ساعت جهش کرد timedatectl ، chronyc tracking

وقتی لاگ‌ها «کم» هستند: rate limit در journald

journald پیام‌های پرتکرار یک سرویس را بی‌صدا حذف می‌کند. اگر وسط لاگ چنین خطی دیدید، لاگ ناقص است:

systemd-journald: Suppressed 4210 messages from myapp.service
journalctl -u systemd-journald | grep -i suppressed | tail
# سقف را فقط برای همان سرویس بالا ببرید (drop-in در unit سرویس):
#   [Service]
#   LogRateLimitIntervalSec=30s
#   LogRateLimitBurst=20000

بالابردن سقف برای همهٔ سرویس‌ها پیشنهاد نمی‌شود؛ علت تکرار خطا را رفع کنید.

یک خط زمانی از چند منبع

برای یافتن «چه چیزی قبل از قطعی تغییر کرد؟» لاگ‌های چند منبع را در یک منطقهٔ زمانی کنار هم بگذارید:

export TZ=UTC
journalctl --since '2026-10-30 09:00' --until '2026-10-30 09:30' -o short-iso --no-pager > /var/tmp/tl-journal.txt
# در nginx از $time_iso8601 در log_format استفاده کنید تا ستون اول زمان قابل‌مرتب‌شدن باشد
sort -s -k1,1 /var/tmp/tl-journal.txt /var/tmp/tl-nginx.txt > /var/tmp/timeline.txt

فایل‌های موقت را بعد از رخداد، اگر حساس هستند، پاک کنید.

لاگ مرکزی با rsyslog (TLS و صف دیسکی)

بدون لاگ مرکزی، مهاجم یا خود خرابی می‌تواند شواهد محلی را از بین ببرد. نمونهٔ زیر با rsyslogd -N1 اعتبارسنجی شده و دریافت لاگ از راه دور هم در آزمایش کار کرد. گواهی‌ها و آدرس را با خودتان جایگزین کنید.

کلاینت (/etc/rsyslog.d/90-forward.conf؛ بستهٔ rsyslog-gnutls لازم است):

global(
  DefaultNetstreamDriver="gtls"
  DefaultNetstreamDriverCAFile="/etc/ssl/certs/log-ca.pem"
)
action(type="omfwd" target="192.0.2.50" port="6514" protocol="tcp"
       StreamDriver="gtls" StreamDriverMode="1" StreamDriverAuthMode="x509/name"
       StreamDriverPermittedPeers="logs.example.internal"
       action.resumeRetryCount="-1"
       queue.type="LinkedList" queue.filename="fwd-main"
       queue.maxDiskSpace="1g" queue.saveOnShutdown="on")

سرور (/etc/rsyslog.d/10-receive.conf):

module(load="imtcp")
global(
  DefaultNetstreamDriver="gtls"
  DefaultNetstreamDriverCAFile="/etc/ssl/certs/log-ca.pem"
  DefaultNetstreamDriverCertFile="/etc/rsyslog.d/tls/server.crt"
  DefaultNetstreamDriverKeyFile="/etc/rsyslog.d/tls/server.key"
)
input(type="imtcp" port="6514" StreamDriver.Name="gtls" StreamDriver.Mode="1" StreamDriver.AuthMode="anon")
template(name="PerHost" type="string" string="/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log")
if $inputname == "imtcp" then {
  action(type="omfile" dynaFile="PerHost" dirCreateMode="0750" fileCreateMode="0640")
  stop
}
sudo rsyslogd -N1                          # اعتبارسنجی پیکربندی قبل از restart
sudo systemctl restart rsyslog
logger -t forward-test "hello from $(hostname)"   # روی کلاینت؛ باید روی سرور در /var/log/remote/ دیده شود

نکات: AuthMode="anon" روی سرور یعنی هر کلاینتی که به CA اعتماد دارد می‌تواند لاگ بفرستد؛ سرور را فقط در شبکهٔ مدیریت و پشت فایروال باز کنید. صف دیسکی باعث می‌شود قطعی سرور لاگ مرکزی، لاگ‌های کلاینت را گم نکند. روی سرور مرکزی هم logrotate و پایش فضای دیسک را فراموش نکنید.

روش پیشنهادی عیب‌یابی

  1. زمان دقیق شروع مشکل را مشخص کنید (و منطقهٔ زمانی همهٔ لاگ‌ها را یکسان کنید).
  2. systemctl status و لاگ همان سرویس را بخوانید.
  3. فضای دیسک و inode (df -hT, df -ih) را بررسی کنید.
  4. پورت و اتصال شبکه را بررسی کنید (ss, curl, nc).
  5. آخرین تغییرات پیکربندی یا بسته‌ها را پیدا کنید: ls -lt /etc | head ، grep -E ' (install|upgrade) ' /var/log/dpkg.log | tail ، dnf history | head.
  6. فقط یک تغییر در هر مرحله اعمال کنید؛ بعد از هر تغییر لاگ جدید را بخوانید.
  7. پس از رفع، با آزمون کاربردی و لاگ جدید تأیید کنید.

خطاهای رایج

نشانه علت احتمالی اقدام
-- No entries -- بازهٔ زمانی/فیلتر اشتباه یا journal فقط RAM --list-boots ، Storage=persistent
لاگ برنامه ناپدید شد چرخش لاگ ls -l /var/log/app* ، zgrep
دیسک /var پر از لاگ چرخش نیست/خطای حلقه‌ای du -xhd1 /var/log ، logrotate ، علت تکرار خطا
زمان لاگ‌ها با هم نمی‌خواند ساعت/timezone timedatectl ، لاگ‌ها را UTC کنید
Permission denied روی لاگ عضو گروه adm/systemd-journal نیستید id ، sudo journalctl
بعد از ریبوت دلیل نمی‌دانیم journal غیرپایدار Storage=persistent (بالا)

نکات امنیتی و عملیاتی

  • در رخداد امنیتی، لاگ‌ها را قبل از چرخش یا پاک‌سازی کپی کنید (journalctl -b -o export > /var/tmp/…)؛ --vacuum را اجرا نکنید.
  • لاگ را مرکزی کنید؛ مهاجم لاگ محلی را پاک می‌کند (نمونهٔ rsyslog با TLS بالا).
  • رمز، توکن و داده شخصی را وارد لاگ نکنید؛ هنگام اشتراک خروجی لاگ، حساس‌ها را حذف کنید.
  • همهٔ سرورها NTP داشته باشند؛ بدون ساعت درست، هم‌بستگی لاگ‌ها ناممکن است.

خلاصهٔ اجرایی

  1. journalctl -xeu SERVICE اولین قدم؛ -b -1 برای دلیل ریبوت (journal پایدار لازم است).
  2. tail -F نه -f؛ zgrep برای لاگ‌های چرخش‌یافته.
  3. تغییر ← مشاهدهٔ لاگ ← تغییر بعدی؛ یکی یکی.
  4. شواهد را قبل از پاک‌سازی ذخیره کنید.
  5. اگر لاگ ناقص است، Suppressed N messages را بررسی کنید؛ عبارت‌های کلیدی را از «دیکشنری امضای لاگ» جست‌وجو کنید.