1import sys2print("started", flush=True)3print("<3>no config", file=sys.stderr)$ journalctl -u hello… hello[812]: started… hello[812]: no config$ journalctl -p err… hello[812]: no configjournaldkernel/var/log/journalmax 10% of disk · ≤ 4 GiBinfo · hellostartederr · hellono configinfo · kernelusb 1-1-u hello-p err
Back: the program (app.py) and the terminal. Middle: journald and the kernel. Front: the journalEntry boxes: priority · source on top, the message belowEnvelopes: log lines on their way; tags: journalctl queriesAt the back, the program app.py (run as hello.service) and the terminal. In the middle, systemd-journald collects log lines; at the front left, the kernel adds its own. At the front, the journal in /var/log/journal grows one entry per stored line. journalctl reads the journal files and prints the entries that match: -u picks a unit, -p a priority. To stay under its size cap, journald deletes the oldest archived files first. The journalctl lines on the card are cut: each really starts with the date, time and host name, for example "Oct 25 09:14:02 lab hello[812]: started". On a real machine -p err also lists errors from other services, and -u hello also shows systemd's own lines about the unit, such as "systemd[1]: Started hello.service."

What a log is

  1. A log is a diary a program keeps while it runs: one line per event, each with a time. When something breaks, the logs tell you what happened and when. On most Linux systems, one service collects them all.

Into the journal

Reading the journal

What happens

;; step 1/9 · what
log        one line per event, each with a time
  who      which program wrote it
  what     the message
  how bad  a priority, from emergency to debug
collector  systemd-journald (on systems that run systemd)
reader     journalctl

The journalctl lines on the card are cut: each really starts with the date, time and host name, for example "Oct 25 09:14:02 lab hello[812]: started". On a real machine -p err also lists errors from other services, and -u hello also shows systemd's own lines about the unit, such as "systemd[1]: Started hello.service." The unit file sets SyslogIdentifier=hello. Without it, journald would tag the lines with the process name, python3. Python buffers stdout when it is not a terminal, so a service's prints can arrive late or all at once. flush=True (or the PYTHONUNBUFFERED=1 setting) sends each line at once. stderr is line-buffered already.