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> sampling { interval <duration> first <number> thereafter <number> } }
-
logger_name是此站点记录器名称的可选覆盖。
默认情况下,记录器名称是自动生成的,例如
log0、log1等,具体取决于 Caddyfile 中站点的顺序。仅当您希望从全局选项中定义的另一个记录器可靠地引用此记录器的输出时,这才有用。见下面的示例。 -
hostnames是此记录器适用的主机名的可选覆盖。
默认情况下,记录器适用于它出现的站点块的主机名,即站点地址。如果您希望在wildcard site block中的每个子域定义不同的记录器,这非常有用。见下面的示例。
-
no_hostname阻止记录器与任何站点块的主机名关联。默认情况下,记录器与
log指令出现的site address关联。当您想要根据某些条件(例如请求路径或方法)使用
log_name指令将请求记录到不同的文件时,这非常有用。 -
output配置日志写入位置。见下面的
output模块。默认:
stderr。 -
format描述如何对日志进行编码或格式化。见下面的
format模块。默认:如果检测到
stderr是终端,则为console,否则为json。 -
level是登录的最低入门级别。默认:
INFO。请注意,访问日志目前仅发出
INFO和ERROR级别的日志。 -
sampling配置日志采样以减少日志量。如果指定了采样,则启用采样,并且以下默认值生效。省略此项将禁用采样。
-
interval是进行采样的持续时间窗口。默认:
1s(禁用)。 -
first 是在给定级别和每个时间间隔的消息中保留多少日志。默认:
100。 -
thereafter是在第一个保留日志之后的每个间隔中要跳过的日志数。默认:
100。
例如,对于
interval 1s、first 5和thereafter 10,在每 10 秒间隔内,将保留前 5 个日志条目,然后将允许在该秒内通过具有相同级别和消息的每 10 个日志条目。 -
输出模块
output 子指令允许您自定义日志写入位置。
标准错误
标准错误(控制台,是默认值)。
output stderr
标准输出
标准输出(控制台)。
output stdout
丢弃
无输出。
output discard
文件
一个文件。默认情况下,日志文件根据大小进行轮换(“滚动”),以防止磁盘空间耗尽。
日志滚动由timberjack 提供
output file <filename> { mode <mode> roll_disabled roll_size <size> roll_interval <duration> roll_minutes <minutes...> roll_at <times...> roll_uncompressed roll_local_time roll_keep <num> roll_keep_for <days> backup_time_format <format> }
-
<filename> 是日志文件的路径。
滚动时,文件将使用模板
<name>-<timestamp>-<reason>.log重命名。时间戳根据backup_time_format选项进行格式化。原因是size或time,具体取决于哪个触发了轮换。如果文件被压缩,.gz会附加到文件名中。例如,如果文件名为
access.log,则滚动文件如果因大小而滚动,则可能命名为access-2026-01-30T22-15-42.123-size.log;如果因时间滚动,则滚动文件可能命名为access-2025-01-30T00-00-00.000-time.log。 -
mode是用于日志文件的 Unix 文件模式/权限。该模式由 1 到 4 个八进制数字组成(与 Unixchmod
命令接受的数字格式相同,除了全零模式被解释为默认模式
600)。例如:
0600会将模式设置为rw-,---,---(日志文件所有者有读/写权限,其他任何人都没有权限);0640将模式设置为rw-,r--,---(对文件所有者具有读/写权限,对组仅具有读权限);644将模式设置为rw-,r--,r--向日志文件所有者提供读/写权限,但仅向群组所有者和其他用户提供读权限。 -
roll_disabled禁用日志滚动。这可能会导致磁盘空间耗尽,因此仅当您的日志文件以其他方式维护时才使用此选项。
-
roll_size是滚动日志文件的大小。当前的实现支持兆字节分辨率;小数值向上舍入到下一个整数兆字节。例如,
1.1MiB向上舍入为2MiB。该功能始终处于启用状态。如果写入日志导致文件超过指定大小,则日志将立即轮转。备份文件名将包含
size作为原因。默认:
100MiB -
roll_interval是日志轮转之间的最大持续时间。该值为持续时间字符串,之后滚动日志文件。
启用后,自上次轮换后经过此持续时间后,文件将在下次写入日志时轮换。备份文件名将包含
time作为原因。请注意,如果设置为
24h,则不一定在午夜滚动,而是在自上次旋转以来的 24 小时标记处滚动。如果由于尺寸而发生滚动,则下一次旋转的时间将比上一次旋转的时间有所偏移。您可以使用roll_at或roll_minutes选项在特定时间进行滚动。默认值:禁用
-
roll_minutes是滚动日志文件的分钟值 (0-59) 列表。例如,
10 40每 30 分钟滚动一次日志文件,xx:10和xx:40每小时滚动一次日志文件。旋转与时钟分钟(秒 0)对齐。启用此功能会生成一个 goroutine 计时器,该计时器会在指定的分钟值处触发日志轮换(即引入少量后台处理)。除了
roll_interval和roll_size之外,还可以进行此操作。备份文件名将包含time作为原因。默认值:禁用
-
roll_at是滚动日志文件的时间值列表(采用 24 小时格式)。例如,
00:00 12:00每天会在午夜和中午滚动日志文件两次。旋转与时钟分钟(秒 0)对齐。启用此功能会生成一个 goroutine 计时器,该计时器会在指定时间触发日志轮换(即引入少量后台处理)。除了
roll_interval和roll_size之外,还可以进行此操作。备份文件名将包含time作为原因。默认值:禁用
-
roll_uncompressed关闭 gzip 日志压缩。
默认:
gzip压缩已启用。 -
roll_local_time设置滚动以在文件名中使用本地时间戳。 默认:使用 UTC 时间。
-
roll_keep是在删除最旧的日志文件之前保留多少个日志文件。创建新日志文件时触发。
默认:
10 -
roll_keep_for是将滚动文件保留为持续时间字符串的时间。创建新日志文件时触发。 当前的实现支持日分辨率;小数值向上舍入到下一个整天。例如,
36h(1.5 天)向上舍入为48h(2 天)。默认:
2160h(90 天) -
backup_time_format是备份文件名中使用的时间格式。必须是有效的时间布局字符串;详情请参见Go documentation。
默认:
2006-01-02T15-04-05
网
一个网络套接字。如果套接字出现故障,它会在尝试重新连接时将日志转储到 stderr。
output net <address> { dial_timeout <duration> soft_start }
-
<address> 是要写入日志的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日志条目的 stacktrace 字段的键。
-
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.000Z0700-rfc3339示例:2006-01-02T15:04:05Z07:00-rfc3339_nano示例:2006-01-02T15:04:05.999999999Z07:00-wall示例:2006/01/02 15:04:05-wall_milli示例:2006/01/02 15:04:05.000-wall_nano示例:2006/01/02 15:04:05.000000000-common_log示例:02/Jan/2006:15:04:05 -0700- 或者,任何兼容的时间布局字符串;详情请参见Go documentation。
请注意,格式字符串的部分是布局的特殊常量;所以
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 颜色。
安慰
控制台编码器格式化日志条目以供人类可读,同时保留一些结构。
format console
json
将每个日志条目格式化为 JSON 对象。
format json
筛选
允许按字段过滤。
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块内指定过滤器。
这些是可用的过滤器:
删除
标记要跳过编码的字段。
<field> delete
重命名
重命名日志字段的键。
<field> rename <key>
代替
标记要在编码时用提供的字符串替换的字段。
<field> replace <replacement>
IP掩码
使用 CIDR 掩码(即 IP 中要保留的位数,从左侧开始)屏蔽字段中的 IP 地址。如果该字段是字符串数组(例如 HTTP 标头),则数组中的每个值都会被屏蔽。该值可以是逗号分隔的 IP 地址字符串。
IPv4 和 IPv6 地址有单独的配置,因为它们具有不同的总位数。
最常见的是,要过滤的字段是:
-request>remote_ip为直连客户端
- 配置
trusted_proxies时解析的“真实客户端”为request>client_ip-request>headers>X-Forwarded-For(如果位于反向代理后面)
<field> ip_mask [<ipv4> [<ipv6>]] { ipv4 <cidr> ipv6 <cidr> }
询问
将字段标记为执行一项或多项操作,以操作 URL 字段的查询部分。最常见的是,要过滤的字段是request>uri。
<field> query { delete <key> replace <key> <replacement> hash <key> }
可用的操作有:
-
delete 从查询中删除给定的键。
-
replace 用 replacement 替换给定查询键的值。用于插入密文占位符;您将看到查询键位于 URL 中,但值是隐藏的。
-
hash 将给定查询键的值替换为该值的 SHA-256 哈希值的前 4 个字节(小写十六进制)。如果值敏感,则有助于模糊该值,同时能够注意到每个请求是否具有不同的值。
曲奇饼
将字段标记为执行一个或多个操作,以操作CookieHTTP 标头的值。最常见的是,要过滤的字段是request>headers>Cookie。
<field> cookie { delete <name> replace <name> <replacement> hash <name> }
可用的操作有:
-
delete 从标头中按名称删除给定的 cookie。
-
replace 用 replacement 替换给定 cookie 的值。用于插入密文占位符;您会看到 cookie 位于标头中,但值是隐藏的。
-
hash 将给定 cookie 的值替换为该值的 SHA-256 哈希值的前 4 个字节(小写十六进制)。如果值敏感,则有助于模糊该值,同时能够注意到每个请求是否具有不同的值。
如果为同一个 cookie 名称定义了多个操作,则仅应用第一个操作。
正则表达式
将字段标记为在编码时应用正则表达式替换。如果该字段是字符串数组(例如 HTTP 标头),则数组中的每个值都会应用替换。
<field> regexp <pattern> <replacement>
使用的正则表达式语言是 RE2,包含在 Go 中。请参阅RE2 语法参考和Go regexp 语法概述。
在替换字符串中,可以使用${group}引用捕获组,其中group是表达式中捕获组的名称或编号。捕获组0是完整的正则表达式匹配,1是第一个捕获组,2是第二个捕获组,依此类推。
散列
标记要替换为编码时值的 SHA-256 哈希值的前 4 个字节(8 个十六进制字符)的字段。如果字段是字符串数组(例如 HTTP 标头),则数组中的每个值都会进行哈希处理。
如果值敏感,则有助于模糊该值,同时能够注意到每个请求是否具有不同的值。
<field> hash
附加
将字段附加到所有日志条目。
format append { fields { <field> <value> } <field> <value> wrap <encode_module> ... }
它对于添加有关生成日志条目的 Caddy 实例的信息最有用,可能是通过环境变量。字段值可以是全局占位符(例如{env.*}),但不是每个请求占位符,因为日志是在 HTTP 请求上下文之外写入的。
指定wrap是可选的;如果省略,则根据当前输出模块是stderr还是stdout来选择默认值,并且是交互式终端,在这种情况下选择console,否则选择json。
可以省略fields块,并且可以直接在append块内指定字段。
示例
启用对默认记录器的访问日志记录。
换句话说,默认情况下记录到stderr,但是可以通过使用log 全局选项重新配置default记录器来更改:
example.com { log }
将日志写入文件(使用日志滚动,默认启用):
example.com { log { output file /var/log/access.log } }
自定义日志滚动,每天午夜或日志文件达到 1 GB 时滚动(以先到者为准),并保留 5 个滚动文件或 30 天的日志:
example.com { log { output file /var/log/access.log { roll_at 00:00 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 } } } }
从请求中屏蔽远程地址,保留 IPv4 地址的前 16 位(即 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为wildcard site block中的每个子域写入单独的日志文件。这里使用snippet来避免重复:
(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 }
通过采样减少日志量,例如保留每秒前 5 个请求,然后每 10 个请求保留 1 个:
example.com { log { sampling { interval 1s first 5 thereafter 10 } } }