简介
Lager的trace功能十分强大,在生产环境中,我们需要使用日志来配合定位问题,而如果只是单纯的使用日志等级来控制,在日志量非常大的时候可能会有性能负担,同时日志文件也难以查找,使用trace可以清晰的筛选我们想要的日志,且不影响其他日志逻辑
日志重定向
Lager支持简单的根据日志的属性来将日志内容进行重定向,每条Lager生成的日志都带了pid, module, function ,line ,除此之外,也可以在生成日志的时候进行扩展
lager:warning([{request, RequestID},{vhost, Vhost}], "Permission denied to ~s", [User])
第一个字段填入tupleList可以给日志消息增加额外属性,可供后续Trace的时候进行筛选
如果使用pid进行筛选的时候,pid字段使用字符串进行表述
lager:trace_console([{pid, "<0.410.0>"}])
加入属性后就可以根据属性进行筛选跟踪
lager:trace_file("logs/example.com.error", [{vhost, "example.com"}], error)
- Trace调用的三参,也就是日志等级,是可选参数,如果没有填充,默认为debug等级
- Trace本身和原生的日志逻辑没有直接关系,你可以在日志等级是error的情况下,trace一条debug的日志,第一个参数是日志重定向的文件,第二个参数是筛选消息的条件,第三个为日志等级
- Trace消息并不会影响原本的日志打印,不会因为Trace少掉日志消息或者增加了日志
除了通过调用双参日志接口给日志增加属性外,还可以在进程内调用lager:md/1存储属性,所有该进程生成的日志都会携带md设定的属性,这在你有多个工作进程的时候会非常有用,日志进行运行的代码相同,但是你可以通过设定工作进程的属性将用户id写到日志属性中,之后就可以使用用户id对日志进行重定向,这在生产环境下将会非常有用
lager:md/1每次调用都会覆盖之前的内容,并且不提供置空函数,因此最好不要让属性太过复杂,或者自己在进程逻辑中进行管理,md的参数是键值是atom的TupleList
lager:md([{zone, forbidden}])
同样可以在命令行中进行日志跟踪
lager:trace_console([{request, 117}])
如果需要限定日志等级,可以加入二参表示要跟踪的日志等级
要注意的是debug这种日志等级表示的是debug和debug以上的日志,如果只需要指定等级的日志,根据下面的日志等级标识进行选择
| 关键字 | 释义 |
|---|---|
| info | 高于info级别包括info,类似于>= |
| =debug | 只有debug等级的日志 |
| !=info | 所有除了info级别的日志 |
| <=notice | 比notice级别低的所有等级包括notice |
| <warning | 所有比warning级别低的日志,不包括warning |
筛选器可以存在多个,筛选的值可以通过*来表示存在,但可以是任意值,同样可以用!表示不存在
日志也可以重定向到原本的日志文件中,但是在使用Slink的情况需要特别注意,后面章节会讲到
可以使用lager:status()查看当前的跟踪器,使用lager:clear_all_traces()删除所有的跟踪器
如果要对单个的跟踪器进行操作,则需要在创建的时候对跟踪器进行保存
{ok, Trace} = lager:trace_file("log/error.log", [{module, mymodule}]),
...
lager:stop_trace(Trace)
过滤器
从3.3.1开始,第二个元素可以是比较运算符然后扩展为3元组。当前支持的比较运算符为:
| 运算符 | 释义 |
|---|---|
| < | 小于 |
| =< | 小于等于 |
| = | 等于 |
| != | 不等于 |
| > | 大于 |
| >= | 大于等于 |
lager:trace_console([{ request,'>',117 },{ request,'<',120 }])
使用=等效于2元组形式。
过滤器组合
从3.3.1开始,过滤器也可以通过and or 进行复杂组合,例如上面的例子可以改写成
lager:trace_console([{all, [{request, '>', 117}, {request, '<', 120}]}])
null过滤器
null字符作为筛选器的键值有特殊的意义,{null, false}表示任何消息都不会被跟踪,反之,’{null, true}'表示所有的消息都会被跟踪,如果你的生产环境中,有进程不想被trace追踪则可以使用md设定null属性,之后的trace就会屏蔽该进程
多接受器
如果启用了slink,那么跟踪的时候要注意要加入slink的限定筛选
lager:trace_file("log/security.log", [{sink, audit_event}, {function, myfunction}], warning)
{sink, audit_event},表示现在trace的是,audit:info这种生成的日志
如果没有指定,那么使用Lager默认的slink
这会有两个影响点:
- 其他slink的消息就无法被trace
- 如果日志文件要重定向到已经打开的日志,那么加了slink限定的,只能重定向到自己的日志文件中
前者可以通过设定多个跟踪器来规避,后者将来可以通过配置控制来解决,但是当前版本还不支持
Trace配置
Lager支持从其配置文件启动跟踪。定义它们的关键字是traces,后跟一个元组属性列表,该元组定义了一个处理程序,并且在所需列表中有零个或多个过滤器,最后是日志等级。
一个示例如下所示:
{lager, [
{handlers, [...]},
{traces, [
%% handler, filter, message level (defaults to debug if not given)
{lager_console_backend, [{module, foo}], info },
{{lager_file_backend, "trace.log"}, [{request, '>', 120}], error},
{{lager_file_backend, "event.log"}, [{module, bar}] } %% implied debug level here
]}
]}.
在此示例中,我们具有三个跟踪器。一种使用控制台,另外两种使用文件。如果忽略了日志等级参数,则默认debug
traces关键字同样适用于slink
三元组的配置必须是3.3.1之后,而二元组格式在3.2.0才被添加
在编译时设置动态元数据
Lager支持通过注册回调函数从外部源提供元数据。即使该进程死亡,该元数据在整个进程中也是持久的
一般情况下不需要这个特性,但是在一下情况会非常有用:
- seq_trace提供的跟踪信息
- 有关Application的上下文信息
- 静态值无法准确描述正确的值
- 在每次记录调用之前必须设置元数据的情况
可以通过配置项{lager_parse_transform_functions, X}进行设定,但是前提必须启用parse_transform,对于rebar,可以直接添加到erl_opts中
{erl_opts, [{parse_transform, lager_transform},
{lager_function_transforms,
[
%% Placeholder Resolve type Callback tuple
{metadata_placeholder, on_emit, {module_name, function_name}},
{other_metadata_placeholder, on_log, {module_name, function_name}}
]}]}.
第一个字段是将来formatter中要使用的atom字段
第二个字段是解析类型。此参数指定要在发出消息时或在记录调用回调的方式。只能是on_emit或on_log。没有固定的说法应该使用哪种,因此请阅读每种的用法/注意事项,然后选择最适合要求的选项。
on_emit:
- 直到消息被backend处理,回调函数才会被调用。
- 如果回调函数无法解析,未加载或产生未处理的错误,则将返回undefined。
- 由于回调函数依赖于进程,因此有可能依赖进程死亡导致返回undefined
on_log:
- 无论是否发出消息,都将解析回调函数
- lager不会处理函数返回异常的情况
- 回调中的任何潜在错误都应在回调函数本身中处理
- 由于该函数在日志记录时已解析,因此在写日志前,依赖进程死机的可能性应该较小,尤其是对包含回调的应用程序进行日志记录时
第三个参数是具体的回调参数,回调参数要符合以下条件:
- 函数必须被导出
- 函数必须是0参
- 函数只能返回iolist,atom或者undefined中的一种
- 如果回调函数可能发生异常,那么参照上面的文档进行处理
如果回调返回,undefined则它将遵循“ 自定义格式”部分中记录的相同的运算符规则 。意味着可以使用其他默认值替代undefined的情况
下面的例子是一个典型的应该使用on_emit的场景,这时候不应该使用on_log:
-export([my_callback/0]).
my_callback() ->
my_app_serv:call('some options').
下面的例子on_emit on_log都可以使用:
-export([my_callback/0]).
my_callback() ->
try my_app_serv:call('some options') of
Result ->
Result
catch
_ ->
%% You could define any traditional iolist elements you wanted here
undefined
end.
使用的回调函数不一定是项目代码中的一部分,你可以调用任何可执行的函数,例如:
-export([reductions/0]).
reductions() ->
proplists:get_value(reductions, erlang:process_info(self())).
-export([seq_trace/0]).
seq_trace() ->
case seq_trace:get_token(label) of
{label, TraceLabel} ->
TraceLabel;
_ ->
undefined
end.
重要,无论是on_emit还是on_log都意味着在生成日志的时候会进行回调操作,意味着日志记录会被回调操作拖累性能,因此不要为了图方便就在日志中随意使用,需要考虑性能拖累
常用使用范例
- 跟踪某个指定module
lager:trace_file("module_log.log", [{module, MODULE}], debug).
lager:trace_console([{module, MODULE}], debug).
- 跟踪指定模块指定函数的日志打印
lager:trace_file("module_log.log", [{module, MODULE},{function, FunName}], debug).
lager:trace_console([{module, MODULE},{function, FunName}], debug).
- 跟踪指定pid的日志
lager:trace_file("pid_log.log", [{pid, PidStr}], debug).
lager:trace_console([{pid, PidStr}], debug).
本文详细介绍了Erlang库Lager的Trace功能,包括日志重定向、过滤器、多接受器、Trace配置等,强调了如何在生产环境中有效利用Trace功能进行问题定位,同时避免对性能造成影响。
1万+

被折叠的 条评论
为什么被折叠?



