Jak naprawdę działają logi w Linuksie: journald, rsyslog i logrotate
Logi w Linuksie wyglądają na pierwszy rzut oka banalnie. Jest /var/log/messages, /var/log/maillog, czasem journalctl, gdzieś obok rsyslog, a raz dziennie lub raz w tygodniu logrotate coś tam sobie obraca.
Dopóki wszystko działa.
Kiedy jednak rsyslog zaczyna wpadać w:
rsyslog.service: Start request repeated too quickly.
rsyslog.service: Failed with result 'start-limit-hit'.
warto przestać traktować logi jako zestaw magicznych konfiguracji i zobaczyć, co naprawdę dzieje się pod spodem.
Cały mechanizm w dużym uproszczeniu
Na współczesnym systemie z systemd przepływ logów często wygląda mniej więcej tak:
aplikacja / kernel / usługa
|
v
systemd-journald
|
v
rsyslog
|
v
/var/log/...
|
v
logrotate
To model uproszczony. W zależności od konfiguracji rsyslog może czytać journal przez imjournal albo odbierać klasyczne komunikaty syslog przez gniazdo obsługiwane przez imuxsock.
Każdy z tych elementów robi coś innego.
systemd-journald zbiera komunikaty z kernela, usług systemd, stdout i stderr procesów oraz innych źródeł.
rsyslog może odbierać te komunikaty, filtrować je i zapisywać do klasycznych plików:
/var/log/messages
/var/log/secure
/var/log/maillog
/var/log/cron
logrotate natomiast nie generuje logów i nie zajmuje się ich przesyłaniem.
Jego zadanie jest dużo prostsze:
nie dopuścić do tego, żeby pliki logów rosły bez końca.
Co właściwie robi rsyslog?
Załóżmy, że rsyslog zapisuje dane do:
/var/log/messages
Proces otwiera ten plik i system operacyjny przydziela mu deskryptor pliku.
W uproszczeniu:
rsyslog
|
| file descriptor
v
inode pliku /var/log/messages
I tu pojawia się bardzo ważna rzecz.
Proces nie zapisuje każdej kolejnej linii w sposób:
znajdź plik o nazwie
/var/log/messagesi dopisz coś na końcu.
On ma już otwarty plik.
A dokładniej: deskryptor wskazujący na konkretny inode.
Możemy to zobaczyć na przykład przez:
lsof /var/log/messages
albo:
ls -li /var/log/messages
gdzie -i pokaże numer inode.
I wtedy przychodzi logrotate
Załóżmy, że mamy tygodniową rotację.
logrotate może zrobić:
/var/log/messages
|
v
/var/log/messages-20261004
a następnie utworzyć nowy:
/var/log/messages
Problem polega na tym, że rsyslog nadal ma otwarty deskryptor do starego pliku.
Zmiana nazwy pliku nie zmienia inode.
Czyli sytuacja wygląda mniej więcej tak:
rsyslog
|
v
inode 12345
|
v
/var/log/messages-20261004
A nowy:
/var/log/messages
może mieć już zupełnie inny inode.
Jeśli nic więcej nie zrobimy, rsyslog może nadal pisać do starego, obróconego pliku.
Dlatego istnieje postrotate
Typowa konfiguracja wygląda tak:
/var/log/cron
/var/log/maillog
/var/log/messages
/var/log/secure
/var/log/spooler
{
missingok
sharedscripts
postrotate
/usr/bin/systemctl reload rsyslog.service >/dev/null 2>&1 || true
endscript
}
Po obróceniu logów wykonywany jest:
systemctl reload rsyslog.service
W przypadku rsysloga unit systemd może mieć:
ExecReload=/usr/bin/kill -HUP $MAINPID
Czyli reload oznacza w praktyce wysłanie procesu sygnału SIGHUP.
Proces nie jest zabijany.
Nie dostaje nowego PID-u.
Dostaje informację, że powinien zamknąć otwarte pliki wyjściowe. Gdy pojawią się kolejne komunikaty, rsyslog ponownie otworzy pliki wskazane w konfiguracji.
I właśnie tego potrzebowaliśmy.
Warto przy tym pamiętać, że we współczesnym rsyslogu SIGHUP nie oznacza pełnego ponownego wczytania konfiguracji. Służy przede wszystkim do zamknięcia otwartych plików i wykonania prac porządkowych.
Reload to nie restart
To drobna różnica w komendzie, ale duża różnica w działaniu.
Reload
systemctl reload rsyslog
Proces nadal żyje.
PID pozostaje ten sam.
W uproszczeniu:
PID 1258
|
| SIGHUP
v
PID 1258
Restart
systemctl restart rsyslog
oznacza:
PID 1258
|
v
STOP
START
|
v
PID 48213
Cały proces jest zatrzymywany i uruchamiany ponownie.
Jeśli daemon potrafi ponownie otworzyć pliki po HUP, restartowanie go tylko dlatego, że obróciliśmy log, jest zwykle niepotrzebne.
Co daje sharedscripts?
Spójrzmy ponownie na:
/var/log/cron
/var/log/maillog
/var/log/messages
/var/log/secure
/var/log/spooler
{
missingok
sharedscripts
postrotate
systemctl reload rsyslog
endscript
}
W jednym bloku mamy pięć plików.
Dzięki:
sharedscripts
skrypt postrotate wykonywany jest raz dla całego bloku, a nie osobno dla każdego obróconego pliku.
Czyli:
cron \
maillog \
messages ----> rotacja ----> jeden reload rsysloga
secure /
spooler /
To ważny szczegół.
sharedscripts działa jednak tylko w obrębie konkretnego bloku.
Jeśli stworzymy osiem osobnych plików w /etc/logrotate.d/ i w każdym umieścimy:
postrotate
systemctl restart rsyslog
endscript
to każdy z nich wykona swój własny restart, o ile objęty nim log rzeczywiście został obrócony.
I wtedy zaczyna się robić ciekawie.
Jak zrobić mały DoS na własnym rsyslogu
Załóżmy, że mamy kilka konfiguracji:
/etc/logrotate.d/rsyslog
/etc/logrotate.d/maillog
/etc/logrotate.d/policyd
/etc/logrotate.d/app-json
/etc/logrotate.d/audit-json
Każda zawiera:
postrotate
systemctl restart rsyslog
endscript
O północy logrotate zaczyna pracę:
restart rsyslog
restart rsyslog
restart rsyslog
restart rsyslog
restart rsyslog
Wszystko w ciągu jednej lub dwóch sekund.
Systemd ma zabezpieczenie przed usługami, które są uruchamiane zbyt często. Limit dotyczy wszystkich prób startu, w tym uruchomień wywołanych ręcznie.
Po kilku startach w krótkim czasie systemd może powiedzieć:
Start request repeated too quickly.
Failed with result 'start-limit-hit'.
I nagle rsyslog przestaje działać.
Nie dlatego, że konfiguracja rsysloga jest błędna.
Nie dlatego, że daemon się wysypał.
Tylko dlatego, że logrotate kilka razy pod rząd kazał go zatrzymać i ponownie uruchomić.
Każdy pojedynczy fragment konfiguracji wygląda poprawnie.
Problem pojawia się dopiero wtedy, kiedy spojrzymy na system jako całość.
Jak sprawdzić konfigurację rsysloga?
Najprościej:
rsyslogd -N1
Przykładowy poprawny wynik:
rsyslogd: version 8.2510.0
rsyslogd: End of config validation run. Bye.
To sprawdza składnię konfiguracji.
Jeżeli rsyslog nadal ma problem, warto zobaczyć:
systemctl status rsyslog
oraz:
journalctl -u rsyslog
Jeżeli widzimy:
Stopping System Logging Service...
Stopped System Logging Service.
Starting System Logging Service...
to nie wygląda jak crash.
Ktoś wyraźnie wydaje systemd polecenie zatrzymania i ponownego uruchomienia usługi.
Jak znaleźć restart rsysloga w logrotate?
Prosty grep:
grep -Rni 'restart rsyslog' /etc/logrotate.d
albo szerzej:
grep -RniE 'rsyslog|postrotate' \
/etc/logrotate.conf \
/etc/logrotate.d/
Jeśli zobaczymy kilka takich wpisów:
/etc/logrotate.d/policyd: systemctl restart rsyslog
/etc/logrotate.d/maillog: systemctl restart rsyslog
/etc/logrotate.d/app-json: systemctl restart rsyslog
warto zadać pytanie:
czy naprawdę każdy z tych plików musi restartować cały daemon?
Najczęściej odpowiedź brzmi: nie.
Jak zobaczyć, co zrobi logrotate?
Bardzo użyteczne jest:
logrotate -d -v /etc/logrotate.conf
Opcja:
-d
oznacza debug.
Logrotate pokazuje, co zrobiłby w normalnym przebiegu, ale niczego faktycznie nie rotuje i nie aktualizuje pliku stanu.
Możemy zobaczyć na przykład:
considering log /var/log/messages
Last rotated at 2026-10-06 00:00
log does not need rotating
albo:
not running postrotate script, since no logs were rotated
To bardzo wygodny sposób diagnozowania konfiguracji bez rozwalania logów podczas testów.
Globalne i lokalne ustawienia logrotate
Typowy /etc/logrotate.conf może wyglądać tak:
weekly
rotate 4
create
dateext
compress
include /etc/logrotate.d
Czyli domyślnie:
- rotacja raz w tygodniu,
- cztery stare kopie,
- tworzenie nowego pliku,
- data w nazwie,
- kompresowanie starych logów.
Konfiguracje z /etc/logrotate.d/* dziedziczą wcześniejsze ustawienia globalne, chyba że je nadpiszą. Kolejność ma znaczenie: ustawienia globalne umieszczone po dyrektywie include nie wpływają wstecz na wcześniej wczytane pliki.
Dlatego plik:
/var/log/messages {
missingok
}
może efektywnie oznaczać:
weekly
rotate 4
create
dateext
compress
missingok
Nie wszystko musi być zapisane w jednym miejscu.
Jak sprawdzić, czy ktoś modyfikował konfigurację z RPM-a?
Na systemach RPM bardzo przydatne jest:
rpm -qf /etc/logrotate.conf
które pokaże właściciela pliku.
Potem:
rpm -V logrotate
Jeżeli zobaczymy:
S.5....T. c /etc/logrotate.conf
oznacza to między innymi:
S - zmienił się rozmiar
5 - zmieniła się suma kontrolna
T - zmienił się czas modyfikacji
c - plik konfiguracyjny
Czyli plik różni się od wersji dostarczonej przez RPM.
Do szybkiej administracyjnej archeologii jest to bardzo użyteczne.
A co z copytruncate?
Czasami daemon nie potrafi ponownie otworzyć pliku logu.
Wtedy możemy spotkać:
copytruncate
Mechanizm jest inny.
Zamiast:
rename starego logu
utworzenie nowego pliku
reload procesu
logrotate robi mniej więcej:
kopiuj log -> archiwum
wyzeruj istniejący plik
Proces nadal ma otwarty ten sam inode.
Nie musi niczego ponownie otwierać.
Ma to jednak wadę.
Między kopiowaniem a wyzerowaniem pliku istnieje krótkie okno, w którym mogą pojawić się nowe wpisy i część z nich może zostać utracona.
Dlatego jeśli aplikacja obsługuje reopen, reload, HUP albo własną komendę do rotacji, zwykle jest to lepsze rozwiązanie.
Najlepszy przypadek: aplikacja wie, jak obracać swoje logi
Niektóre programy mają własny mechanizm.
Przykład z chrony:
postrotate
/usr/bin/chronyc cyclelogs
endscript
Tu nie restartujemy całej usługi.
Mówimy aplikacji dokładnie:
zamknij bieżące pliki logów i zacznij pisać do nowych.
Podobnie inne usługi mogą reagować na HUP, USR1 albo udostępniać własną komendę administracyjną.
Dla wielu usług sensowna kolejność preferencji wygląda następująco, choć ostateczny wybór zależy od sposobu działania konkretnej aplikacji:
1. własny mechanizm aplikacji
2. reload / HUP
3. copytruncate albo kontrolowany restart — zależnie od aplikacji
Pełny restart nie powinien być automatyczną reakcją na rotację pliku, ale w niektórych przypadkach może być bezpieczniejszy niż copytruncate i związane z nim ryzyko utraty wpisów.
A gdzie w tym wszystkim jest journald?
Na systemach z systemd logi bardzo często najpierw trafiają do:
systemd-journald
Możemy je zobaczyć przez:
journalctl
Rsyslog może następnie czytać journal przez moduł imjournal albo odbierać klasyczne komunikaty przez imuxsock.
Dlatego czasem w jego logach zobaczymy:
imjournal: journal files changed, reloading...
Nie oznacza to restartu rsysloga.
Moduł zauważył po prostu zmianę plików journala i odświeża swoje uchwyty.
Czyli warto rozróżniać:
journalctl
jako narzędzie do odczytu danych z journald
oraz:
/var/log/messages
/var/log/maillog
jako klasyczne pliki, do których może pisać rsyslog.
Oba światy mogą działać jednocześnie.
Co warto z tego zapamiętać?
Nie trzeba pamiętać wszystkich dyrektyw logrotate.
Warto natomiast rozumieć mechanizm:
proces
|
v
otwarty file descriptor
|
v
inode
|
v
plik logu
Potem:
logrotate
|
v
rename starego pliku
|
v
nowy plik
|
v
HUP/reload
|
v
proces otwiera nowy plik
Jeśli rozumiemy ten przepływ, dużo łatwiej odpowiedzieć na pytania:
- po co istnieje
postrotate, - dlaczego potrzebny jest
reload, - dlaczego
restartczęsto jest przesadą, - po co jest
sharedscripts, - czym różni się
copytruncate, - dlaczego proces może pisać do pliku, którego nazwa została już zmieniona.
I przede wszystkim łatwiej zauważyć konfigurację, która pojedynczo wygląda poprawnie, ale jako całość nie ma sensu.
W Linuksie bardzo często problemem nie jest brak znajomości kolejnej komendy.
Problemem jest brak zrozumienia, co ta komenda naprawdę robi.
