---
id: logs-journald
title: "journalctl"
shortTitle: "journalctl"
track: logs
trackTitle: "Xem log trên server"
order: 2
level: core
status: published
estimatedMinutes: 35
prerequisites:
  - logs-files
  - linux-process-management
next: networking-osi-protocols
reviewedAgainst: 2026-10-12
sources:
  - title: "journalctl(1)"
    url: https://man7.org/linux/man-pages/man1/journalctl.1.html
  - title: "systemd-journald.service"
    url: https://man7.org/linux/man-pages/man8/systemd-journald.service.8.html
---

# journalctl

Bạn cần một máy chạy systemd. Container tối giản và một số image không có journald, còn WSL2 bản mới thường có.

## Để làm gì

Bạn đọc log của một service systemd theo khoảng thời gian, theo mức độ nghiêm trọng, và theo lần boot hiện tại. Khi app chạy dưới systemd, bạn không còn phải đoán log nằm ở file nào trong `/var/log`.

## Gồm những gì

- `journalctl -u`, `--since`, `--until`, `-p`, `-b`, `-f`.
- Log lưu ở đâu: volatile và persistent.
- Output ngắn và output JSON.

Bài này không nói về cách ship log sang Loki hay Elastic; phần đó thuộc chặng Log tập trung.

## Dùng khi nào

Dùng `journalctl` khi unit đã hiện trong `systemctl status`. `status` chỉ hiện vài dòng cuối, còn `journalctl -u` cho bạn xem đúng khoảng thời gian mình chỉ định.

Nếu service tự ghi file trong `/var/log`, journal chỉ chứa phần stdout/stderr mà unit được cấu hình gửi vào, và không thay được access log của Nginx. Khi đó bạn phải đọc cả hai nguồn.

## Kiến thức cốt lõi

journald nhận log từ các service systemd, từ syslog và từ kernel. `journalctl` là tool đọc kho log đó.

Mặc định, kho nằm trong `/run/log/journal` và mất khi reboot. Nếu bạn tạo `/var/log/journal`, journald chuyển sang persistent và log còn lại sau reboot. Man page của `systemd-journald.service` mô tả option `Storage=` với các giá trị `volatile`, `persistent`, `auto`; với `auto`, journald lưu persistent nếu thư mục `/var/log/journal` tồn tại.

`-u nginx` chọn một unit. `--since "1 hour ago"` và `--until "2026-10-12 09:00:00"` giới hạn khoảng thời gian. Chuỗi thời gian này do systemd parse, không theo cú pháp cron.

`-p err` lấy mức err và các mức nặng hơn (crit, alert, emerg). `-p warning` lấy từ warning trở lên. `-b` là lần boot hiện tại, `-b -1` là lần boot trước, rất có ích khi service không lên sau một lần reboot.

`-f` follow log mới, giống `tail -f`. `-o cat` chỉ hiện message. `-o json` in mỗi dòng một object để script xử lý; khi đọc bằng mắt thì đừng dùng JSON.

Chạy `journalctl` không có argument sẽ in ra rất nhiều. Lúc nào cũng nên gắn `-u` hoặc `--since`.

## Lệnh và code mẫu

Xem log của sshd; tuỳ máy, unit có thể tên là `ssh.service` hoặc `sshd.service`:

```bash
systemctl list-units --type=service --all | grep -E 'ssh|nginx'
journalctl -u ssh.service -b --no-pager -n 20
```

Nếu máy không có unit ssh, dùng một unit chắc chắn có, ví dụ `systemd-journald.service`.

```bash
journalctl -u systemd-journald.service --since "1 hour ago" -p info --no-pager
journalctl -u systemd-journald.service -n 5 -o cat --no-pager
```

Theo log mới:

```bash
journalctl -u systemd-journald.service -f
```

`Ctrl-C` để thoát.

Xem kho đang nằm đâu:

```bash
journalctl --disk-usage
ls -ld /run/log/journal /var/log/journal
```

Nếu không có `/var/log/journal` thì journal đang ở chế độ volatile. Muốn bật persistent, bạn cần quyền root:

```bash
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald
```

Chỉ chạy khối sudo này trên máy lab của bạn, đừng chạy trên production chỉ để thử bài.

## Thực hành

1. Chạy `journalctl -u systemd-journald.service -n 10 --no-pager`.
2. Thu hẹp kết quả bằng `--since "30 min ago"`.
3. Thử `-o cat` và `-o json -n 1`. Output JSON phải là một object có field `MESSAGE`.
4. Thử `-b -1`. Nếu máy mới cài và chưa reboot lần nào, lệnh sẽ báo không có boot đó. Ghi lại thông báo này; nó không có nghĩa journal bị hỏng.
5. Chạy `journalctl --disk-usage` và kiểm tra máy có `/var/log/journal` hay chỉ có `/run/log/journal`.

## Kết quả mong đợi

- `-n 10` in tối đa 10 dòng, mỗi dòng có timestamp.
- `--since` không hiện dòng nào cũ hơn mốc vừa chỉ định.
- `-o json -n 1` có `"MESSAGE"`.
- `--disk-usage` in dung lượng kho journal.

## Checklist

- [ ] Gắn `-u` hoặc `--since` khi mở journalctl.
- [ ] Phân biệt `-p err` với "mọi dòng có chữ error".
- [ ] Dùng `-b -1` sau reboot để xem boot trước.
- [ ] Biết log mất sau reboot nếu chưa có `/var/log/journal`.
- [ ] Không dùng journalctl để thay access log của Nginx.

## Thẻ ôn

### logs-journald-storage
kind: default
cue: Reboot xong journal trống thường vì log đang nằm trong `/run/log/journal`, mất theo lần boot. Muốn giữ thì có `/var/log/journal` hoặc `Storage=persistent`.

Q: Máy vừa reboot, `journalctl` không còn dòng nào của lần chạy trước. Có phải ai đó đã xóa log không, và làm sao để lần reboot sau vẫn còn?

A: Chưa chắc đã bị xóa. Kho mặc định nằm ở `/run`, tức `/run/log/journal`, và `/run` là tmpfs, mất khi reboot. Journald với `Storage=auto` chỉ ghi lâu dài khi thư mục `/var/log/journal` đã tồn tại. Không có thư mục đó thì mỗi lần boot là một sổ mới, sổ cũ không còn trên đĩa. Tạo `/var/log/journal`, hoặc đặt `Storage=persistent`, thì log còn sau reboot. Nhìn sổ trống ngay sau reboot thì kiểm tra chỗ lưu trước khi kết luận bị xóa.

### logs-journald-filter
kind: command
cue: Log nginx một giờ qua là `journalctl -u nginx --since "1 hour ago"`. `journalctl` không tham số là cả máy. Journal cũng không thay access log Nginx ghi ra file.

Q: Cần đọc log của nginx trong một giờ qua. Mở `journalctl` không tham số rồi tự mắt tìm có ổn không, và journal có thay file access log không?

A: Không ổn. Không tham số là journal của cả máy, mọi service, rất dài và dễ đọc nhầm dòng của chỗ khác. Gắn `-u nginx` để chỉ unit đó, và `--since "1 hour ago"` để đúng khoảng thời gian. `systemctl status` chỉ nhét vài dòng cuối, không đủ một giờ. Thêm một việc: journal là log mà nginx gửi vào journald. Access log nginx tự ghi ra file là một dòng khác. Không thấy request trong journal chưa có nghĩa là không có access log.

### logs-journald-priority
kind: distinguish
cue: `journalctl -p err` lấy các dòng có mức err trở lên, không phải mọi dòng có chữ error trong nội dung.

Q: Bạn chạy `journalctl -p err` để tìm mọi chỗ ứng dụng in chữ "error". Tập kết quả có đúng là các dòng chứa chữ đó không?

A: Không. `-p err` lọc theo priority mà chương trình gắn khi ghi journal: mức err, và các mức nặng hơn là crit, alert, emerg. Một dòng priority info mà nội dung có chữ error sẽ không vào. Một dòng priority err mà nội dung không có chữ error vẫn vào. Đây là mức, không phải `grep`. Muốn tìm chữ trong nội dung thì tìm chữ. Muốn xem sự cố đã được đánh mức nặng thì dùng `-p`.

### logs-journald-boot
kind: command
cue: Service không lên sau reboot thì xem boot trước bằng `journalctl -b -1`. Trong script đừng dùng `-f`.

Q: Sau một lần reboot, service không lên. Log trên màn hình hiện tại là của boot này, còn lý do nó chết nằm ở lần boot trước. Cờ nào xem boot hiện tại, cờ nào xem boot trước? Trong script vì sao không dùng `-f`?

A: `-b` không có số là boot hiện tại. `-b -1` là boot ngay trước đó, nơi còn dấu của lần start thất bại trước khi máy được reboot lại. Đọc nhầm boot hiện tại thì bạn chỉ thấy "service đang không chạy", không thấy lỗi lúc nó chết. Trong script đừng thêm `-f`. Cờ đó theo log mãi và không tự thoát, job sẽ treo. Lấy một khoảng rồi dừng bằng `--since` cùng `--no-pager`.

## Lỗi thường gặp

**`journalctl -u nginx` không ra gì.** Có thể unit mang tên khác (dù `nginx.service` đúng trên hầu hết máy), hoặc Nginx ghi thẳng ra file nên stdout không vào journal. Kiểm tra `systemctl status nginx` và `/var/log/nginx/error.log`.

**Thấy journal trống sau reboot rồi kết luận bị hacker xoá log.** Kho volatile nằm ở `/run`, nên reboot là mất. Hãy kiểm tra `Storage=` và thư mục `/var/log/journal` trước khi kết luận.

**Dùng `-f` trong script.** Lệnh sẽ không bao giờ tự thoát. Trong script và CI, dùng `--since` cùng `--no-pager` thay vì follow.

## Nguồn

1. [journalctl(1)](https://man7.org/linux/man-pages/man1/journalctl.1.html) — `-u`, `--since`, `-p`, `-b`, `-f`, `-o`.
2. [systemd-journald.service(8)](https://man7.org/linux/man-pages/man8/systemd-journald.service.8.html) — `Storage=`, `/run/log/journal`, `/var/log/journal`.

## Tiếp theo

[Mô hình mạng và protocol](../networking/01-osi-and-protocols.md)
