往期精彩推薦
SpringBoot系列(一)idea新建Springboot項目
Springboot系列(七) 集成接口文檔swagger,使用,測試
SpringBoot系列(八)分分鍾學會Springboot多種解決跨域方式
SpringBoot系列(十)優雅的處理統一異常處理與統一結果返回
SpringBoot系列(十一)攔截器與攔截器鏈的配置與使用詳解,你知道多少?
本文目錄
一、SpringBoot中的日志
在我們運行項目的時候,你會發現控制台是有日志打印的,這個日志就是SpringBoot默認配置的日志框架處理的。SpringBoot默認是運用logback+slf4j處理日志,slf4j是抽象層,logback是實現層。
但是不同的框架可能會有不同日志處理方式,如果我們在SpringBoot中集成了不同的框架的話,是不是日志的輸出也會混亂呢?很顯然,如果你有一點經驗的話,你會發現,只要你不修改SpringBoot的默認日志配置,它的日志輸出格式是不會變得。這是因為,在SpringBoot管理日志的時候,它都將其他框架的日志通過一些中間包的形式將其他的日志抽象成了slf4j接口,而統一用logback的形式實現。
本文我們來講講怎么來配置日志格式以及運用AOP+自定義注解簡化日志的記錄。
二、自定義日志常用配置
1. 日志輸出級別
SpringBoot中默認的日志輸出級別是info,也就是說我們平常在控制台輸出的那些日志都是info級別以及更高級別的日志。我們可以自己定義日志的輸出級別,一般有以下幾個級別:
trace,debug,info,warn,error //級別遞增
trace 是追蹤日志,debug是調式日志,info一般是自定義日志或者是信息日志,warn是警告日志,error則是錯誤日志。
可能這么說你也不知道這個級別有什么用,來看看這個代碼:
@RestController
public class TestLogController {
Logger logger = LoggerFactory.getLogger(TestLogController.class);
@GetMapping("/testLog")
public void testLog(){
logger.trace("這是trace級別的日志");
logger.debug("這是debug級別的日志");
logger.info("這是正常自定義日志");
logger.warn("這是警告日志");
logger.error("這是錯誤日志");
}
}
代碼說明:(上面的Logger包這里是使用的org.slf4j.Logger)
首先我們獲取一個日志記錄器Logger對象,然后分別在代碼中記錄不同級別日志的輸出。運行項目,然后訪問接口。
你會發現前面的trace日志和debug日志是不會輸出的,這你就知道了吧,不同等級的日志有不同的功效,只會在特定的情況下輸出。這時候我們也可以自定義日志級別了,在配置文件(yml)
logging:
level:
com:
example:
demolog: debug
配置說明:
這是什么意思呢?我的包名是com.example.demolog,所以說這個配置就是說配置日志所在包的輸出級別,是不是很高級。這樣就能輸出debug日志了。如果你想輸出trace日志你就將等級設置為trace就行了。
2. 日志輸出到文件
日志輸出到控制台查看起來不是很方便,怎么辦?沒關系,SpringBoot中還能將日志輸出到指定的文件中,yml,添加如下配置。
logging:
file:
path: /spring/test/
這個配置是說將日志輸出到指定的目錄文件,並且會生成一個spring.log的日志文件用來記錄日志(如果你自己指定了文件名,它就會按照你自己設定的名字生成文件。),運行項目你就能直接看到生成的日志所在,這個目錄如果你寫的和上面一致,那么你的日志文件就會在項目的運行根路徑,比如D盤,然后在D盤生成你寫的文件目錄/spring/test/,最后在文件目錄下面生成spring.log的日志文件。這個路徑你也可以直接寫絕對路徑(直接指定這個文件在那個盤,那個文件夾)。
file下面還有一個配置就是name屬性,
logging:
file:
name: test.log
這個是直接指定你的日志的文件名稱,默認生成的位置是在項目所在的目錄,你也可以自己寫絕對路徑配置日志文件的位置,但是必須要自己設定文件的名稱。
file的name屬性和path屬性只能指定一個,如果兩個同時指定的話,只有name屬性會生效。
3. 自定義日志輸出格式
有時候你可能會覺得這個日志的輸出格式太難看了,想自己定義一個日志輸出格式,完全ojbk!SpringBoot說:滿足你,自己想怎么玩就怎么玩!
logging:
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss}----- 這是全棧學習筆記 [%thread] %-5level %logger{50} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss}----- 這是全棧學習筆記 [%thread] %-5level %logger{50} - %msg%n"
# d表示日期時間,
# %thread表示線程名,
# %‐5level:級別從左顯示5個字符寬度
# %logger{50} 表示logger名字最長50個字符,否則按照句點分割。
# %msg:日志消息,
# %n是換行符
上面的配置分別定義了控制台的日志輸出格式與文件的日志輸出格式,是不是很方便。輸出的格式大概就是這樣。
當然我們還有一個更好的日志配置,利用xml文件進行配置,一步到位就是這么爽。
三、xml文件實現日志配置的方式
直接上xml文件的內容,建議將文件命名為logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:當此屬性設置為true時,配置文件如果發生改變,將會被重新加載,默認值為true。
scanPeriod:設置監測配置文件是否有修改的時間間隔,如果沒有給出時間單位,默認單位是毫秒當scan為true時,此屬性生效。默認的時間間隔為1分鍾。
debug:當此屬性設置為true時,將打印出logback內部日志信息,實時查看logback運行狀態。默認值為false。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
<!-- 定義日志的根目錄 建議寫絕對路徑 如果不寫默認在項目運行的根路徑( D盤,C盤這種)-->
<property name="LOG_HOME" value="D:/app/log" />
<!-- 定義日志文件名稱 -->
<property name="appName" value="testxml"></property>
<!-- ch.qos.logback.core.ConsoleAppender 表示控制台輸出 -->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<!--
日志輸出格式:
%d表示日期時間,
%thread表示線程名,
%-5level:級別從左顯示5個字符寬度
%logger{50} 表示logger名字最長50個字符,否則按照句點分割。
%msg:日志消息,
%n是換行符
-->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} --學習筆記--[%thread] %-5level %logger{50} - %msg%n</pattern>
<!-- <springProfile name="dev">-->
<!-- <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} == 學習筆記 == [%thread] -–> %-5level %logger{50} - %msg%n</pattern>-->
<!-- </springProfile>-->
<!-- <springProfile name="prod">-->
<!-- <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==學習筆記==[%thread] %-5level %logger{50} - %msg%n</pattern>-->
<!-- </springProfile>-->
<!-- 如果將這個文件的名字改成logback-spring.xml 就可以使用上面的功能,
上面的功能是說可以根據不同的生產環境做不同的日志打印
-->
</layout>
</appender>
<!-- 滾動記錄文件,先將日志記錄到指定文件,當符合某個條件時,將日志記錄到其他文件 -->
<appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 指定日志文件的名稱 -->
<file>${LOG_HOME}/${appName}.log</file>
<!--
當發生滾動時,決定 RollingFileAppender 的行為,涉及文件移動和重命名
TimeBasedRollingPolicy: 最常用的滾動策略,它根據時間來制定滾動策略,既負責滾動也負責出發滾動。
-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--
滾動時產生的文件的存放位置及文件名稱 %d{yyyy-MM-dd}:按天進行日志滾動
%i:當文件大小超過maxFileSize時,按照i進行文件滾動
-->
<fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
<!--
可選節點,控制保留的歸檔文件的最大數量,超出數量就刪除舊文件。假設設置每天滾動,
且maxHistory是365,則只保存最近365天的文件,刪除之前的舊文件。注意,刪除舊文件是,
那些為了歸檔而創建的目錄也會被刪除。
-->
<MaxHistory>365</MaxHistory>
<!--
當日志文件超過maxFileSize指定的大小是,根據上面提到的%i進行日志文件滾動 注意此處配置SizeBasedTriggeringPolicy是無法實現按文件大小進行滾動的,必須配置timeBasedFileNamingAndTriggeringPolicy
-->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 日志輸出格式: -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
</layout>
</appender>
<!--
logger主要用於存放日志對象,也可以定義日志類型、級別
name:表示匹配的logger類型前綴,也就是包的前半部分
level:要記錄的日志級別,包括 TRACE < DEBUG < INFO < WARN < ERROR
additivity:作用在於children-logger是否使用 rootLogger配置的appender進行輸出,
false:表示只用當前logger的appender-ref,true:
表示當前logger的appender-ref和rootLogger的appender-ref都有效
-->
<!-- hibernate logger -->
<logger name="com.example.demolog" level="debug" ></logger>
<!-- Spring framework logger -->
<logger name="org.springframework" level="debug" additivity="false"></logger>
<!--
root與logger是父子關系,沒有特別定義則默認為root,任何一個類只會和一個logger對應,
要么是定義的logger,要么是root,判斷的關鍵在於找到這個logger,然后判斷這個logger的appender和level。
-->
<root level="info">
<appender-ref ref="stdout" />
<appender-ref ref="appLogAppender" />
</root>
</configuration>
上面的xml配置文件配置就不細說了,里面都有詳細的注釋說明。配置文件默認位置應該是直接放在resources下面,和yml,properties文件同級,當然你也可以自己配置文件位置的。
logging
config: classpath:static/logback.xml
這樣就將xml配置文件放在static路徑下面時能自動識別了。也可以設置為絕對路徑。
上面我們建議將日志文件設置為logback-spring.xml,如果我們的xml文件的名稱是logback.xml,它就會直接被日志框架識別,如果你的xml文件是用logback-spring.xml命名,那么他會被SpringBoot來識別並解析日志配置,可以使用SpringBoot的高級Profile功能。這個高級功能我在xml文件中有注釋說明。往上看。你也可以去看Spring的官網,有詳細的配置說明。
四、AOP + 自定義注解實現統一日志處理
引入aop依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
自定義注解,還不會注解的,看這里,注解詳細介紹注解干貨
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
String value() default "";
}
自定義切面類:
@Aspect
@Component
public class LogAspect {
private Logger logger = LoggerFactory.getLogger(LogAspect.class);
@Pointcut("@annotation(com.example.demolog.annotation.MyLog)")
public void myPointCut(){
//簽名,可以理解成這個切入點的一個名稱
}
@Before("myPointCut()")
public void doBefore(JoinPoint joinPoint){
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//獲取url,請求方法,ip地址,類名以及方法名,參數
logger.info("url={},method={},ip={},class_method={},args={}", request.getRequestURI(),request.getMethod(),request.getRemoteAddr(),joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName(),joinPoint.getArgs());
}
@AfterReturning(pointcut = "myPointCut()")
public void printLog(JoinPoint joinPoint){
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
MyLog myLog = method.getAnnotation(MyLog.class);
String value = null;
if (myLog!=null){
value = myLog.value();
}
logger.info(new Date()+"-----"+value);
}
}
上面配置完成之后再去controller的方法之上添加一個自定義的 @Mylog注解
@GetMapping("/testLog")
@MyLog("測試一個日志")
public void testLog(){
//和上面的一致
}
代碼說明:
-
@Aspect:標明這是一個切面類
-
@Component:標明這是一個bean
-
@Pointcut("@annotation(com.example.demolog.annotation.MyLog)") 定義切入點為自定義的注解,也可以是一個類或者是一個包,包的寫法如下:
@Pointcut("execution(public * com.example.demolog.*(..))")
上面的意思是切入點是 所有在com.example,demolog包下面的以public為修飾,不限制返回值(*),不限制參數不限制名稱的類。
擴展知識:
- @befor:前置通知,在一個方法執行之前被調用。
- @after:在方法執行之后調用的通知,無論方法執行是否成功。
- @after-returning:僅當方法成功完成之后通知。
- @after-throwing:在方法拋出異常退出時執行的通知。
- @around:在方法執行之前和之后調用的通知。
然后我們再來測試一下接口:localhost:8098/testLog
本期分享到此結束,總結一下下!
五、總結
本文先講解SpringBoot的默認日志配置,然后自己在配置文件配置日志的輸出等級,輸出格式,將日志輸出到文件中,然后通過xml文件來配置日志。最后我們引出了利用aop,簡化日志的輸出,並且統一日志的輸出格式。如果你覺得本文有用的話,點個贊吧!另外需要源碼的看下面。