SkyWalking Agent 端日志插件的编写历程与使用说明

本贴最后更新于 356 天前,其中的信息可能已经斗转星移

概述

前一段时间顺利完成了 SkyWalking Agent 端 logger-plugin 插件的开发,在此做个总结。一方面给插件的使用方法写一中文说明,另一方面分享一下该插件开发过程中的一些考量以及收获。

logger-plugin 插件,主要作用实现将将程序在调用过程中产生的日志比如错误日志信息,存入到 span log 中。然后可以通过 web 端直接查询,便于开发者排错与分析。同时为了提高使用的灵活性,我们还提供了配置文件,通过配置文件可以对需要存入到 span log 的日志来源(log4j2、logbak、log4j)、包名、日志级别、内容进行控制。但同时需要提醒的一点是,由于该插件直接作用于 agent 端,可能会造成一定程度的性能损失,请谨慎使用。

相关 PR 地址如下:Support collecting logs of log4j, log4j2, and logback in the tracing context,以供参考。

使用方式

基于性能的考量,该插件在设计之初定位为可选插件,因而默认情况下该插件的功能并不会启动。如需使用,需要在 8.4.0 发布之后,将插件从 apache-skywalking-apm-bin/agent/optional-plugins/ 目录下复制到 apache-skywalking-apm-bin/agent/plugins/ 下,方能生效。

配置文件

首先需要说明的一点是,发布时默认是没有配置文件的,插件会按照如下默认配置内容运行:

 log4j.packages=*
 log4j.level=error
 log4j2.packages=*
 log4j2.level=error
 logback.packages=*
 logback.level=error

上述配置文件含义如下:

  1. 该插件默认会对所有支持的日志框架生效,包括 log4j、log4j2、logbback。
  2. 插件之后将高于 error 级别的日志信息存入到 span log 中,而对 trace, debug, info, warn 级别的日志信息不生效。
  3. 默认情况下,会收集所有包级别的日志信息。

.如果需要自定义插件的配置信息,需要在 apache-skywalking-apm-bin/agent/config/logger-plugin/ 目录下创建 logconfig.properties 文件进行配置。

配置文件可配置内容及其含义如下:

packages

含义:指定需要转存的日志信息的包名,默认匹配所有包。

取值:

Level

含义:所需转存的日志信息的级别,默认情况是 error 级别的日志信息

日志级别从小到大排列如下:

trace < debug < info <warn< error < fatal

同时我们在自定义配置信息的时候需要注意,由于 logback 不支持 fatal 级别的日志信息,因而如果错误的进行如下配置:

 logback.level=error

则会造成针对 logback 的配置失效,也即不会收集任何关于 logback 日志插件产生的日志信息,并会产生错误日志,以供用户排查。

设计思路

其实这个插件刚开始插件之初,由于个人工程经验不足,当时考虑想要实现功能很多,比如需要支持正则表达式用以过滤,支持日志的格式化配置等等。但在和其他社区成员的讨论过程中发现,需要功能其实和该插件的定位不同,比如过滤日志信息的功能,日志系统中已经支持了。如果该插件再增加该功能一方面会增加插件使用的复杂程度,另一方面,也会造成更大的性能损耗。而针对另一功能日志的格式化,这与该插件的定位也是不符合的,apm 系统本身就是为了实现追踪和快速定位,对收集到的日志信息希望尽量简练,有效,许多格式化的信息,根本无需收集。因而该功能最终也被废止。

同时针对日志转存到 span 的格式,和社区成员也进行仔细的沟通,为了适应未来 SkyWalking 的日志查询功能,因此在转存日志信息时,存储到 OAP 端的 span log 日志是结构化的类似于下面的形式:

 "logs": [
  {
     "time": 1605225365711,
     "data": [
      {
         "key": "event",
         "value": "warn"
      },
      {
         "key": "log.kind",
         "value": "org.apache.skywalking.oap.server.analyzer.provider.trace.parser.listener.MultiScopesAnalysisListener"
      },
      {
         "key": "message",
         "value": "span {} has illegal status code {}"
      },
      {
         "key": "param.1",
         "value": "*span*"
      },
      {
         "key": "param.2",
         "value": "*tag.getValue()*"
      }
    ]
  }
 ]

便于后续 Web 端实现灵活的查询。关于日志格式详细的讨论过程,可以参考《What format should we have in mind for logging to spans?》

同时在设计的过程中,刚开始找到插入点也是不合理的,比如刚开始选择的直接增强 sl4j.Logger 接口,这回造成一个问题,一旦用户没有使用 Log4j 创建 Logger 对象就会造成日志插件失效,最终在 BFergerson 的帮助下,换成了一个更加合理的插入点。

总结

这篇文章更多的可能是个人在开发 logger-plugin 插件过程中踩过坑的总结吧,在整个开发过程中,其实收获蛮多的,比如并非一个软件功能越多越好。这个需要从该软件本身的定位出发,兼顾性能和易用性,做到一种平衡。同时简单写了写 logger-plugin 插件的使用方法,希望能够给感兴趣的同学以帮助。

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...