日誌規範化落地方案

日誌規範化落地方案

問題背景

程序日誌規範化的必要性,相信不少人早就意識到了,可是接下來立刻就會面臨如何快速簡單的落地日誌規範的問題。本文主要介紹一下咱們是如何解決這個問題的java

規範

固然首先須要一個日誌規範,咱們收集了最經常使用的字段,最後定的規範以下表:git

序號 字段名 類型 說明 Elasticsearch存儲
1 LogAt DateTime 日誌時間 iso8601
2 TraceId string 跟蹤Id 不分詞
3 Department string 部門 不分詞,統一小寫
4 Team string 團隊 不分詞,統一小寫
5 Project string 項目名稱 不分詞,統一小寫
6 Host string 域名 不分詞,統一小寫
7 ServerIP string 服務器IP 不分詞,統一小寫
8 ContextPath string 虛擬目錄 不分詞,統一小寫
9 UriStem string url主幹 不分詞,統一小寫
10 QueryString string GET參數 分詞
11 FormString string POST參數 分詞
12 UserAgent string UserAgent 分詞
13 Level string 日誌級別 不分詞,統一大寫
14 Class string 記錄日誌所在類名 不分詞
15 Method string 記錄日誌所在方法名 不分詞
16 MethodParams string 拋出自定義異常方法參數 分詞
17 Line int 行號 整數
18 Logger string 日誌名 不分詞
19 IOType string 自定義異常io類型 不分詞
20 ExceptionType string 異常類型 不分詞
21 ExceptionMessage string 異常信息 分詞
22 CustomMessage string 自定義信息 分詞
23 StackTrace string 堆棧信息 分詞
24 HawkKey string Key 不分詞

這個日誌格式設計具備以下特色:github

  • 異常消息(ExceptionMessage)和自定義消息(CustomMessage)分離
  • 異常類型(ExceptionType),異常堆棧(StackTrace),異常消息(ExceptionMessage)相互分離
  • 擴展了Web相關字段。域名(Host), url主幹(UriStem)等

這些設計都是爲了後面方便日誌搜索和監控報警。web

另外咱們發現不少同事打日誌懼怕異常信息和堆棧丟失,喜歡把異常消息,堆棧和自定義消息拼接到一塊兒,例如:redis

logger.error("my message, " + ex.toString, ex);

形成異常信息,堆棧丟失的緣由是由於原先的日誌裏沒有自動把異常信息,堆棧,包含到格式裏。這也是咱們順帶須要解決的一個問題。spring

規範化實現

咱們團隊統一使用的是slf4j + log4j2來打日誌,log4j2自己提供了很是友好的插件擴展, 這樣咱們就能夠擴展一個本身的Layout出來,將日誌格式規範內化到Layout裏,這樣使用者 只須要應用咱們的Layout,無需關心日誌格式,打出來就是規範的日誌格式,這樣推廣就會 簡單不少,並且還能夠方便的擴展字段,順便解決掉異常消息和堆棧丟失的問題。服務器

咱們擴展的Layout實現類名爲: Autolog4jCsvLayout, 如下配置就能夠實現日誌規範化。post

<RollingFile name="ProgramError" ignoreExceptions="false"
                     fileName="${sys:log.path}/project_error.log"
                     filePattern="${sys:log.path}/project_error.log_%d{yyyy-MM-dd}">
    <Autolog4jCsvLayout charset="UTF-8" department="${sys:department}" team="${sys:team}" project="${sys:project}" />
    <Policies>
        <TimeBasedTriggeringPolicy interval="1" modulate="true" />
    </Policies>
    <EnumFilter allowLevels="WARN,ERROR,FATAL" />
</RollingFile>

<RollingFile name="ProgramRun" ignoreExceptions="false"
             fileName="${sys:log.path}/project_run.log"
             filePattern="${sys:log.path}/project_run.log_%d{yyyy-MM-dd}">
    <Autolog4jCsvLayout charset="UTF-8" department="${sys:department}" team="${sys:team}" project="${sys:project}" />
    <Policies>
        <TimeBasedTriggeringPolicy interval="1" modulate="true" />
    </Policies>
    <EnumFilter allowLevels="TRACE,DEBUG,INFO" />
</RollingFile>

這裏將錯誤日誌和INFO日誌分開寫入。另外咱們發現log4j2的日誌級別過濾器很難理解,因此實現了一種直接枚舉日誌級別的過濾器(EnumFilter),簡單好懂。 解決了異常信息堆棧丟失的問題,你們就能夠開心的使用以下方式打日誌:ui

logger.error("my message", ex);

打出的文本日誌舉例:url

"2018-04-20T15:18:59.773+08:00"	"-"	"dealer"    "dealer.arch"	"projectname"	"-"	"10.1.1.1"	"-"	"-"	"-"	"-"	"-"	"ERROR"	"org.springframework.test.context.TestContextManager"	"prepareTestInstance"	"-"	"234"	"org.springframework.test.context.TestContextManager"	"unknown"	"java.lang.IllegalStateException"	"Failed to load ApplicationContext"	"Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener@17a756db] to prepare test instance [com.autohome.daimon.job.service.integration.HawkeyeServiceTest@2d10160a]"	"java.lang.IllegalStateException: Failed to load ApplicationContext
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hawkeyeService': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'redisDao' is expected to be of type 'RedisDao' but was actually of type 'RedisDao$$EnhancerBySpringCGLIB$$17f5ad58'
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:321)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1268)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
	... 24 more
Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'redisDao' is expected to be of type 'IRedisDao' but was actually of type 'RedisDao$$EnhancerBySpringCGLIB$$17f5ad58'
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:384)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	... 40 more
"	"b83539ac4a40de0d"

目前,咱們已經把日誌擴展類庫開源,但願對別人也有所幫助。github地址: autolog4j

另外日誌規範只是日誌建設的第一步,咱們還開源了基於Elasticsearch數據的開源日誌監控系統, 歡迎使用。frostmourne

相關文章
相關標籤/搜索