Apache logging: access formats, error logs, and safe structured output
Find Apache logs, decode Common and Combined records, add timing, avoid unsafe JSON templates, and validate collection and HTTP status fields in Guance.
Apache HTTP Server separates request records from diagnostic messages. The access log tells you which request was handled, its final HTTP status, and how much data was returned. The error log explains server startup failures and problems encountered while processing requests. Start with both when investigating a failure: a 500 response identifies an affected request, while the corresponding error message may explain its cause.
This guide covers Apache 2.4 log locations, Common and Combined formats, custom fields, JSON limitations, error verbosity, and collection across multiple servers.
Find the active log files
Distribution packages commonly use these paths:
| System | Access log | Error log |
|---|---|---|
| Debian and Ubuntu | /var/log/apache2/access.log |
/var/log/apache2/error.log |
| RHEL-family systems and Fedora | /var/log/httpd/access_log |
/var/log/httpd/error_log |
These are conventions, not paths enforced by Apache. Check CustomLog and ErrorLog in the loaded configuration, including each virtual host. Container images may send output to standard streams instead.
sudo tail -f /var/log/apache2/access.log
sudo tail -f /var/log/apache2/error.log
sudo grep ' 500 ' /var/log/apache2/access.log
The last command is a quick search for Common or Combined logs. It is not a reliable field-aware query for arbitrary custom formats. Access logging can also be conditional, so verify coverage before treating record counts as total traffic.
Read Common and Combined records
The standard Common Log Format uses %b for response body size:
LogFormat "%h %l %u %t \"%r\" %>s %b" common
An illustrative record:
127.0.0.1 - - [06/Jun/2026:11:26:42 +0800] "GET /index.html HTTP/1.1" 200 3477
| Token | Meaning |
|---|---|
%h |
Remote hostname, or IP address with the default HostnameLookups Off |
%l |
Remote ident identity; normally unavailable and shown as - |
%u |
HTTP authentication username, or - |
%t |
Request arrival time, including the UTC offset |
%r |
Request line: method, request target, and protocol |
%>s |
Final response status after internal redirects |
%b |
Response size excluding headers; - when no body bytes are recorded |
Use %a when you specifically want the client IP field. Behind a proxy, its meaning depends on whether trusted proxy handling such as mod_remoteip is configured. An address alone does not identify a person.
Combined adds the incoming Referer and User-Agent headers:
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
Do not confuse %b with %O. The latter requires mod_logio and measures bytes sent including headers. %B is useful when you need a numeric body-size field because it emits 0 instead of a dash.
Add timing and enable a custom format
A LogFormat nickname only defines a format; CustomLog selects it for an output destination. This example records protocol, method, path, query string, received bytes, final status, sent bytes, and duration in milliseconds:
LogFormat "%t %H %m %U %q %I %>s %O %{ms}T" custom
CustomLog ${APACHE_LOG_DIR}/access.log custom
%I and %O require mod_logio. %{ms}T requires Apache 2.4.13 or later. %U excludes the query string; %q contains a leading ? when a query is present. Query strings may contain secrets, so omit %q unless those values are needed and appropriately controlled.
${APACHE_LOG_DIR} is a Debian/Ubuntu packaging convention. On another installation, use its configured log directory. On a systemd-based Debian/Ubuntu host, validate the configuration before applying it:
sudo apache2ctl configtest
sudo systemctl reload apache2
Use the equivalent httpd validation and service command on other distributions. A graceful reload usually suffices for log configuration changes; check the service result and send a test request afterward.
Structure fields without assuming JSON escaping
Structured records simplify downstream queries, but a LogFormat string wrapped in braces is not a general JSON encoder. Apache escapes some input bytes as \xHH, which JSON does not accept. Quotes and backslashes are only part of the problem; headers and request targets can contain other troublesome bytes. A template that inserts %{User-Agent}i into a quoted JSON field can therefore produce invalid JSON for some requests. Apache documents its escaping rules.
A deliberately narrow format containing numeric fields avoids that arbitrary-string problem:
LogFormat "{\"time_unix_ms\":%{msec}t,\"http_status_code\":%>s,\"body_bytes\":%B,\"duration_ms\":%{ms}T}" json_metrics
CustomLog ${APACHE_LOG_DIR}/access-metrics.log json_metrics
This example trades request details for a straightforward numeric schema. If you also need method, URI, query, virtual host, and User-Agent, retain a known text format and parse it with an Apache-aware collector, or use a logging component that actually serializes strings as JSON. Test quotes, backslashes, non-ASCII bytes, missing values, and aborted requests before relying on the result.
Whichever approach you choose, define timestamp units and numeric types. JSON syntax alone does not configure file collection, event-time parsing, or field mapping in a log platform.
Control error detail separately
ErrorLog ${APACHE_LOG_DIR}/error.log
ErrorLogFormat "[%{u}t] [%m:%l] [pid %P:tid %T] [client %a] %M"
LogLevel warn
ErrorLogFormat determines the layout. LogLevel sets the threshold; warn is the default. Severity runs from emerg, alert, crit, error, warn, notice, and info down to debug and the trace levels. trace8 is more verbose than trace1.
Temporarily raise detail for the module involved instead of enabling debug output everywhere. For example, LogLevel warn core:info includes core informational messages, including some missing-file diagnostics that are absent at the default threshold. Restore the normal setting after the investigation. Access-log selection is independent of this error-log threshold.
An error message may include an AH identifier. Search the exact identifier together with the module and Apache version in official documentation or source; not every identifier has its own troubleshooting page. Some messages concern startup or background work and have no client request to correlate.
Compare servers and virtual hosts in Guance
Configure DataKit log collection for the actual files or container stream. Access and error logs need different parsing rules. Add stable service, host, and virtual-host identifiers so that similar paths on different sites remain distinguishable.
For a validated JSON format, use the appropriate JSON extraction and time conversion. For Common or Combined text, use a matching Pipeline parser and verify real samples. Store the numeric HTTP code as http_status_code; Guance's status field represents log severity. Keep duration units consistent across servers.
Send known successful and failed requests, find their records, and compare the parsed values with the raw line. Then build 5xx counts or rates with an explicit time window and traffic denominator. A missing record or broken parser should not look like a healthy service.
Frequently asked questions
Should I use %s or %>s?
Use %>s for the final status. With internal redirects, %s refers to the original request status. Preserve the distinction if you are investigating rewrite or internal error handling.
How do I separate virtual-host logs?
Place separate CustomLog and ErrorLog destinations in each VirtualHost. A virtual host with its own CustomLog normally does not inherit the main server's CustomLog; simply having one at each level does not automatically duplicate every request. Multiple applicable CustomLog directives, or GlobalLog in addition to virtual-host logging, can deliberately write additional copies.
Does structured output eliminate Pipeline configuration?
No. You still need to verify extraction, event time, severity, numeric HTTP status, and field names. Keep a raw sample available when diagnosing a parsing failure.
What should I do after changing formats?
Check configuration syntax, apply the change, issue representative requests, and validate both access and error outputs. Ensure log rotation and collector permissions still cover the selected paths.
Further reading
Technical references checked October 14, 2026.
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan