Lager库的Trace功能使用

本文详细介绍了Erlang库Lager的Trace功能,包括日志重定向、过滤器、多接受器、Trace配置等,强调了如何在生产环境中有效利用Trace功能进行问题定位,同时避免对性能造成影响。

简介

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_emiton_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都意味着在生成日志的时候会进行回调操作,意味着日志记录会被回调操作拖累性能,因此不要为了图方便就在日志中随意使用,需要考虑性能拖累

常用使用范例

  1. 跟踪某个指定module
   lager:trace_file("module_log.log", [{module, MODULE}], debug).
   lager:trace_console([{module, MODULE}], debug).
  1. 跟踪指定模块指定函数的日志打印
	lager:trace_file("module_log.log", [{module, MODULE},{function, FunName}], debug).
	lager:trace_console([{module, MODULE},{function, FunName}], debug).
  1. 跟踪指定pid的日志
	lager:trace_file("pid_log.log", [{pid, PidStr}], debug).
	lager:trace_console([{pid, PidStr}], debug).
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值