SkyWalking Agent端日誌插件的編寫歷程與使用說明

概述

前一段時間順利完成了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

含義:指定需要轉存的日誌信息的包名,默認匹配所有包。

取值:

  • 包名:例如org.apache。若有多個包名中間需用逗號隔開
  • *:匹配所有包級別的日誌信息。

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插件的使用方法,希望能夠給感興趣的同學以幫助。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章