۹. لاگها و عیبیابی عمومی¶
لاگ شاهد صحنهٔ جرم است: اول بخوانید، بعد تغییر دهید. اکثر مشکلات با «چه چیزی، کِی، و بلافاصله قبلش چه تغییری شد؟» حل میشوند.
راهنمای سریع¶
| فرمان | کاربرد | ریسک |
|---|---|---|
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 یا systemd-cat به journal وصل کنید تا خروجی گم نشود.
گردش لاگ¶
-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 پیامهای پرتکرار یک سرویس را بیصدا حذف میکند. اگر وسط لاگ چنین خطی دیدید، لاگ ناقص است:
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 و پایش فضای دیسک را فراموش نکنید.
روش پیشنهادی عیبیابی¶
- زمان دقیق شروع مشکل را مشخص کنید (و منطقهٔ زمانی همهٔ لاگها را یکسان کنید).
systemctl statusو لاگ همان سرویس را بخوانید.- فضای دیسک و inode (
df -hT,df -ih) را بررسی کنید. - پورت و اتصال شبکه را بررسی کنید (
ss,curl,nc). - آخرین تغییرات پیکربندی یا بستهها را پیدا کنید:
ls -lt /etc | head،grep -E ' (install|upgrade) ' /var/log/dpkg.log | tail،dnf history | head. - فقط یک تغییر در هر مرحله اعمال کنید؛ بعد از هر تغییر لاگ جدید را بخوانید.
- پس از رفع، با آزمون کاربردی و لاگ جدید تأیید کنید.
خطاهای رایج¶
| نشانه | علت احتمالی | اقدام |
|---|---|---|
-- 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 داشته باشند؛ بدون ساعت درست، همبستگی لاگها ناممکن است.
خلاصهٔ اجرایی¶
journalctl -xeu SERVICEاولین قدم؛-b -1برای دلیل ریبوت (journal پایدار لازم است).tail -Fنه-f؛zgrepبرای لاگهای چرخشیافته.- تغییر ← مشاهدهٔ لاگ ← تغییر بعدی؛ یکی یکی.
- شواهد را قبل از پاکسازی ذخیره کنید.
- اگر لاگ ناقص است،
Suppressed N messagesرا بررسی کنید؛ عبارتهای کلیدی را از «دیکشنری امضای لاگ» جستوجو کنید.