nginx/nginx
Logging
Active contributors: Maxim Dounin, Sergey Kandaurov
Purpose
NGINX has two main logs:
- Error log (
error_log) — process-wide diagnostic stream, written by everyngx_log_errorcall. Levels:debug<info<notice<warn<error<crit<alert<emerg. - Access log (
access_log) — per-request log lines for HTTP and Stream, configurable vialog_format.
Both can write to files, to stderr, to syslog, or to a custom destination via the audit_log shared-zone helpers used by some third-party modules.
Files
| File | Role |
|---|---|
src/core/ngx_log.{c,h} |
Error log: ngx_log_error, ngx_log_debug* |
src/core/ngx_syslog.{c,h} |
Syslog target (UDP) |
src/http/modules/ngx_http_log_module.c |
HTTP access log + log_format engine |
src/stream/ngx_stream_log_module.c |
Stream access log |
Error log
ngx_log_error(level, log, errno, fmt, ...) is the universal entry point. It:
- Checks
levelagainst the log's configured threshold (cheap — early-out). - Formats the message into a stack buffer using the custom formatter (
ngx_vslprintf). - Appends
errnotranslated to a string when nonzero. - Appends a stack trace marker if compiled with
--with-debugand the level isdebug. - Writes to each target attached to the log.
ngx_log_t is a chain — multiple targets (e.g., a file plus syslog) can be attached. The cycle owns the master log (cycle->log); each connection gets its own ngx_log_t derived from it; per-request loggers carry request-specific context (the $request_id, the $server_name, etc.).
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, "msg") (and _debug1 ... _debug8 for printf-style args) compile to no-ops unless --with-debug is configured. The NGX_LOG_DEBUG_* constants are bitmasks: NGX_LOG_DEBUG_CORE, _ALLOC, _MUTEX, _EVENT, _HTTP, _MAIL, _STREAM. The debug_connection directive lets you enable debug only for a specific client.
Format
2026/04/30 12:34:56 [error] 12345#67: *890 something happened, client: 1.2.3.4, server: example.com, request: "GET / HTTP/1.1", host: "example.com"The pieces:
- Timestamp (cached
ngx_cached_log_time). - Level in brackets.
pid#tidof the writer.*<conn_id>if a connection is in scope.- Message.
- Optional connection / request context appended by helpers (
r->log_handler,c->log->handler).
The connection counter *N comes from the per-cycle ngx_connection_counter atomic, useful for grepping all log lines of one connection.
Access log
access_log /var/log/nginx/access.log <format_name>; writes one line per HTTP request at the LOG phase. The format is precompiled at config time to avoid per-request parsing.
log_format directive:
log_format combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';The format is compiled by ngx_http_log_compile_format into a series of ngx_http_log_op_t operations, each with an op->run function pointer. At LOG time, each op runs against the request and produces bytes into the log buffer. Variables go through ngx_http_get_indexed_variable (fast path).
A handful of operations are specialized so they don't go through the variable system:
| Op | What it produces |
|---|---|
ngx_http_log_copy |
A constant string literal |
ngx_http_log_pipe |
A pipe character |
ngx_http_log_time |
Cached time |
ngx_http_log_msec |
Cached msec time |
ngx_http_log_request_time |
The $request_time value |
ngx_http_log_status |
The $status value |
ngx_http_log_bytes_sent |
The total bytes sent |
Buffering and gzip
access_log path buffer=64k flush=5s; buffers writes in memory and flushes periodically. gzip=... compresses on the fly using zlib. Both keep the access log out of the hot path on busy servers.
ngx_http_log_module allocates the buffer in the worker's pool but keeps a per-target structure so multiple access_log directives in the same scope each get their own buffer.
Conditional logging
access_log /path/to/file format if=$loggable; evaluates $loggable per request and skips the line if false. Used for filtering out health checks, etc.
Syslog
error_log syslog:server=...,facility=...,severity=...,tag=... format; opens a UDP socket to the syslog server (typically localhost:514) at first write and sends RFC 3164 messages. Implementation: src/core/ngx_syslog.c. A failure to send doesn't block — syslog is best-effort.
The same syntax works for access_log syslog:....
Log rotation
SIGUSR1 to the master triggers ngx_reopen_files() (src/core/ngx_cycle.c), which closes and reopens every ngx_open_file_t in cycle->open_files. Workers receive NGX_CMD_REOPEN over the channel and do the same. The standard rotation procedure:
mv access.log access.log.old
nginx -s reopen
gzip access.log.oldLogs that target stderr or syslog don't need this.
$request_id and request tracing
$request_id is a 16-byte hex string (ngx_random()-derived) generated at request start. Useful for correlating across access log + error log + upstream X-Request-ID header when forwarding via proxy_set_header X-Request-ID $request_id;.
Integration points
- Every module uses
ngx_log_errorandngx_log_debug*. There's no second logging API. - Variables — access log relies on the variable system; everything that can appear in a
log_formatmust be a registered variable. - Event loop / time —
ngx_cached_log_timeis updated once per loop iteration, so all log lines in one event-loop pass share a timestamp (good enough at the second granularity of error log;$msecand$request_timeuse the cached msec clock). - Cycle / process model — log rotation goes through the master/worker channel.
Entry points for modification
Adding a new log target (e.g., a structured-JSON sink) is typically a third-party module that registers its own ngx_open_file_t and log_format. Adding a new built-in log_format op goes in ngx_http_log_module.c. Changing error-log formatting touches ngx_log_error_core in src/core/ngx_log.c — be careful, every line of every log file depends on it.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.