文档
一个 项目

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)。 例如,644 为日志文件的所有者提供读/写访问权限,但仅为组所有者和其他用户提供读取访问权限; 600 为日志文件的所有者提供读/写访问权限,并且不向任何人提供访问权限。

    默认值:600

  • 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

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 是可选的; 如果省略,则会根据当前输出模块是 stderr 还是 stdout 以及是否为交互式终端来选择默认值,在这种情况下选择 console,否则选择 json

作为快捷方式,可以省略 fields 块,并且可以直接在 filter 块中指定过滤器。

以下是可用的过滤器

delete

标记要从编码中跳过的字段。

<field> delete
rename

重命名日志字段的键。

<field> rename <key>
replace

标记要在编码时替换为提供的字符串的字段。

<field> replace <replacement>
ip_mask

使用 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>
}
query

标记要执行一个或多个操作的字段,以操作 URL 字段的查询部分。 最常见的,要过滤的字段是 request>uri

<field> query {
	delete  <key>
	replace <key> <replacement>
	hash    <key>
}

可用的操作是

  • delete 从查询中删除给定的键。

  • replace 将给定查询键的值替换为 replacement。 用于插入编辑占位符; 您将看到 URL 中存在查询键,但值是隐藏的。

  • hash 将给定查询键的值替换为该值 SHA-256 哈希的前 4 个字节,小写十六进制。 用于模糊处理值(如果它是敏感的),同时能够注意到每个请求是否具有不同的值。

标记要执行一个或多个操作的字段,以操作 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 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,但这可以通过使用 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 {
			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_ipclient_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
}