log
启用并配置 HTTP 请求日志记录(也称为访问日志)。
log
指令适用于它所在的站点块的主机名,除非被 hostnames
子指令覆盖。
配置后,默认情况下,对站点的所有请求都将被记录。要从日志记录中条件性地跳过某些请求,请使用 skip_log
指令。
从 Caddy v2.5 开始,默认情况下,包含潜在敏感信息的标头(Cookie
、Set-Cookie
、Authorization
和 Proxy-Authorization
)将以空值记录。可以使用 log_credentials
全局服务器选项禁用此行为。
语法
log [<logger_name>] {
hostnames <hostnames...>
output <writer_module> ...
format <encoder_module> ...
level <level>
}
-
logger_name 是此站点日志记录器名称的可选覆盖。
默认情况下,日志记录器名称是自动生成的,例如
log0
、log1
等,具体取决于 Caddyfile 中站点的顺序。这只有在你希望从全局选项中定义的另一个日志记录器可靠地引用此日志记录器的输出时才有用。请参阅下面的 示例。 -
hostnames 是此日志记录器适用的主机名的可选覆盖。
默认情况下,日志记录器适用于它所在的站点块的主机名,即站点地址。如果你希望在 通配符站点块 中为每个子域定义不同的日志记录器,这将很有用。请参阅下面的 示例。
-
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> {
roll_disabled
roll_size <size>
roll_uncompressed
roll_local_time
roll_keep <num>
roll_keep_for <days>
}
-
<filename> 是日志文件的路径。
-
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.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 文档。
请注意,格式字符串的各个部分是布局的特殊常量;因此
2006
是年份,01
是月份,Jan
是月份的字符串形式,02
是日期。不要在格式字符串中使用实际的当前日期数字。 -
time_local 使用本地系统时间记录日志,而不是默认的 UTC 时间。
-
duration_format 持续时间的格式。
默认值:
seconds
。可以是以下之一
seconds
经过时间的秒数的浮点数。nano
经过时间的纳秒数的整数。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 {
wrap <encode_module> ...
fields {
<field> <filter> ...
}
}
嵌套字段可以通过用 >
表示嵌套层来引用。换句话说,对于像 {"a":{"b":0}}
这样的对象,内部字段可以被引用为 a>b
。
以下字段是日志的基本字段,不能被过滤,因为它们是由底层日志记录库作为特殊情况添加的:ts
、level
、logger
和 msg
。
以下是可用的过滤器
delete
标记一个字段,使其在编码时被跳过。
<field> delete
rename
重命名日志字段的键。
<field> rename <key>
replace
标记一个字段,使其在编码时被替换为提供的字符串。这对于插入一个隐藏占位符很有用;你会看到查询键在 URL 中,但值被隐藏了。
<field> replace <replacement>
ip_mask
使用 CIDR 掩码(即从左侧开始保留的 IP 位数)掩盖字段中的 IP 地址。如果字段是字符串数组(例如 HTTP 标头),则数组中的每个值都会被掩盖。该值可以是逗号分隔的 IP 地址字符串。
IPv4 和 IPv6 地址有单独的配置,因为它们具有不同的总位数。
最常见的是,要过滤的字段是
request>remote_ip
用于直接连接的客户端request>client_ip
用于解析的“真实客户端”,当配置了trusted_proxies
时request>headers>X-Forwarded-For
如果在反向代理后面
<field> ip_mask {
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 将给定 cookie 的值替换为 replacement。这对于插入一个隐藏占位符很有用;你会看到 cookie 在标头中,但值被隐藏了。
-
hash 将给定 cookie 的值替换为该值 SHA-256 哈希的前 4 个字节,小写十六进制。这对于隐藏值很有用,如果值是敏感的,同时能够注意到每个请求是否具有不同的值。
如果为同一个 cookie 名称定义了多个操作,则只应用第一个操作。
regexp
标记一个字段,使其在编码时应用正则表达式替换。如果字段是字符串数组(例如 HTTP 标头),则数组中的每个值都会应用替换。
<field> regexp <pattern> <replacement>
使用的正则表达式语言是 RE2,包含在 Go 中。请参阅 RE2 语法参考 和 Go 正则表达式语法概述。
在替换字符串中,可以使用 ${group}
引用捕获组,其中 group
是表达式中捕获组的名称或编号。捕获组 0
是完整的正则表达式匹配,1
是第一个捕获组,2
是第二个捕获组,依此类推。
hash
标记一个字段,以便在编码时用该字段值的 SHA-256 哈希的前 4 个字节(8 个十六进制字符)替换。如果该字段是字符串数组(例如 HTTP 头部),则对数组中的每个值进行哈希处理。
如果值敏感,则此方法有助于隐藏值,同时能够注意到每个请求是否具有不同的值。
<field> hash
示例
启用对默认日志记录器的访问日志记录。
换句话说,默认情况下,这会将日志记录到 stderr
,但可以通过使用 log
全局选项 重新配置 default
日志记录器来更改此行为。
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 {
wrap console
fields {
request>headers>User-Agent delete
}
}
}
}
屏蔽多个敏感 Cookie。(请注意,默认情况下,某些敏感头部会以空值记录;请参阅 log_credentials
全局选项 以启用记录 Cookie
头部值)。
example.com {
log {
format filter {
wrap console
fields {
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 {
wrap console
fields {
request>remote_ip ip_mask {
ipv4 16
ipv6 32
}
}
}
}
}
通过覆盖每个日志记录器的 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
}