Configuration: logging

This page documents logging configuration for Ferron, including log signals, log formatters, available fields, and how trace IDs appear in log output.

Info
  • For trace context propagation, Baggage, and trace sampling, see Tracing.
  • For OTLP export configuration, see OTLP observability.

Log signals

Ferron emits two log signals: access logs and application logs.

SignalWhat it captures
Access logsPer-request HTTP request/response data (method, path, status, duration, etc.)
Application logsServer-level messages (startup, config reloads, errors, debug output)

Access logs are configured per-host via the log directive. Application logs are configured via the console_log and error_log directives (core-directives) or the observability block with provider console or provider file. There is no separate “error log” signal — the error_log directive is simply the file sink for the application log signal.

Directives

Access logging

Access logs are configured via log blocks inside host or global scopes:

example.com {
    log "access" {
        format json
        fields "method" "path" "status" "duration_secs"
    }
}
Nested directiveArgumentsDescriptionDefault
format<string>Log formatter to use. Available formatters depend on which observability modules are loaded.none
fields<string>...Field names to include in the log output. When omitted, all available fields are emitted.all fields

Access log fields

Each access log entry contains the following fields:

FieldDescription
pathThe request URI path (e.g. /index.html)
path_and_queryThe request URI with path and query
methodThe HTTP request method (e.g. GET, POST)
versionThe HTTP version (e.g. HTTP/1.1, HTTP/2.0)
schemeThe request scheme (http or https)
client_ipThe client IP address
client_portThe client port number
client_ip_canonicalThe client IP in canonical form
server_ipThe server IP address
server_portThe server port number
server_ip_canonicalThe server IP in canonical form
auth_userThe authenticated username, or - if not authenticated
statusThe HTTP response status code
content_lengthThe response content length, or - if not available
duration_secsRequest processing duration in seconds
timestampRequest timestamp in CLF format
header_<name>Request header values (one field per header)
span_idOptional trace span ID for the request (if W3C trace context is available)
trace_idOptional trace ID for the request (if W3C trace context is available)
Important

Access logs don’t contain sensitive fields (such as header_cookie, header_authorization). This is to ensure sensitive data is not exposed in log output.

Log formatters

json

The JSON formatter serializes each access log entry as a single-line JSON object. Provided by the logformat-json module.

example.com {
    log "access" {
        format json
    }
}

Example output:

{"method":"GET","path":"/index.html","status":200,"duration_secs":0.012,"client_ip":"127.0.0.1","remote_ip":"127.0.0.1"}

Use the fields directive to limit which fields appear in the JSON output. If fields is not specified, all available access log fields are emitted.

text

The text formatter generates each access log entry as a plain text string using a configurable pattern. Provided by the logformat-text module.

By default, it uses the Combined Log Format (CLF), the same format used by Apache and Nginx.

Configuration example:

example.com {
    log "access" {
        format text
    }
}

Example output:

127.0.0.1 - frank [05/Apr/2026:14:32:01 +0200] "GET /index.html HTTP/1.1" 200 1234 "http://www.example.com/start.html" "Mozilla/5.0"

Pattern syntax

The access_pattern directive supports the following tokens:

TokenDescriptionExample
%field_nameAccess log field%client_ip, %status, %method
%{Header-Name}iRequest header%{Referer}i, %{User-Agent}i
%{format}tTimestamp with custom format%{%Y-%m-%d %H:%M:%S}t
%tTimestamp (uses timestamp_format or CLF default)%t
%%Literal % character%%
Other textPassed through literally", “, -

Request headers are available via the %{Header-Name}i syntax. The header name is case-insensitive and hyphens are converted to underscores internally.

Application log formats

The error_format directive (in the observability { provider file ... } block or the error_log shorthand block) controls how application log messages are formatted. It supports the same formatters as access logs: text (default) and json.

example.com {
    error_log /var/log/ferron/error.log {
        error_format json
    }
}

The text formatter produces human-readable lines:

[2026-04-05 14:32:01.123 INFO] Request processed successfully
[2026-04-05 14:32:01.124 DEBUG] Cache miss for key: user:123
[2026-04-05 14:32:01.125 ERROR] [trace=abc123def456] Upstream connection refused

The json formatter produces structured JSON records:

{"timestamp":1781327817042,"summary":"Request processed successfully","level":"INFO","target":"ferron::http","attributes":{},"trace_context":null}
{"timestamp":1781327818364,"summary":"Upstream connection refused","level":"ERROR","target":"ferron::proxy","attributes":{"upstream":"http://10.0.0.1:3000"},"trace_context":{"trace_id":"abc123def456","span_id":"789012345678","sampled":true}}
FieldDescription
timestampThe Unix timestamp in milliseconds when the log event occurred
summaryThe log message summary
levelLog severity level (ERROR, WARN, INFO, DEBUG)
targetThe web server module target that emitted the log
attributesTyped key-value pairs attached to the log event
trace_contextW3C trace context (trace_id, span_id, sampled), or null
Note

The error_format directive is available for the file observability provider and the error_log shorthand. Console logs always use their native formatting based on the log level.

Log levels

The log_level directive (in the observability block or via console_log/error_log aliases) controls the minimum severity level for application logs:

LevelWhen to use
errorProduction default. Only errors are logged.
warnDebugging performance issues.
infoRequest-level detail. Use for troubleshooting.
debugDeep debugging. High volume.

Configuration example:

example.com {
    observability {
        provider console
        log_level debug
    }
}

Console vs file vs OTLP

The format directive (json/text) applies to file and console sinks. OTLP also a different mechanism:

SinkFormatting directiveConfiguration
File (provider file)format (access) / error_format (application)observability { provider file }
Console (provider console)format (access only)observability { provider console }
OTLP (provider otlp)log_style modern or log_style legacy (with format json or format text)observability { provider otlp }
PrometheusN/A (metrics only)observability { provider prometheus }
Note

Prometheus is metrics-only — it does not export logs. For log export, configure OTLP or use file/console sinks.

Tip

If log files are not being written, verify file paths are accessible and the Ferron process has write permissions. For global observability configuration, see Core directives.

Trace ID in console and file logs

Console and file loggers prefix log messages with [trace=<trace_id>] when a trace context is available. This enables grep-based filtering by trace ID without requiring an OTLP backend.

Example log output:

[2026-04-05 14:32:01.123 INFO] [trace=abc123def456] Request processed successfully
[2026-04-05 14:32:01.124 DEBUG] [trace=abc123def456] Cache miss for key: user:123
Tip

To enable trace IDs in logs without full OTLP tracing, set force_trace true in the HTTP configuration. See HTTP host directives.