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