log
启用并配置 HTTP 请求日志(也称为访问日志)。
log 指令应用于其所在站点块的主机名,除非通过 hostnames 子指令覆盖。
配置后,默认会记录该站点的所有请求。要有条件地跳过某些请求的记录,请使用 log_skip 指令。
要向日志条目添加自定义字段,请使用 log_append 指令。
默认情况下,可能包含敏感信息的头部(Cookie、Set-Cookie、Authorization 和 Proxy-Authorization)在访问日志中将被记录为 REDACTED。此行为可以通过全局服务器选项中的 log_credentials 禁用。
语法
log [<logger_name>] {
hostnames <hostnames...>
no_hostname
output <writer_module> ...
format <encoder_module> ...
level <level>
}
-
logger_name 是可选的,用于覆盖该站点的记录器名称。
默认情况下,记录器名称会自动生成,例如
log0、log1等,取决于 Caddyfile 中站点的顺序。仅当您希望从全局选项中可靠地引用由另一个记录器生成的输出时,这才有用。见下方 示例。 -
hostnames 是可选的,用于覆盖该记录器适用的主机名。
默认情况下,记录器适用于其出现的站点块的主机名,即站点地址。如果您希望在 通配符站点块 中为不同子域定义不同的记录器,这将很有用。见下方 示例。
-
no_hostname 阻止记录器与任何站点块的主机名关联。默认情况下,记录器与
log指令出现的 站点地址 相关联。当您希望基于某些条件(例如请求路径或方法)将请求记录到不同文件时,这很有用,可结合
log_name指令 使用。 -
output 配置日志写入的位置。见下文
output模块。默认值:
stderr。 -
format 描述如何编码或格式化日志。见下文
format模块。默认值:如果检测到
stderr为终端则为console,否则为json。 -
level 是要记录的最低条目级别。默认值:
INFO。请注意,访问日志当前仅发出
INFO和ERROR级别的日志。
输出模块
output 子指令允许自定义日志的写入位置。
stderr
标准错误(控制台,为默认)。
output stderr
stdout
标准输出(控制台)。
output stdout
discard
不输出。
output discard
file
写入文件。默认情况下,为防止磁盘空间耗尽,会对日志文件进行轮换(“滚动”)。
日志轮换由 lumberjack 提供。
output file <filename> {
mode <mode>
roll_disabled
roll_size <size>
roll_uncompressed
roll_local_time
roll_keep <num>
roll_keep_for <days>
}
-
<filename> 是日志文件的路径。
-
mode 是用于日志文件的 Unix 文件模式/权限。模式由 1 到 4 个八进制数字组成(与 Unix chmod
命令接受的数字格式相同,不过全零模式被解释为默认模式
600)。例如:0600会将模式设置为rw-,---,---(文件所有者可读写,其他人无权限);0640会设置为rw-,r--,---(文件所有者可读写,组用户只读);644设置为rw-,r--,r--,为文件所有者提供读/写访问,而组所有者和其他用户仅提供读取权限。 -
roll_disabled 禁用日志轮换。可能导致磁盘空间耗尽,因此仅在日志文件以其他方式维护时使用。
-
roll_size 是滚动日志文件的大小阈值。当前实现支持兆字节分辨率;小数值会向上取整到下一个整兆。例如,
1.1MiB会被向上取整为2MiB。默认值:
100MiB -
roll_uncompressed 关闭 gzip 日志压缩。
默认值:启用 gzip 压缩。
-
roll_local_time 将滚动文件名使用本地时间戳。
默认值:使用 UTC 时间。
-
roll_keep 是在删除最旧文件之前要保留的日志文件数量。
默认值:
10 -
roll_keep_for 是保留滚动文件的时长,使用持续时间字符串。当前实现支持按天为单位的精度;小数值会向上取整到下一个整天。例如,
36h(1.5 天)会向上取整为48h(2 天)。默认值:2160h(90 天)
net
网络套接字。如果套接字断开,将在尝试重新连接期间将日志转储到 stderr。
output net <address> {
dial_timeout <duration>
soft_start
}
-
<address> 是要写入日志的 地址。
-
dial_timeout 是等待与日志套接字成功连接的最长时间。如果套接字断开,日志发送可能会被阻塞最长达该时间。
-
soft_start 在连接套接字时将忽略错误,允许您在远程日志服务不可用时也能加载配置。日志将改为输出到 stderr。
格式模块
format 子指令允许自定义日志的编码(格式化)。它出现在 log 块内。
除了每个编码器的单独语法外,大多数编码器还可以设置这些通用属性:
format <encoder_module> {
message_key <key>
level_key <key>
time_key <key>
name_key <key>
caller_key <key>
stacktrace_key <key>
line_ending <char>
time_format <format>
time_local
duration_format <format>
level_format <format>
}
-
message_key 日志条目消息字段的键。默认值:
msg -
level_key 日志条目级别字段的键。默认值:
level -
time_key 日志条目时间字段的键。默认值:
ts -
name_key 日志条目名称字段的键。默认值:
name -
caller_key 日志条目调用者字段的键。
-
stacktrace_key 日志条目堆栈跟踪字段的键。
-
line_ending 要使用的行结尾字符。
-
time_format 时间戳的格式。
默认值:如果格式默认为
console则为wall_milli,否则为unix_seconds_float。可选值包括:
unix_seconds_float自 Unix 纪元起的秒数(浮点数)。unix_milli_float自 Unix 纪元起的毫秒数(浮点数)。unix_nano自 Unix 纪元起的纳秒数(整数)。iso8601示例:2006-01-02T15:04:05.000Z0700rfc3339示例:2006-01-02T15:04:05Z07:00rfc3339_nano示例:2006-01-02T15:04:05.999999999Z07:00wall示例:2006/01/02 15:04:05wall_milli示例:2006/01/02 15:04:05.000wall_nano示例:2006/01/02 15:04:05.000000000common_log示例:02/Jan/2006:15:04:05 -0700- 或任意兼容的时间布局字符串;详见 Go 文档 获取完整细节。
注意:格式字符串的各部分是布局的特殊常量;例如
2006表示年份,01表示月份,Jan表示月份的字符串形式,02表示日期。不要在格式字符串中使用当前的实际日期数字。 -
time_local 使用本地系统时间记录,而不是默认的 UTC 时间。
-
duration_format 持续时间的格式。
默认值:
seconds。可选值包括:
s、second或seconds使用浮点数秒数表示经过时间。ms、milli或millis使用浮点数毫秒数表示经过时间。ns、nano或nanos使用整数纳秒数表示经过时间。string使用 Go 内置的字符串格式,例如1m32.05s或6.31ms。
-
level_format 级别的格式。
默认值:如果格式默认为
console则为color,否则为lower。可选值包括:
lower小写。upper大写。color大写并带 ANSI 颜色。
console
控制台编码器将日志条目格式化以便人类可读,同时保留一定的结构。
format console
json
将每个日志条目格式化为 JSON 对象。
format json
filter
允许对字段进行逐项过滤。
format filter {
fields {
<field> <filter> ...
}
<field> <filter> ...
wrap <encode_module> ...
}
可以通过用 > 表示嵌套层来引用嵌套字段。换言之,对于对象 {"a":{"b":0}},内部字段可以引用为 a>b。
以下字段是日志的基本字段,不能被过滤,因为它们作为特殊情况由底层日志库添加:ts、level、logger 和 msg。
指定 wrap 为可选;如果省略,将根据当前输出模块是否为交互式终端的 stderr 或 stdout 来选择默认值:如果是交互式终端则选择 console,否则选择 json。
作为简写,fields 块可以省略,过滤器可以直接在 filter 块中指定。
以下是可用的过滤器:
delete
标记某字段在编码时跳过编码(删除)。
<field> delete
rename
重命名日志字段的键。
<field> rename <key>
replace
标记某字段在编码时被替换为提供的字符串。
<field> replace <replacement>
ip_mask
使用 CIDR 掩码屏蔽字段中的 IP 地址,即从左侧开始保留的位数。如果字段是字符串数组(例如 HTTP 头),则数组中的每个值都会被掩码。该值也可以是以逗号分隔的 IP 地址字符串。
IPv4 和 IPv6 地址有单独的配置,因为它们的总位数不同。
最常见需要过滤的字段是:
request>remote_ip用于直接连接的客户端request>client_ip在配置了trusted_proxies时表示解析出的“真实客户端”IPrequest>headers>X-Forwarded-For如果位于反向代理之后
<field> ip_mask [<ipv4> [<ipv6>]] {
ipv4 <cidr>
ipv6 <cidr>
}
query
标记某字段对 URL 字段的查询部分执行一个或多个操作。最常见要过滤的字段是 request>uri。
<field> query {
delete <key>
replace <key> <replacement>
hash <key>
}
可用操作包括:
-
delete 从查询中删除给定的键。
-
replace 用 replacement 替换给定查询键的值。用于插入脱敏占位符;您仍能看到查询键在 URL 中,但其值被隐藏。
-
hash 用给定值的 SHA-256 哈希的前 4 个字节(小写十六进制)替换该查询键的值。用于在值敏感时进行模糊处理,同时还能辨别每次请求的值是否不同。
cookie
标记某字段对 Cookie HTTP 头的值执行一个或多个操作。最常见要过滤的字段是 request>headers>Cookie。
<field> cookie {
delete <name>
replace <name> <replacement>
hash <name>
}
可用操作包括:
-
delete 从头中按名称移除给定的 cookie。
-
replace 用 replacement 替换给定 cookie 的值。用于插入脱敏占位符;您仍能看到 cookie 出现在头中,但其值被隐藏。
-
hash 用给定值的 SHA-256 哈希的前 4 个字节(小写十六进制)替换该 cookie 的值。用于在值敏感时进行模糊处理,同时还能辨别每次请求的值是否不同。
如果为同一 cookie 名称定义了多个操作,则仅应用第一个操作。
regexp
标记某字段在编码时应用正则表达式替换。如果字段是字符串数组(例如 HTTP 头),数组中的每个值都会应用替换。
<field> regexp <pattern> <replacement>
所使用的正则表达式语言是 Go 中包含的 RE2。参见 RE2 语法参考 与 Go regexp 语法概述。
在替换字符串中,捕获组可以用 ${group} 引用,其中 group 是捕获组的名称或编号。捕获组 0 表示完整的正则匹配,1 表示第一个捕获组,2 表示第二个捕获组,依此类推。
hash
标记某字段在编码时被替换为其值的 SHA-256 哈希的前 4 个字节(8 个十六进制字符)。如果字段是字符串数组(例如 HTTP 头),数组中的每个值都会被哈希。
用于在值敏感时进行模糊处理,同时还能辨别每次请求的值是否不同。
<field> hash
append
向所有日志条目追加字段。
format append {
fields {
<field> <value>
}
<field> <value>
wrap <encode_module> ...
}
最常用于向生成日志条目的 Caddy 实例添加信息,可能通过环境变量提供。字段值可以是全局占位符(例如 {env.*}),但不能是每请求占位符,因为日志是在 HTTP 请求上下文之外写入的。
指定 wrap 为可选;如果省略,将根据当前输出模块是否为交互式终端的 stderr 或 stdout 来选择默认值:如果是交互式终端则选择 console,否则选择 json。
fields 块可以省略,字段可以直接在 append 块中指定。
示例
启用默认记录器的访问日志。
换句话说,默认情况下这会记录到 stderr,但可以通过在全局选项中重新配置 default 记录器来更改,参见 log 全局选项:
example.com {
log
}
将日志写入文件(启用日志轮换,默认开启):
example.com {
log {
output file /var/log/access.log
}
}
自定义日志轮换:
example.com {
log {
output file /var/log/access.log {
roll_size 1gb
roll_keep 5
roll_keep_for 720h
}
}
}
从日志中删除 User-Agent 请求头:
example.com {
log {
format filter {
request>headers>User-Agent delete
}
}
}
对多个敏感 cookie 进行脱敏。(注意:某些敏感头部默认以空值记录;参见全局选项 log_credentials 以启用 Cookie 头的记录):
example.com {
log {
format filter {
request>headers>Cookie cookie {
replace session REDACTED
delete secret
}
}
}
}
对请求的远程地址进行掩码,保留前 16 位(即 IPv4 的 255.255.0.0),以及 IPv6 地址的前 32 位。
注意,自 Caddy v2.7 起,既记录 remote_ip 又记录 client_ip,其中 client_ip 在配置了 trusted_proxies 时为“真实 IP”:
example.com {
log {
format filter {
request>remote_ip ip_mask 16 32
request>client_ip ip_mask 16 32
}
}
}
将来自环境变量的服务器 ID 追加到所有日志条目,并将其与 filter 链接以删除某个头:
example.com {
log {
format append {
server_id {env.SERVER_ID}
wrap filter {
request>headers>Cookie delete
}
}
}
}
在 通配符站点块 中为每个子域写入单独的日志文件,通过为每个记录器覆盖 hostnames 实现。这里使用了一个 片段 来避免重复:
(subdomain-log) {
log {
hostnames {args[0]}
output file /var/log/{args[0]}.log
}
}
*.example.com {
import subdomain-log foo.example.com
@foo host foo.example.com
handle @foo {
respond "foo"
}
import subdomain-log bar.example.com
@bar host bar.example.com
handle @bar {
respond "bar"
}
}
将特定子域的访问日志写入两个不同的文件,并使用不同的格式(一个使用 transform-encoder 插件 ,另一个使用
json)。
这是通过在站点块中将记录器名称覆盖为 foo,然后在全局选项的两个记录器中使用 include http.log.access.foo 来包含该记录器生成的访问日志实现的:
{
log access-formatted {
include http.log.access.foo
output file /var/log/access-foo.log
format transform "{common_log}"
}
log access-json {
include http.log.access.foo
output file /var/log/access-foo.json
format json
}
}
foo.example.com {
log foo
}