Logowanie¶
Zmniejszona gadatliwość: Loki/Grafana/Alloy ustawione na warn lub error.
Docker log driver — rotacja lokalna¶
Wszystkie usługi używają drivera local (binarny protobuf, mniejszy niż json-file)
przez wspólny YAML anchor x-logging na górze każdego pliku Compose. Kompresja (gzip)
dotyczy tylko zrotowanych plików — aktywny plik jest nieskompresowany, więc tailowanie
pokazuje czytelny tekst między markerami ramek.
x-logging: &default-logging
driver: "local"
options:
max-size: "${LOG_MAX_SIZE:-150m}"
max-file: "${LOG_MAX_FILE:-5}"
Anchory nie przekraczają granic include:
YAML anchory nie przechodzą między plikami include: — każdy z 7 plików Compose
ma własną definicję x-logging. To celowe: zero edycji daemon.json, wszystko
wersjonowane. Dodając nowy serwis: dołącz logging: *default-logging, inaczej
spadnie do nierotowanego json-file.
Defaulty: 150m × 5 = 750MB per kontener (~3–4GB sufit dla ~20 kontenerów, zmniejszone przez gzip na segmentach) — to bufor zanim Alloy wyśle logi do Loki, nie retencja czasowa.
Loki — retencja czasowa per service¶
Konfigurowana przez limits_config.retention_stream po labelu service (ustawianym
przez Alloy z com.docker.compose.service):
| Service | Zmienna w .env |
Domyślnie | Po co |
|---|---|---|---|
appserver |
LOKI_RETENTION_APPSERVER |
2160h (90 d) |
logi Django do debugowania incydentów |
dbserver |
LOKI_RETENTION_DBSERVER |
2160h (90 d) |
slow queries, locki |
webserver |
LOKI_RETENTION_WEBSERVER |
4320h (180 d) |
access log nginx, compliance/ruch |
| (default) | LOKI_RETENTION_DEFAULT |
720h (30 d) |
workery, infrastruktura, monitoring |
Strój przez .env, nie przez plik. $BPP_CONFIGS_DIR/loki/local-config.yaml jest
renderowany i nadpisywany przy każdym make up — ręczna zmiana w nim przepadnie
przy najbliższym git pull. Poprawnie:
# w $BPP_CONFIGS_DIR/.env
LOKI_RETENTION_WEBSERVER=8760h # rok
make up # renderuje config i przeładowuje Loki
Format wartości to <liczba><jednostka> (h, d, m, s) — dokładnie to, co
przyjmuje Loki. Wartość w innym formacie jest ignorowana i podmieniana na
domyślną z repo: Loki z niepoprawnym duration w ogóle nie wstaje, a wyglądałoby to
jak awaria monitoringu, nie jak literówka w .env.
Migracja starszych instalacji jest automatyczna
Do sierpnia 2026 retencja siedziała wprost w local-config.yaml i ta strona
kazała edytować ten plik. Przy pierwszym make up po aktualizacji wartości
zostają odczytane z Twojego pliku i przepisane do .env — Twoje strojenie
jest zachowane, nie trzeba nic robić ręcznie.
Poziom logu (detected_level)¶
Loki nie dostaje poziomu logu z kontenerów — trzeba go wywnioskować z treści linii.
Robi to defaults/alloy/config.alloy i wstawia wynik jako label detected_level.
Dozwolone wartości to zamknięty zbiór siedmiu:
| Wartość | Skąd |
|---|---|
critical |
critical, crit, fatal, alert, emerg |
error |
error, err |
warn |
warn, warning i wszystko kończące się na Warning (SecurityWarning, DeprecationWarning…) — oraz każde trafienie WAF-a |
info |
info, notice |
debug |
debug |
trace |
trace |
unknown |
nic nie pasowało |
Zbiór jest zamknięty celowo: normalizacja jest jedną bramką na końcu pipeline'u, a nie łańcuchem poprawek. Wartość spoza tabeli nie ma jak trafić na label.
Jedno źródło — wbudowane wykrywanie w Loki jest wyłączone
Loki 3.x ma własne discover_log_levels (domyślnie włączone), które dokleja
detected_level jako structured metadata ze swoim słownikiem
(trace/debug/info/warn/error/fatal/critical). Przy dwóch detektorach pod tą samą
nazwą dropdown „Log Level" w Grafanie pokazywał sumę obu słowników — równolegle
warn i warning, do tego fatal i przeciekłe securitywarning — a w szczegółach
linii pojawiał się detected_level_extracted (Loki dokleja ten sufiks przy kolizji
stream labela ze structured metadata).
Wyłącza to flaga -validation.discover-log-levels=false w command: usługi loki
w docker-compose.monitoring.yml. Flaga powstała, gdy local-config.yaml był
jeszcze copy_if_missing — klucz w tym pliku nie dotarłby wtedy na żadną
istniejącą instalację. Od sierpnia 2026 plik jest renderowany i force-syncowany
(patrz wyżej), więc discover_log_levels: false w nim działa już wszędzie;
oba zapisy są zgodne i celowo zostawione razem.
Stare dane zachowują poprzednie wartości aż do wygaśnięcia retencji — dropdown czyści
się stopniowo, po 30 dniach dla większości usług i po 180 dla webserver. To normalne,
nie wymaga kasowania danych.
service_name obok service
Bliźniacza funkcja Loki discover_service_name dokleja label service_name,
duplikujący nasz service. Zostaje włączona: analogiczna flaga jej nie wyłącza
(-validation.discover-service-name= dopisuje pusty wpis do listy domyślnej,
zamiast ją czyścić), a wyłączenie przez YAML zadziałałoby tylko na świeżych
instalacjach. To kosmetyczny duplikat — używaj service.
Weryfikacja pipeline'u: make test-alloy.
nginx access log — dwa cele jednocześnie¶
Główny ruch loguje się w formacie bpp_access (defaults/webserver/00-log-format.conf:
combined + $request_length/$request_time/$upstream_response_time) z
vhost.conf.template do dwóch sinków:
access_log /dev/stdout bpp_access;→ Docker → Alloy → Loki → Grafana ({service="webserver"}, search/forensics).access_log /var/log/nginx-shared/bpp_access.log bpp_access;→ wolumennginx_access_log(RO w Netdacie) → kolektorweb_log(metryki + alerty).
Szumne locationy (/healthz, /static, /media, acme, security-blocks) mają własne
access_log off w _bpp-locations.conf i nadpisują oba sinki. Plik na wolumenie rotuje
Ofelia codziennie 04:10 (scripts/nginx-access-log-rotate.sh: mv na .1 +
nginx -s reopen, max 2 generacje) — Docker log driver rotuje tylko stdout/stderr,
nie ten plik.