簡(jiǎn)單的說(shuō)惫撰,日志就是記錄程序的運(yùn)行軌跡呆万,方便查找關(guān)鍵信息筹误,也方便快速定位解決問(wèn)題。
本篇文章分為三部分講解:
- 常用日志框架
- SpringBoot 配置Logback
- 阿里日志規(guī)約
常用日志框架
-
Logging
這是 Java 自帶的日志工具類躯泰,在 JDK 1.5 開(kāi)始就已經(jīng)有了,在 java.util.logging 包下华糖。 -
Log4j
Log4j 是 Apache 的一個(gè)開(kāi)源日志框架麦向,也是市場(chǎng)占有率最多的一個(gè)框架。大多數(shù)沒(méi)用過(guò) Java Logging客叉, 但沒(méi)人敢說(shuō)沒(méi)用過(guò) Log4j 吧诵竭,反正從我接觸 Java 開(kāi)始就是這種情況,做 Java 項(xiàng)目必有 Log4j 日志框架兼搏。 -
commons-logging
commons-logging 就是日志的門面接口卵慰,它也是 apache 最早提供的日志門面接口,用戶可以根據(jù)喜好選擇不同的日志實(shí)現(xiàn)框架佛呻,而不必改動(dòng)日志定義裳朋,這就是日志門面的好處,符合面對(duì)接口抽象編程吓著。 -
Slf4j
全稱:Simple Logging Facade for Java鲤嫡,即簡(jiǎn)單日志門面接口,和 Apache 的 commons-logging 是一樣的概念绑莺,它們都不是具體的日志框架暖眼,你可以指定其他主流的日志實(shí)現(xiàn)框架。 -
Logback
Logback 是 Slf4j 的原生實(shí)現(xiàn)框架纺裁,同樣也是出自 Log4j 一個(gè)人之手诫肠,但擁有比 log4j 更多的優(yōu)點(diǎn)、特性和更做強(qiáng)的性能欺缘,現(xiàn)在基本都用來(lái)代替 log4j 成為主流栋豫。
為什么 Logback 會(huì)成為主流?
無(wú)論從設(shè)計(jì)上還是實(shí)現(xiàn)上浪南,Logback相對(duì)log4j而言有了相對(duì)多的改進(jìn)笼才。
更快的執(zhí)行速度
基于我們先前在log4j上的工作,logback 重寫(xiě)了內(nèi)部的實(shí)現(xiàn)络凿,在某些特定的場(chǎng)景上面骡送,甚至可以比之前的速度快上10倍。在保證logback的組件更加快速的同時(shí)絮记,同時(shí)所需的內(nèi)存更加少摔踱。
日志框架總結(jié)
- commons-loggin扣泊、slf4j 只是一種日志抽象門面链患,不是具體的日志框架。
- log4j仰泻、logback 是具體的日志實(shí)現(xiàn)框架。
- 一般首選強(qiáng)烈推薦使用 slf4j + logback篮愉。當(dāng)然也可以使用slf4j + log4j腐芍、commons-logging + log4j 這兩種日志組合框架。
SpringBoot logback
Spring Boot會(huì)用Logback來(lái)記錄日志试躏,并用INFO級(jí)別輸出到控制臺(tái)猪勇。在運(yùn)行應(yīng)用程序和其他例子時(shí),你應(yīng)該已經(jīng)看到很多INFO級(jí)別的日志了颠蕴。
日志依賴
依賴 spring-boot-starter-web 默認(rèn)包含spring-boot-starter-logging
那么泣刹,我們的Spring Boot應(yīng)用將自動(dòng)使用logback作為應(yīng)用日志框架,Spring Boot啟動(dòng)的時(shí)候犀被,由org.springframework.boot.logging.Logging-Application-Listener根據(jù)情況初始化并使用椅您。
默認(rèn)配置屬性支持
Spring Boot為我們提供了很多默認(rèn)的日志配置,所以寡键,只要將spring-boot-starter-logging作為依賴加入到當(dāng)前應(yīng)用的classpath掀泳,則“開(kāi)箱即用”。 下面介紹幾種在application.properties就可以配置的日志相關(guān)屬性西轩。
控制臺(tái)輸出
日志級(jí)別從低到高分為TRACE < DEBUG < INFO < WARN < ERROR < FATAL开伏,如果設(shè)置為WARN,則低于WARN的信息都不會(huì)輸出遭商。 Spring Boot中默認(rèn)配置ERROR、WARN和INFO級(jí)別的日志輸出到控制臺(tái)捅伤。您還可以通過(guò)啟動(dòng)您的應(yīng)用程序–debug標(biāo)志來(lái)啟用“調(diào)試”模式(開(kāi)發(fā)的時(shí)候推薦開(kāi)啟),以下兩種方式皆可:
- 在運(yùn)行命令后加入–debug標(biāo)志劫流,如:$ java -jar springTest.jar --debug。
- 在application.properties中配置debug=true丛忆,該屬性置為true的時(shí)候祠汇,核心Logger(包含嵌入式容器、hibernate熄诡、spring)會(huì)輸出更多內(nèi)容可很,但是你自己應(yīng)用的日志并不會(huì)輸出為DEBUG級(jí)別。
文件輸出
默認(rèn)情況下凰浮,Spring Boot將日志輸出到控制臺(tái)我抠,不會(huì)寫(xiě)到日志文件。如果要編寫(xiě)除控制臺(tái)輸出之外的日志文件袜茧,則需在application.properties中設(shè)置logging.file或logging.path屬性菜拓。
- logging.file,設(shè)置文件笛厦,可以是絕對(duì)路徑纳鼎,也可以是相對(duì)路徑。如:logging.file=my.log。
- logging.path贱鄙,設(shè)置目錄劝贸,會(huì)在該目錄下創(chuàng)建spring.log文件,并寫(xiě)入日志內(nèi)容逗宁,如:logging.path=/var/log映九。
如果只配置 logging.file,會(huì)在項(xiàng)目的當(dāng)前路徑下生成一個(gè) xxx.log 日志文件疙剑。
如果只配置 logging.path氯迂,在 /var/log文件夾生成一個(gè)日志文件為 spring.log。
級(jí)別控制
所有支持的日志記錄系統(tǒng)都可以在Spring環(huán)境中設(shè)置記錄級(jí)別(例如在application.properties中) 格式為:’logging.level.* = LEVEL’
- logging.level:日志級(jí)別控制前綴言缤,*為包名或Logger名
- LEVEL:選項(xiàng)TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF
舉例:
- logging.level.com.mrbird=DEBUG:com.mrbird包下所有class以DEBUG級(jí)別輸出嚼蚀。
- logging.level.root=WARN:root日志以WARN級(jí)別輸出。
自定義日志配置
由于日志服務(wù)一般都在ApplicationContext創(chuàng)建前就初始化了管挟,它并不是必須通過(guò)Spring的配置文件控制轿曙。因此通過(guò)系統(tǒng)屬性和傳統(tǒng)的Spring Boot外部配置文件依然可以很好的支持日志控制和管理。
根據(jù)不同的日志系統(tǒng)僻孝,你可以按如下規(guī)則組織配置文件名导帝,就能被正確加載:
- Logback:logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy
- Log4j:log4j-spring.properties, log4j-spring.xml, log4j.properties, log4j.xml
- Log4j2:log4j2-spring.xml, log4j2.xml
- JDK (Java Util Logging):logging.properties
Spring Boot官方推薦優(yōu)先使用帶有-spring的文件名作為你的日志配置(如使用logback-spring.xml,而不是logback.xml)穿铆,命名為logback-spring.xml的日志配置文件您单,spring boot可以為它添加一些spring boot特有的配置項(xiàng)(下面會(huì)提到)。
上面是默認(rèn)的命名規(guī)則荞雏,并且放在src/main/resources下面即可虐秦。
如果你即想完全掌控日志配置,但又不想用logback.xml作為L(zhǎng)ogback配置的名字凤优,可以在application.properties配置文件里面通過(guò)logging.config屬性指定自定義的名字:
logging.config=classpath:logging-config.xml
雖然一般并不需要改變配置文件的名字悦陋,但是如果你想針對(duì)不同運(yùn)行時(shí)Profile使用不同的日 志配置,這個(gè)功能會(huì)很有用筑辨。
下面我們來(lái)看看一個(gè)普通的logback-spring.xml例子:
<?xml version="1.0" encoding="UTF-8"?>
<!-- scan:當(dāng)此屬性設(shè)置為true時(shí)俺驶,配置文件如果發(fā)生改變,將會(huì)被重新加載棍辕,默認(rèn)值為true暮现。
scanPeriod:設(shè)置監(jiān)測(cè)配置文件是否有修改的時(shí)間間隔,如果沒(méi)有給出時(shí)間單位楚昭,默認(rèn)單位是毫秒送矩。當(dāng)scan為true時(shí),此屬性生效哪替。默認(rèn)的時(shí)間間隔為1分鐘栋荸。
debug:當(dāng)此屬性設(shè)置為true時(shí),將打印出logback內(nèi)部日志信息,實(shí)時(shí)查看logback運(yùn)行狀態(tài)晌块。默認(rèn)值為false爱沟。-->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 每個(gè)logger都關(guān)聯(lián)到logger上下文,默認(rèn)上下文名稱為“default”匆背。但可以使用設(shè)置成其他名字呼伸,用于區(qū)分不同應(yīng)用程序的記錄。
一旦設(shè)置钝尸,不能修改,可以通過(guò)%contextName來(lái)打印日志上下文名稱括享。-->
<contextName>logback</contextName>
<!-- 設(shè)置變量<property> 用來(lái)定義變量值的標(biāo)簽,有兩個(gè)屬性珍促,name和value铃辖;其中name的值是變量的名稱,value的值時(shí)變量定義的值猪叙。
通過(guò)定義的值會(huì)被插入到logger上下文中娇斩。定義變量后,可以使“${}”來(lái)使用變量穴翩。-->
<property name="log.path" value="log" />
<!-- appender用來(lái)格式化日志輸出節(jié)點(diǎn)犬第,有倆個(gè)屬性name和class,class用來(lái)指定哪種輸出策略芒帕,常用就是控制臺(tái)輸出策略和文件輸出策略歉嗓。-->
<!-- <encoder>表示對(duì)日志進(jìn)行編碼:
%d{HH: mm:ss.SSS}——日志輸出時(shí)間。
%thread——輸出日志的進(jìn)程名字背蟆,這在Web應(yīng)用以及異步任務(wù)處理中很有用遥椿。
%-5level——日志級(jí)別,并且使用5個(gè)字符靠左對(duì)齊淆储。
%logger{36}——日志輸出者的名字。
%msg——日志消息家浇。
%n——平臺(tái)的換行符本砰。
ThresholdFilter為系統(tǒng)定義的攔截器,例如我們用ThresholdFilter來(lái)過(guò)濾掉ERROR級(jí)別以下的日志不輸出到文件中钢悲。如果不用記得注釋掉点额,不然你控制臺(tái)會(huì)發(fā)現(xiàn)沒(méi)日志~-->
<!--輸出到控制臺(tái)-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>-->
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- <fileNamePattern>${log.path}/logback.%d{yyyy-MM-dd}.log</fileNamePattern>定義了日志的切分方式——把每一天的日志歸檔到一個(gè)文件中;
<maxHistory>30</maxHistory>表示只保留最近30天的日志莺琳,以防止日志填滿整個(gè)磁盤空間还棱。同理,可以使用%d{yyyy-MM-dd_HH-mm}來(lái)定義精確到分的日志切分方式惭等;
<totalSizeCap>1GB</totalSizeCap>用來(lái)指定日志文件的上限大小珍手,例如設(shè)置為1GB的話,那么到了這個(gè)值,就會(huì)刪除舊的日志琳要。-->
<!--輸出到文件-->
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/logback.%d{yyyy-MM-dd}.log</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- root節(jié)點(diǎn)是必選節(jié)點(diǎn)寡具,用來(lái)指定最基礎(chǔ)的日志輸出級(jí)別,只有一個(gè)level屬性稚补,
用來(lái)設(shè)置打印級(jí)別童叠,大小寫(xiě)無(wú)關(guān):TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,不能設(shè)置為INHERITED或者同義詞NULL课幕。-->
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="file" />
</root>
<!-- <logger>用來(lái)設(shè)置某一個(gè)包或者具體的某一個(gè)類的日志打印級(jí)別厦坛、以及指定<appender>。<logger>僅有一個(gè)name屬性乍惊,一個(gè)可選的level和一個(gè)可選的addtivity屬性杜秸。-->
<!-- logback為java中的包 -->
<logger name="top.lconcise.controller.HelloController"/>
<!-- name:用來(lái)指定受此logger約束的某一個(gè)包或者具體的某一個(gè)類。
level:用來(lái)設(shè)置打印級(jí)別污桦,大小寫(xiě)無(wú)關(guān):TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF亩歹,還有一個(gè)特俗值INHERITED或者同義詞NULL,
代表強(qiáng)制執(zhí)行上級(jí)的級(jí)別凡橱。如果未設(shè)置此屬性小作,那么當(dāng)前l(fā)ogger將會(huì)繼承上級(jí)的級(jí)別。
addtivity:是否向上級(jí)logger傳遞打印信息稼钩。默認(rèn)是true顾稀。-->
<!--logback.LogbackDemo:類的全路徑 -->
<logger name="top.lconcise.controller.HelloController2" level="WARN" additivity="false">
<appender-ref ref="console"/>
</logger>
</configuration>
阿里日志規(guī)約
1.【強(qiáng)制】應(yīng)用中不可直接使用日志系統(tǒng)(Log4j、 Logback) 中的 API坝撑,而應(yīng)依賴使用日志框架
SLF4J 中的 API静秆,使用門面模式的日志框架,有利于維護(hù)和各個(gè)類的日志處理方式統(tǒng)一巡李。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(A.class);
2.【強(qiáng)制】日志文件推薦至少保存 15 天抚笔,因?yàn)橛行┊惓>邆湟浴爸堋睘轭l次發(fā)生的特點(diǎn)。
3.【強(qiáng)制】應(yīng)用中的擴(kuò)展日志(如打點(diǎn)侨拦、臨時(shí)監(jiān)控殊橙、訪問(wèn)日志等) 命名方式:
appName_logType_logName.log。 logType:日志類型狱从,推薦分類有
stats/desc/monitor/visit 等膨蛮; logName:日志描述。這種命名的好處:通過(guò)文件名就可知
道日志文件屬于什么應(yīng)用季研,什么類型敞葛,什么目的,也有利于歸類查找与涡。
正例: mppserver 應(yīng)用中單獨(dú)監(jiān)控時(shí)區(qū)轉(zhuǎn)換異常惹谐,如:
mppserver_monitor_timeZoneConvert.log
說(shuō)明: 推薦對(duì)日志進(jìn)行分類持偏, 如將錯(cuò)誤日志和業(yè)務(wù)日志分開(kāi)存放,便于開(kāi)發(fā)人員查看豺鼻,也便于
通過(guò)日志對(duì)系統(tǒng)進(jìn)行及時(shí)監(jiān)控综液。
4.【強(qiáng)制】對(duì) trace/debug/info 級(jí)別的日志輸出,必須使用條件輸出形式或者使用占位符的方式儒飒。
說(shuō)明:
logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);
如果日志級(jí)別是 warn谬莹,上述日志不會(huì)打印,但是會(huì)執(zhí)行字符串拼接操作桩了,如果 symbol 是對(duì)象附帽,會(huì)執(zhí)行 toString()方法,浪費(fèi)了系統(tǒng)資源井誉,執(zhí)行了上述操作蕉扮,最終日志卻沒(méi)有打印。
正例: (條件)
if (logger.isDebugEnabled()) {
logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);
}
正例: (占位符)
logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);
5.【強(qiáng)制】避免重復(fù)打印日志颗圣,浪費(fèi)磁盤空間喳钟,務(wù)必在 log4j.xml 中設(shè)置 additivity=false。
正例: <logger name="com.taobao.dubbo.config" additivity="false">
6.【強(qiáng)制】異常信息應(yīng)該包括兩類信息:案發(fā)現(xiàn)場(chǎng)信息和異常堆棧信息在岂。如果不處理奔则,那么通過(guò)
關(guān)鍵字 throws 往上拋出。
正例: logger.error(各類參數(shù)或者對(duì)象 toString + "_" + e.getMessage(), e);
7.【推薦】謹(jǐn)慎地記錄日志蔽午。生產(chǎn)環(huán)境禁止輸出 debug 日志易茬; 有選擇地輸出 info 日志; 如果使
用 warn 來(lái)記錄剛上線時(shí)的業(yè)務(wù)行為信息及老,一定要注意日志輸出量的問(wèn)題抽莱,避免把服務(wù)器磁盤
撐爆,并記得及時(shí)刪除這些觀察日志骄恶。
說(shuō)明: 大量地輸出無(wú)效日志食铐,不利于系統(tǒng)性能提升,也不利于快速定位錯(cuò)誤點(diǎn)僧鲁。 記錄日志時(shí)請(qǐng)
思考:這些日志真的有人看嗎虐呻?看到這條日志你能做什么?能不能給問(wèn)題排查帶來(lái)好處悔捶?
8.【參考】可以使用 warn 日志級(jí)別來(lái)記錄用戶輸入?yún)?shù)錯(cuò)誤的情況,避免用戶投訴時(shí)单芜,無(wú)所適
從蜕该。注意日志輸出的級(jí)別, error 級(jí)別只記錄系統(tǒng)邏輯出錯(cuò)洲鸠、異常等重要的錯(cuò)誤信息堂淡。如非必
要馋缅,請(qǐng)不要在此場(chǎng)景打出 error 級(jí)別。
測(cè)試源碼:https://github.com/lbshold/springboot/tree/master/Spring-Boot-Log
參考文章:https://mrbird.cc/Spring-Boot-logback.html