رفتن به محتوا
LoopX

لاگ‌ها و عیب‌یابی

توی این درس یاد می‌گیری وقتی چیزی خراب می‌شود، جواب تقریباً همیشه در لاگ است. لاگ‌های متنی در /var/log هستند و لاگ یکپارچه‌ی systemd در journal. با journalctl (فیلتر زمان با --since "1 hour ago"، سطح با -p err و سرویس با -u)، tail -f /var/log/syslog (دنبال‌کردن زنده)، dmesg -T (پیام‌های هسته) و logger (نوشتن لاگ خودت) کار می‌کنی؛ می‌فهمی لاگ‌ها با logrotate چرخانده می‌شوند تا دیسک را پر نکنند؛ و یک روش منظم قدم‌به‌قدم برای عیب‌یابی یاد می‌گیری. تمرین: یک سرویس را عمداً خراب کن و علتش را از لاگ پیدا کن.

مسئله: «کار نمی‌کند» به‌تنهایی اطلاعات نیست

Section titled “مسئله: «کار نمی‌کند» به‌تنهایی اطلاعات نیست”

کسی می‌گوید «سایت باز نمی‌شود». این جمله علت نیست؛ فقط یک نشانه است. بدون یک روش، آدم به‌جای فهمیدن، حدس می‌زند و هر چیزی را عوض می‌کند و اوضاع بدتر می‌شود. لاگ‌ها ثبت دقیق آنچه واقعاً رخ داد هستند: چه برنامه‌ای، چه ساعتی، چه چیزی گفت. فرق یک مدیر سرور حرفه‌ای با یک تازه‌کار، بیشتر در این است که اول لاگ را می‌خواند.

تشبیه: جعبه‌ی سیاه هواپیما

Section titled “تشبیه: جعبه‌ی سیاه هواپیما”

سرور هم مثل هواپیما یک جعبه‌ی سیاه دارد: هر برنامه و خود هسته، هر اتفاق مهمی را با ساعت دقیق در آن می‌نویسند. وقتی چیزی خراب می‌شود، تو بازرس سقوط هستی: از روی ترتیب و زمان رویدادها پیدا می‌کنی کدام اولین چیز اشتباه بود (که معمولاً علت است، نه آخرین پیام خطا که فقط پیامد است).

برنامه‌ها و هسته پیام‌هایشان را به journald می‌دهند؛ journald آن‌ها را ساخت‌یافته نگه می‌دارد و (روی Debian/Ubuntu) rsyslog نسخه‌ی متنی را در /var/log می‌نویسد. تو هر دو را با ابزارهای متنی یا journalctl می‌خوانی.

این درس روی ماشین systemd‌دار آزمایشی (Ubuntu، با journald، rsyslog و nginx واقعی) اجرا شده است و من در آن root هستم (روی سرور خودت اگر کاربر عادی هستی sudo بگذار).

مثال ۱: /var/log، لاگ‌های متنی

Section titled “مثال ۱: /var/log، لاگ‌های متنی”
Terminal window
ls -l /var/log
echo "--- شکل چند خط syslog (۳ خط آخرِ غیر از پیام‌های kernel):"
grep -v ' kernel: ' /var/log/syslog | tail -n 3 | cut -c1-140
خروجی
total 5360
lrwxrwxrwx 1 root root 39 Oct 3 12:33 README -> ../../usr/share/doc/systemd/README.logs
-rw-r--r-- 1 root root 18904 Oct 3 19:31 alternatives.log
drwxr-xr-x 1 root root 4096 Oct 3 21:58 apt
-rw-r----- 1 syslog adm 159119 Oct 4 08:09 auth.log
-rw-r--r-- 1 root root 61237 Sep 17 02:10 bootstrap.log
-rw-rw---- 1 root utmp 800 Oct 4 08:06 btmp
-rw-r----- 1 root adm 1247273 Oct 3 21:15 dmesg
-rw-r----- 1 root adm 1097519 Oct 3 21:14 dmesg.0
-rw-r----- 1 root adm 116218 Oct 3 21:09 dmesg.1.gz
-rw-r----- 1 root adm 18316 Oct 3 12:34 dmesg.2.gz
-rw-r--r-- 1 root root 282202 Oct 3 21:58 dpkg.log
-rw-r--r-- 1 root root 0 Sep 17 02:10 faillog
drwxr-sr-x+ 1 root systemd-journal 4096 Oct 3 12:34 journal
-rw-r----- 1 syslog adm 1116361 Oct 4 07:54 kern.log
-rw-rw-r-- 1 root utmp 297184 Oct 4 08:07 lastlog
drwxr-xr-x 1 root adm 4096 Oct 3 12:33 nginx
drwx------ 2 root root 4096 Oct 3 12:33 private
-rw-r----- 1 syslog adm 1285360 Oct 4 08:10 syslog
-rw-rw-r-- 1 root utmp 26800 Oct 4 07:56 wtmp
--- شکل چند خط syslog (۳ خط آخرِ غیر از پیام‌های kernel):
2026-10-04T08:10:25.207646+00:00 server nightly: منبع /srv/data وجود ندارد؛ بکاپ انجام نشد
2026-10-04T08:10:26.235677+00:00 server nightly: بکاپ سالم ساخته شد: /var/backups/nightly/data-2026-10-04_0810.tar.gz
2026-10-04T08:10:32.629987+00:00 server crontab[17803]: (root) LIST (root)

هر خط زمان، نام ماشین، برنامه[PID] و پیام دارد. چند فایل مهم:

فایل چه چیزی
/var/log/syslog تقریباً همه‌چیز سیستم (Debian/Ubuntu؛ روی Red Hat /var/log/messages)
/var/log/auth.log ورودها: ssh، sudo، su (روی Red Hat /var/log/secure)
/var/log/kern.log و dmesg پیام‌های هسته
/var/log/dpkg.log و apt/ نصب و حذف بسته‌ها
/var/log/nginx/ access.log و error.log وب‌سرور
/var/log/journal/ journal (باینری؛ با journalctl خوانده می‌شود)
/var/log/wtmp و btmp تاریخچه‌ی ورودها (last) و ورودهای ناموفق (lastb)

به دسترسی‌ها دقت کن: syslog و auth.log برای کاربر syslog و گروه adm خواندنی‌اند (-rw-r-----)؛ کاربر عادی نمی‌تواند بخواندشان (اشتباهات رایج).

مثال ۲: نوشتن و دنبال‌کردن لاگ، logger و tail -f

Section titled “مثال ۲: نوشتن و دنبال‌کردن لاگ، logger و tail -f”

برای تمرین لازم نیست منتظر یک خطا بمانیم: logger پیام دلخواه را به سیستم لاگ می‌دهد (برنامه‌ها همین کار را می‌کنند). -t یک برچسب (نام برنامه) و -p سطح را تعیین می‌کند:

Terminal window
logger -t myapp "برنامه شروع شد"
logger -t myapp -p user.err "اتصال به دیتابیس شکست خورد"
sleep 1
echo "--- در فایل syslog (با grep):"
grep myapp /var/log/syslog | cut -c1-110
echo "--- همان‌ها در journal (با -t برچسب):"
journalctl -t myapp --no-pager | tail -2
echo "--- فقط خطاها (-p err):"
journalctl -t myapp -p err --no-pager -o cat
خروجی
--- در فایل syslog (با grep):
2026-10-04T07:54:17.253735+00:00 server myapp: برنامه شروع شد
2026-10-04T07:54:17.254724+00:00 server myapp: اتصال به دیتابیس شکست خورد
2026-10-04T07:54:19.312502+00:00 server myapp: رویداد زنده‌ی شماره 1
2026-10-04T07:54:20.324316+00:00 server myapp: رویداد زنده‌ی شماره 2
2026-10-04T07:54:21.345506+00:00 server myapp: رویداد زنده‌ی شماره 3
2026-10-04T07:54:39.567211+00:00 server myapp: آزمایش خطا
2026-10-04T08:06:38.168357+00:00 server myapp: برنامه شروع شد
2026-10-04T08:06:38.171838+00:00 server myapp: اتصال به دیتابیس شکست خورد
2026-10-04T08:06:40.206604+00:00 server myapp: رویداد زنده‌ی شماره 1
2026-10-04T08:06:41.214250+00:00 server myapp: رویداد زنده‌ی شماره 2
2026-10-04T08:06:42.221035+00:00 server myapp: رویداد زنده‌ی شماره 3
2026-10-04T08:06:46.284238+00:00 server myapp: disk usage at 91 percent
2026-10-04T08:10:33.788921+00:00 server myapp: برنامه شروع شد
2026-10-04T08:10:33.790238+00:00 server myapp: اتصال به دیتابیس شکست خورد
--- همان‌ها در journal (با -t برچسب):
Oct 04 08:10:33 server myapp[17831]: برنامه شروع شد
Oct 04 08:10:33 server myapp[17832]: اتصال به دیتابیس شکست خورد
--- فقط خطاها (-p err):
اتصال به دیتابیس شکست خورد
آزمایش خطا
اتصال به دیتابیس شکست خورد
اتصال به دیتابیس شکست خورد

هر پیام هم در فایل متنی هست و هم در journal (همان مسیر شکل بالا). و -p err فقط پیام‌های سطح خطا و جدی‌تر را می‌آورد. سطح‌ها (priority) از ۰ تا ۷ هستند:

عدد نام معنی
0 emerg سیستم از کار افتاده
1 alert فوراً اقدام کن
2 crit حالت بحرانی
3 err خطا
4 warning هشدار
5 notice رویداد عادی ولی مهم
6 info اطلاع‌رسانی
7 debug جزئیات اشکال‌زدایی

-p err یعنی ۳ و کوچک‌تر (emerg تا err). حالا زنده دنبال‌کردن: tail -f انتهای فایل را باز نگه می‌دارد و هر خط تازه را همان لحظه چاپ می‌کند (در ترمینال واقعی تا Ctrl+C). من ۶ ثانیه دنبالش می‌کنم و در همین فاصله سه پیام می‌نویسم:

Terminal window
timeout 6 tail -n 0 -f /var/log/syslog > /tmp/loglab-tail.out &
sleep 1
for i in 1 2 3; do logger -t myapp "رویداد زنده‌ی شماره $i"; sleep 1; done
wait
echo "--- آنچه tail -f هنگام دنبال‌کردن دید:"
cut -c1-110 /tmp/loglab-tail.out
خروجی
--- آنچه tail -f هنگام دنبال‌کردن دید:
2026-10-04T08:10:35.836344+00:00 server myapp: رویداد زنده‌ی شماره 1
2026-10-04T08:10:36.851116+00:00 server myapp: رویداد زنده‌ی شماره 2
2026-10-04T08:10:37.857514+00:00 server myapp: رویداد زنده‌ی شماره 3

-n 0 یعنی از خط‌های قدیمی چیزی نشان نده و فقط تازه‌ها. معادل journal: journalctl -f (مثال ۳).

⚡ بررسی سریع

کدام دستور فایل لاگ را زنده دنبال می‌کند و هر خط تازه را همان لحظه نشان می‌دهد؟

مثال ۳: journalctl، فیلتر با زمان، سطح و سرویس

Section titled “مثال ۳: journalctl، فیلتر با زمان، سطح و سرویس”

journalctl به‌تنهایی کل journal را چاپ می‌کند (ده‌ها هزار خط!). مهارت اصلی فیلتر کردن است:

Terminal window
echo "--- ۳ خط آخر (-n 3):"
journalctl -n 3 --no-pager | cut -c1-130
echo "--- فقط یک سرویس (-u) و فقط از امروز (--since today):"
journalctl -u cron --since today --no-pager | tail -2 | cut -c1-130
echo "--- بازه‌ی زمانی: بین ۱۰ دقیقه و ۱ دقیقه‌ی پیش، چند خط؟"
journalctl --since "10 minutes ago" --until "1 minute ago" --no-pager | wc -l
خروجی
--- ۳ خط آخر (-n 3):
Oct 04 08:10:35 server myapp[17842]: رویداد زنده‌ی شماره 1
Oct 04 08:10:36 server myapp[17844]: رویداد زنده‌ی شماره 2
Oct 04 08:10:37 server myapp[17846]: رویداد زنده‌ی شماره 3
--- فقط یک سرویس (-u) و فقط از امروز (--since today):
Oct 04 08:09:01 server nightly[17749]: بکاپ سالم ساخته شد: /var/backups/nightly/data-2026-10-04_0809.tar.gz
Oct 04 08:09:01 server CRON[17737]: pam_unix(cron:session): session closed for user root
--- بازه‌ی زمانی: بین ۱۰ دقیقه و ۱ دقیقه‌ی پیش، چند خط؟
198

بالا سرویس و زمان را فیلتر کردی؛ فیلتر مهم دیگر سطح (-p) است. پنج پیام با پنج سطح مختلف می‌نویسم و با فیلترهای مختلف می‌خوانم:

Terminal window
for p in debug info warning err crit; do logger -t prio-demo -p user.$p "پیام با سطح $p"; done
sleep 1
echo "--- همه‌ی پیام‌ها (بدون فیلتر):"
journalctl -t prio-demo --no-pager -o cat
echo "--- فقط warning و جدی‌تر (-p warning):"
journalctl -t prio-demo -p warning --no-pager -o cat
echo "--- فقط err و جدی‌تر (-p err):"
journalctl -t prio-demo -p err --no-pager -o cat
خروجی
--- همه‌ی پیام‌ها (بدون فیلتر):
پیام با سطح debug
پیام با سطح info
پیام با سطح warning
پیام با سطح err
پیام با سطح crit
پیام با سطح debug
پیام با سطح info
پیام با سطح warning
پیام با سطح err
پیام با سطح crit
--- فقط warning و جدی‌تر (-p warning):
پیام با سطح warning
پیام با سطح err
پیام با سطح crit
پیام با سطح warning
پیام با سطح err
پیام با سطح crit
--- فقط err و جدی‌تر (-p err):
پیام با سطح err
پیام با سطح crit
پیام با سطح err
پیام با سطح crit

هر -p همه‌ی سطح‌های همان و جدی‌تر را نشان می‌دهد؛ برای همین -p err فقط err و crit را آورد. فیلترهای پرکاربرد:

گزینه کار
-u نام فقط یک سرویس (-u nginx -u ssh چند تا)
--since "1 hour ago" از یک زمان؛ فرمت‌ها: "2026-10-04 07:00"، today، yesterday، "30 min ago"
--until "..." تا یک زمان
-p err سطح خطا و جدی‌تر (-p warning..err برای بازه)
-b / -b -1 همین بوت / بوت قبلی (--list-boots همه)
-k فقط پیام‌های هسته
-n 50 ۵۰ خط آخر
-f زنده دنبال کن
-r از جدید به قدیم
-o cat فقط متن پیام، بدون ساعت و نام
-g عبارت جستجوی عبارت (--grep)
-t برچسب بر اساس برچسب برنامه (SYSLOG_IDENTIFIER)

و اطلاعات خود journal:

Terminal window
echo "--- بوت‌هایی که journal دارد:"
journalctl --list-boots --no-pager | tail -3
echo "--- حجم journal:"
journalctl --disk-usage
logger -t myapp "disk usage at 91 percent"
sleep 1
echo "--- جستجوی یک عبارت داخل پیام‌ها (-g، فقط ۵ دقیقه‌ی اخیر):"
journalctl --since "5 minutes ago" -g 'percent' -o cat --no-pager
خروجی
--- بوت‌هایی که journal دارد:
IDX BOOT ID FIRST ENTRY LAST ENTRY
0 3d4eccadb48542a4888f8ac6fbea5081 Sat 2026-10-03 12:34:09 UTC Sun 2026-10-04 08:10:40 UTC
--- حجم journal:
Archived and active journals take up 83.4M in the file system.
--- جستجوی یک عبارت داخل پیام‌ها (-g، فقط ۵ دقیقه‌ی اخیر):
disk usage at 91 percent
disk usage at 91 percent

مثال ۴: dmesg، پیام‌های هسته

Section titled “مثال ۴: dmesg، پیام‌های هسته”

هسته پیام‌هایش (سخت‌افزار، درایورها، دیسک، حافظه، شبکه) را در یک بافر حلقوی نگه می‌دارد و dmesg آن را می‌خواند. مثلاً وقتی یک دیسک خطا می‌دهد یا پروسه‌ای به‌خاطر کمبود حافظه کشته می‌شود (OOM killer)، اول اینجا ثبت می‌شود:

Terminal window
echo "--- چند خط اول بافر هسته (زمان به‌صورت ثانیه از شروع بوت):"
dmesg | head -3 | cut -c1-110
echo "--- با زمان خوانا (-T) و فقط هشدار و خطا (--level):"
dmesg -T --level=err,warn | tail -3 | cut -c1-130
echo "--- نشانه‌های رایج مشکل (اگر باشند):"
dmesg | grep -iE 'out of memory|oom-kill|oom_reaper|i/o error|segfault' | tail -2 || true
echo "(بالا خالی یعنی چنین مشکلی ثبت نشده)"
خروجی
--- چند خط اول بافر هسته (زمان به‌صورت ثانیه از شروع بوت):
[86954.638718] br-fb2628254424: port 1(vetha31ea25) entered blocking state
[86954.639370] br-fb2628254424: port 1(vetha31ea25) entered disabled state
[86954.639397] vetha31ea25: entered allmulticast mode
--- با زمان خوانا (-T) و فقط هشدار و خطا (--level):
[Sat Oct 3 22:34:54 2026] overlayfs: lowerdir is in-use as upperdir/workdir of another mount, accessing files from both mounts wi
[Sat Oct 3 23:10:12 2026] overlayfs: lowerdir is in-use as upperdir/workdir of another mount, accessing files from both mounts wi
--- نشانه‌های رایج مشکل (اگر باشند):
(بالا خالی یعنی چنین مشکلی ثبت نشده)

-T زمان را به ساعت خوانا تبدیل می‌کند (روی ماشین‌هایی که خوابیده‌اند ممکن است کمی نادقیق باشد) و --level سطح را فیلتر می‌کند. پیام‌هایی که اینجا می‌بینی مال داکری هستند که این ماشین آزمایشی روی آن اجرا می‌شود (هسته با کانتینر مشترک است)؛ روی سرور خودت پیام‌های سخت‌افزار و دیسک واقعی را می‌بینی. عبارت‌های مهم برای جستجو: Out of memory، I/O error، segfault، EXT4-fs error.

مثال ۵: لاگ ورود و امنیت، auth.log

Section titled “مثال ۵: لاگ ورود و امنیت، auth.log”

هر تلاش ورود با ssh (موفق یا ناموفق) و هر sudo ثبت می‌شود. یک تلاش ناموفق می‌سازم (کاربری که وجود ندارد) و دنبالش را در لاگ می‌گیرم:

Terminal window
ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new nosuchuser@localhost true 2>&1 | tail -1
su - ali -c 'sudo whoami' > /dev/null
sleep 1
echo "--- در auth.log:"
grep -E 'Invalid user|Failed' /var/log/auth.log | tail -2 | cut -c1-130
echo "--- و فعالیت‌های sudo:"
grep 'sudo:' /var/log/auth.log | grep COMMAND | tail -2 | cut -c1-140
echo "--- ورودهای موفق اخیر (last):"
last -n 3 | head -3
خروجی
nosuchuser@localhost: Permission denied (publickey,password).
--- در auth.log:
2026-10-04T08:06:47.454405+00:00 server sshd[17448]: Invalid user nosuchuser from ::1 port 54762
2026-10-04T08:10:43.112733+00:00 server sshd[17882]: Invalid user nosuchuser from ::1 port 60168
--- و فعالیت‌های sudo:
2026-10-04T08:06:47.468325+00:00 server sudo: ali : PWD=/home/ali ; USER=root ; COMMAND=/usr/bin/whoami
2026-10-04T08:10:43.126274+00:00 server sudo: ali : PWD=/home/ali ; USER=root ; COMMAND=/usr/bin/whoami
--- ورودهای موفق اخیر (last):
root pts/0 tmux(16895).%0 Sun Oct 4 07:55 - 07:56 (00:00)
root pts/0 tmux(15989).%0 Sun Oct 4 07:41 - 07:41 (00:00)
root pts/0 tmux(15434).%0 Sun Oct 4 07:30 - 07:31 (00:00)

Invalid user nosuchuser from آدرس port N: این همان الگوی حمله‌های حدس رمز است که به‌محض باز شدن پورت ۲۲ روی اینترنت می‌بینی (و fail2ban در پروژه‌ی پایانی از همین لاگ برای بستن مهاجم‌ها استفاده می‌کند). last تاریخچه‌ی ورودها را از wtmp می‌خواند و lastb ورودهای ناموفق را (به root نیاز دارد).

مثال ۶: یک سرویس را عمداً خراب کن و علتش را پیدا کن (تمرین اصلی)

Section titled “مثال ۶: یک سرویس را عمداً خراب کن و علتش را پیدا کن (تمرین اصلی)”

بهترین راه یاد گرفتن عیب‌یابی این است که خودت یک خرابی بسازی. فایل تنظیمات nginx را با یک غلط تایپی خراب می‌کنم (یک directive که وجود ندارد) و آن را ریستارت می‌کنم:

Terminal window
cp /etc/nginx/nginx.conf /tmp/loglab-nginx.conf.bak
sed -i '0,/^events {/s//foo_bar on;\nevents {/' /etc/nginx/nginx.conf
echo "--- ریستارت سرویس:"
systemctl restart nginx
echo "کد خروج: $?"
خروجی
--- ریستارت سرویس:
Job for nginx.service failed because the control process exited with error code.
See "systemctl status nginx.service" and "journalctl -xeu nginx.service" for details.
کد خروج: 1

سرویس بالا نیامد و systemctl فقط یک راهنمایی کلی داد. قدم اول: وضعیت سرویس. قدم دوم: لاگ خود آن سرویس:

Terminal window
echo "--- ۱) systemctl status:"
systemctl status nginx --no-pager | head -9
echo "--- ۲) لاگ سرویس (journalctl -u):"
journalctl -u nginx --no-pager -n 6 -o cat
echo "--- ۳) خود برنامه می‌تواند تنظیمات را آزمایش کند (nginx -t):"
nginx -t 2>&1
خروجی
--- ۱) systemctl status:
× nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; preset: enabled)
Active: failed (Result: exit-code) since Sun 2026-10-04 08:10:44 UTC; 10ms ago
Docs: man:nginx(8)
Process: 17901 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=1/FAILURE)
CPU: 4ms
Oct 04 08:10:44 server systemd[1]: Starting nginx.service - A high performance web server and a reverse proxy server...
Oct 04 08:10:44 server nginx[17901]: 2026/10/04 08:10:44 [emerg] 17901#17901: unknown directive "foo_bar" in /etc/nginx/nginx.conf:7
--- ۲) لاگ سرویس (journalctl -u):
Starting nginx.service - A high performance web server and a reverse proxy server...
2026/10/04 08:10:44 [emerg] 17901#17901: unknown directive "foo_bar" in /etc/nginx/nginx.conf:7
nginx: configuration file /etc/nginx/nginx.conf test failed
nginx.service: Control process exited, code=exited, status=1/FAILURE
nginx.service: Failed with result 'exit-code'.
Failed to start nginx.service - A high performance web server and a reverse proxy server.
--- ۳) خود برنامه می‌تواند تنظیمات را آزمایش کند (nginx -t):
2026/10/04 08:10:44 [emerg] 17906#17906: unknown directive "foo_bar" in /etc/nginx/nginx.conf:7
nginx: configuration file /etc/nginx/nginx.conf test failed

علت پیدا شد: unknown directive "foo_bar" in /etc/nginx/nginx.conf:N با شماره‌ی خط. دقت کن آخرین خط لاگ («Failed to start…») فقط پیامد است؛ اولین خط خطا ([emerg] unknown directive) علت واقعی. حالا درست می‌کنم و بررسی:

Terminal window
cp /tmp/loglab-nginx.conf.bak /etc/nginx/nginx.conf
nginx -t 2>&1
systemctl restart nginx && echo "سرویس بالا آمد"
curl -sI http://localhost | head -1
خروجی
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
سرویس بالا آمد
HTTP/1.1 200 OK

یک نوع خرابی دیگر: سرویس بالا می‌آید ولی سایت خطا می‌دهد. اینجا لاگ خود برنامه (/var/log/nginx/error.log) راه است. با گرفتن دسترسی فایل صفحه (درس دسترسی‌ها):

Terminal window
chmod 000 /var/www/html/index.nginx-debian.html
echo "--- پاسخ سایت:"
curl -sI http://localhost | head -1
echo "--- لاگ خطای nginx:"
tail -n 1 /var/log/nginx/error.log | cut -c1-170
chmod 644 /var/www/html/index.nginx-debian.html
echo "--- بعد از درست‌کردن دسترسی:"
curl -sI http://localhost | head -1
خروجی
--- پاسخ سایت:
HTTP/1.1 403 Forbidden
--- لاگ خطای nginx:
2026/10/04 08:10:44 [error] 17915#17915: *2 open() "/var/www/html/index.nginx-debian.html" failed (13: Permission denied), client: ::1, server: _, request: "HEAD / HTTP/1
--- بعد از درست‌کردن دسترسی:
HTTP/1.1 200 OK

403 Forbidden و در error.log دلیلش: open() ... failed (13: Permission denied). دو خرابی متفاوت، دو جای متفاوت برای لاگ: اگر سرویس بالا نمی‌آید journalctl -u، اگر بالاست ولی کار نمی‌کند لاگ‌های خود برنامه (/var/log/نام/). و یک مورد سوم که از درس پورت‌ها می‌شناسی، پورت اشغال‌شده:

Terminal window
systemctl stop nginx
python3 -m http.server 80 --bind 0.0.0.0 > /dev/null 2>&1 &
sleep 1
systemctl restart nginx
echo "کد خروج: $?"
journalctl -u nginx --no-pager -n 12 -o cat | grep -E 'bind\(\)' | head -2
echo "--- چه کسی پورت 80 را گرفته؟"
ss -tlnp 'sport = :80' | awk 'NR>1 {print $4, $6}' | cut -c1-70
pkill -f '[h]ttp.server 80'
sleep 1
systemctl restart nginx && echo "بعد از آزاد شدن پورت: بالا آمد"
خروجی
Job for nginx.service failed because the control process exited with error code.
See "systemctl status nginx.service" and "journalctl -xeu nginx.service" for details.
کد خروج: 1
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
--- چه کسی پورت 80 را گرفته؟
0.0.0.0:80 users:(("python3",pid=17930,fd=3))
بعد از آزاد شدن پورت: بالا آمد

مثال ۷: چرخش لاگ با logrotate

Section titled “مثال ۷: چرخش لاگ با logrotate”

لاگ‌ها بی‌نهایت بزرگ می‌شوند و دیسک را پر می‌کنند (درس منابع). logrotate (که روزانه با یک timer اجرا می‌شود) فایل‌های بزرگ را «می‌چرخاند»: app.log را app.log.1 می‌کند، قبلی‌ها را فشرده می‌کند (.2.gz)، از قدیمی‌ها فقط تعداد مشخصی نگه می‌دارد و یک فایل خالی تازه می‌سازد. برای یک برنامه‌ی نمونه یک قاعده می‌نویسم (در /etc/logrotate.d/). به‌جای «روزانه»، برای آزمایش گفته‌ام وقتی از ۱ کیلوبایت گذشت بچرخان:

Terminal window
cat > /etc/logrotate.d/lx-app <<'EOF'
/var/log/lx-app.log {
su root adm
size 1k
rotate 3
compress
delaycompress
missingok
notifempty
create 640 root adm
}
EOF
for i in $(seq 1 60); do echo "$(date +%T) خط شماره‌ی $i از لاگ برنامه ........................" >> /var/log/lx-app.log; done
echo "--- قبل:"
ls -l /var/log/lx-app.log* | awk '{print $5, $9}'
echo "--- logrotate -d (فقط توضیح می‌دهد چه می‌کرد، چیزی عوض نمی‌شود):"
logrotate -d /etc/logrotate.d/lx-app 2>&1 | grep -E 'considering|log needs|renaming /var/log/lx-app.log to'
خروجی
--- قبل:
4971 /var/log/lx-app.log
--- logrotate -d (فقط توضیح می‌دهد چه می‌کرد، چیزی عوض نمی‌شود):
considering log /var/log/lx-app.log
log needs rotating
renaming /var/log/lx-app.log to /var/log/lx-app.log.1

معنی هر خط قاعده:

خط معنی
su root adm با چه کاربر و گروهی بچرخان. روی Ubuntu لازم است (اشتباهات رایج ۷)
size 1k وقتی بزرگ‌تر از ۱ کیلوبایت شد. (در واقعیت: daily یا weekly یا size 100M)
rotate 3 ۳ نسخه‌ی قدیمی نگه دار؛ قدیمی‌تر پاک می‌شود
compress نسخه‌های قدیمی را با gzip فشرده کن
delaycompress فشرده‌سازی را یک دور عقب بینداز: .1 متنی می‌ماند تا برنامه‌ای که هنوز به آن می‌نویسد گیر نکند
missingok اگر فایل لاگ نبود خطا نده
notifempty فایل خالی را نچرخان
create 640 root adm بعد از چرخش یک فایل خالی تازه با این مالک و دسترسی بساز

حالا tail -f و tail -F را هر دو روی این فایل باز می‌کنم، لاگ را می‌چرخانم و یک خط تازه می‌نویسم تا فرق ببینی. (tail -F بعد از چرخش چند ثانیه طول می‌کشد تا فایل تازه را ببیند، پس اجازه می‌دهم ۹ ثانیه کار کنند.)

Terminal window
timeout 9 tail -n 0 -f /var/log/lx-app.log > /tmp/loglab-f.out 2>&1 &
timeout 9 tail -n 0 -F /var/log/lx-app.log > /tmp/loglab-F.out 2>&1 &
sleep 1
echo "خط قبل از چرخش" >> /var/log/lx-app.log
sleep 1
logrotate -f /etc/logrotate.d/lx-app
echo "--- بعد از چرخش:"
ls -l /var/log/lx-app.log* | awk '{print $5, $9}'
echo "خط بعد از چرخش" >> /var/log/lx-app.log
wait
echo "--- tail -f (به فایل قدیمی چسبیده ماند):"
cat /tmp/loglab-f.out
echo "--- tail -F (فایل تازه را پیدا کرد و دنبال کرد):"
cat /tmp/loglab-F.out
خروجی
--- بعد از چرخش:
0 /var/log/lx-app.log
4997 /var/log/lx-app.log.1
--- tail -f (به فایل قدیمی چسبیده ماند):
خط قبل از چرخش
--- tail -F (فایل تازه را پیدا کرد و دنبال کرد):
خط قبل از چرخش
tail: '/var/log/lx-app.log' has been replaced; following new file
خط بعد از چرخش

بعد از چرخش app.log به app.log.1 رفت و یک app.log خالی تازه ساخته شد. tail -f به فایل قدیمی (که حالا .1 است) چسبیده ماند و «خط بعد از چرخش» را ندید؛ tail -F (حرف بزرگ؛ follow by name, retry) متوجه شد فایل با آن اسم عوض شده (has been replaced; following new file) و دنبال فایل تازه رفت. برای هر لاگی که چرخانده می‌شود همیشه tail -F بزن. و قاعده‌های چرخش برنامه‌های سیستم:

Terminal window
rm -f /var/log/lx-app.log* /etc/logrotate.d/lx-app
sed -i '/lx-app/d' /var/lib/logrotate/status
echo "--- قاعده‌های logrotate سیستم:"
ls /etc/logrotate.d
echo "--- تنظیمات عمومی (بدون توضیح):"
grep -v '^#' /etc/logrotate.conf | grep -v '^$' | head -8
echo "--- کی اجرا می‌شود؟"
systemctl list-timers logrotate.timer --no-pager | cut -c1-110
خروجی
--- قاعده‌های logrotate سیستم:
alternatives
apt
btmp
dpkg
nginx
rsyslog
ufw
wtmp
--- تنظیمات عمومی (بدون توضیح):
weekly
su root adm
rotate 4
create
include /etc/logrotate.d
--- کی اجرا می‌شود؟
NEXT LEFT LAST PASSED UNIT ACTIVATES
Mon 2026-10-05 00:00:00 UTC 15h Sun 2026-10-04 00:00:01 UTC 6h ago logrotate.timer logrotate.service
1 timers listed.
Pass --all to see loaded but inactive timers, too.

مثال ۸: ده دقیقه‌ی اول عیب‌یابی، یک اسکریپت چک

Section titled “مثال ۸: ده دقیقه‌ی اول عیب‌یابی، یک اسکریپت چک”

وقتی به یک سرور مشکوک وصل می‌شوی، همیشه یک مجموعه‌ی ثابت از چیزها را اول نگاه کن. می‌شود این‌ها را در یک اسکریپت گذاشت:

/usr/local/bin/lx-diag.sh
#!/bin/bash
# یک نگاه سریع به سلامت سرور
echo "== ۱) بار و مدت روشن‌بودن:"; uptime
echo "== ۲) دیسک (ریشه):"; df -h / | tail -1
echo "== ۳) حافظه (MB):"; free -m | awk 'NR==2 {print "کل="$2" مصرف="$3" آزاد="$4" available="$7}'
echo "== ۴) سرویس‌های شکست‌خورده:"; systemctl --failed --no-legend | cut -c1-80 || true
echo "== ۵) آخرین خطاهای journal (این بوت، ۳ خط):"; journalctl -p err -b --no-pager -n 3 -o cat 2>/dev/null | cut -c1-110
echo "== ۶) پورت‌های در حال گوش‌دادن:"; ss -tlnp | awk 'NR>1 {print $4}' | sort -u | tr '\n' ' '; echo
Terminal window
chmod +x /usr/local/bin/lx-diag.sh
/usr/local/bin/lx-diag.sh
خروجی
== ۱) بار و مدت روشن‌بودن:
08:10:57 up 1 day, 11:33, 0 user, load average: 0.06, 0.10, 0.14
== ۲) دیسک (ریشه):
overlay 453G 22G 408G 6% /
== ۳) حافظه (MB):
کل=3916 مصرف=1294 آزاد=1373 available=2621
== ۴) سرویس‌های شکست‌خورده:
== ۵) آخرین خطاهای journal (این بوت، ۳ خط):
پیام با سطح crit
Failed to start nginx.service - A high performance web server and a reverse proxy server.
Failed to start nginx.service - A high performance web server and a reverse proxy server.
== ۶) پورت‌های در حال گوش‌دادن:
0.0.0.0:22 0.0.0.0:80 [::]:22 [::]:80

این شش نگاه، بیشتر مشکل‌ها را در یک دقیقه دسته‌بندی می‌کند: بار زیاد (CPU)، دیسک یا حافظه‌ی پر، یک سرویس شکست‌خورده، خطای تازه‌ی journal، یا یک پورت که باید باز باشد و نیست.

پشت پرده: syslog، journald و rsyslog

Section titled “پشت پرده: syslog، journald و rsyslog”

دو سیستم لاگ کنار هم کار می‌کنند. journald پیام‌ها را از همه‌جا (stdout سرویس‌ها، syslog، هسته) می‌گیرد و در یک پایگاه داده‌ی ساخت‌یافته نگه می‌دارد: هر پیام کلی فیلد دارد، نه فقط یک خط متن. ببین یک پیام ساده چند فیلد دارد:

Terminal window
journalctl -t myapp -n 1 -o verbose --no-pager | grep -E '^ (_PID|_UID|_COMM|_HOSTNAME|SYSLOG_IDENTIFIER|PRIORITY|MESSAGE|_TRANSPORT)=' | cut -c1-90
خروجی
_UID=0
_HOSTNAME=server
_TRANSPORT=syslog
PRIORITY=5
SYSLOG_IDENTIFIER=myapp
_COMM=logger
MESSAGE=disk usage at 91 percent
_PID=17868

برای همین می‌شود بر اساس _PID، _UID، PRIORITY، _SYSTEMD_UNIT و… دقیق فیلتر کرد (journalctl _PID=123). rsyslog هم یک زمان‌بندی کلاسیک است که طبق قاعده‌هایش (facility.priority → فایل) نسخه‌ی متنی را در /var/log می‌نویسد:

Terminal window
echo "--- قاعده‌های rsyslog (facility.priority ← فایل):"
grep -v '^#' /etc/rsyslog.d/50-default.conf | grep -v '^$' | head -5
echo "--- journal کجا ذخیره می‌شود؟ (دائمی یا فقط RAM)"
ls -d /var/log/journal /run/log/journal 2>&1
خروجی
--- قاعده‌های rsyslog (facility.priority ← فایل):
auth,authpriv.* /var/log/auth.log
*.*;auth,authpriv.none -/var/log/syslog
kern.* -/var/log/kern.log
mail.* -/var/log/mail.log
mail.err /var/log/mail.err
--- journal کجا ذخیره می‌شود؟ (دائمی یا فقط RAM)
/run/log/journal
/var/log/journal
  • facility نوع منبع: kern، user، mail، auth، daemon… و priority همان ۰ تا ۷ بالا. auth,authpriv.* یعنی همه‌ی سطح‌های ورود و احراز هویت ← /var/log/auth.log.
  • اگر /var/log/journal وجود داشته باشد journal دائمی است (بعد از ریبوت می‌ماند)؛ وگرنه فقط در /run/log/journal (RAM) است و با ریبوت پاک می‌شود، که برای بررسی «چرا ریبوت شد؟» فاجعه است.

روش عیب‌یابی قدم‌به‌قدم

Section titled “روش عیب‌یابی قدم‌به‌قدم”

بعد از همه‌ی ابزارها، مهم‌تر از آن‌ها روش است:

  1. نشانه را دقیق بنویس: چه چیزی کار نمی‌کند؟ از چه ساعتی؟ برای همه یا یک نفر؟ چه چیزی تغییر کرده بود (دیپلوی، تنظیمات، به‌روزرسانی)؟
  2. از بیرون به داخل بسنج: آیا ماشین جواب می‌دهد (ping)؟ پورت باز است (nc -zv)؟ سرویس بالاست (systemctl status)؟ (درس شبکه و پورت‌ها.)
  3. لاگ را بخوان، با کران زمانی: journalctl -u سرویس --since "30 min ago"؛ لاگ برنامه در /var/log/. اولین خطای نزدیک زمان مشکل را پیدا کن، نه آخرین.
  4. منابع را ببین: df -h (دیسک پر؟)، free -h، uptime، ss -tlnp.
  5. یک فرضیه، یک تغییر: هر بار فقط یک چیز را عوض کن و بعد آزمایش کن؛ اگر چند چیز را با هم عوض کنی نمی‌فهمی کدام درست کرد (یا خراب‌تر کرد).
  6. قبل از تغییر پشتیبان بگیر (cp فایل فایل.bak) و بعد از تغییر آزمایش کن (nginx -t، sshd -t).
  7. ثبت کن: چه شد، چرا شد، چه کردی. دفعه‌ی بعد (یا همکارت) همان را می‌خواهد.
دستور کار
tail -n 50 /var/log/syslog ۵۰ خط آخر
tail -F /var/log/app.log دنبال‌کردن زنده (حتی بعد از چرخش)
grep -i error /var/log/app.log جستجو (بدون حساسیت به حروف)
journalctl -u نام -n 50 لاگ یک سرویس
journalctl -u نام -f زنده
journalctl --since "1 hour ago" -p err خطاهای یک ساعت اخیر
journalctl -b -1 بوت قبلی
journalctl -k / dmesg -T پیام‌های هسته
logger -t برچسب -p user.err "پیام" نوشتن لاگ
last / lastb تاریخچه‌ی ورودها / ورودهای ناموفق
logrotate -d فایل / -f فایل آزمایش / اجبار چرخش
journalctl --disk-usage حجم journal
systemctl --failed سرویس‌های شکست‌خورده

۱) خواندن لاگ با کاربر عادی

Section titled “۱) خواندن لاگ با کاربر عادی”
Terminal window
useradd -m -s /bin/bash bob
echo "--- bob: auth.log را می‌خواند:"
su - bob -c 'cat /var/log/auth.log' 2>&1 | head -1
echo "--- راه‌حل: bob را عضو گروه adm می‌کنم:"
usermod -aG adm bob
su - bob -c 'tail -n 1 /var/log/auth.log | cut -c1-80'
userdel -r bob 2>/dev/null
خروجی
--- bob: auth.log را می‌خواند:
cat: /var/log/auth.log: Permission denied
--- راه‌حل: bob را عضو گروه adm می‌کنم:
2026-10-04T08:10:58.047953+00:00 server su[18082]: pam_unix(su-l:session): sessi

لاگ‌ها برای همه خواندنی نیستند (ممکن است راز داشته باشند). راه‌حل: sudo یا عضو کردن کاربر در گروه adm (برای فایل‌های /var/log) و systemd-journal (برای journal). کاربر عادی بدون این گروه‌ها در journalctl فقط پیام‌های خودش را می‌بیند؛ و journalctl در این حالت خودش یک یادآوری می‌نویسد که اعضای کدام گروه‌ها همه‌چیز را می‌بینند.

۲) tail -f روی لاگی که چرخانده می‌شود

Section titled “۲) tail -f روی لاگی که چرخانده می‌شود”

مثال ۷: بعد از چرخش tail -f به فایل قدیمی چسبیده می‌ماند و ساکت می‌شود. راه‌حل: tail -F.

۳) خواندن همه‌چیز بدون کران

Section titled “۳) خواندن همه‌چیز بدون کران”

journalctl بدون -u و --since ده‌ها هزار خط می‌دهد و علت در انبوه گم می‌شود. راه‌حل: همیشه سرویس و بازه‌ی زمانی را محدود کن (-u، --since، -n، -p).

۴) توجه به پیامد به‌جای علت

Section titled “۴) توجه به پیامد به‌جای علت”

آخرین خط لاگ معمولاً «Failed to start» است؛ علت چند خط قبل‌تر است (مثال ۶). راه‌حل: از اولین نشانه‌ی خطا (نزدیک زمان مشکل) به جلو بخوان.

۵) فراموش‌کردن منطقه‌ی زمانی

Section titled “۵) فراموش‌کردن منطقه‌ی زمانی”

لاگ‌ها ممکن است به وقت UTC باشند و ساعت دیواری تو چیز دیگر. راه‌حل: date و journalctl --utc، و هنگام گزارش مشکل منطقه را هم بنویس.

du -sh /var/log و journalctl --disk-usage را گاهی ببین؛ journal را با journalctl --vacuum-size=200M محدود کن و برای فایل‌های لاگ برنامه‌ها logrotate بنویس (مثال ۷):

Terminal window
echo "--- محدودکردن journal به 1G (اگر کوچک‌تر باشد چیزی پاک نمی‌شود):"
journalctl --vacuum-size=1G 2>&1 | tail -1
خروجی
--- محدودکردن journal به 1G (اگر کوچک‌تر باشد چیزی پاک نمی‌شود):
Vacuuming done, freed 0B of archived journals from /var/log/journal.

روی Ubuntu پوشه‌ی /var/log برای گروه syslog قابل‌نوشتن است، و logrotate برای فایل‌های داخل پوشه‌ای که «ناامن» حساب می‌شود، چرخش را رد می‌کند:

Terminal window
cat > /etc/logrotate.d/lx-nosu <<'EOF'
/var/log/lx-nosu.log {
size 1k
rotate 2
}
EOF
seq 1 200 > /var/log/lx-nosu.log
logrotate -f /etc/logrotate.d/lx-nosu 2>&1 | cut -c1-120
echo "کد خروج: ${PIPESTATUS[0]}"
rm -f /etc/logrotate.d/lx-nosu /var/log/lx-nosu.log*
sed -i '/lx-nosu/d' /var/lib/logrotate/status
خروجی
error: skipping "/var/log/lx-nosu.log" because parent directory has insecure permissions (It's world writable or writabl
کد خروج: 1

skipping ... because parent directory has insecure permissions ... Set "su" directive: خود پیام راه‌حل را می‌گوید. راه‌حل: یک خط su root adm به قاعده اضافه کن (مثال ۷). نتیجه‌ی بدتر: اگر این خطا را نبینی، لاگ هیچ‌وقت نمی‌چرخد و یک شب دیسک پر می‌شود؛ پس بعد از نوشتن هر قاعده‌ی تازه حتماً یک بار با logrotate -d (و در صورت لزوم -f) آزمایشش کن.

✎ تمرینآسان

در ۱۰ دقیقه‌ی اخیر چند پیام سطح خطا (err) یا بدتر در journal ثبت شده؟ و آخرین ۲ تای آن‌ها چیست (فقط متن)؟

دیدن جواب
Terminal window
echo "تعداد خطاها در ۱۰ دقیقه‌ی اخیر: $(journalctl --since "10 minutes ago" -p err --no-pager -o cat | wc -l)"
journalctl --since "10 minutes ago" -p err --no-pager -o cat | tail -2 | cut -c1-120
خروجی
تعداد خطاها در ۱۰ دقیقه‌ی اخیر: 12
Failed to start nginx.service - A high performance web server and a reverse proxy server.
Failed to start nginx.service - A high performance web server and a reverse proxy server.

با -p err فقط خطاها و بدتر؛ و با --since کران زمانی. این دو را همیشه با هم بزن.

✎ تمرینمتوسط

تمرین اصلی: یک سرویس را عمداً خراب کن و علتش را از لاگ پیدا کن. در nginx، پورت listen را به یک مقدار بی‌معنی (listen abc;) تغییر بده، سرویس را ریستارت کن، با journalctl -u nginx و nginx -t علت را پیدا کن، فایل را برگردان و سرویس را بالا بیاور.

دیدن جواب
Terminal window
cp /etc/nginx/sites-available/default /tmp/loglab-default.bak
sed -i '0,/listen 80 default_server;/s//listen abc;/' /etc/nginx/sites-available/default
systemctl restart nginx 2>&1 | head -2
echo "--- علت از journal:"
journalctl -u nginx --no-pager -n 6 -o cat | grep -iE 'emerg|invalid|error' | head -2
echo "--- و با nginx -t:"
nginx -t 2>&1 | head -2
cp /tmp/loglab-default.bak /etc/nginx/sites-available/default
nginx -t 2>&1 | tail -1
systemctl restart nginx && echo "سرویس دوباره بالا آمد"
خروجی
Job for nginx.service failed because the control process exited with error code.
See "systemctl status nginx.service" and "journalctl -xeu nginx.service" for details.
--- علت از journal:
2026/10/04 08:10:58 [emerg] 18115#18115: host not found in "abc" of the "listen" directive in /etc/nginx/sites-enabled/default:22
--- و با nginx -t:
2026/10/04 08:10:58 [emerg] 18120#18120: host not found in "abc" of the "listen" directive in /etc/nginx/sites-enabled/default:22
nginx: configuration file /etc/nginx/nginx.conf test failed
nginx: configuration file /etc/nginx/nginx.conf test is successful
سرویس دوباره بالا آمد
✎ تمرینسخت

یک «دیده‌بان» ساده بنویس: فایل لاگی را با tail -F دنبال کن و هر وقت خطی با ERROR آمد، یک پیام هشدار چاپ کن. با یک حلقه چند خط (INFO و ERROR) به آن بنویس و ثابت کن فقط خطاها هشدار دادند.

دیدن جواب
Terminal window
: > /tmp/loglab-watch.log
( timeout 6 tail -n 0 -F /tmp/loglab-watch.log | grep --line-buffered ERROR | while read -r line; do echo "هشدار! $line"; done ) &
sleep 1
echo "INFO شروع" >> /tmp/loglab-watch.log
echo "ERROR دیسک پر است" >> /tmp/loglab-watch.log
echo "INFO ادامه" >> /tmp/loglab-watch.log
echo "ERROR اتصال قطع شد" >> /tmp/loglab-watch.log
wait
خروجی
هشدار! ERROR دیسک پر است
هشدار! ERROR اتصال قطع شد
؟ آزمونک
  1. کدام دستور لاگ‌های خطا و بدتر یک سرویس را از یک ساعت اخیر نشان می‌دهد؟

  2. سرویسی بالا نمی‌آید. اولین جایی که نگاه می‌کنی؟

  3. تفاوت tail -f و tail -F برای لاگی که چرخانده می‌شود؟

  4. در لاگ، «آخرین» خط خطا یا «اولین» خط خطا به علت اصلی نزدیک‌تر است؟

  5. dmesg چه چیزی را نشان می‌دهد؟

  6. logrotate چه می‌کند و چرا لازم است؟

  • لاگ‌ها دو جا هستند: متنی در /var/log (syslog، auth.log، nginx/، dpkg.log) و journal با journalctl؛ dmesg برای هسته. فایل‌ها برای گروه adm خواندنی‌اند.
  • journalctl: همیشه فیلتر کن: -u سرویس، --since/--until زمان، -p err سطح، -b بوت، -n، -f زنده، -o cat، -g جستجو. logger -t برچسب -p user.err "پیام" برای نوشتن لاگ.
  • سطح‌ها: emerg(0) تا debug(7)؛ -p err یعنی ۳ و کمتر.
  • tail -f زنده؛ برای لاگ چرخان tail -F. logrotate فایل‌ها را می‌چرخاند و فشرده می‌کند تا دیسک پر نشود.
  • عیب‌یابی: نشانه‌ی دقیق ← از بیرون به داخل (ping، nc -zv، systemctl status) ← لاگ با کران زمانی، اولین خطا ← منابع (df، free) ← یک فرضیه و یک تغییر ← پشتیبان و آزمایش (nginx -t) ← ثبت.
  • سرویس بالا نمی‌آید: journalctl -u؛ بالاست ولی کار نمی‌کند: لاگ خود برنامه.
برگه‌ی تقلب این درس
دستورکاری که می‌کند
tail -n 50 /var/log/syslog / tail -F فایل۵۰ خط آخر / دنبال‌کردن زنده
journalctl -u نام -n 50 --no-pagerلاگ یک سرویس
journalctl -u نام -fزنده
journalctl --since "1 hour ago" -p errخطاهای یک ساعت اخیر
journalctl -b -1 / journalctl --list-bootsبوت قبلی / فهرست بوت‌ها
journalctl -g عبارت -o catجستجو، فقط متن
dmesg -T --level=err,warnهشدار و خطای هسته با زمان خوانا
logger -t myapp -p user.err "پیام"نوشتن لاگ
grep -E "Invalid user|Failed" /var/log/auth.logتلاش‌های ورود ناموفق
systemctl --failedسرویس‌های شکست‌خورده
logrotate -d /etc/logrotate.d/appآزمایش قاعده‌ی چرخش
journalctl --disk-usageحجم journal