當前位置:
首頁 >
前端技术
> javascript
>内容正文
javascript
SpringBoot使用Slf4j+Log4j2完成项目的日志记录
生活随笔
收集整理的這篇文章主要介紹了
SpringBoot使用Slf4j+Log4j2完成项目的日志记录
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
SpringBoot使用Slf4j+Log4j完成項目的日志記錄
前言
本示例采用SpringBoot項目使用SpringAOP記錄日志,Slf4j作為日志門面,Log4j2作為日志實現實,實現開發中的日志記錄.
部分效果展示 :
日志文件 :
日志信息 :
代碼具體實現如下 :
避坑 : 在網上有很多人說是在 spring-boot-starter-web 這個啟動器里面進行依賴排除,但是經過我的測試這種方法有時候并不是有效的,所以復制上面的依賴就好。
二、編寫log4j2-spring.xml : log4j2的配置文件
代碼如下:
<?xml version="1.0" encoding="UTF-8"?> <!--status = "warn" 日志框架本身的輸入日志級別monitorInterval = "5" 自動加載配置文件的時間間隔不低于5s--> <Configuration status="warn" monitorInterval="5"><!-- 日志級別以及優先級排序 :在log4j2中, 共有8個級別,按照從低到高為:ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF。All:最低等級的,用于打開所有日志記錄.Trace:是追蹤,就是程序推進一下.Debug:指出細粒度信息事件對調試應用程序是非常有幫助的.Info:消息在粗粒度級別上突出強調應用程序的運行過程.Warn:輸出警告及warn以下級別的日志.Error:輸出錯誤信息日志.Fatal:輸出每個嚴重的錯誤事件將會導致應用程序的退出的日志.OFF:最高等級的,用于關閉所有日志記錄.程序會打印高于或等于所設置級別的日志,設置的日志等級越高,打印出來的日志就越少。--><!--集中配置屬性進行管理--><Properties><!--定義格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度%msg:日志消息,%n是換行符%logger{36} 表示 Logger 名字最長36個字符--><property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight{%-5level}[%thread] %style{%logger{36}}{cyan} : %msg%n" /><!-- 定義日志存儲的路徑,絕對路徑 --><property name="FILE_PATH" value="存儲的絕對路徑" /><property name="FILE_NAME" value="項目名稱" /></Properties><!--日志處理--><Appenders><!--*********************控制臺日志***********************--><!--target: SYSTEM_OUT 或 SYSTEM_ERR,一般只設置默認:SYSTEM_OUT.--><console name="Console" target="SYSTEM_OUT"><!--輸出日志的格式和顏色--><PatternLayout pattern="${LOG_PATTERN}" disableAnsi="false" noConsoleNoAnsi="false"/><!--控制臺只輸出level及其以上級別的信息(onMatch)放行,其他的直接拒絕(onMismatch)--><ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/></console><!--*********************文件日志***********************--><!--按照一定規則查分日志文件的appender /logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyy-MM-dd-HH-mm}-%i.log/logs:放在logs這個目錄下,/$${date:yyyy-MM-dd}:以天為單位生成文件夾myrollog-%d{yyyy-MM-dd-HH-mm}-%d: 以分鐘為單位到達了指定大小在進行拆分.gz 進行壓縮歸檔--><!--error 運行時異常日志信息--><RollingFile name = "errorRollingFile" fileName = "${FILE_NAME}/error日志.log"filePattern = "${FILE_PATH}/$${date:yyyy-MM-dd}/error-%d{yyyy-MM-dd-HH-mm}-%i.log.gz"><!--日志級別過濾器,文件只輸出level及以上級別的信息(onMatch),其他的直接拒絕(onMismatch)--><ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/><!--日志的消息格式--><PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %msg%n"/><!--在系統每次啟動時,觸發拆分規則,生產一個新的日志文件--><OnStartupTriggeringPolicy/><!--按照文件大小進行拆分--><SizeBasedTriggeringPolicy size = "10 MB"/><!--按照時間節點進行拆分--><TimeBasedTriggeringPolicy/><!--在同一個目錄下,文件的個數限定為30個,超過按照實際進行覆蓋--><DefaultRolloverStrategy max="30"/></RollingFile><!--fatal 正常運行時日志--><RollingFile name = "fatalRollingFile" fileName = "${FILE_NAME}/fatal日志.log"filePattern = "${FILE_PATH}/$${date:yyyy-MM-dd}/fatal-%d{yyyy-MM-dd-HH-mm}-%i.log.gz"><!--日志級別過濾器,文件只輸出level及以上級別的信息(onMatch),其他的直接拒絕(onMismatch)--><ThresholdFilter level="fatal" onMatch="ACCEPT" onMismatch="DENY"/><!--日志的消息格式--><PatternLayout pattern="${LOG_PATTERN}"/><!--在系統每次啟動時,觸發拆分規則,生產一個新的日志文件--><OnStartupTriggeringPolicy/><!--按照文件大小進行拆分--><SizeBasedTriggeringPolicy size = "10 MB"/><!--按照時間節點進行拆分--><TimeBasedTriggeringPolicy/><!--在同一個目錄下,文件的個數限定為30個,超過按照實際進行覆蓋--><DefaultRolloverStrategy max="30"/></RollingFile><!--info 操作日志--><RollingFile name = "infoRollingFile" fileName = "${FILE_NAME}/info日志.log"filePattern = "${FILE_PATH}/$${date:yyyy-MM-dd}/info-%d{yyyy-MM-dd-HH-mm}-%i.log.gz"><!--日志級別過濾器,文件只輸出level及以上級別的信息(onMatch),其他的直接拒絕(onMismatch)--><ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/><!--日志的消息格式--><PatternLayout pattern="${LOG_PATTERN}"/><!--在系統每次啟動時,觸發拆分規則,生產一個新的日志文件--><OnStartupTriggeringPolicy/><!--按照文件大小進行拆分--><SizeBasedTriggeringPolicy size = "10 MB"/><!--按照時間節點進行拆分--><TimeBasedTriggeringPolicy/><!--在同一個目錄下,文件的個數限定為30個,超過按照實際進行覆蓋--><DefaultRolloverStrategy max="30"/></RollingFile></Appenders><!--Logger節點用來單獨指定日志的形式,比如要為指定包下的class指定不同的日志級別等。--><!--然后定義loggers,只有定義了logger并引入的appender,appender才會生效--><loggers><!--過濾掉spring和mybatis的一些無用的信息--><logger name="org.mybatis" level="info" additivity="false"><AppenderRef ref="Console"/></logger><!--監控系統信息--><!--若是additivity設為false,則 子Logger 只會在自己的appender里輸出,而不會在 父Logger 的appender里輸出。--><Logger name="org.springframework" level="info" additivity="false"><AppenderRef ref="Console"/></Logger><root level="info"><!--控制臺--><appender-ref ref="Console"/><!--用戶操作文件--><appender-ref ref="infoRollingFile"/><!--調試錯誤文件--><appender-ref ref="errorRollingFile"/><!--正常運行文件--><appender-ref ref="fatalRollingFile"/></root></loggers></Configuration>三、添加全局異步日志 : log4j2.component.properties
Log4j2的最大優點就是它的異步Logger,主要就是性能更好,這個這里不做過多解釋。
#全局異步日志開啟,提高日志性能 Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector四、聲明log4j2的配置文件路徑 :
在application.properties的SpringBoot主配置文件中添加如下配置 :
我這里是給配置文件添加了個config的包目錄,你要改成你自己的路徑。
五、自定義注解類 :
自定義注解 : 主要是作用就是為了自定義方法操作,用于向AOP中添加方法操作的日志信息,既然已經到項目日志階段那么我相信你也已經不是一個小白了,注解就是一個標注這里也不做過多解釋。
//作用在方法上 @Target(ElementType.METHOD) //運行時 @Retention(RetentionPolicy.RUNTIME) @Documented public @interface LogAnnotationMethod {//模塊名稱String module() default "";//操作名稱String operator() default "";//擴展屬性String value() default ""; }六、創建HttpContextUtil工具類用于在IP工具類中獲取IP使用 :
public class HttpContextUtil {public static HttpServletRequest getHttpServletRequest(){return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();}}七、創建IP獲取工具類 :
用于獲取訪問的用戶IP,并記錄到日志中。
@Slf4j public class IpUtils {/*** 獲取IP地址* <p>* 使用Nginx等反向代理軟件, 則不能通過request.getRemoteAddr()獲取IP地址* 如果使用了多級反向代理的話,X-Forwarded-For的值并不止一個,而是一串IP地址,X-Forwarded-For中第一個非unknown的有效IP字符串,則為真實IP地址*/public static String getIpAddr(HttpServletRequest request) {String ip = null, unknown = "unknown", seperator = ",";int maxLength = 15;try {ip = request.getHeader("x-forwarded-for");if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (StringUtils.isEmpty(ip) || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_CLIENT_IP");}if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_X_FORWARDED_FOR");}if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}} catch (Exception e) {log.error("IpUtils ERROR ", e);}// 使用代理,則獲取第一個IP地址if (StringUtils.isEmpty(ip) && ip.length() > maxLength) {int idx = ip.indexOf(seperator);if (idx > 0) {ip = ip.substring(0, idx);}}return ip;}/*** 獲取ip地址** @return*/public static String getIpAddr() {HttpServletRequest request = HttpContextUtil.getHttpServletRequest();return getIpAddr(request);} }八、 編寫AOP日志切面類
此類用于保存和執行具體的日志信息。
/*** @author 碼不多* @version 1.0* @description: 此類采用SpringAOP,用于記錄日志*///聲明AOP類 @Aspect //聲明組件 @Component //使用Slf4j日志門面 @Slf4j public class AopLogUtil {//定義切入點方法 標注這個自定義注解@Pointcut("@annotation(com.shouzhong.epidemicprevention.annotation.LogAnnotationMethod)")public void pt(){}/*** 功能描述: 執行通知日志的方法* @author 碼不多* @date 2021/8/16* @param point* @return java.lang.Object*///攔截所有的Controller或者RestController或者自定義注解標識的方法@Around("@within(org.springframework.stereotype.Controller) ||"+"@within(org.springframework.web.bind.annotation.RestController) ||"+"pt()")private Object runAndSaveLog(ProceedingJoinPoint point){//獲取獲取當前時間long beginTime = System.currentTimeMillis();//執行原始方法Object result = null;try {result = point.proceed();} catch (Throwable throwable) {//出現異常打印error日志log.error("Aop中方法出現異常",throwable);}//獲取方法執行時間long runtime = System.currentTimeMillis() - beginTime;//調用保存日志的方法recordLog(point,runtime);//將結果返回return result;}/*** 功能描述: 保存日志的方法* @author 碼不多* @date 2021/8/16* @param joinPoint time* @return void*/private void recordLog(ProceedingJoinPoint joinPoint,long time){//獲取類名Object target = joinPoint.getTarget();String canonicalName = target.getClass().getCanonicalName();//獲取模塊名MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();Method method = methodSignature.getMethod();//獲取方法名Signature signature = joinPoint.getSignature();String methodName = signature.getName();//獲取請求參數Object[] args = joinPoint.getArgs();String params = JSON.toJSONString(args[0]);//獲取request,設置ip地址HttpServletRequest request = HttpContextUtil.getHttpServletRequest();//獲取IP地址String ipAddr = IpUtils.getIpAddr(request);//獲取注解對象LogAnnotationMethod logAnnotation = method.getAnnotation(LogAnnotationMethod.class);//添加類名到日志,采用占位符賦值log.info("類名: {}",canonicalName);//添加模塊日志信息,采用占位符賦值,通過注解對象獲取注解中的值log.info("模塊名: {}",logAnnotation.module());//添加方法名到日志,采用占位符賦值log.info("方法名: {}",methodName);//添加操作到日志,采用占位符賦值,通過注解對象獲取注解中的值log.info("操作: {}",logAnnotation.operator());//添加請求參數信息到日志,采用占位符賦值log.info("請求參數: {}:",params);//添加ip地址到日志,采用占位符賦值log.info("ip地址: {}",ipAddr);//添加執行時間到日志,采用占位符賦值log.info("執行時間: {} ms",time);//日志結束log.info("#####################log End####################");補充001 ://不使用IPUtils獲取ip和一些其他請求頭日志信息的方法: logger.info不在這里定義。還是在上面的代碼中/* // 接收請求,記錄請求中的內容ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();// 記錄下請求內容//請求的urllogger.info("URL : " + request.getRequestURL().toString());//請求的方法類型 : GET、POST...logger.info("HTTP_METHOD : " + request.getMethod());//IP地址logger.info("IP : " + request.getRemoteAddr());//類方法logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());//參數數組logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));//獲取所有參數方法 ://獲取請求參數們Enumeration<String> enu=request.getParameterNames();//遍歷while(enu.hasMoreElements()){//取出請求參數名String paraName=(String)enu.nextElement();//請求的具體參數值System.out.println(paraName+": "+request.getParameter(paraName));}}*/補充002:/*//如果有文件上傳的參數MultipartFile類型,為了避免沖突可以給它們添加進集合展示// 接收請求,記錄請求中的內容ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();//創建集合存儲請求參數ArrayList paramsList = new ArrayList();//獲取所有參數方法 :Enumeration<String> enu=request.getParameterNames();while(enu.hasMoreElements()){String paraName=(String)enu.nextElement();//將參數添加到集合paramsList.add(paraName+": "+request.getParameter(paraName));}log.info("請求參數 :{}",paramsList)*/} }九、控制器日志添加
在你需要記錄操作日志的Controller層的請求映射的方法上添加你的自定義注解,并將你的方法日志信息添加到你自定義的屬性值中。
//自定義注解,聲明方法日志@LogAnnotationMethod(module = "獲得信息列表",operator = "分頁查詢或多條件查詢信息需求列表")@RequestMapping("/InforDS")public Pagination<InforDemandSide> inforDemandSide(Pagination pagination){xxxxxxxxxxxxxxxxx;}十、開啟控制臺Mybatis的sql輸出 :
在application.properties的主配置文件中添加如下配置
簡單總結一下 :
整體日志記錄就是采用面向切面編程,你可以在配置文件中配置你的Appender或者修改你的日志級別。
如果你想在方法中使用日志記錄具體的操作,你可以在你需要記錄的類上添加@Slf4j這個注解,在你需要記錄日志的地方直接使用 log.xxx() 直接記錄你的日志即可。
如果這篇文章對你有幫助請為我點個小贊支持一下!
總結
以上是生活随笔為你收集整理的SpringBoot使用Slf4j+Log4j2完成项目的日志记录的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AI理论知识基础(25)-机器学习常见损
- 下一篇: @GetMapping和@PostMap