Logowanie do journald z poziomu Bash'a
Tworząc narzędzia systemowe, daemony lub usługi działające na systemach opartych o systemd
, warto logować zdarzenia bezpośrednio do journald
. Dzięki temu logi trafiają do centralnego dziennika systemowego.
W tym artykule pokażę, jak logować do journald
w Bashu - zarówno przy użyciu polecenia systemd-cat
, jak i bezpośrednio przez logger
oraz journalctl
.
Wymagania
Wszystkie narzędzia są dostępne w standardowych dystrybucjach z systemd
:
systemd-cat
- przekierowanie stdout/stderr do journaldlogger
- standardowe narzędzie syslog/journald
Metoda 1: systemd-cat
Parametry:
-t IDENTIFIER
- identyfikator źródła loga (np. nazwa skryptu)-p PRIORITY
- poziom ważności (nazwa lub cyfra)-f FACILITY
- facility syslog (domyślnieuser
)
Przykład z różnymi poziomami:
#!/bin/bash
echo "App started" | systemd-cat -t my_app -p info
echo "Error occured!" | systemd-cat -t my_app -p err
echo "Low memory" | systemd-cat -t my_app -p warning
Metoda 2: logger
Standardowe narzędzie Unix/Linux do logowania przez syslog/journald:
#!/bin/bash
logger -t my_script "Script started"
# Z określonym poziomem
logger -t my_script -p user.err "Error occurred"
logger -t my_script -p user.warning "Warning: low memory"
logger -t my_script -p user.info "just info message"
Parametry logger
:
-t TAG
- identyfikator źródła logu-p FACILITY.PRIORITY
- facility i poziom (np.user.err
,daemon.info
)-s
- dodatkowo wyświetl na stderr
Poziomy PRIORITY
:
Nazwa | Cyfra | Opis |
---|---|---|
emerg |
0 | Krytyczny - system nie działa |
alert |
1 | Wymaga natychmiastowej reakcji |
crit |
2 | Krytyczny błąd |
err |
3 | Błąd |
warning |
4 | Ostrzeżenie |
notice |
5 | Istotna informacja |
info |
6 | Informacyjny |
debug |
7 | Debugowanie |
Metoda 3: Funkcje pomocnicze 🔧
Dla większych skryptów warto stworzyć funkcje logujące:
#!/bin/bash
SCRIPT_NAME="my_service"
log_info() {
logger -t "$SCRIPT_NAME" -p user.info "$1"
}
log_warning() {
logger -t "$SCRIPT_NAME" -p user.warning "$1"
}
log_error() {
logger -t "$SCRIPT_NAME" -p user.err "$1"
}
log_debug() {
logger -t "$SCRIPT_NAME" -p user.debug "$1"
}
log_info "Script started"
log_warning "Low memory: $(free -h | awk 'NR==2{print $3}')"
log_error "Cannot connect to database"
Zaawansowane funkcje z dodatkową funkcjonalnością:
#!/bin/bash
SCRIPT_NAME="$(basename "$0")"
DEBUG_MODE=${DEBUG:-false}
log_with_level() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
logger -t "$SCRIPT_NAME" -p "user.$level" "$message"
if [[ "$DEBUG_MODE" == "true" ]]; then
echo "[$timestamp] [$level] $message"
fi
}
log_info() { log_with_level "info" "$1"; }
log_warn() { log_with_level "warning" "$1"; }
log_error() { log_with_level "err" "$1"; }
log_debug() {
if [[ "$DEBUG_MODE" == "true" ]]; then
log_with_level "debug" "$1"
fi
}
log_info "App started"
log_warn "Low memory"
log_error "Connection error"
log_debug "Configured PATH: $PATH"
Uruchomienie z debugowaniem:
gdzie DEBUG
to zmienna środowiskowa, która włącza dodatkowe logowanie na konsolę, a my_script.sh
to nazwa skryptu.
Wynikiem będzie wyświetlenie logów zarówno w konsoli, jak i w journald
, np:
[2025-06-01 12:00:00] [info] App started
[2025-06-01 12:00:01] [warning] Low memory
[2025-06-01 12:00:02] [err] Connection error
[2025-06-01 12:00:03] [debug] Configured PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Odczyt logów w journalctl 🔍
Sprawdzenie logów:
# Wszystkie logi dla danego identyfikatora
journalctl -t my_script
# Tylko błędy i krytyczne
journalctl -t my_script -p err
# Ostatnie 50 wpisów
journalctl -t my_script -n 50
# Śledzenie na żywo
journalctl -t my_script -f
# Pełne dane (z PRIORITY, PID, etc.)
journalctl -t my_script -o verbose
# Logi z ostatniej godziny
journalctl -t my_script --since "1 hour ago"
Metoda 4: Logowanie całego skryptu
Przekierowanie całego wyjścia skryptu do journald:
#!/bin/bash
exec > >(systemd-cat -t my_script -p info)
exec 2> >(systemd-cat -t my_script -p err)
echo "This is an info message"
echo "This is an error" >&2
Przykład z obsługą sygnałów:
#!/bin/bash
SCRIPT_NAME="my_daemon"
exec > >(systemd-cat -t "$SCRIPT_NAME" -p info)
exec 2> >(systemd-cat -t "$SCRIPT_NAME" -p err)
cleanup() {
logger -t "$SCRIPT_NAME" -p user.info "Stopped due to signal"
exit 0
}
trap cleanup SIGTERM SIGINT
echo "Daemon started (PID: $$)"
while true; do
echo "Doing task..."
sleep 3
echo "Task done"
done
Rozwiązywanie problemów
1. Brak logów w journalctl?
Sprawdź, czy journald działa:
2. Logi nie pojawiają się natychmiast?
Dodaj --flush
aby wymusić zapis:
3. Debugowanie z verbose:
4. Sprawdzenie dostępnych facility:
# Standardowe facility
logger -t test -p mail.info "Test mail facility"
logger -t test -p daemon.warning "Test daemon facility"
logger -t test -p local0.err "Test local0 facility"
Podsumowanie ✅
systemd-cat
- prosty sposób przekierowania stdout/stderr do journaldlogger
- elastyczne narzędzie do logowania z różnymi poziomami- funkcje pomocnicze - ułatwiają zarządzanie logowaniem w skryptach
journalctl
- filtrowanie logów po identyfikatorze (-t
) i poziomie (-p
)- obsługa sygnałów - ważna dla daemonów i długo działających skryptów
Dla prostych skryptów używaj systemd-cat
, a dla bardziej złożonych rozwiązań - logger
z funkcjami pomocniczymi.