Caddy 日志配置指南:JSON 结构化、字段过滤与敏感信息保护
Caddy 在非终端默认输出 JSON、终端默认输出 console 格式,访问日志默认关闭。本文讲解 Caddy log 指令的开启方法、output/format 配置、filter 编码器的字段删除/重命名/IP 脱敏、查询参数处理,以及接入观测云实现集中监控的路径。
Caddy 日志(Caddy Logging)是基于高性能 Zap 库实现的结构化日志体系,默认在终端输出 console 格式、在非终端输出 JSON 格式的服务器运行事件;HTTP 访问日志(request log)默认关闭,需在 Caddyfile 中用 log 指令显式开启。 内置 JSON 编码便于它与日志平台对接。
核心要点速览
- Caddy 运行日志默认编码取决于是否输出到终端;访问日志需用
log指令手动开启,默认写到 stderr; output决定去向(stdout/stderr/file/net/discard),文件输出自带轮转参数;format filter编码器支持字段删除、重命名、正则替换、IP 掩码与哈希,是脱敏与降噪的利器;- Cookie、Authorization 等敏感请求头默认自动脱敏为 REDACTED,但启用
log_credentials会关闭此保护; - 集中采集时要验证 JSON 字段提取,并区分 HTTP 状态码与日志级别。
开启访问日志:log 指令
Caddyfile 中最简配置——一行开启访问日志:
example.com {
log
}
默认输出到 stderr。用 output 子指令改变去向:
example.com {
log {
output file /var/log/caddy/access.log {
roll_size 10MB # 单文件超 10MB 轮转
roll_keep 5 # 保留 5 份
roll_keep_for 336h # Caddy 2.11+:按 14 天上限清理轮转文件
}
}
}
roll_keep 和 roll_keep_for 同时限制保留量,不能保证完整保留 14 天;旧版使用 roll_keep_days。同一路径的文件写入器配置变更可能需要重启,或改用新路径。
output 可选项:stdout、stderr、file(自带轮转,见上)、net(发到网络地址)、discard(彻底关闭)。Docker 部署建议输出到 stdout/stderr,交给容器日志链路;虚拟机直装可用内置文件轮转,省去 logrotate。
format:控制编码与字段命名
format json(非终端输出的默认编码)适合机器处理;format console 在时间戳与级别上更易读,但请求详情仍是 JSON。format 还支持字段级定制——对齐企业统一的日志规范时非常实用:
log {
output stdout
format json {
message_key msg
level_key severity
time_key timestamp
time_format "2006-01-02 15:04:05 MST"
level_format "upper"
duration_format "ms"
}
}
filter 编码器:删、改、脱敏
format filter 是 Caddy 日志的精华能力,嵌套路径用 > 表示:
删除冗余/敏感字段:
format filter {
request>headers>Authorization delete
resp_headers>Server delete
wrap json
}
默认 JSON 中 ts 为 Unix 秒、duration 为秒;前文 format json 配置中的 duration_format ms 显式把 duration 改为毫秒,当前 wrap json 示例仍使用默认秒单位。filter 不能任意更改编码器的核心字段。
注意:filter 不支持通配符(如 Sec-*),多个字段需逐行列出。
重命名以对齐规范:
format filter {
request>uri rename path
status rename status_code
wrap json
}
上例把 URI 改名为 request.path,仍位于 request 内,且保留查询参数,并非只保留路径。
替换与正则脱敏:
format filter {
user_id replace [REDACTED]
request>uri regexp apikey=[A-Za-z0-9]+ apikey=[REDACTED]
wrap json
}
上述正则只覆盖字母数字组成的 apikey,不能脱敏所有令牌;查询参数优先使用下文的 query 过滤器。
IP 掩码处理(合规场景常用):
format filter {
request>remote_ip ip_mask {
ipv4 24
ipv6 56
}
request>client_ip ip_mask {
ipv4 24
ipv6 56
}
wrap json
}
也可对 request>client_ip、request>remote_ip 做 hash;同时检查代理请求头是否还保留原始 IP。该哈希截取 SHA-256 的 4 字节(8 位十六进制),可能碰撞或被枚举,不等于匿名化或访客身份。
URL 查询参数处理——token、邮箱经常混在 query 里:
format filter {
request>uri query {
delete apikey
replace secret_code [REDACTED]
hash email_address
}
wrap json
}
值得一提的是,Caddy 默认就会抹除 Cookie、Set-Cookie、Authorization、Proxy-Authorization 四个头的值(显示为 REDACTED);启用 log_credentials 会关闭这一保护。
反向场景:想要传统文本格式?
需要 Apache Common Log 这类纯文本格式时,可编译带 transform-encoder 插件的自定义镜像,然后 format transform "{common_log}"。但生产环境仍建议坚持 JSON——结构化便于后续分析,传统文本也可经解析提取字段。
采集并验证 Caddy 日志
文件部署可用 DataKit 的日志采集器读取 /var/log/caddy/access.log。先确认采集进程有读取权限,再配置 source、service 与 Pipeline;容器部署则沿用 stdout/stderr 的采集路径,避免同时采集同一份文件造成重复。
按 Pipeline 快速开始提取 JSON 字段。Caddy 的 status 是 HTTP 状态码,应提取为 http_status_code 等独立字段;将 level 映射为日志级别 status,并确认 ts 或自定义时间字段的格式和单位。发送一次正常请求和一次失败请求,核对时间、URI、状态码和耗时,再建立错误统计。
总结
Caddy 日志配置的主线:log 开启、output 定向、format 定形、filter 提纯。非终端默认 JSON 与默认敏感头脱敏便于接入日志平台,但仍需验证全部敏感字段。
常见问题(FAQ)
Q:为什么我的 Caddy 容器里没有访问日志?
访问日志默认关闭,必须在站点的 log 块中显式开启;开启后 Docker 场景用 docker logs 即可看到 JSON 格式的请求记录。
Q:filter 能批量删除一类字段吗?
目前不支持通配符匹配,需逐字段列出 delete。用含敏感值的测试请求检查输出,确认需要删除的请求头和查询参数没有遗漏。
Q:ip_mask 和 hash 两种 IP 脱敏怎么选?
只需关联相同 IP 时可考虑 hash,但共享 IP、地址变化和哈希碰撞使它不能识别"同一访客";只需按网段统计地理/运营商分布选 ip_mask。哈希和掩码不等于消除了重新识别风险,仍需评估其他字段能否关联到个人。
Q:Caddy 的 JSON 日志进观测云还要写 Pipeline 吗?
要按采集配置验证。JSON 便于用 Pipeline 提取字段,但时间、HTTP 状态码和日志级别仍需正确映射;Caddy 的 filter 负责输出前处理,不能替代采集端的解析验证。
系列阅读:Nginx 日志完全指南 | 日志格式化最佳实践