You still need to encapsulate this in a log
directive.
I think that format transform "{common_log}"
is about as simple as it’s going to get, unfortunately.
If you’ve got a tool like jq
you can filter for it with the default access logs.
Caddy’s default access logs look a little something like this:
2024/08/30 00:05:42.988 info http.log.access.log0 handled request {"request": {"remote_ip": "::1", "remote_port": "52657", "client_ip": "::1", "proto": "HTTP/2.0", "method": "GET", "host": "localhost", "uri": "/", "headers": {"User-Agent": ["curl/8.9.1"], "Accept": ["*/*"]}, "tls": {"resumed": false, "version": 772, "cipher_suite": 4865, "proto": "h2", "server_name": "localhost"}}, "bytes_read": 0, "user_id": "", "duration": 0.000005, "size": 0, "status": 0, "resp_headers": {"Server": ["Caddy"], "Alt-Svc": ["h3=\":443\"; ma=2592000"]}}
2024/08/30 00:06:01.403 error http.log.access.log0 handled request {"request": {"remote_ip": "::1", "remote_port": "52675", "client_ip": "::1", "proto": "HTTP/2.0", "method": "GET", "host": "localhost", "uri": "/", "headers": {"User-Agent": ["curl/8.9.1"], "Accept": ["*/*"]}, "tls": {"resumed": false, "version": 772, "cipher_suite": 4865, "proto": "h2", "server_name": "localhost"}}, "bytes_read": 0, "user_id": "", "duration": 0.000010875, "size": 14, "status": 403, "resp_headers": {"Server": ["Caddy"], "Alt-Svc": ["h3=\":443\"; ma=2592000"], "Content-Type": ["text/plain; charset=utf-8"]}}
jq
can rip the JSON from these lines with a filter:
jq -R '.[index("{"): 1+rindex("}")] | fromjson' caddy.log
From there you could select
for 403 responses:
jq 'select(.status==403)'
And print just the client IP:
jq '.request.client_ip'
All together in one:
jq -R '.[index("{"): 1+rindex("}")] | fromjson | select(.status==403) | .request.client_ip' caddy.log
Which prints ::1
when run on the above two lines.