文档
一个 项目

log

启用并配置 HTTP 请求日志(也称为访问日志)。

log 指令应用于其所在站点块的主机名,除非通过 hostnames 子指令覆盖。

配置后,默认会记录该站点的所有请求。要有条件地跳过某些请求的记录,请使用 log_skip 指令

要向日志条目添加自定义字段,请使用 log_append 指令

默认情况下,可能包含敏感信息的头部(CookieSet-CookieAuthorizationProxy-Authorization)在访问日志中将被记录为 REDACTED。此行为可以通过全局服务器选项中的 log_credentials 禁用。

语法

log [<logger_name>] {
	hostnames <hostnames...>
	no_hostname
	output <writer_module> ...
	format <encoder_module> ...
	level  <level>
}
  • logger_name 是可选的,用于覆盖该站点的记录器名称。

    默认情况下,记录器名称会自动生成,例如 log0log1 等,取决于 Caddyfile 中站点的顺序。仅当您希望从全局选项中可靠地引用由另一个记录器生成的输出时,这才有用。见下方 示例

  • hostnames 是可选的,用于覆盖该记录器适用的主机名。

    默认情况下,记录器适用于其出现的站点块的主机名,即站点地址。如果您希望在 通配符站点块 中为不同子域定义不同的记录器,这将很有用。见下方 示例

  • no_hostname 阻止记录器与任何站点块的主机名关联。默认情况下,记录器与 log 指令出现的 站点地址 相关联。

    当您希望基于某些条件(例如请求路径或方法)将请求记录到不同文件时,这很有用,可结合 log_name 指令 使用。

  • output 配置日志写入的位置。见下文 output 模块

    默认值:stderr

  • format 描述如何编码或格式化日志。见下文 format 模块

    默认值:如果检测到 stderr 为终端则为 console,否则为 json

  • level 是要记录的最低条目级别。默认值:INFO

    请注意,访问日志当前仅发出 INFOERROR 级别的日志。

输出模块

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.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

    可选值包括:

    • ssecondseconds 使用浮点数秒数表示经过时间。
    • msmillimillis 使用浮点数毫秒数表示经过时间。
    • nsnanonanos 使用整数纳秒数表示经过时间。
    • string 使用 Go 内置的字符串格式,例如 1m32.05s6.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

以下字段是日志的基本字段,不能被过滤,因为它们作为特殊情况由底层日志库添加:tslevelloggermsg

指定 wrap 为可选;如果省略,将根据当前输出模块是否为交互式终端的 stderrstdout 来选择默认值:如果是交互式终端则选择 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 时表示解析出的“真实客户端”IP
  • request>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 从查询中删除给定的键。

  • replacereplacement 替换给定查询键的值。用于插入脱敏占位符;您仍能看到查询键在 URL 中,但其值被隐藏。

  • hash 用给定值的 SHA-256 哈希的前 4 个字节(小写十六进制)替换该查询键的值。用于在值敏感时进行模糊处理,同时还能辨别每次请求的值是否不同。

标记某字段对 Cookie HTTP 头的值执行一个或多个操作。最常见要过滤的字段是 request>headers>Cookie

<field> cookie {
	delete  <name>
	replace <name> <replacement>
	hash    <name>
}

可用操作包括:

  • delete 从头中按名称移除给定的 cookie。

  • replacereplacement 替换给定 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 为可选;如果省略,将根据当前输出模块是否为交互式终端的 stderrstdout 来选择默认值:如果是交互式终端则选择 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
}