otel-logs-v0_9 index with service.name: caddy and OpenTelemetry HTTP attributes (http.request.method, url.path, http.response.status_code, client.address, user_agent.original, …) pre-populated.
Prerequisites
- A running Rootprint instance and its base URL — you’ll substitute it for
<your-rootprint>. - A host running Caddy that writes its logs to files. Stock Caddy logs to
journald; this page enables file logging below. - A log agent that can read those files — the OpenTelemetry Collector or Vector.
- An ingest API key scoped to your target index. See API keys.
- OpenTelemetry Collector
- Vector
This config assumes the agent runs where it can read
/var/log/caddy.1
Enable Caddy file logging
Stock Caddy logs to
journald. Add a log block per site so Caddy writes JSON to file, then sudo systemctl reload caddy. Keep Caddy’s default JSON encoder; do not add a format directive.2
Configure the Collector
Replace
<your-rootprint> with your Rootprint base URL and <your-ingest-token> with the key from above.3
Grant read access to /var/log/caddy
caddy group that owns the logs (the Debian/RPM Collector package creates otelcol-contrib).4
Start the Collector
Start (or restart) the Collector so it loads the config. Startup logs should show no config-parse or exporter errors.
5
Send a test request
6
Verify in Rootprint
Open Search, pick
otel-logs-v0_9, and query service_name:caddy. Records typically appear within 5–10 seconds.What you get in Rootprint
Tworemap transforms (Vector) or OTTL transform statements (OTEL Collector) map Caddy’s JSON fields to OpenTelemetry semantic conventions. Both agents produce the same attributes in otel-logs-v0_9.
service.name is hard-coded to caddy (resource attribute). The if exists(.request) / where body["request"] != nil guard makes one config handle both access and error logs cleanly: error-log entries (no .request block) get only log.file.path/caddy.logger, while access-log entries get the full HTTP attribute set.
timeUnixNano is derived from Caddy’s .ts (float seconds since epoch); if the field is missing or unparseable, the agent’s read time is used as a fallback.
Useful searches
Run these in the Rootprint search box againstotel-logs-v0_9.
Every 5xx response Caddy returned, across all sites:
Troubleshooting
- Records arrive but
attributes.http.*are missing: the entry came fromerror.log(no.requestblock), by design. Filter access-log entries withattributes.caddy.logger:http.log.access. bodylooks like raw JSON instead of just the message: the access log is using a non-defaultformatdirective in the Caddyfile. The parser expects Caddy’s default JSON encoder. Either remove theformatdirective or extend the config to handle the alternate shape.severity_textis alwaysINFOeven for 5xx responses: by design. Caddy emits every HTTP access atlevel: inforegardless of status code; both agents trust the source. Filter onattributes.http.response.status_code:>=500to surface server errors.- Ingest transport errors (
401,403,413,415): see OTLP reference → Response codes.
Related
- Send Nginx logs: sibling page for Nginx.
- Send logs with Vector: generic Vector setup for tailing files on a host.
- OTLP reference: endpoint URL, response codes, body limits.
- Indexes: the
otel-logs-v0_9schema, so you know what you can search.
