ล็อกใน Linux: journalctl, /var/log, rsyslog และ logrotate
บนดิสโทรสมัยใหม่ journald เป็นตัวเก็บล็อก และ journalctl เป็นคำสั่งสำหรับอ่าน เช่น journalctl -u nginx แสดงเฉพาะบันทึกของบริการหนึ่ง -f แสดงแบบเรียลไทม์ และ --since กับ -p กรองตามเวลาและความสำคัญ ล็อกแบบข้อความอยู่ใน /var/log ซึ่งเขียนโดย rsyslog หรือโปรแกรมเอง และ logrotate บีบอัดและลบไฟล์เก่าเป็นระยะ เพื่อไม่ให้ดิสก์เต็ม
เมื่อบริการล่มตอนกลางคืน คำตอบมักอยู่ในล็อกเสมอ คำถามมีเพียงว่าจะหาที่ไหน ในดิสโทรสมัยใหม่ (Ubuntu, Debian, RHEL, Fedora, Arch) เหตุการณ์ต่าง ๆ ถูกเก็บโดยบริการ journald และอ่านด้วยคำสั่ง journalctl ขณะเดียวกันโปรแกรมจำนวนมากเขียนไฟล์ข้อความธรรมดาลงใน /var/log และ logrotate คอยดูแลขนาดของไฟล์เหล่านั้น ด้านล่างจะอธิบายการใช้ล็อกของ journalctl บน Linux ในทางปฏิบัติ ว่าล็อกอื่นอยู่ที่ไหน และจะไม่ทำให้สูญเสียข้อมูลสำคัญระหว่างการหมุนไฟล์ได้อย่างไร
ล็อกบน Linux อยู่ที่ไหน
มีแหล่งอยู่สามแห่ง การจำไว้เป็นแผนที่จะช่วยได้มาก:
- journald คือวารสารแบบไบนารีของ systemd รับ stdout และ stderr ของทุกบริการ ข้อความของเคอร์เนล และ syslog เก็บไว้ที่
/var/log/journal/(ถาวร) หรือ/run/log/journal/(จนกว่าจะรีบูต) - /var/log คือไฟล์ข้อความ เขียนโดย
rsyslog(เช่น/var/log/syslog,/var/log/auth.log) หรือโดยโปรแกรมเอง เช่น nginx, PostgreSQL, MySQL - ring buffer ของเคอร์เนล คือข้อความจากไดรเวอร์และฮาร์ดแวร์ ดูได้ด้วย
dmesgหรือjournalctl -k
| ไฟล์ | เนื้อหา |
|---|---|
/var/log/syslog (Debian/Ubuntu) | ล็อกระบบรวม |
/var/log/messages (RHEL/Fedora) | สิ่งเดียวกันในตระกูล Red Hat |
/var/log/auth.log หรือ /var/log/secure | การล็อกอิน sudo และ SSH |
/var/log/kern.log | ข้อความของเคอร์เนล |
/var/log/dpkg.log, /var/log/apt/ | การติดตั้งแพ็กเกจ |
/var/log/nginx/ | access.log และ error.log ของเว็บเซิร์ฟเวอร์ |
/var/log/wtmp, /var/log/btmp | ล็อกการล็อกอินแบบไบนารี อ่านด้วย last และ lastb |
ไฟล์ส่วนใหญ่ใน /var/log อ่านได้เฉพาะ root หรือกลุ่ม adm ดังนั้นคำสั่งอ่านมักต้องรันผ่าน sudo
journalctl: คำสั่งพื้นฐาน
หากไม่ใส่อาร์กิวเมนต์ journalctl จะแสดงวารสารทั้งหมดตั้งแต่รายการเก่าสุด ผ่านตัวแสดงผลแบบแบ่งหน้า less ซึ่งแทบไม่มีใครต้องการ มักจะจำกัดผลลัพธ์ทันที
journalctl # วารสารทั้งหมด จากเก่าไปใหม่
journalctl -e # เปิดที่ท้ายสุดทันที
journalctl -n 50 # 50 บรรทัดล่าสุด
journalctl -r # เรียงย้อนกลับ ใหม่สุดอยู่บน
journalctl --no-pager # ไม่ใช้ less เหมาะกับสคริปต์และ grep
ภายในตัวแสดงผลใช้ปุ่มของ less ได้ตามปกติ: / ค้นหา G ไปท้ายสุด q ออก บรรทัดยาวจะถูกตัดตามความกว้างหน้าจอ ลูกศรซ้ายและขวาเลื่อนดูได้
ล็อกของบริการเฉพาะ: journalctl -u
สถานการณ์ที่พบบ่อยที่สุดคือดูว่ายูนิต systemd หนึ่งตัวเขียนอะไรไว้ โดยไม่ต้องหาไฟล์และใช้ grep:
$ sudo journalctl -u nginx -n 20
Oct 14 03:12:41 web1 systemd[1]: Starting nginx.service - A high performance web server...
Oct 14 03:12:41 web1 nginx[1834]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Oct 14 03:12:41 web1 systemd[1]: nginx.service: Control process exited, code=exited, status=1/FAILURE
Oct 14 03:12:41 web1 systemd[1]: nginx.service: Failed with result 'exit-code'.
แฟล็ก -u ใส่ซ้ำได้: journalctl -u nginx -u php8.2-fpm แสดงทั้งสองบริการสลับกันตามเวลา ซึ่งสะดวกเมื่อข้อผิดพลาดเกิดขึ้นตรงรอยต่อ สำหรับยูนิตผู้ใช้ (systemctl --user) ใช้ journalctl --user -u ชื่อ
ล็อกแบบเรียลไทม์: journalctl -f
sudo journalctl -u nginx -f
นี่คือแอนะล็อกของ tail -f คำสั่งจะแสดง 10 รายการล่าสุดแล้วรอรายการใหม่ เหมาะกับการเปิดไว้ในเทอร์มินัลอีกหน้าต่างหนึ่งระหว่างรีสตาร์ตบริการหรือทำให้ข้อผิดพลาดเกิดซ้ำ กด Ctrl+C เพื่อออก
กรองตามเวลาและการบูต
หากรู้ว่าเหตุขัดข้องเกิดขึ้นเมื่อไร ใช้แฟล็ก --since และ --until เพื่อเลือกช่วงเวลา รองรับทั้งวันที่แบบระบุตรงและค่าสัมพัทธ์:
journalctl --since "2026-10-14 02:50" --until "2026-10-14 03:30"
journalctl --since "1 hour ago"
journalctl --since yesterday --until today
journalctl -u nginx --since "10 min ago"
การแบ่งอีกแบบที่มีประโยชน์คือตามการบูตของระบบ หากเซิร์ฟเวอร์รีบูตเอง มักหาสาเหตุที่ท้ายเซสชันก่อนหน้า:
$ journalctl --list-boots
-2 8f1c... Sat 2026-10-12 09:01:12 UTC Sun 2026-10-13 22:47:03 UTC
-1 a7d2... Sun 2026-10-13 22:48:30 UTC Tue 2026-10-14 03:05:51 UTC
0 c3e9... Tue 2026-10-14 03:07:02 UTC Tue 2026-10-14 09:15:40 UTC
$ journalctl -b -1 -e # ท้ายการบูตครั้งก่อน
$ journalctl -b # เฉพาะการบูตปัจจุบัน
รายการการบูตใช้ได้เฉพาะเมื่อวารสารถูกเก็บลงดิสก์ วิธีตั้งค่านั้นอยู่ด้านล่างในส่วนการตั้งค่า journald
กรองตามความสำคัญ: journalctl -p
ทุกรายการมีระดับความสำคัญ syslog ตั้งแต่ 0 ถึง 7 แฟล็ก -p แสดงระดับที่ระบุและทุกระดับที่สำคัญกว่า:
| รหัส | ชื่อ | เมื่อใช้ |
|---|---|---|
| 0 | emerg | ระบบใช้งานไม่ได้ |
| 1 | alert | ต้องแก้ไขทันที |
| 2 | crit | ข้อผิดพลาดร้ายแรง |
| 3 | err | ข้อผิดพลาด |
| 4 | warning | คำเตือน |
| 5 | notice | เหตุการณ์สำคัญแต่เป็นปกติ |
| 6 | info | ข้อความข้อมูล |
| 7 | debug | การดีบัก |
journalctl -p err -b # ข้อผิดพลาดทั้งหมดตั้งแต่เริ่มการบูต
journalctl -p warning..err -u ssh # ช่วงระดับสำหรับบริการเดียว
journalctl -p err -b เป็นการตรวจสอบเบื้องต้นที่ดีหลังรีบูตหรืออัปเดต เพราะแสดงให้เห็นภายในไม่กี่วินาทีว่าอะไรผิดพลาด
ค้นหาและตัวกรองอื่น ๆ
journald เก็บฟิลด์ที่มีโครงสร้างไว้กับแต่ละรายการ จึงกรองตามฟิลด์เหล่านั้นได้โดยไม่ต้องใช้ grep:
journalctl _PID=1834 # ตามหมายเลขโปรเซส
journalctl _COMM=sshd # ตามชื่อโปรแกรม
journalctl _UID=1000 # ตามผู้ใช้
journalctl /usr/sbin/sshd # ตามพาธของไฟล์ที่รัน
journalctl -k # เฉพาะข้อความเคอร์เนล (เหมือน dmesg)
journalctl -t CRON # ตามตัวระบุ syslog
journalctl -g "Out of memory" # ค้นหาข้อความด้วย regex
แฟล็ก -g (--grep) มีตั้งแต่ systemd 237 ในระบบเก่ากว่าให้ใช้ท่อธรรมดา journalctl --no-pager | grep ... ตัวกรองหลายฟิลด์จะรวมกันด้วย "และ" ดังนั้น journalctl -u nginx -p err --since today แสดงเฉพาะข้อผิดพลาดของ nginx ในวันนี้
รูปแบบผลลัพธ์
แฟล็ก -o เปลี่ยนรูปแบบ ตัวเลือกที่มีประโยชน์ที่สุด:
-o short-isoเวลาตามมาตรฐาน ISO 8601 เทียบกับล็อกของแอปพลิเคชันได้ง่าย-o verboseทุกฟิลด์ของรายการ รวมถึง_SYSTEMD_UNIT,_PID,_BOOT_ID-o jsonหรือjson-prettyสำหรับประมวลผลด้วยสคริปต์หรือjq-o catเฉพาะข้อความ ไม่มีวันที่และชื่อโฮสต์
journalctl -u myapp -o json --since today | jq -r '.MESSAGE'
ตั้งค่า journald: การเก็บและขนาด
การตั้งค่าอยู่ใน /etc/systemd/journald.conf (การแก้ไขทับค่าควรใส่ไว้ใน /etc/systemd/journald.conf.d/*.conf จะสะดวกกว่า) พารามิเตอร์หลักคือ Storage:
persistentเขียนลงดิสก์ที่/var/log/journalและคงอยู่หลังรีบูตvolatileเก็บไว้ในหน่วยความจำเท่านั้น ที่/run/log/journalauto(ค่าเริ่มต้น) เขียนลงดิสก์เมื่อไดเรกทอรี/var/log/journalมีอยู่
บนอิมเมจขั้นต่ำบางตัวไม่มีไดเรกทอรีนี้ ประวัติจึงหายไปหลังรีบูต วิธีเปิดการเก็บถาวร:
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald
เพื่อไม่ให้วารสารกินพื้นที่ดิสก์ทั้งหมด ตั้งขีดจำกัด:
# /etc/systemd/journald.conf.d/size.conf
[Journal]
Storage=persistent
SystemMaxUse=500M
MaxRetentionSec=1month
ค่าเริ่มต้นของ SystemMaxUse คือ 10% ของขนาดระบบไฟล์ แต่ไม่เกิน 4 GB
วิธีล้างวารสาร journalctl
$ journalctl --disk-usage
Archived and active journals take up 1.8G in the file system.
sudo journalctl --vacuum-size=300M # เก็บไว้ไม่เกิน 300 MB
sudo journalctl --vacuum-time=2weeks # ลบรายการที่เก่ากว่าสองสัปดาห์
sudo journalctl --rotate # ปิดไฟล์ที่กำลังใช้ เพื่อให้ลบได้ด้วย
ไม่ควรลบไฟล์ใน /var/log/journal ด้วย rm เพราะ vacuum ทำงานเดียวกันได้อย่างปลอดภัยและไม่แตะไฟล์ที่กำลังถูกเขียน
rsyslog: ล็อกข้อความแบบดั้งเดิม
rsyslog คือเดมอนที่รับข้อความ syslog (รวมถึงข้อความที่ส่งต่อจาก journald) และแยกเก็บลงไฟล์ตามกฎ การตั้งค่าอยู่ที่ /etc/rsyslog.conf และ /etc/rsyslog.d/*.conf กฎประกอบด้วยตัวเลือก แหล่งที่มา.ระดับ และการกระทำ:
# /etc/rsyslog.d/50-default.conf (ส่วนหนึ่ง)
auth,authpriv.* /var/log/auth.log
*.*;auth,authpriv.none -/var/log/syslog
kern.* -/var/log/kern.log
เครื่องหมายลบหน้าพาธหมายถึงการเขียนโดยไม่ซิงก์ลงดิสก์หลังทุกบรรทัด ซึ่งเร็วขึ้น แต่เมื่อระบบขัดข้องบรรทัดท้ายอาจหายได้ หลังแก้ไขการตั้งค่า ตรวจไวยากรณ์และรีสตาร์ตบริการ:
sudo rsyslogd -N1
sudo systemctl restart rsyslog
เหตุผลสำคัญที่ยังเก็บ rsyslog ไว้ข้าง journald คือการส่งล็อกไปยังเซิร์ฟเวอร์กลาง บรรทัดเดียวก็ส่งทุกอย่างผ่าน TCP ได้:
# /etc/rsyslog.d/90-remote.conf
*.* @@logs.example.com:514
เครื่องหมาย @ ตัวเดียวหมายถึง UDP ส่วน @@ สองตัวหมายถึง TCP หากเซิร์ฟเวอร์ทั้งเครื่องล่ม สำเนาของล็อกยังอยู่บนเครื่องอื่น หากต้องการเขียนข้อความของตัวเองลงล็อกระบบจากสคริปต์ ใช้ logger:
logger -t backup -p user.err "การสำรองฐานข้อมูลล้มเหลว"
journalctl -t backup -n 5
logrotate: การหมุนไฟล์ล็อก
ไฟล์ล็อกข้อความจะโตขึ้นเรื่อย ๆ จนกว่าจะมีใครตัด logrotate ทำหน้าที่นี้: วันละครั้ง (ผ่าน cron หรือ timer logrotate.timer) มันเปลี่ยนชื่อไฟล์ปัจจุบัน บีบอัดไฟล์เก่า และลบไฟล์ที่เก่าที่สุด การตั้งค่าทั่วไปอยู่ที่ /etc/logrotate.conf และกฎของแต่ละโปรแกรมอยู่ที่ /etc/logrotate.d/
# /etc/logrotate.d/myapp
/var/log/myapp/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0640 myapp adm
postrotate
systemctl reload myapp > /dev/null 2>&1 || true
endscript
}
| คำสั่ง | ความหมาย |
|---|---|
daily / weekly / size 100M | เมื่อใดที่ควรหมุนไฟล์ |
rotate 14 | เก็บไฟล์เก่ากี่ไฟล์ |
compress, delaycompress | บีบอัดด้วย gzip แต่ไฟล์ล่าสุดยังไม่ถูกบีบอัด |
missingok, notifempty | ไม่แจ้งข้อผิดพลาดเมื่อไม่มีไฟล์ และไม่หมุนไฟล์ที่ว่าง |
create 0640 user group | สิทธิ์ของไฟล์ใหม่ที่จะสร้าง |
copytruncate | คัดลอกและล้างไฟล์ให้ว่าง สำหรับโปรแกรมที่เปิดล็อกใหม่เองไม่ได้ |
postrotate ... endscript | คำสั่งหลังการหมุนไฟล์ มักเป็นการส่งสัญญาณให้แอปพลิเคชันเปิดไฟล์ใหม่ |
ทดสอบกฎโดยไม่เปลี่ยนแปลงอะไร และสั่งให้หมุนทันที:
sudo logrotate -d /etc/logrotate.d/myapp # ทดสอบแบบแห้งพร้อมข้อความดีบัก
sudo logrotate -f /etc/logrotate.d/myapp # หมุนทันที
ข้อผิดพลาดที่พบบ่อยคือลืม postrotate หรือ copytruncate เมื่อนั้นแอปพลิเคชันจะเขียนต่อไปที่ app.log.1 ที่ถูกเปลี่ยนชื่อผ่าน descriptor เดิม ส่วน app.log ใหม่จะว่างเปล่า
การอ่านไฟล์ล็อกข้อความ
สำหรับไฟล์ใน /var/log เครื่องมือมาตรฐานก็เพียงพอ:
sudo tail -n 100 /var/log/syslog # บรรทัดท้ายสุด
sudo tail -F /var/log/nginx/error.log # ติดตามโดยไม่หลุดเมื่อมีการหมุนไฟล์
sudo less +G /var/log/auth.log # เปิดที่ท้ายสุด
sudo zgrep "Failed password" /var/log/auth.log.*.gz # ค้นในไฟล์บีบอัด
sudo grep -c " 500 " /var/log/nginx/access.log # มีคำตอบ 500 กี่ครั้ง
สังเกต tail -F ตัวพิมพ์ใหญ่: ต่างจาก -f ตรงที่มันเปิดไฟล์ใหม่เมื่อ logrotate สลับไฟล์
ขั้นตอนวิเคราะห์เหตุขัดข้อง
สถานการณ์ "บริการล่มตอนกลางคืน" ควรวิเคราะห์ตามลำดับนี้:
systemctl status myappสถานะปัจจุบันและบรรทัดล่าสุดของวารสารjournalctl -u myapp --since "03:00" --until "03:30"บริการบันทึกอะไรไว้ในช่วงที่ล่มjournalctl -p err -bมีข้อผิดพลาดอื่นในระบบในช่วงเวลาเดียวกันหรือไม่journalctl -k --since "03:00" -g "Out of memory|Killed process"ตัว OOM killer ของเคอร์เนลฆ่าโปรเซสไปหรือไม่- ล็อกของแอปพลิเคชันเองใน
/var/log/myapp/รวมถึงไฟล์เก็บ.gz - หากเซิร์ฟเวอร์เคยรีบูต ดู
journalctl -b -1 -e
สรุป
สำหรับงานประจำวันกับล็อกบน Linux คำสั่งไม่กี่ตัวก็พอ: journalctl -u สำหรับบริการ -f สำหรับการติดตามสด --since และ -p err สำหรับการจำกัดผลลัพธ์ และ -b -1 สำหรับการวิเคราะห์การรีบูต ล็อกข้อความอยู่ใน /var/log rsyslog จัดเก็บและส่งต่อ ส่วน logrotate กับ journalctl --vacuum-* ช่วยไม่ให้ล็อกกินดิสก์ทั้งหมด การทำงานกับ journalctl การหมุนไฟล์ และการวินิจฉัยโหลดระบบถูกฝึกใช้จริงในหลักสูตร Linux
คำถามที่พบบ่อย
ทำไม journalctl แสดงเฉพาะรายการตั้งแต่การบูตล่าสุด?
วารสารถูกเก็บในหน่วยความจำ (/run/log/journal) สร้างไดเรกทอรี /var/log/journal หรือกำหนด Storage=persistent ใน journald.conf แล้วรีสตาร์ต systemd-journald
จะให้ผู้ใช้ทั่วไปอ่านวารสารของระบบได้โดยไม่ใช้ sudo อย่างไร?
เพิ่มผู้ใช้เข้ากลุ่ม systemd-journal (หรือ adm บน Debian และ Ubuntu): sudo usermod -aG systemd-journal ชื่อ แล้วล็อกอินใหม่
ดูวารสารของระบบอื่น เช่นจากดิสก์ที่เชื่อมต่อไว้ได้หรือไม่?
ได้ ระบุไดเรกทอรีวารสารด้วยแฟล็ก -D: journalctl -D /mnt/var/log/journal แฟล็ก --file อ่านไฟล์ .journal แต่ละไฟล์
ยังจำเป็นต้องใช้ rsyslog หรือไม่ หากมี journald แล้ว?
ไม่จำเป็น แต่มีประโยชน์เมื่อต้องการไฟล์ข้อความสำหรับเครื่องมือเก่า หรือส่งล็อกไปยังเซิร์ฟเวอร์กลาง บนอิมเมจขั้นต่ำหลายตัวจึงไม่ได้ติดตั้งไว้แล้ว
จะส่งออกล็อกของบริการเป็นไฟล์เพื่อส่งให้เพื่อนร่วมงานอย่างไร?
รัน journalctl -u ชื่อ --since today --no-pager > service.log จะได้ไฟล์ข้อความธรรมดา
หัวข้อนี้สอนอย่างละเอียดพร้อมฝึกปฏิบัติในคอร์ส “Linux: с нуля до уверенного администрирования”
ไปที่คอร์ส