查看源代码 日志记录器 (日志记录器 v1.16.2)
Elixir 应用程序的日志记录器。
此应用程序主要是一个围绕 Erlang 的 :logger
功能的包装器,用于将消息翻译和格式化为 Elixir 项。
总的来说,您会发现 Logger
提供所有 7 个 syslog 级别(尽管调试、信息、警告和错误是最常用的)。
支持基于消息的日志记录和结构化日志记录。
与 Erlang 的
:logger
集成并支持自定义过滤器和处理程序。在客户端格式化和截断消息,以避免阻塞
Logger
处理程序。在同步和异步模式之间切换,以便在需要时保持性能,但在压力下也施加反压。
允许为特定模块、应用程序或进程覆盖日志级别。
日志记录对于跟踪系统中发生的感兴趣事件很有用。例如,在删除用户时记录日志可能会有所帮助。
def delete_user(user) do
Logger.info("Deleting user from the system: #{inspect(user)}")
# ...
end
该 Logger.info/2
宏在 :info
级别发出提供的消息。请注意,给定 info/2
的参数仅在记录消息时才会计算。例如,如果日志记录器级别设置为 :warning
,则永远不会记录 :info
消息,因此上述给定的参数甚至不会执行。
还有其他宏用于其他级别。
Logger 还允许通过 :compile_time_purge_matching
选项(见下文)完全删除日志命令。
有关动态记录消息的信息,请参阅 bare_log/3
。但请注意 bare_log/3
始终计算其参数(除非参数是匿名函数)。
级别
支持的级别,按重要性排序,分别是
:emergency
- 当系统不可用时,会发生恐慌:alert
- 用于警报,必须立即采取的操作,例如数据库损坏:critical
- 用于严重情况:error
- 用于错误:warning
- 用于警告:notice
- 用于正常但重要的消息:info
- 用于任何类型的信息:debug
- 用于调试相关消息
例如,:info
优先于 :debug
。如果您的日志级别设置为 :info
,则所有 :info
、:notice
及以上级别将传递给处理程序。如果您的日志级别设置为 :alert
,则只打印 :alert
和 :emergency
。
消息
Logger 可用于记录非结构化数据和结构化数据。
非结构化数据是字符串或字符串列表
Logger.info("hello world!")
Logger.info(["hello ", "world!"])
结构化数据,也称为报告,是关键字列表和映射
Logger.info([new_user: user.id, account_type: :admin])
Logger.info(%{new_user: user.id, account_type: :admin})
日志函数还接受一个零元组匿名函数作为消息
Logger.info(fn -> "hello world!" end)
匿名函数可以返回一条消息或一个包含消息和附加元数据的元组(将在下一节中描述)。
在所有情况下,给定 Logger
宏的参数仅在当前日志级别需要时才会计算。唯一的例外是 bare_log/3
函数,它是用于日志记录的原始机制。
元数据
每当记录一条消息时,都可以通过元数据提供附加信息。每个日志操作(例如 Logger.info/2
)都允许将元数据作为参数提供。
此外,元数据可以使用 Logger.metadata/1
在每个进程中设置。
但是,某些元数据在 Logger 尽可能时总是会自动添加。这些是
:application
- 当前应用程序:mfa
- 当前模块、函数和元数:file
- 当前文件:line
- 当前行:pid
- 当前进程标识符:initial_call
- 启动进程的初始调用:registered_name
- 进程注册名,以原子形式表示:domain
- 记录消息的域列表。例如,所有 Elixir 报告默认设置为[:elixir]
。Erlang 报告可能以[:otp]
或[:sasl]
开头:crash_reason
- 一个包含两个元素的元组,其中第一个参数是抛出/错误/退出原因,第二个参数是堆栈跟踪。抛出始终为{:nocatch, term}
。错误始终是Exception
结构体。所有其他条目都是退出。默认格式化程序默认情况下会忽略此元数据,但它可能对某些处理程序有用,例如将错误报告给第三方服务的处理程序
有两个特殊的元数据键,:module
和 :function
,它们从 :mfa
中提取相关位。
请注意,所有元数据都是可选的,并且可能并不总是可用。当使用 Logger
宏时,:mfa
、:file
、:line
以及类似的元数据会自动包含在内。 Logger.bare_log/3
默认情况下不包含任何超出 :pid
的元数据。其他元数据,例如 :crash_reason
、:initial_call
和 :registered_name
仅在行为(如 GenServer、Supervisor 等)内部可用。
例如,您可能希望在日志中包含一个自定义的 :error_code
元数据
Logger.error("We have a problem", [error_code: :pc_load_letter])
默认情况下,不会记录任何元数据。我们将了解如何在接下来的几节中启用它。
配置
Logger
支持多种配置。
此配置分为三类
引导配置 - 此配置在日志记录器启动时读取,并配置 Elixir 如何挂钩到 Erlang 自己的日志记录器
编译配置 - 这必须在编译代码之前设置
运行时配置 - 可以在启动
:logger
应用程序之前设置,但可以在运行时更改
引导配置
当 Logger
启动时,它会配置 Erlang 中的 :default
日志处理程序,以翻译和格式化 Elixir 项。作为开发人员,您可以自定义默认处理程序、默认格式化程序以及许多其他选项。
以下配置必须通过配置文件(例如 config/config.exs
)在 :logger
键下设置,然后再启动您的应用程序
:default_formatter
- 一个关键字列表,用于配置默认处理程序使用的默认格式化程序。有关完整配置列表,请参阅Logger.Formatter
。:default_handler
- 此选项配置用于日志记录的默认处理程序。默认处理程序是一个:logger_std_h
实例,它还支持文件日志记录和日志轮换。您可以将其设置为false
以完全禁用默认日志记录。有关更多信息,请参阅下面的示例。:handle_otp_reports
- 如果应该记录 Erlang/OTP 消息。默认为true
。:handle_sasl_reports
- 如果应该记录主管、崩溃和进度报告。默认为false
。此选项仅在:handle_otp_reports
为真时有效。:metadata
- 要包含在所有日志消息中的全局主元数据。默认为[]
。这可以在进程级别使用metadata/1
或根据需要在每次日志调用中覆盖。
例如,要配置 Logger
以使用 config/config.exs
文件重定向所有 Erlang 消息
config :logger,
handle_otp_reports: true,
handle_sasl_reports: true
要配置默认格式化程序,例如,要使用不同的格式并包含一些元数据
config :logger, :default_formatter,
format: "[$level] $message $metadata\n",
metadata: [:error_code, :file]
或者要配置默认处理程序,例如,要将日志记录到一个文件中,该文件具有对日志轮换和压缩的内置支持
config :logger, :default_handler,
config: [
file: ~c"system.log",
filesync_repeat_interval: 5000,
file_check: 5000,
max_no_bytes: 10_000_000,
max_no_files: 5,
compress_on_rotate: true
]
有关所有相关配置(包括过载保护)的信息,请参阅 :logger_std_h
。或者将 :default_handler
设置为 false 以完全禁用默认日志记录
config :logger, :default_handler, false
如何在后面的部分中添加新的处理程序。
关键字或映射
虽然 Erlang 的日志记录器期望
:config
是一个映射,但 Elixir 的 Logger 允许使用关键字列表设置默认处理程序配置。例如,这允许您的config/*.exs
文件(例如config/dev.exs
)覆盖在config/config.exs
中定义的各个键。使用 Erlang 的 API 读取处理程序配置时,配置将始终以映射形式读取(和写入)。
编译配置
以下配置必须通过配置文件(例如 config/config.exs
)在 :logger
应用程序下设置,然后再编译代码
:always_evaluate_messages
- 如果应该计算消息,即使日志级别低于配置的最小级别。默认为false
。这对于日志级别在测试环境中较高的用例(例如:error
)很有用,这在避免日志与测试输出混合时很常见。在这种情况下,您可能会发现仅在将代码部署到生产环境时(其中日志级别较低(例如:info
))才包含运行时错误的日志消息。这些运行时错误可能是由(例如)在日志消息中插入未实现String.Chars
协议的内容引起的,例如"PID: #{self()}"
(因为 PID 无法使用String.Chars
转换为字符串)。:compile_time_application
- 在编译时将:application
元数据值设置为配置的值。此配置由 Mix 自动设置,并在日志记录时作为元数据提供。:compile_time_purge_matching
- 在编译时清除所有匹配给定条件的调用。这意味着Logger
的日志级别低于此选项的调用将在编译时完全删除,在运行时不会产生任何开销。此配置需要一个关键字列表。每个关键字列表包含一个元数据键和应该清除的匹配值。支持一些特殊的键。:level_lower_than
- 清除所有日志级别低于此选项的日志消息。:module
- 清除所有匹配模块的日志消息。:function
- 清除所有匹配"函数/参数个数"的日志消息。
请记住,如果您想从依赖项中清除日志调用,则必须重新编译该依赖项。
例如,要清除在编译时发生的日志级别低于 :info
的所有调用,可以在 config/config.exs
文件中添加以下配置。
config :logger,
compile_time_purge_matching: [
[level_lower_than: :info]
]
如果您想清除名为 :foo
的应用程序中的所有日志调用,并且只保留 Bar.foo/3
的错误,您可以设置两个不同的匹配条件。
config :logger,
compile_time_purge_matching: [
[application: :foo],
[module: Bar, function: "foo/3", level_lower_than: :error]
]
运行时配置
以下所有配置都可以在配置文件(例如 config/config.exs
)中设置,也可以通过 Logger.configure/1
在运行时动态更改。
:level
- 日志级别。尝试记录任何低于配置级别的严重性的消息,都将导致该消息被忽略。请记住,每个处理程序也可能具有其特定的级别。除了上面提到的级别之外,它还支持 2 个“元级别”。:all
- 将记录所有消息,在概念上等同于:debug
:none
- 根本不会记录任何消息。
:translator_inspect_opts
- 在翻译 OTP 报告和错误时,必须在错误报告中检查最后一条消息和状态。此配置允许开发人员更改检查数据的方式和程度。
例如,要配置 config/config.exs
文件中的 :level
选项,您可以这样做:
config :logger, level: :warning
此外,Logger
允许将 Erlang 发送的消息通过翻译器翻译成 Elixir 格式。可以使用 add_translator/1
和 remove_translator/1
API 在任何时候添加翻译器。有关更多信息,请查看 Logger.Translator
。
Erlang/OTP 处理程序
处理程序代表了与日志系统集成的能力,以处理每个记录的日志消息/事件。Elixir 自动配置默认处理程序,但您可以使用 Erlang 的 :logger
模块添加其他处理程序。
Erlang/OTP 处理程序必须列在您自己的应用程序下。例如,要设置一个额外的处理程序,以便您写入控制台和文件,您可以这样做:
config :my_app, :logger, [
{:handler, :file_log, :logger_std_h, %{
config: %{
file: ~c"system.log",
filesync_repeat_interval: 5000,
file_check: 5000,
max_no_bytes: 10_000_000,
max_no_files: 5,
compress_on_rotate: true
},
formatter: Logger.Formatter.new()
}}
]
每个处理程序的形状为 {:handler, name, handler_module, config_map}
。一旦定义,处理程序就可以在您的 Application.start/2
回调中使用 add_handlers/1
显式地附加。
Logger.add_handlers(:my_app)
您也可以开发自己的处理程序。处理程序在与记录消息/事件的进程相同的进程中运行。这为开发人员提供了灵活性,但他们应该避免在处理程序中执行任何长时间运行的操作,因为这可能会大大降低正在执行的操作的速度。目前,Erlang 处理程序没有内置的过载保护,因此您有责任实现它。
或者,您可以使用 :logger_backends
项目。它设置了一个带有过载保护的日志处理程序,并允许将传入的事件调度到多个后端。
过滤
您可以向 Erlang 的 :logger
添加过滤器。例如,要过滤掉包含特定字符串的日志,您可以创建一个模块:
defmodule LogFilter do
def filter(log_event, _opts) do
case log_event do
%{msg: msg} when is_binary(msg) ->
if msg =~ "password" do
:stop
else
:ignore
end
_ ->
:ignore
end
end
end
然后,当您启动应用程序时,例如在 Application.start/2
回调中:
:logger.add_primary_filter(:word_filter, {&LogFilter.filter/2, []})
后端和向后兼容性
在 Elixir v1.15 之前,可以使用 Logger 后端实现自定义日志记录。编写 Logger 后端的主要 API 已移至 :logger_backends
项目。但是,出于向后兼容性考虑,后端 API 仍然是 Elixir 的一部分。
重要说明
如果设置了
:backends
键,并且它不包含:console
条目,我们假设您要禁用内置日志记录。您可以通过设置config :logger, :default_handler, []
来强制日志记录。:console
后端配置将自动映射到默认处理程序和默认格式化程序。以前,您会设置:config :logger, :console, level: :error, format: "$time $message $metadata"
现在等同于:
config :logger, :default_handler, level: :error config :logger, :default_formatter, format: "$time $message $metadata"
所有以前的控制台配置(除了
:level
)现在都在:default_formatter
下。如果您想使用基于 Logger 后端的以前
:console
实现,您仍然可以设置backends: [Logger.Backends.Console]
并将配置放在config :logger, Logger.Backends.Console
下。但请考虑在这种情况下使用:logger_backends
项目,因为Logger.Backends.Console
本身将在未来的版本中被弃用。Logger.Backends
只接收:debug
、:info
、:warning
和:error
消息。:notice
映射到:info
。:warn
映射到:warnings
。所有其他消息都映射到:error
。
摘要
函数
添加一个新的后端。
添加在给定 app
的 :logger
应用程序参数中配置的处理程序。
添加一个新的翻译器。
记录一条警报消息。
比较日志级别。
配置日志记录器。
配置给定的后端。
记录一条严重错误消息。
记录一条调试消息。
返回 Logger 使用的默认格式化程序。
将所有模块的日志级别重置为主要级别。
将给定应用程序中的所有模块的日志级别重置为主要级别。
将给定模块的日志级别重置为主要级别。
将当前进程的日志级别重置为主要级别。
禁用当前进程的日志记录。
启用当前进程的日志记录。
返回给定进程的日志记录是否已启用。
记录一条错误消息。
刷新日志记录器。
获取给定模块的日志级别。
获取当前进程的日志级别。
记录一条信息消息。
返回所有可用的级别。
使用给定的 level
记录消息。
读取当前进程的元数据。
根据给定的关键字列表更改当前进程的元数据。
记录一条通知消息。
为给定应用程序中的模块设置日志级别。
为给定模块设置日志级别。
为当前进程设置日志级别。
移除一个后端。
移除一个翻译器。
将当前进程的元数据重置为给定的关键字列表。
记录一条警告消息。
类型
@type level() ::
:emergency
| :alert
| :critical
| :error
| :warning
| :warn
| :notice
| :info
| :debug
@type message() :: :unicode.chardata() | String.Chars.t() | report()
@type metadata() :: keyword()
函数
添加一个新的后端。
添加在给定 app
的 :logger
应用程序参数中配置的处理程序。
这用于在日志系统中注册新的处理程序。有关更多信息,请参阅模块文档。
添加一个新的翻译器。
记录一条警报消息。
返回 :ok
。
示例
记录消息(字符串或 I/O 数据)
Logger.alert("this is an alert message")
报告消息(映射或关键字)
# as keyword list
Logger.alert([something: :reported, this: :alert])
# as map
Logger.alert(%{this: :alert, something: :reported})
带有元数据的报告消息(映射或关键字)
# as a keyword list
Logger.alert("this is an alert message", [user_id: 42, request_id: "xU32kFa"])
# as map
Logger.alert("this is an alert message", %{user_id: 42, request_id: "xU32kFa"})
动态记录消息。
与 log/3
、debug/2
、info/2
等函数相反,传递给 bare_log/3
的参数总是会被评估。但是,您可以将匿名函数传递给 bare_log/3
,这些函数只有在需要记录内容时才会被评估。
比较日志级别。
接收两个日志级别,并比较 left
级别与 right
级别,并返回
:lt
如果left
小于right
:eq
如果left
和right
相等:gt
如果left
大于right
示例
iex> Logger.compare_levels(:debug, :warning)
:lt
iex> Logger.compare_levels(:error, :info)
:gt
@spec configure(keyword()) :: :ok
配置日志记录器。
请查看 Logger
模块文档中的“运行时配置”部分,了解可用选项。在此处所做的更改将自动持久化到 :logger
应用程序环境。
配置给定的后端。
记录一条严重错误消息。
返回 :ok
。
示例
记录消息(字符串或 I/O 数据)
Logger.critical("this is a critical message")
报告消息(映射或关键字)
# as keyword list
Logger.critical([something: :reported, this: :critical])
# as map
Logger.critical(%{this: :critical, something: :reported})
带有元数据的报告消息(映射或关键字)
# as a keyword list
Logger.critical("this is a critical message", [user_id: 42, request_id: "xU32kFa"])
# as map
Logger.critical("this is a critical message", %{user_id: 42, request_id: "xU32kFa"})
记录一条调试消息。
返回 :ok
。
示例
记录消息(字符串或 I/O 数据)
Logger.debug("this is a debug message")
报告消息(映射或关键字)
# as keyword list
Logger.debug([something: :reported, this: :debug])
# as map
Logger.debug(%{this: :debug, something: :reported})
带有元数据的报告消息(映射或关键字)
# as a keyword list
Logger.debug("this is a debug message", [user_id: 42, request_id: "xU32kFa"])
# as map
Logger.debug("this is a debug message", %{user_id: 42, request_id: "xU32kFa"})
@spec default_formatter(keyword()) :: {module(), :logger.formatter_config()}
返回 Logger 使用的默认格式化程序。
它返回基于 :default_formatter
配置构建的 Logger.Formatter
。
config :logger, :default_formatter,
format: "\n$time $metadata[$level] $message\n",
metadata: [:user_id]
如果是列表,则可以提供一组 overrides
来合并到列表中。有关所有选项,请参阅 Logger.Formatter.new/1
。
示例
Logger
会在启动时自动将默认格式化程序加载到默认处理程序中。但是,如果您希望以编程方式替换处理程序格式化程序,则可以使用此函数。例如,在测试中,您可能希望更改格式化程序设置。
setup tags do
formatter = Logger.default_formatter(colors: [enabled: false])
:logger.update_handler_config(:default, :formatter, formatter)
on_exit(fn ->
:logger.update_handler_config(:default, :formatter, Logger.default_formatter())
end)
end
但是,请注意,您不应在 config
文件中调用此函数,因为此函数期望 Logger
已经配置并启动。要使用此格式化程序启动一个全新的处理程序,请使用 Logger.Formatter.new/1
。
@spec delete_all_module_levels() :: :ok
将所有模块的日志级别重置为主要级别。
@spec delete_application_level(application) :: :ok | {:error, {:not_loaded, application}} when application: atom()
将给定应用程序中的所有模块的日志级别重置为主要级别。
等同于
appname |> Application.spec(:modules) |> Logger.delete_module_level()
将给定模块的日志级别重置为主要级别。
@spec delete_process_level(pid()) :: :ok
将当前进程的日志级别重置为主要级别。
目前唯一接受的 PID 是 self()
。
@spec disable(pid()) :: :ok
禁用当前进程的日志记录。
目前唯一接受的 PID 是 self()
。
等同于
put_process_level(pid, :none)
记录一条紧急消息。
返回 :ok
。
示例
记录消息(字符串或 I/O 数据)
Logger.emergency("this is an emergency message")
报告消息(映射或关键字)
# as keyword list
Logger.emergency([something: :reported, this: :emergency])
# as map
Logger.emergency(%{this: :emergency, something: :reported})
带有元数据的报告消息(映射或关键字)
# as a keyword list
Logger.emergency("this is an emergency message", [user_id: 42, request_id: "xU32kFa"])
# as map
Logger.emergency("this is an emergency message", %{user_id: 42, request_id: "xU32kFa"})
@spec enable(pid()) :: :ok
启用当前进程的日志记录。
目前唯一接受的 PID 是 self()
。
等同于
delete_process_level(pid)
返回给定进程的日志记录是否已启用。
目前唯一接受的 PID 是 self()
。
记录一条错误消息。
返回 :ok
。
示例
记录消息(字符串或 I/O 数据)
Logger.error("this is an error message")
报告消息(映射或关键字)
# as keyword list
Logger.error([something: :reported, this: :error])
# as map
Logger.error(%{this: :error, something: :reported})
带有元数据的报告消息(映射或关键字)
# as a keyword list
Logger.error("this is an error message", [user_id: 42, request_id: "xU32kFa"])
# as map
Logger.error("this is an error message", %{user_id: 42, request_id: "xU32kFa"})
@spec flush() :: :ok
刷新日志记录器。
这保证所有日志处理程序都将被刷新。这在测试中很有用,不应在生产代码中调用。
获取给定模块的日志级别。
返回值将是使用的有效值。如果未为给定模块设置任何值,则它将不会出现在返回的列表中。
获取当前进程的日志级别。
目前唯一接受的 PID 是 self()
。
返回值将是使用的有效值。如果未为给定进程设置任何值,则返回 nil
。
记录一条信息消息。
返回 :ok
。
示例
记录消息(字符串或 I/O 数据)
Logger.info("this is an info message")
报告消息(映射或关键字)
# as keyword list
Logger.info([something: :reported, this: :info])
# as map
Logger.info(%{this: :info, something: :reported})
带有元数据的报告消息(映射或关键字)
# as a keyword list
Logger.info("this is an info message", [user_id: 42, request_id: "xU32kFa"])
# as map
Logger.info("this is an info message", %{user_id: 42, request_id: "xU32kFa"})
@spec level() :: level()
检索 Logger
级别。
可以通过 configure/1
更改 Logger
级别。
@spec levels() :: [level(), ...]
返回所有可用的级别。
使用给定的 level
记录消息。
返回 :ok
。
宏 debug/2
、info/2
、notice/2
、warning/2
、error/2
、critical/2
、alert/2
和 emergency/2
比此宏更受欢迎,因为如果需要,它们可以自动在编译时完全消除对 Logger
的调用(请参阅 Logger
模块的文档)。
@spec metadata() :: metadata()
读取当前进程的元数据。
这不会返回“全局”日志记录元数据(通过 :logger
应用程序配置中的 :metadata
键设置),而只返回进程元数据。
@spec metadata(metadata()) :: :ok
根据给定的关键字列表更改当前进程的元数据。
此函数会将给定的关键字列表合并到现有元数据中,除了将键设置为 nil
,这会从元数据中删除该键。
请注意,并非所有键都可以设置为元数据。日志记录器自动添加的元数据(如模块文档中所述)将始终覆盖自定义元数据。
记录一条通知消息。
返回 :ok
。
示例
记录消息(字符串或 I/O 数据)
Logger.notice("this is a notice message")
报告消息(映射或关键字)
# as keyword list
Logger.notice([something: :reported, this: :notice])
# as map
Logger.notice(%{this: :notice, something: :reported})
带有元数据的报告消息(映射或关键字)
# as a keyword list
Logger.notice("this is a notice message", [user_id: 42, request_id: "xU32kFa"])
# as map
Logger.notice("this is a notice message", %{user_id: 42, request_id: "xU32kFa"})
为给定应用程序中的模块设置日志级别。
这将优先于设置的主级别,因此可用于提高或降低项目某些部分的详细程度。
等同于
appname |> Application.spec(:modules) |> Logger.put_module_level(level)
为给定模块设置日志级别。
这将优先于设置的主级别,因此可用于提高或降低项目某些部分的详细程度。
示例
defmodule Foo do
require Logger
def log, do: Logger.debug("foo")
end
Logger.configure(level: :error)
Logger.put_module_level(Foo, :all)
Foo.log()
# This will print the message even if global level is :error
为当前进程设置日志级别。
目前唯一接受的 PID 是 self()
。
这将优先于设置的主级别,因此可用于提高或降低正在运行的系统某些部分的详细程度。
移除一个后端。
移除一个翻译器。
@spec reset_metadata(metadata()) :: :ok
将当前进程的元数据重置为给定的关键字列表。
记录一条警告消息。
返回 :ok
。
示例
记录消息(字符串或 I/O 数据)
Logger.warning("this is a warning message")
报告消息(映射或关键字)
# as keyword list
Logger.warning([something: :reported, this: :warning])
# as map
Logger.warning(%{this: :warning, something: :reported})
带有元数据的报告消息(映射或关键字)
# as a keyword list
Logger.warning("this is a warning message", [user_id: 42, request_id: "xU32kFa"])
# as map
Logger.warning("this is a warning message", %{user_id: 42, request_id: "xU32kFa"})