日志记录
该 日志记录模块提供了一种将计算的历史和进度记录为事件日志的方法。 事件是通过在源代码中插入日志记录语句来创建的,例如:
@warn "Abandon printf debugging, all ye who enter here!"
┌ Warning: Abandon printf debugging, all ye who enter here!
└ @ Main REPL[1]:1
与调用源代码相比,该系统提供了几个优点 打印(). 首先,它允许您控制消息的可见性和呈现,而无需编辑源代码。 例如,与 @警告 以上
@debug "The sum of some values $(sum(rand(100)))"
默认情况下不会产生输出。 此外,在源代码中留下这样的调试语句是非常便宜的,因为系统避免评估消息,如果它稍后会被忽略。 在这种情况下 总和(兰特(100)) 除非启用调试日志记录,否则永远不会执行关联的字符串处理。
其次,日志记录工具允许您将任意数据作为一组键值对附加到每个事件。 这允许您捕获局部变量和其他程序状态以供以后分析。 例如,要附加局部数组变量 A 和一个向量的和 v 作为关键 s 您可以使用
A = ones(Int, 4, 4)
v = ones(100)
@info "Some variables" A s=sum(v)
# output
┌ Info: Some variables
│ A =
│ 4×4 Matrix{Int64}:
│ 1 1 1 1
│ 1 1 1 1
│ 1 1 1 1
│ 1 1 1 1
└ s = 100.0
所有日志宏 @调试, @资讯, @警告 和 @错误 共享更通用宏的文档中详细描述的常见功能 @logmsg.
日志事件结构
每个事件都会生成几条数据,一些由用户提供,一些自动提取。 让我们先检查用户定义的数据:
*_log level_是用于早期过滤的消息的广泛类别。 类型有几个标准级别 日志级别;用户定义的级别也是可能的。 每个都有不同的目的:
** 日志记录。调试/调试(日志级别-1000)是面向程序开发人员的信息。 默认情况下禁用这些事件。
** Logging.Info(日志级别0)用于向用户提供一般信息。 把它看作是使用的替代方案 打印,打印 直接。
** 日志记录。警告(日志级别1000)意味着某些事情是错误的,可能需要采取行动,但目前该程序仍在工作。
** 日志记录。错误(日志级别2000)意味着某些事情是错误的,它不太可能被恢复,至少通过这部分代码。 通常这种日志级别是不必要的,因为抛出异常可以传达所有必需的信息。
*_message_是描述事件的对象。 按惯例 抽象字符串s作为消息传递被假定为标记格式. 其他类型将显示使用 打印(io,obj) 或 字符串(obj) 对于基于文本的输出和可能 显示(io,mime,obj) 于安装的记录器中使用的其他多媒体显示器。
*可选_key—value pairs_允许将任意数据附加到每个事件。 某些键具有常规含义,可以影响事件的解释方式(请参阅 @logmsg).
系统还为每个事件生成一些标准信息:
* 模块 在其中扩展了日志记录宏。
* 档案 和 行 日志记录宏发生在源代码中的位置。
*留言 身份证 这是出现日志记录宏的_source code statement_的唯一固定标识符。 只要日志记录语句本身保持不变,即使文件的源代码发生更改,此标识符也被设计为相当稳定。
*一个 团体 对于事件,该事件默认设置为文件的基本名称,没有扩展名。 这可用于将消息分组为比日志级别更精细的类别(例如,所有弃用警告都具有组 :德瓦恩),或跨模块或模块内的逻辑分组。
请注意,默认情况下不包括一些有用的信息,如事件时间。 这是因为提取此类信息的成本可能很高,并且对于当前记录器也是_dynamically_可用的。 定义一个 自定义记录器根据需要增加事件数据的时间,回溯,全局变量的值和其他有用的信息。
处理日志事件
正如您在示例中看到的,logging语句没有提及日志事件的去向或处理方式。 这是一个关键的设计特性,使系统可组合和自然的并发使用。 它通过分离两个不同的关注点来实现这一点:
*_creating_日志事件是模块作者的关注点,他们需要决定事件在哪里触发以及包含哪些信息。 *日志事件的_Processing_-即显示,过滤,聚合和记录-是应用程序作者需要将多个模块组合到一个协作应用程序中的关注点。
伐木工
事件的处理由_logger_执行,它是查看事件的第一段用户可配置代码。 所有记录器必须是 [医抽象日志].
触发事件时,通过查找具有全局记录器作为后备的任务本地记录器来找到适当的记录器。 这里的想法是,应用程序代码知道日志事件应该如何处理,并且存在于调用堆栈顶部的某个地方。 因此,我们应该通过调用堆栈查找以发现记录器-也就是说,记录器应该是_dynamically scoped_。 (这与日志框架形成了鲜明的对比,其中记录器是_lexic scoped_;由模块作者显式提供或作为简单的全局变量提供。 在这样的系统中,在从多个模块组成功能的同时控制日志记录是很尴尬的。)
全局记录器可以设置为 global_logger,以及使用 with_logger. 新生成的任务继承父任务的记录器。
库提供了三种记录器类型。 [医安慰者]是启动REPL时看到的默认记录器。 它以可读的文本格式显示事件,并尝试对格式和过滤进行简单但用户友好的控制。 NullLogger碌录潞陆是在必要时删除所有消息的方便方法;它是日志记录的等价物 德夫努尔流。 [医简易记录器]是一个非常简单的文本格式化记录器,主要用于调试日志系统本身。
自定义记录器应该带有重载,用于 参考部分。
早期过滤和消息处理
当事件发生时,会进行几个步骤的早期过滤,以避免生成将被丢弃的消息:
-
消息日志级别根据全局最低级别进行检查(通过
禁用/禁用). 这是一个粗糙但非常便宜的全球环境。 -
将查找当前记录器状态,并根据记录器缓存的最低级别检查消息级别,如通过调用
日志记录。min_enabled_级别. 可以通过环境变量复盖此行为(稍后会详细介绍)。 -
该
日志记录。应该博客函数与当前记录器一起调用,获取一些可以静态计算的最小信息(级别,模块,组,id)。 最有用的是,应该博客传递一个事件身份证它可用于基于缓存谓词早期丢弃事件。
如果所有这些检查都通过,消息和键值对将被完整评估,并通过 日志记录。处理/处理功能。 handle_message() 可以根据需要执行额外的过滤,并将事件显示到屏幕上,将其保存到文件等。
默认情况下,会捕获并记录生成日志事件时发生的异常。 这可以防止单个损坏的事件使应用程序崩溃,这在生产系统中启用少量使用的调试事件时很有帮助。 此行为可以通过扩展对每个记录器类型进行自定义 日志记录。catch_exceptions.
测试日志事件
日志事件是运行正常代码的副作用,但您可能会发现自己想要测试特定的信息性消息和警告。 该 测试 模块提供了一个 @test_logs可用于对日志事件流进行模式匹配的宏。
环境变量
消息过滤可以通过影响 朱莉亚*德布格环境变量,并用作为文件或模块启用调试日志记录的简单方法。 加载朱莉娅与 JULIA_DEBUG=加载 将激活 @调试 登录消息 装货。jl. 例如,在Linux shell中:
$JULIA_DEBUG=使用OhMyREPL加载julia-e' ◦调试:拒绝缓存文件/home/user/。julia/编译/v0.7/OhMyREPL。ji由于它包含不兼容的缓存标头 └@基本加载。jl:1328 [信息:重新编译陈旧的缓存文件/home/user/。julia/编译/v0.7/OhMyREPL。ji用于模块OhMyREPL ◦调试:拒绝缓存文件/home/user/。julia/compiled/v0.7/Tokenize。ji由于它包含不兼容的缓存标头 └@基本加载。jl:1328 ...
在windows上,同样可以在 CMD公司 通过第一次运行 设置JULIA_DEBUG="加载" 而在 Powershell的 经 $env:JULIA_DEBUG="加载".
同样,环境变量可用于启用模块的调试日志记录,例如 Pkg,Pkg,或模块根(见 基地。模块化). 要启用所有调试日志记录,请使用特殊值 全部.
要从REPL打开调试日志记录,请设置 ENV["JULIA_DEBUG"] 到感兴趣的模块的名称。 REPL中定义的函数属于module 主要;他们的日志记录可以这样启用:
julia> foo() = @debug "foo"
foo (generic function with 1 method)
julia> foo()
julia> ENV["JULIA_DEBUG"] = Main
Main
julia> foo()
┌ Debug: foo
└ @ Main REPL[1]:1
使用逗号分隔符为多个模块启用调试: JULIA_DEBUG=加载,主.
例子:
示例:将日志事件写入文件
有时将日志事件写入文件会很有用。 下面是如何使用任务本地和全局记录器将信息写入文本文件的示例:
# Load the logging module
julia> using Logging
# Open a textfile for writing
julia> io = open("log.txt", "w+")
IOStream(<file log.txt>)
#创建一个简单的记录器
julia>记录器=SimpleLogger(io)
SimpleLogger(IOStream(<文件日志。txt>),Info,Dict{Any,Int64}())
#记录特定于任务的消息
julia>with_logger(记录器)do
@info("特定于上下文的日志消息")
结束
#将所有缓冲的消息写入文件
朱莉娅>冲水(io)
#设置全局记录器为logger
julia>global_logger(记录器)
SimpleLogger(IOStream(<文件日志。txt>),Info,Dict{Any,Int64}())
#此消息现在也将写入文件
julia>@info("全局日志消息")
#关闭文件
朱莉娅>关闭(io)
示例:启用调试级消息
下面是一个创建一个 [医安慰者]它允许通过日志级别高于或等于的任何消息 日志记录。调试/调试.
julia> using Logging
# Create a ConsoleLogger that prints any log messages with level >= Debug to stderr
julia> debuglogger = ConsoleLogger(stderr, Logging.Debug)
# Enable debuglogger for a task
julia> with_logger(debuglogger) do
@debug "a context specific log message"
end
# Set the global logger
julia> global_logger(debuglogger)
参考资料
日志模块
# *`日志记录。日志记录`*-模式_
用于捕获,过滤和呈现日志事件流的实用程序。 通常你不需要导入 日志记录 创建日志事件;为此,标准的日志宏,如 @资讯 已由 基地 默认情况下可用。
创建事件
# *`日志记录。@logmsg`*-马科罗_
@debug message [key=value | value ...]
@info message [key=value | value ...]
@warn message [key=value | value ...]
@error message [key=value | value ...]
@logmsg level message [key=value | value ...]
创建具有信息性的日志记录 信息. 为方便起见,四个日志宏 @调试, @资讯, @警告 和 @错误 定义标准严重性级别的日志 调试/调试, 资料, 警告 和 错误. @logmsg 允许 水平 以编程方式设置为任何 日志级别 或自定义日志级别类型。
信息 应该是一个表达式,它计算为一个字符串,该字符串是日志事件的人类可读描述。 按照惯例,此字符串在呈现时将格式化为markdown。
的可选列表 键=值 pairs支持任意用户定义的元数据,这些元数据将作为日志记录的一部分传递到日志记录后端。 如果只有一个 价值 表达式被提供,一个表示表达式的键将使用 符号. 例如, x 成为 x=x,而 食物(10) 成为 符号("foo(10)")=foo(10). 要拆分键值对列表,请使用正常的拆分语法, @info"blah"kws。...
有一些键允许自动生成的日志数据被复盖:
* _module=mod 可用于从消息的源位置指定不同的始发模块。
* _group=符号 可用于复盖消息组(这通常从源文件的基本名称派生)。
* _id=符号 可用于复盖自动生成的唯一消息标识符。 如果您需要非常紧密地关联在不同源行上生成的消息,这将非常有用。
* _file=字符串 和 _line=整数 可用于复盖日志消息的明显源位置。
还有一些具有常规含义的键值对:
* maxlog=整数 应作为提示后端消息应显示不超过 最大博客 时代。
* 异常=ex 应用于传输带有日志消息的异常,通常与 @错误. 一个关联的回溯 英国电信 可以使用元组附加 异常=(ex,bt).
*例子*
@debug "Verbose debugging information. Invisible by default"
@info "An informational message"
@warn "Something was odd. You should pay attention"
@error "A non fatal error occurred"
x=10
@info"附加到消息的一些变量"x a=42.0
@调试开始
sA=总和(A)
"sum(A)=$sA是一个昂贵的操作,只有在 `应该博客` 返回true"
结束
对于i=1:10000
@info"使用默认后端,您只会看到(i=$i)十倍"maxlog=10
@调试"算法1"我进步=i/10000
结束
# *`日志记录。日志级别`*-类型
LogLevel(level)
日志记录的严重性/详细程度。
日志级别提供了一个键,在完成构造日志记录数据结构本身的任何其他工作之前,可以根据该键筛选潜在的日志记录。
*例子*
julia> Logging.LogLevel(0) == Logging.Info
true
使用AbstractLogger处理事件
事件处理由与……相关联的重写函数控制。 [医]抽象日志:
| 实现方法 | 简介 | |
|---|---|---|
处理日志事件 |
||
事件的早期过滤 |
||
接受事件的日志级别下限 |
||
*可选方法* |
*默认定义* |
*简介* |
|
在事件评估期间捕获异常 |
# *`日志记录。[医]抽象日志`*-类型
记录器控制如何过滤和调度日志记录。 生成日志记录时,记录器是第一段用户可配置代码,用于检查记录并决定如何处理它。
# *`日志记录。应该博客`*-函数
shouldlog(logger, level, _module, group, id)
回来吧 真的 何时 伐木工 接受邮件 水平,为 [医]模式, 团体 并具有唯一的日志标识符 身份证.
# *`日志记录。min_enabled_级别`*-函数
min_enabled_level(logger)
返回最低启用级别 伐木工 于早期过滤。 也就是说,过滤所有消息的日志级别低于或等于日志级别。
# *`日志记录。catch_exceptions`*-函数
catch_exceptions(logger)
回来吧 真的 如果记录器应该捕获日志记录构造期间发生的异常。 默认情况下,会捕获消息。
默认情况下,捕获所有异常以防止日志消息生成崩溃程序。 这使用户可以放心地在生产系统中切换少量使用的功能,例如调试日志记录。
如果您想使用日志记录作为审计跟踪,则应为您的记录器类型禁用此功能。
# *`日志记录。禁用/禁用`*-函数
disable_logging(level)
禁用日志级别等于或小于的所有日志消息 水平. 这是一个_global_设置,旨在使调试日志记录在禁用时非常便宜。 请注意,这不能用于启用其他机制当前禁用的日志记录。
*例子*
Logging.disable_logging(Logging.Info) # Disable debug and info
使用记录器
记录器安装和检查:
# *`日志记录。global_logger`*-函数
global_logger()
返回全局记录器,用于在当前任务不存在特定记录器时接收消息。
global_logger(logger)
将全局记录器设置为 伐木工,并返回之前的全局记录器。
# *`日志记录。with_logger`*-函数
with_logger(function, logger)
执行 功能,将所有日志消息定向到 伐木工.
*例子*
function test(x)
@info "x = $x"
end
with_logger(logger) do
test(1)
test([1,2])
end
随系统提供的记录器:
# *`基地。CoreLogging。[医]安慰者`*-类型
ConsoleLogger([stream,] min_level=Info; meta_formatter=default_metafmt,
show_limited=true, right_justify=0)
在文本控制台中为可读性优化了格式的记录器,例如与Julia REPL的交互工作。
日志级别小于 最小级别 被过滤掉。
此记录器是线程安全的,具有用于消息限制的两个编排的锁,即 最大博客,并写入流。
可以通过设置关键字参数来控制消息格式:
* 元格式,元格式 是一个接受日志事件元数据的函数 (级别,_module,组,id,文件,行) 并返回日志消息的颜色(如传递给printstyled),前缀和后缀。 默认值是使用日志级别作为前缀,并使用包含模块、文件和行位置的后缀。
* show_limited 将大型数据结构的打印限制在可以适合屏幕的东西上,通过设置 :限制 IOContext 格式化期间的密钥。
* 正确的,正确的 是日志元数据正确对齐的整数列。 默认值为零(元数据在自己的行上)。
# *`日志记录。[医]简易记录器`*-类型
SimpleLogger([stream,] min_level=Info)
用于记录级别大于或等于的所有消息的简单记录器 最小级别 到 溪涧. 如果流被关闭,那么日志级别大于或等于 警告 将被记录到 斯德尔 及以下至 标准输出.
这个记录器是线程安全的,在消息限制的编排周围有一个锁。 最大博客,并写入流。