คู่มือ

ล็อกใน Linux: journalctl, /var/log, rsyslog และ logrotate

อัปเดต: · Skillaria.Top

บนดิสโทรสมัยใหม่ 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 แสดงระดับที่ระบุและทุกระดับที่สำคัญกว่า:

รหัสชื่อเมื่อใช้
0emergระบบใช้งานไม่ได้
1alertต้องแก้ไขทันที
2critข้อผิดพลาดร้ายแรง
3errข้อผิดพลาด
4warningคำเตือน
5noticeเหตุการณ์สำคัญแต่เป็นปกติ
6infoข้อความข้อมูล
7debugการดีบัก
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/journal
  • auto (ค่าเริ่มต้น) เขียนลงดิสก์เมื่อไดเรกทอรี /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 สลับไฟล์

ขั้นตอนวิเคราะห์เหตุขัดข้อง

สถานการณ์ "บริการล่มตอนกลางคืน" ควรวิเคราะห์ตามลำดับนี้:

  1. systemctl status myapp สถานะปัจจุบันและบรรทัดล่าสุดของวารสาร
  2. journalctl -u myapp --since "03:00" --until "03:30" บริการบันทึกอะไรไว้ในช่วงที่ล่ม
  3. journalctl -p err -b มีข้อผิดพลาดอื่นในระบบในช่วงเวลาเดียวกันหรือไม่
  4. journalctl -k --since "03:00" -g "Out of memory|Killed process" ตัว OOM killer ของเคอร์เนลฆ่าโปรเซสไปหรือไม่
  5. ล็อกของแอปพลิเคชันเองใน /var/log/myapp/ รวมถึงไฟล์เก็บ .gz
  6. หากเซิร์ฟเวอร์เคยรีบูต ดู 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: с нуля до уверенного администрирования”

ไปที่คอร์ส