Java項目日誌記錄方案


一、概述


1、採用slf4j作爲日誌API,採用logback作爲日誌輸出工具,用slf4j橋接方式替換掉log4j和commons-logging。

2、採用trace(追蹤)、debug(調試)、info(信息)、warn(警告)、error(錯誤)、fatal(致命)共6種日誌級別。

3、採用dev(開發環境)、test(測試環境)、production(生產環境)等不同的日誌配置,根據環境變量自動識別。

4、特殊的記錄,需要大批量寫入日誌文件,應該採用異步線程寫文件。


二、日誌級別定義

    採用trace(追蹤)、debug(調試)、info(信息)、warn(警告)、error(錯誤)、fatal(致命)共6種日誌級別。


日誌級別使用原則:

1、fatal(致命錯誤)使用原則

fatal爲系統級別的異常,發生fatal錯誤,代表服務器整個或者核心功能已經無法工作了!!

1)在服務器啓動時就應該檢查,如果存在致命錯誤,直接拋異常,讓服務器不要啓動起來(啓動了也無法正常工作,不如不啓動)。

2)如果在服務器啓動之後,發生了致命的錯誤,則記錄fatal級別的錯誤日誌,最好是同時觸發相關的修復和告警工作(比如,給開發和維護人員發送告警郵件)。


2、error(錯誤)使用原則

error爲功能或者邏輯級別的嚴重異常,發生error級別的異常,代表功能或者重要邏輯遇到問題、無法正常工作


3、warn(警告)使用原則

warn用在某些邏輯非常規,發生了一些小故障但是沒有大的影響或者重要數據被修改,或者某些操作需要引起重視


4、info(信息)使用原則

info用於記錄一些有用的、關鍵的信息,一般這些信息出現得不頻繁只是在初始化的地方或者重要操作的地方纔記錄


5、debug(調試)使用原則

debug用於記錄一些調試信息,爲了方便查看程序的執行過程和相關數據、瞭解程序的動態


6、trace(跟蹤)使用原則

trace用於記錄一些更詳細的調試信息,這些信息無需每次調試時都打印出來,只在需要更詳細的調試信息時纔開啓。


7、項目穩定運行時的日誌量

1)正常情況下,trace日誌至少是debug日誌的100倍,

trace級別的日誌量  debug級別的日誌量  >  100  1,

也就是說trace日誌非常多,debug日誌相對較少。


2)debug級別的日誌量  info級別的日誌量 > 1000  1,

也就是說正常情況下,info日誌很少,只在部分重要位置會輸出 info日誌。


3)error日誌和warn日誌,正常情況下,幾乎爲0,當出現異常時,error日誌和warn日誌量 也在可控範圍,不會超過debug級別的最大日誌量。


三、日誌輸出(Appender)分類


分爲5個一般類: 

    FILE_EXCEPTION (異常日誌,包括ERROR和WARN)

    FILE_APP (應用日誌,包括當前應用package下面的日誌和DEBUG級別以上的其他日誌)

    FILE_INFO (普通信息日誌)

    FILE_DEBUG (調試日誌)

    FILE_TRACE(追蹤日誌)

    SYSOUT(控制檯輸出,可以包括以上所有日誌)

擴展類: 包括異步輸出的日誌,或者特殊業務日誌。


舉例說明:

假如 

當前應用的 Main Package 爲 cn.zollty.lightning

ROOT_LEVEL爲 trace,應用日誌 LEVEL 爲 debug

有以下日誌打印:


1

2

3

4

5

6

7

8

9

10

11

12

13

Logger loggerA = LoggerFactory.getLogger(cn.zollty.lightning.Tests.class);

loggerA.trace("--------");

loggerA.debug("--------");

loggerA.info("--------");

loggerA.warn("--------");

loggerA.error("--------");

 

Logger loggerB = LoggerFactory.getLogger(org.apache.kafka.Consumer.class);

loggerB.trace("--------");

loggerB.debug("--------");

loggerB.info("--------");

loggerB.warn("--------");

loggerB.error("--------");

那麼,異常日誌(FILE_EXCEPTION)輸出的爲:

loggerA.warn("--------");

loggerA.error("--------");

loggerB.warn("--------");

loggerB.error("--------");

控制檯(SYSOUT)輸出日誌的爲;

loggerA.debug("--------");

loggerA.info("--------");

loggerA.warn("--------");

loggerA.error("--------");

loggerB.trace("--------");

loggerB.debug("--------");

loggerB.info("--------");

loggerB.warn("--------");

loggerB.error("--------");

應用日誌(FILE_APP)輸出的爲:

loggerA.debug("--------");

loggerA.info("--------");

loggerA.warn("--------");

loggerA.error("--------");

loggerB.info("--------");

loggerB.warn("--------");

loggerB.error("--------");


2、歷史日誌文件

異常日誌(error和warn)最多保存 9000 M(生產,測試)

app日誌最多保存 9000 M (生產,測試)

trace日誌最多保存 1000 M (僅供測試用,一般不用)

debug、info日誌最多保存 5000 M(一般不用,用app日誌就夠了)


四、各環境默認日誌定義


開發環境

1)默認日誌級別定義爲:

    app包爲TRACE級別。日誌的ROOT Level爲DEBUG級別。

2)

啓用 System.out 控制檯輸出日誌;

啓用error.log爲錯誤和警告日誌、app.log爲應用日誌(包括app包下的日誌和其他INFO級別以上的日誌)。


測試環境

1)默認日誌級別定義爲:

    app包爲DEBUG級別。日誌的ROOT Level爲DEBUG級別。

2)

禁用 System.out 控制檯輸出日誌;

啓用error.log爲錯誤和警告日誌、app.log爲應用日誌(包括app包下的日誌和其他INFO級別以上的日誌)。


生產環境

1)默認日誌級別定義爲:

    app包爲DEBUG級別。日誌的ROOT Level爲INFO級別。

2)

禁用 System.out 控制檯輸出日誌;

啓用error.log爲錯誤和警告日誌、app.log爲應用日誌(包括app包下的日誌和其他INFO級別以上的日誌)。


五、根據環境自動選擇日誌配置(藉助Logback


關鍵點1:使用logback的環境變量定義和讀取功能

例如下面的各種環境變量定義:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<!-- 部署的環境類型:dev、test、product -->

<property name="DEPLOY_ENV" value="${deploy.env:-dev}" />

 

<!-- 日誌路徑,這裏是相對路徑,web項目eclipse下會輸出到當前目錄./logs/下,如果部署到linux上的tomcat下,會輸出到tomcat/logs/目錄 下 -->

<property name="LOG_HOME" value="${catalina.base:-.}/logs" />

 

<!-- 日誌文件大小,超過這個大小將被壓縮 -->

<property name="LOG_MAX_SIZE" value="100MB" />

<!-- 日誌輸出格式 -->

<property name="LOG_COMMON_PATTERN" value="%d{HH:mm:ss.SSS} [%thread] [%level] %logger - %msg%n" />

<property name="LOG_DEV_PATTERN" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{48}:%line - %msg%n" />

 

<!-- 主日誌級別 -->

<property name="ROOT_LEVEL" value="${log.root.level:-DEBUG}" />

 

<!-- APP 日誌級別 -->

<property name="APP_LEVEL" value="${log.app.level:-TRACE}" />

<!-- APP Package 前綴: cn.zollty.lightning -->

<property name="APP_PACKAGE" value="cn.zollty.lightning" />

其中 ${deploy.env:-dev} 代表的意思是,如果環境變量中沒有 deploy.env,則使用默認值dev。


一個小技巧:可以自定義類似下面這個類,在logback初始化之前,先設置變量的值:


1

<statusListener class="cn.zollty.commons.logbackext.InitConfigOnConsoleStatusListener" />

這個類繼承自ch.qos.logback.core.status.OnConsoleStatusListener。


關鍵點2:使用logback的 if-then 條件語法


1

2

3

4

5

6

7

8

9

10

11

12

<root level="${ROOT_LEVEL}">

  <!-- Required: exception log -->

  <appender-ref ref="FILE_EXCEPTION"/>

  <!-- Required: app log  -->

  <appender-ref ref="FILE_APP"/>

 

  <if condition='p("DEPLOY_ENV").contains("dev")'>

    <then>

    <appender-ref ref="STDOUT" />

    </then>

  </if>

</root>


參考配置:

logback.xml:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

<?xml version="1.0" encoding="UTF-8"?>

<configuration scan="true" scanPeriod="60 seconds" debug="false">

 

  <statusListener class="cn.zollty.commons.logbackext.InitConfigOnConsoleStatusListener" />

 

  <!-- 部署的環境類型:dev、test、product -->

  <property name="DEPLOY_ENV" value="${deploy.env:-dev}" />

 

  <!-- 日誌路徑,這裏是相對路徑,web項目eclipse下會輸出到當前目錄./logs/下,如果部署到linux上的tomcat下,會輸出到tomcat/logs/目錄 下 -->

  <property name="LOG_HOME" value="${catalina.base:-.}/logs" />

   

  <!-- 日誌文件大小,超過這個大小將被壓縮 -->

  <property name="LOG_MAX_SIZE" value="100MB" />

  <!-- 日誌輸出格式 -->

  <property name="LOG_COMMON_PATTERN" value="%d{HH:mm:ss.SSS} [%thread] [%level] %logger - %msg%n" />

  <property name="LOG_DEV_PATTERN" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{48}:%line - %msg%n" />

 

  <!-- 主日誌級別 -->

  <property name="ROOT_LEVEL" value="${log.root.level:-DEBUG}" />

 

  <!-- APP 日誌級別 -->

  <property name="APP_LEVEL" value="${log.app.level:-TRACE}" />

  <!-- APP Package 前綴: cn.cstonline.zollty -->

  <property name="APP_PACKAGE" value="cn.zollty.lightning" />

   

  <include resource="includedConfig.xml"/>

   

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">

    <encoder>

      <pattern>${LOG_DEV_PATTERN}</pattern>

    </encoder>

  </appender>

   

  <appender name="FILTER-DATA" class="ch.qos.logback.core.rolling.RollingFileAppender">

    <file>${LOG_HOME}/filter.log</file>

    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

      <fileNamePattern>${LOG_HOME}/filter/filter-%d{yyyy-MM-dd}-%i.log.zip</fileNamePattern>

      <maxHistory>90</maxHistory>

      <TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">

        <MaxFileSize>100MB</MaxFileSize>

      </TimeBasedFileNamingAndTriggeringPolicy>

    </rollingPolicy>

    <encoder>

      <pattern>${LOG_COMMON_PATTERN}</pattern>

    </encoder>

  </appender>

   

  <appender name="ASYNC1" class="ch.qos.logback.classic.AsyncAppender">

    <appender-ref ref="FILTER-DATA" />

  </appender>

   

  <include resource="special_log_level.xml"/>

 

  <logger name="${APP_PACKAGE}" level="${APP_LEVEL}" />

   

  <logger name="FILTER-LOGGER" level="${APP_LEVEL}" additivity="false">

    <appender-ref ref="ASYNC1" />

  </logger>

 

  <root level="${ROOT_LEVEL}">

    <!-- Required: exception log -->

    <appender-ref ref="FILE_EXCEPTION"/>

    <!-- Required: app log  -->

    <appender-ref ref="FILE_APP"/>

     

    <!-- Optional: show all debug or trace info -->

    <!-- <appender-ref ref="FILE_DEBUG"/> -->

    <!-- <appender-ref ref="FILE_TRACE"/> -->

     

    <if condition='p("DEPLOY_ENV").contains("dev")'>

      <then>

        <appender-ref ref="STDOUT" />

      </then>

    </if>

     

  </root>

 

</configuration>

includedConfig.xml


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

<?xml version="1.0" encoding="UTF-8" ?>

 

<included>

 

  <!-- WARN and ERROR -->

  <appender name="FILE_EXCEPTION" class="ch.qos.logback.core.rolling.RollingFileAppender">

    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">

      <level>WARN</level>

    </filter>

    <file>${LOG_HOME}/error.log</file>

    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

      <!-- rollover daily -->

      <fileNamePattern>${LOG_HOME}/error/error-%d{yyyy-MM-dd}-%i.log.zip</fileNamePattern>

      <maxHistory>90</maxHistory>

      <TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">

        <!-- or whenever the file size reaches 100MB -->

        <MaxFileSize>${LOG_MAX_SIZE}</MaxFileSize>

      </TimeBasedFileNamingAndTriggeringPolicy>

    </rollingPolicy>

    <encoder>

      <pattern>${LOG_COMMON_PATTERN}</pattern>

    </encoder>

  </appender>

 

  <!-- INFO or Greater -->

  <appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">

    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">

      <level>INFO</level>

    </filter>

    <file>${LOG_HOME}/info.log</file>

    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

      <fileNamePattern>${LOG_HOME}/info/info-%d{yyyy-MM-dd}-%i.log.zip</fileNamePattern>

      <maxHistory>50</maxHistory>

      <TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">

        <MaxFileSize>${LOG_MAX_SIZE}</MaxFileSize>

      </TimeBasedFileNamingAndTriggeringPolicy>

    </rollingPolicy>

    <encoder>

      <pattern>${LOG_COMMON_PATTERN}</pattern>

    </encoder>

  </appender>

 

  <!-- DEBUG or Greater-->

  <appender name="FILE_DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">

    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">

      <level>DEBUG</level>

    </filter>

    <file>${LOG_HOME}/debug.log</file>

    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

      <fileNamePattern>${LOG_HOME}/debug/debug-%d{yyyy-MM-dd}-%i.log.zip</fileNamePattern>

      <maxHistory>50</maxHistory>

      <TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">

        <MaxFileSize>${LOG_MAX_SIZE}</MaxFileSize>

      </TimeBasedFileNamingAndTriggeringPolicy>

    </rollingPolicy>

    <encoder>

      <pattern>${LOG_COMMON_PATTERN}</pattern>

    </encoder>

  </appender>

 

  <!-- TRACE and ALL -->

  <appender name="FILE_TRACE" class="ch.qos.logback.core.rolling.RollingFileAppender">

    <file>${LOG_HOME}/trace.log</file>

    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

      <fileNamePattern>${LOG_HOME}/trace/trace-%d{yyyy-MM-dd}-%i.log.zip</fileNamePattern>

      <maxHistory>10</maxHistory>

      <TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">

        <MaxFileSize>${LOG_MAX_SIZE}</MaxFileSize>

      </TimeBasedFileNamingAndTriggeringPolicy>

    </rollingPolicy>

    <encoder>

      <pattern>${LOG_COMMON_PATTERN}</pattern>

    </encoder>

  </appender>

   

  <!-- (INFO or Greater) or logname prefix = ${APP_PACKAGE} -->

  <appender name="FILE_APP" class="ch.qos.logback.core.rolling.RollingFileAppender">

    <filter class="cn.zollty.lightning.common.PackageOrThresholdFilter">

      <level>INFO</level>

      <prefix>${APP_PACKAGE}</prefix>

    </filter>

    <file>${LOG_HOME}/app.log</file>

    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

      <fileNamePattern>${LOG_HOME}/app/app-%d{yyyy-MM-dd}-%i.log.zip</fileNamePattern>

      <maxHistory>90</maxHistory>

      <TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">

        <MaxFileSize>${LOG_MAX_SIZE}</MaxFileSize>

      </TimeBasedFileNamingAndTriggeringPolicy>

    </rollingPolicy>

    <encoder>

      <pattern>${LOG_COMMON_PATTERN}</pattern>

    </encoder>

  </appender>

 

</included>

special_log_level.xml


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

<?xml version="1.0" encoding="UTF-8" ?>

<included>

 

  <logger name="org.apache.zookeeper.ClientCnxn" level="ERROR" />

  <logger name="org.apache.kafka.clients.consumer.internals.ConsumerCoordinator" level="INFO" />

  <logger name="kafka.producer.BrokerPartitionInfo" level="INFO" />

  <logger name="kafka.producer.async.ProducerSendThread" level="INFO" />

  <logger name="kafka.producer.async.DefaultEventHandler" level="INFO" />

  <logger name="org.apache.kafka.common.metrics.Metrics" level="INFO" />

  <logger name="org.apache.kafka.clients.Metadata" level="INFO" />

   

  <logger name="org.apache.kafka.clients.consumer.internals.AbstractCoordinator" level="INFO" />

  <logger name="org.apache.kafka.clients.consumer.internals.Fetcher" level="INFO" />

  <logger name="org.apache.kafka.clients.NetworkClient" level="INFO" />

   

</included>


也可以把變量定義到properties文件中,本地就放在 

src/resources/conf/logback_val.properties

服務器上放在

${catalina.base}/conf/logback_val.properties

配置如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

<?xml version="1.0" encoding="UTF-8"?>

<configuration scan="true" scanPeriod="60 seconds" debug="false">

 

  <if condition='isDefined("catalina.base")'>

    <then>

      <property file="${catalina.base}/conf/logback_val.properties" />

    </then>

    <else>

      <property resource="./conf/logback_val.properties" />

    </else>

  </if>

   

  ...

logback_val.properties:


1

2

3

4

5

6

7

8

9

10

# \u90E8\u7F72\u7684\u73AF\u5883\u7C7B\u578B\uFF1Adev\u3001test\u3001product

deploy.env=dev

# \u4E3B\u65E5\u5FD7\u7EA7\u522B

log.root.level=DEBUG

# APP \u65E5\u5FD7\u7EA7\u522B

log.app.level=TRACE

# Kafka LogStash \u9009\u62E9\u4F7F\u7528 value= "yes" or "no"

log.use.logstash=yes

# LogStash Kafka URL

log.logstash.kafka=172.16.1.164:9092,172.16.1.165:9092,172.16.1.166:9092


參考資料

http://logback.qos.ch/manual/configuration.html#conditional


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