Rejestrowanie dostępu

OxPHP emituje ustrukturyzowane logi dostępu w formacie JSON dla każdego żądania HTTP, zapisywane na stdout. Rejestrowanie jest asynchroniczne, więc nigdy nie blokuje obsługi żądań.

Jak to działa

Gdy ustawiona jest zmienna ACCESS_LOG, OxPHP zapisuje na stdout jedną linię JSON po każdym zakończonym żądaniu. Zapisy te są buforowane w wątku zapisującym działającym w tle, więc rejestrowanie nigdy nie blokuje potoku żądań.

Tryb decyduje o tym, co jest zapisywane. ACCESS_LOG=all rejestruje każde żądanie; ACCESS_LOG=error rejestruje tylko odpowiedzi ze statusem 400 lub wyższym.

O tym, czy te linie są zapisywane, decyduje wyłącznie ACCESS_LOG; LOG_LEVEL nie ma na nie wpływu. Serwer z diagnostyką ustawioną na warn nadal zapisuje log dostępu, który włącza ACCESS_LOG. Jedynym wyjątkiem jest dyrektywa RUST_LOG, która jawnie wskazuje cel access_log; zobacz Filtrowanie szczegółowe.

Każda linia logu zawiera pole request_id, które łączy wpisy logu dostępu z logami Twojej aplikacji. Gdy włączona jest propagacja W3C Trace Context, wpisy zawierają dodatkowo pola trace_id i span_id.

Konfiguracja

Zmienna Domyślnie Opis
ACCESS_LOG (nieustawiona) Tryb logu dostępu. all rejestruje każde żądanie; error rejestruje tylko odpowiedzi 4xx i 5xx. Pozostaw nieustawioną lub pustą, aby wyłączyć
Note

Jedyne akceptowane wartości to all i error. Ustawienie nierozpoznanej wartości powoduje zapisanie ostrzeżenia i wyłączenie rejestrowania dostępu.

Format logu

Każdy wpis logu dostępu to pojedyncza linia JSON zapisywana na stdout:

json
{ "timestamp": "2026-02-11T12:34:56.789012Z", "level": "INFO", "fields": { "request_id": "67890abc12341a2b0042", "method": "GET", "path": "/api/users", "status": 200, "duration_us": 1234, "remote_ip": "10.0.0.1", "user_agent": "Mozilla/5.0 (X11; Linux x86_64; rv:131.0) Gecko/20100101 Firefox/131.0", "message": "request completed" } }

Gdy aktywny jest W3C Trace Context, obok standardowych pól dołączane są trace_id i span_id:

json
{ "timestamp": "2026-02-11T12:34:56.789012Z", "level": "INFO", "fields": { "request_id": "67890abc12341a2b0042", "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", "span_id": "00f067aa0ba902b7", "method": "POST", "path": "/api/orders", "status": 201, "duration_us": 8421, "remote_ip": "10.0.0.1", "user_agent": "Mozilla/5.0 (X11; Linux x86_64; rv:131.0) Gecko/20100101 Firefox/131.0", "message": "request completed" } }

Pola

Pole Typ Opis
request_id string Unikalny identyfikator żądania. Zobacz ID żądań
method string Metoda HTTP (GET, POST itd.)
path string Ścieżka URI żądania
status number Kod statusu odpowiedzi HTTP
duration_us number Całkowity czas obsługi żądania w mikrosekundach
remote_ip string Adres IP klienta (bez portu). Gdy skonfigurowano TRUSTED_PROXIES, pokazuje rzeczywisty adres IP klienta wyodrębniony z nagłówków przekazujących; adres IP proxy tylko wtedy, gdy nie da się z nich uzyskać żadnego adresu (patrz Zaufane proxy)
user_agent string Nagłówek User-Agent żądania (pierwszy, jeśli żądanie wysłało kilka), przycięty do 512 bajtów (wliczając znacznik …(truncated)), gdy jest dłuższy. Bajty niebędące poprawnym UTF-8 są zastępowane przez U+FFFD. Nieobecne, gdy żądanie go nie wysłało
trace_id string ID śledzenia W3C (obecne tylko gdy TRACE_CONTEXT=true)
span_id string ID spanu W3C (obecne tylko gdy TRACE_CONTEXT=true)

Filtrowanie szczegółowe

OxPHP emituje wpisy logu dostępu w ramach wewnętrznego celu logowania access_log i dodaje dyrektywę dla tego celu do własnego filtra logów zawsze, gdy ACCESS_LOG żąda logu dostępu. Dzięki temu oba ustawienia pozostają niezależne: LOG_LEVEL=warn wycisza serwer, a log dostępu działa dalej, bez dodatkowej konfiguracji.

bash
# Quiet diagnostics, access log intact LOG_LEVEL=warn ACCESS_LOG=all

Dyrektywa RUST_LOG, która podaje pełną nazwę celu, ma pierwszeństwo dla tego celu, także przed dyrektywą dodawaną przez OxPHP. Sam poziom jej nie nadpisuje, podobnie jak LOG_LEVEL (RUST_LOG=off wycisza własną diagnostykę serwera, a log dostępu działa dalej). Nie nadpisuje jej też skrócona nazwa, taka jak access=off: jest mniej szczegółową z dwóch dyrektyw i przegrywa. Aby wyciszyć wyłącznie log dostępu, użyj dyrektywy z pełną nazwą:

bash
# Access log off, warnings and errors still reported ACCESS_LOG=all RUST_LOG=warn,access_log=off # `warn` here is what LOG_LEVEL would have been
Warning

RUST_LOG, gdy jest ustawiona i daje się sparsować, zastępuje LOG_LEVEL, zamiast się do niej dodawać, więc umieść w niej swój poziom. Powyższa kombinacja jest też sprzeczna (log dostępu zostaje włączony, a następnie odfiltrowany), dlatego serwer zgłasza ją raz przy starcie, podając obie wartości. Lepiej usuń ustawienie ACCESS_LOG.

Note

Cel access_log jest używany do filtrowania przez RUST_LOG, ale nie jest zawarty w wyjściu JSON. Aby zidentyfikować wpisy logu dostępu w systemach docelowych, użyj pola "message": "request completed" oraz charakterystycznego zestawu pól (method, path, status, duration_us).

Rozwiązywanie problemów

Nie pojawiają się żadne wpisy logu dostępu

Rejestrowanie dostępu jest wyłączone, gdy ACCESS_LOG jest nieustawiona lub pusta.

Rozwiązanie: ustaw zmienną na all lub error:

bash
ACCESS_LOG=all

Jeśli jest już ustawiona, pozostaje jeszcze jedna możliwość: RUST_LOG, która wskazuje cel access_log i go wyłącza; własna dyrektywa operatora wygrywa z tą dodawaną przez OxPHP. Serwer informuje o tym przy starcie:

json
{"timestamp":"…","level":"WARN","fields":{"message":"ACCESS_LOG is set but the log filter drops the access_log target — no access log entries will be written","access_log":"all","filter":"warn,access_log=off"}}

Pole filter pokazuje obowiązujące dyrektywy, którymi są tu dokładnie te ustawione przez Ciebie: dyrektywa dodawana przez OxPHP dla tego celu jest pomijana wszędzie tam, gdzie nadpisuje ją Twoja własna. Usuń z RUST_LOG wpisane przez siebie access_log=…, a wpisy znów będą zapisywane.

Jeśli filtr jest na tyle restrykcyjny, że odrzuca również WARN, ten sam komunikat trafia na stderr jako zwykła linia zamiast JSON, więc ta kombinacja jest zawsze zgłaszana:

text
WARN ACCESS_LOG is set but the log filter drops the access_log target — no access log entries will be written (access_log=all, filter=error,access_log=off)

Samo LOG_LEVEL (trace, debug, info, warn, error) nigdy tego nie powoduje: sam poziom dotyczy wszystkich celów naraz, a dyrektywa wskazująca ten cel jest bardziej szczegółowa z dwóch.

Logi dostępu pokazują tryb `error`, ale brakuje udanych żądań

ACCESS_LOG=error rejestruje tylko odpowiedzi ze statusem 400 lub wyższym. Jest to działanie zamierzone — sprawdź wartość i przełącz na all, jeśli potrzebujesz rejestrowania wszystkich żądań.

Sprawdzenie: potwierdź aktywne ustawienie:

bash
curl -s http://localhost:9090/config | jq '.access_log'
Wpisy logu pojawiają się bez `trace_id` i `span_id`

Pola kontekstu śledzenia są obecne tylko wtedy, gdy włączona jest propagacja W3C Trace Context.

Rozwiązanie: włącz ją za pomocą:

bash
TRACE_CONTEXT=true

Upewnij się też, że Twój klient nadrzędny lub load balancer wysyła nagłówek traceparent w żądaniach.

Przykład Docker

compose.yaml
services: app: image: ghcr.io/oxphp/oxphp:0.13.0 ports: - "80:80" - "9090:9090" volumes: - ./src:/var/www/html:ro environment: ACCESS_LOG: "all" ENTRY_FILE: "index.php" INTERNAL_ADDR: "0.0.0.0:9090"

Dobre praktyki

  • Używaj ACCESS_LOG=error na produkcji, aby zmniejszyć objętość logów, wciąż przechwytując wszystkie nieudane żądania. Udane żądania nie są rejestrowane, ale błędy o dowolnym statusie 400+ są zawsze przechwytywane.
  • Dołączaj ID żądania do logów swojej aplikacji za pomocą oxphp_request_id(), aby móc korelować wpisy logów na poziomie PHP z wpisami logu dostępu.
  • Używaj ustrukturyzowanego agregatora logów, takiego jak Elasticsearch, Loki czy Datadog, aby efektywnie odpytywać i filtrować linie logów JSON.

Integracja

Ponieważ logi są liniami JSON na stdout, integrują się bezpośrednio ze sterownikami logów kontenerów i narzędziami do agregacji:

  • Docker — zbierane automatycznie przez sterownik logów kontenera (json-file, fluentd i inne)
  • Kubernetes — przechwytywane przez agenta logów węzła (Fluentd, Fluent Bit, Filebeat i inne)
  • systemd — przechwytywane podczas uruchomienia jako usługa systemd z logowaniem stdout przez journald

Nie jest potrzebny żaden sidecar ani przesyłanie logów oparte na plikach.

Prompty dla Twojego agenta

Te prompty zakładają agenta z podłączonym serwerem MCP dokumentacji OxPHP — sam przeczyta strony, których potrzebuje.

prompt
Przekaż logi dostępu OxPHP do mojego agregatora logów. Przeczytaj stronę `features/access-logging` przez serwer MCP dokumentacji OxPHP. Wybierz tryb, w jakim ten projekt powinien działać, sprawdź, czy moje ustawienia `LOG_LEVEL` lub `RUST_LOG` nie odfiltrowują celu `access_log`, i pokaż mi, jak mój agregator powinien parsować pola JSON obok logów aplikacji, które to repozytorium już zapisuje.

Zobacz też

Znalazłeś błąd? Zgłoś go →