Salta al contenuto principale

journalctl: leggere i registri di sistema

Inviato da tuxsa il

Stanotte il sito è andato giù per dieci minuti e nessuno sa perché? Un servizio non parte e il suo messaggio d'errore non compare da nessuna parte? Una copia di sicurezza è fallita e vuoi sapere quando? In tutti questi casi la risposta sta nei registri (in inglese log), il diario in cui il sistema annota tutto quello che accade. Sui sistemi moderni, lo strumento per leggerlo è journalctl.

Cos'è

Quasi tutte le distribuzioni oggi usano systemd (vedi l'articolo su systemctl), che comprende un servizio dedicato ai registri, systemd-journald. Raccoglie in un unico posto, il journal (il "giornale"), i messaggi del kernel, dei servizi, degli accessi e dei programmi lanciati dall'utente. A differenza dei classici file di testo in /var/log, il giornale è un archivio binario e indicizzato: non si legge con cat o less, ma con journalctl, che permette di cercare e filtrare in modo molto preciso.

Cosa fa

Legge il giornale e mostra le righe che interessano: quelle di un servizio, di un intervallo di tempo, di un certo livello di gravità, dell'avvio attuale o di uno precedente. Può anche restare in attesa e mostrare le righe nuove man mano che arrivano, e gestire lo spazio occupato dal giornale.

Come si usa

$ journalctl [opzioni] [filtri]

Per leggere tutto il giornale servono i privilegi di amministratore (con sudo, vedi l'articolo relativo), oppure l'appartenenza a uno dei gruppi adm o systemd-journal (vedi l'articolo su id). Un utente normale vede solo i propri messaggi, e journalctl lo segnala con un avviso. Gli esempi usano il server web01 degli articoli precedenti: le righe dei registri sono illustrative, ma i comandi e i formati li ho provati su un giornale di prova.

Esempio 1: gli ultimi messaggi

$ journalctl -n 4
ott 31 23:10:01 web01 CRON[3790]: pam_unix(cron:session): session closed for user root
ott 31 23:12:08 web01 sshd[3811]: Accepted publickey for enzo from 203.0.113.45
ott 31 23:12:08 web01 systemd[1]: Started session-412.scope - Session 412 of User enzo.
ott 31 23:17:44 web01 sshd[3811]: pam_unix(sshd:session): session closed for user enzo

Con -n 4 si vedono le ultime quattro righe (il numero si sceglie). Ogni riga ha lo stesso schema: data e ora, nome della macchina, programma con il suo numero di processo tra parentesi quadre, e il messaggio. Senza opzioni journalctl mostra tutto il giornale, dal più vecchio al più recente, in un lettore a pagine come less (vedi l'articolo relativo): premi q per uscire. Altri modi di partire dalla fine: -e (end) salta in fondo alla pagina, -r (reverse) mostra prima le righe più recenti.

Esempio 2: i messaggi di un servizio, con -u

$ journalctl -u nginx -n 3 -o cat
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx.service: Control process exited, code=exited, status=1/FAILURE
nginx.service: Failed with result 'exit-code'.
$ journalctl -u nginx -u mariadb --since today
$ systemctl status nginx
$ journalctl -xeu nginx

Con -u (unit) si legge solo ciò che riguarda un servizio, nel formato systemd che si usa con systemctl: l'esempio mostra un classico, un servizio che non parte perché la porta 80 è già occupata (per scoprire da chi, vedi lsof -i :80 nell'articolo relativo). Si possono indicare più servizi, ripetendo -u. Il flusso tipico quando un servizio non parte: systemctl status per vedere lo stato e le ultime righe, poi journalctl -xeu nome per i dettagli. L'opzione -x aggiunge, dove esistono, spiegazioni sul significato dei messaggi. Su alcune distribuzioni il nome del servizio SSH è ssh (Debian, Ubuntu), su altre sshd (Fedora, RHEL).

Esempio 3: un periodo di tempo preciso

$ journalctl --since "2026-10-31 02:00" --until "2026-10-31 03:00"
$ journalctl --since yesterday
$ journalctl --since "1 hour ago"
$ journalctl -S today -U now -u nginx
$ journalctl --since "2026-10-31 02:00" --until "+30 minutes"

Con --since e --until (abbreviati -S e -U) si limita l'intervallo. Le date si scrivono nel formato AAAA-MM-GG HH:MM:SS oppure con parole inglesi: yesterday, today, now, "1 hour ago", "2 days ago". L'ora è quella locale del sistema. Quando qualcuno ti dice "il sito è andato giù stanotte verso le due", questo è lo strumento: restringi la finestra e leggi cosa è successo.

Esempio 4: gli avvii precedenti

$ journalctl --list-boots
IDX BOOT ID    FIRST ENTRY                  LAST ENTRY
 -2 9b8e7d6c.. sab 2026-09-12 06:39:15 CEST  dom 2026-10-25 23:50:11 CET
 -1 3c1f9a2e.. dom 2026-10-25 23:51:02 CET   gio 2026-10-29 04:03:44 CET
  0 5e7a1b4d.. gio 2026-10-29 04:04:10 CET   dom 2026-11-01 05:27:29 CET
$ journalctl -b
$ journalctl -b -1 -p err

Il giornale divide i messaggi per avvio del sistema. --list-boots elenca quelli registrati (gli identificativi qui sono accorciati): 0 è l'avvio attuale, -1 il precedente. -b mostra solo l'avvio attuale; -b -1 quello precedente, utile per capire perché la macchina si è riavviata o si è bloccata (vedi l'articolo su uptime): le ultime righe prima dello spegnimento dicono spesso la causa.

Esempio 5: solo i problemi, per gravità

$ journalctl -p err -b
ott 31 02:14:55 web01 kernel: Out of memory: Killed process 4410 (python3)
ott 31 06:00:02 web01 backup[3921]: rsync: connessione rifiutata da 192.168.1.20
$ journalctl -p warning..err --since yesterday
$ journalctl -p 3 -b

Ogni messaggio ha un livello di gravità, dal più grave al meno grave: emerg (0), alert (1), crit (2), err (3), warning (4), notice (5), info (6), debug (7). Con -p err si vedono i messaggi di quel livello e di quelli più gravi, e con un intervallo (warning..err) solo quelli compresi: il numero (-p 3) funziona come il nome. È il primo filtro da usare per ripulire il rumore: nell'esempio compaiono subito la memoria finita durante la notte (vedi l'articolo su free) e la copia di sicurezza fallita.

Esempio 6: seguire i messaggi in tempo reale

$ journalctl -f
$ journalctl -f -u nginx
$ journalctl -n 0 -f -t backup

Con -f (follow) journalctl resta in attesa e mostra le righe nuove appena arrivano, come tail -f (vedi l'articolo su head e tail). Si ferma con Ctrl+C. È il modo più comodo di osservare un servizio mentre lo si riavvia o mentre si prova a riprodurre un errore: in una finestra il journalctl -f, nell'altra il systemctl restart. Con -n 0 si parte senza le righe precedenti, così compare solo ciò che accade da quel momento.

Esempio 7: cercare

$ journalctl -g 'rifiutata' --since yesterday
$ journalctl -t backup -b
$ journalctl _UID=1000 -p warning
$ journalctl /usr/sbin/sshd
$ journalctl -b | grep -i 'failed password'

L'opzione -g (grep) cerca un'espressione nei messaggi: se il testo è tutto in minuscolo, ignora la differenza tra maiuscole e minuscole. Con -t (identifier) si seleziona il nome con cui un programma o uno script si presenta nel giornale (vedi l'esempio 10). Il giornale conosce anche molti campi interni, che si usano come filtri nel formato CAMPO=valore: _UID=1000 (l'utente), _PID=3811 (il processo), _COMM=nome (il comando). Se indichi il percorso di un programma (/usr/sbin/sshd), vedi tutti i suoi messaggi. L'ultimo comando mostra che journalctl si combina con grep e awk (vedi l'articolo su awk), perché stampa semplice testo quando l'output non è un terminale. Per i tentativi di accesso falliti vedi anche lastb nell'articolo su who.

Esempio 8: i messaggi del kernel

$ journalctl -k -b
$ journalctl -k -g 'out of memory'
$ journalctl -k -b -1 -p err

Con -k (kernel) si leggono solo i messaggi del kernel, come con il comando dmesg, ma con il vantaggio di poter risalire anche agli avvii precedenti. Sono quelli in cui compaiono i problemi di disco (vedi gli articoli su lsblk e mount), di memoria finita (OOM killer, vedi l'articolo su free), di schede di rete e di dispositivi USB.

Esempio 9: i formati di output

$ journalctl -t backup -n 1 -o short-iso
2026-10-31T06:00:02+01:00 web01 backup[3921]: copia completata in 42 s
$ journalctl -t backup -n 2 -o cat
rsync: connessione rifiutata da 192.168.1.20
copia completata in 42 s
$ journalctl -t backup -n 1 -o json-pretty
{
    "MESSAGE" : "copia completata in 42 s",
    "PRIORITY" : "5",
    "SYSLOG_IDENTIFIER" : "backup",
    "_HOSTNAME" : "web01",
    "_PID" : "3921",
    "__REALTIME_TIMESTAMP" : "1793512802000000"
}

L'opzione -o (output) cambia la forma. short-iso scrive la data in formato internazionale (utile negli script e per ordinare). cat mostra solo il testo del messaggio, senza data né nome: perfetto per copiare un errore o contare le righe. json e json-pretty producono un formato strutturato adatto ai programmi (con jq, o con uno script Python) ed espongono tutti i campi del messaggio: nell'esempio ne ho mostrati solo alcuni. verbose elenca tutti i campi in forma leggibile. Il campo PRIORITY è il livello di gravità dell'esempio 5 (5 è notice); __REALTIME_TIMESTAMP è la data in microsecondi dal 1970.

Esempio 10: scrivere nel giornale dai propri script

$ logger -t backup "copia completata in 42 s"
$ logger -t backup -p user.err "rsync: connessione rifiutata da 192.168.1.20"
$ journalctl -t backup -n 2
ott 31 06:00:01 web01 backup[3920]: copia completata in 42 s
ott 31 06:00:02 web01 backup[3921]: rsync: connessione rifiutata da 192.168.1.20

Il comando logger scrive una riga nel giornale. Con -t si sceglie il nome con cui compare (l'identificatore), con -p il livello (user.err è un errore, user.notice un'informazione normale). Dentro uno script, al posto di un echo che finisce chissà dove (soprattutto con crontab, vedi l'articolo relativo), permette poi di ritrovare tutto con journalctl -t nome, con la data e il livello giusti.

Esempio 11: lo spazio occupato

$ journalctl --disk-usage
Archived and active journals take up 1.2G in the file system.
$ sudo journalctl --vacuum-size=500M
Vacuuming done, freed 640.0M of archived journals from /var/log/journal.
$ sudo journalctl --vacuum-time=1month
$ sudo nano /etc/systemd/journald.conf
[Journal]
Storage=persistent
SystemMaxUse=500M
MaxRetentionSec=1month
$ sudo systemctl restart systemd-journald

Il giornale cresce, e su un server molto attivo può occupare gigabyte (vedi gli articoli su df e du). --disk-usage dice quanto spazio usa; --vacuum-size e --vacuum-time eliminano i file più vecchi fino a rispettare un limite di dimensione o di età (non si torna indietro). Per un limite permanente si modifica /etc/systemd/journald.conf: SystemMaxUse è lo spazio massimo, MaxRetentionSec la durata massima di conservazione, e Storage=persistent chiede di conservare il giornale anche dopo il riavvio (vedi l'avvertenza qui sotto). Dopo la modifica si riavvia il servizio.

Esempio 12: uso negli script

#!/bin/bash
LOG="$HOME/controlli.log"
ERRORI=$(journalctl -t backup --since "24 hours ago" -p err -q --no-pager | wc -l)

if [ "$ERRORI" -gt 0 ]; then
    echo "$(date '+%F') backup: $ERRORI errori nelle ultime 24 ore" >> "$LOG"
    journalctl -t backup --since "24 hours ago" -p err -q --no-pager >> "$LOG"
fi

Lo script conta gli errori scritti dalla copia di sicurezza nelle ultime 24 ore (-p err prende solo gli errori, wc -l conta le righe: vedi l'articolo su wc) e, se ce ne sono, li copia in un file di controllo. Due opzioni utili negli script: --no-pager evita che journalctl apra il lettore a pagine, e -q (quiet) toglie gli avvisi informativi dall'output. Programmato con crontab ogni mattina, sostituisce il controllo a mano dei registri.

Opzioni utili

  • -u unità: i messaggi di un servizio; -t nome: quelli con un certo identificatore; -k: quelli del kernel.
  • -b, -b -1, --list-boots: l'avvio attuale, il precedente, l'elenco degli avvii.
  • -S / --since, -U / --until: intervallo di tempo.
  • -p livello: gravità (err, warning, warning..err, 0-7).
  • -g testo: cerca nei messaggi; CAMPO=valore: filtra per campo (_UID, _PID, _COMM).
  • -n N: ultime N righe; -r: dal più recente; -e: salta in fondo; -f: segue in tempo reale; -x: aggiunge spiegazioni.
  • -o formato: short-iso, cat, json, json-pretty, verbose; --no-pager, -q, --utc, --no-hostname.
  • --disk-usage, --vacuum-size, --vacuum-time, --rotate, --verify: gestione dello spazio e controllo dei file.
  • logger: scrive nel giornale; systemctl status: stato e ultime righe di un servizio; dmesg: messaggi del kernel.

Attenzione a

  • Su molte installazioni il giornale non sopravvive al riavvio: con l'impostazione Storage=auto il giornale viene salvato su disco solo se esiste la cartella /var/log/journal; altrimenti vive in memoria e si perde a ogni avvio. In quel caso journalctl -b -1 risponde con un avviso (no persistent journal was found) e non mostra niente: proprio quando serviva capire cosa è successo prima di un blocco. Per conservarlo, imposta Storage=persistent (esempio 11) prima che serva.
  • Senza privilegi vedi solo una parte: un utente normale non vede i messaggi del sistema, e un "nessun risultato" può voler dire solo "non puoi vederlo". Usa sudo, o aggiungi l'utente al gruppo systemd-journal.
  • journalctl -u nginx non mostra i registri del sito: il giornale contiene ciò che il servizio scrive a systemd (avvio, arresto, errori di configurazione). I registri di accesso e degli errori di nginx o di Apache restano nei loro file, di solito sotto /var/log (come visto negli articoli su lsof e tail).
  • Le righe lunghe vengono tagliate: nel lettore a pagine le righe più larghe dello schermo sono troncate. Scorri a destra con le frecce, oppure usa --no-pager o -o cat, o manda l'output a un file.
  • L'ora è quella locale: se il server è in un altro fuso orario, o confronti registri di macchine diverse, aggiungi --utc. --since e --until usano anch'essi l'ora locale.
  • Un messaggio non è una prova: se un sistema viene compromesso, i registri possono essere alterati o cancellati (come per who e last). Inviarli a una macchina separata è l'unico modo di avere un registro affidabile.
  • I limiti di frequenza: se un servizio scrive troppo velocemente, journald scarta alcuni messaggi, segnalandolo con una riga tipo "Suppressed N messages". Per un registro completo di un programma molto loquace conviene che scriva nei propri file.
  • --vacuum cancella per sempre: non c'è cestino. Prima di ridurre il giornale, copia le parti che ti servono (per esempio con journalctl ... > file.txt).
  • I registri contengono dati personali: indirizzi IP, nomi utente, orari di accesso. Non incollarli così come sono in forum o segnalazioni pubbliche, e conservali solo per il tempo necessario (MaxRetentionSec aiuta a rispettarlo).
  • Non è il solo posto: i sistemi senza systemd (e alcuni container) non hanno journalctl; su molte distribuzioni convivono anche i file di testo tradizionali (/var/log/syslog, /var/log/auth.log), che si leggono con less e grep.

Riepilogo

ComandoCosa fa
journalctl -n 20Ultime 20 righe
journalctl -u nginxMessaggi di un servizio
journalctl -xeu nginxServizio, con spiegazioni, partendo dalla fine
journalctl --since "1 hour ago"Solo l'ultima ora
journalctl -b / -b -1Avvio attuale / precedente
journalctl --list-bootsElenco degli avvii registrati
journalctl -p err -bSolo errori (e più gravi) dall'avvio
journalctl -fSegue i messaggi in tempo reale
journalctl -g testoCerca un testo nei messaggi
journalctl -kSolo i messaggi del kernel
journalctl -o cat / -o json-prettyCambia il formato di output
logger -t nome "testo"Scrive una riga nel giornale
journalctl --disk-usageSpazio occupato dal giornale
sudo journalctl --vacuum-size=500MRiduce il giornale a 500 MB