varsh
(Varsha)
April 17, 2025, 5:46am
1
1. The problem I’m having:
I’m trying to only log access logs that are >=400. I’ve tried various directives, matchers and conditionals with no luck. I’d like to be able to do something like
log_skip {
status 200
}
or
@no_log status 200
log_skip @no_log
3. Caddy version:
2.9.1
4. How I installed and ran Caddy:
na
d. My complete Caddy config:
{
servers :18443 {
protocols h1 h2
listener_wrappers {
proxy_protocol {
fallback_policy require
}
}
}
}
localhost:18443 {
log {
output file /var/log/caddy/access.log {
mode 644
}
format transform `{access_log}`
}
# tls on_demand is necessary for caddy auto sign cert on the fly.
tls {
on_demand
client_auth {
mode require_and_verify
trusted_ca_cert_file ./client-ca.pem
}
}
log_append access_log {http.access.log}
logger
test_http {
replacer {
http.access.log
}
}
}
logger is a http.handlers.logger custom plugin I wrote that forms a log entry structure with all the fields I need. I use transform encoder to then use the replacer the plugin generates by using log_append
5. Links to relevant resources:
opened 06:24PM - 08 Apr 22 UTC
feature
discussion
This issue is to collect and gauge interest in this feature before we spend time… working on it.
There's demand for having Caddy skip logging access logs selectively; I opened issue #4689 which should solve the specific case of access logs. Caddy doesn't only emit access logs, though. There's all kinds of logs, from many different sources (other Caddy apps, stdlib captured logs, dependencies).
The idea is that we could implement a system for configuring "matchers" for individual log messages based on the contents of the [zap log entry](https://pkg.go.dev/go.uber.org/zap/zapcore#Entry). This could be hooked in implementing a custom [`zapcore.Core`](https://pkg.go.dev/go.uber.org/zap/zapcore#Core) to decide whether to write the log.
We actually already do implement a custom Core to override `Check()` to implement an `include`/`exclude` list by logger name:
https://github.com/caddyserver/caddy/blob/afca2421112eb89e435888f50f8146364a3c60a4/logging.go#L550-L557
This is a good start and may usually be enough for most users to hide certain logs, but it's possible that sometimes only specific patterns of logs from a particular logger should be hidden, not the entire logger.
My idea is that we add support for pluggable log matcher modules which take in the `zapcore.Entry` and `[]zapcore.Field` and can inspect its contents to decide whether we want to log it. It would go in the config in here https://caddyserver.com/docs/json/logging/logs/ (i.e. just under `include` and `exclude`). Not sure what we'd call it, maybe `checkers`? It would be an array of modules that implement an interface like:
```go
interface LogEntryChecker {
ShouldSkip(zapcore.Entry, []zapcore.Field) bool
}
```
We'd have to run this checking logic in `Write(Entry, []Field) error` since that's the only place we have access to both the entry and the fields. This was suggested as a solution in https://github.com/uber-go/zap/issues/1021#issuecomment-962119581 as an answer to a similar question.
We could provide simple built-in matchers like an exact value match of a particular field by path, like `request>method` (similar to [`filter` encoders](https://caddyserver.com/docs/caddyfile/directives/log#filter), a `not` matcher which wraps other matchers to invert the boolean result, etc.
So all that said, this is not an insignificant amount of work, so we'd like to hear what problems this could solve for users. Feedback is welcome on this idea!
opened 06:06PM - 14 Feb 24 UTC
closed 09:53PM - 10 May 24 UTC
bug
Successfully serving an HTTP 404 response to a request will show up in Caddy acc… ess logs with `"level": "ERROR"`, despite the fact that the server hasn't made any error. See [this discussion thread](https://caddy.community/t/caddy-access-logs-log-client-errors-at-level-error/22711) on caddy.community for more details and discussion of some options for solutions.
Mohammed90
(Mohammed Al Sahaf)
April 21, 2025, 11:29pm
2
This is the wrong way to do it. For custom log structure, your module needs to be a log encoder module, not handler.
This works. You just need to adapt it.
example.com {
log
respond /400 400
respond /200 200
intercept {
@200 {
status 200
}
handle_response @200 {
log_skip
}
}
}
stardust
(Stardust)
April 23, 2025, 5:41am
3
Would the intercept
directive be the way to go to handle the following scenario? Especially in reverse proxy setups.
Internal requests (originating from LAN): Log only requests that return a different HTTP status code than 200.
External requests (originating from WAN): Log all requests, regardless of the returned status code.
(Split DNS in use)
The log_skip
accept a request matcher. It is clear how to use a remote_ip
(or a more elaborated CEL version) to determine if the requests comes from inside or outside, but I assume the status code is undetermined at that point.
Mohammed90
(Mohammed Al Sahaf)
April 23, 2025, 8:14am
4
But intercept
knows the status code. I always encourage experimentation to learn any tool thoroughly. Give it a shot, see how it behaves.
stardust
(Stardust)
April 23, 2025, 1:03pm
5
I have certainly been experimenting and keep doing so
But within the context of reverse proxies, wouldn’t it be better to use handle_response
rather than intercept
?
My idea would be something like…
example.com {
<log options omitted>
reverse_proxy localhost:8000 {
@ok status 1xx 2xx 3xx
handle_response @ok {
copy_response
@local `remote_ip('192.168.1.10', '192.168.1.11')`
log_skip @local
}
}
}
Where the intention would be to log all external requests, and both 4xx and 5xx errors originating from two local IP addresses.
PS: I think my questions/comments still align with the original question, but I am happy to create a separated message if preferred.
stardust
(Stardust)
April 24, 2025, 9:12am
6
I also tried the intercept
example from Log_skip for response status - #2 by Mohammed90 .
It does work, but if the site block uses a reverse proxy, the request headers get duplicated. I think this is the equivalent of having a copy_response_headers
in a handle_response block, but I could be wrong. By reading intercept (Caddyfile directive) — Caddy Documentation , my understanding is that intercept should not be used in a reverse proxy setup. Better to use handle_response within the reverse_proxy block.
Mohammed90
(Mohammed Al Sahaf)
April 24, 2025, 8:52pm
7
I’m investigating the duplication of headers, but your intercept_response
approach works just as well.