log4j控制日志輸出文件名稱的兩種方式小結(jié)
log4j控制日志輸出文件名稱
1. 第一種方式
在類對象中用如下方式定義logger變量
private static Logger logger = Logger.getLogger("lemmaXml");
這樣通過名稱的方式獲取logger,需要在log4j.properties文件中定義一個名稱為lemmaXml的appender,配置如下:
log4j.logger.lemmaXml=INFO,lemmaXml log4j.appender.lemmaXml=org.apache.log4j.DailyRollingFileAppender log4j.appender.lemmaXml.Threshold=DEBUG log4j.appender.lemmaXml.File=${catalina.home}/logs/lemmaXml.log log4j.appender.lemmaXml.layout=org.apache.log4j.PatternLayout log4j.appender.lemmaXml.layout.ConversionPattern=%5p %d{yyyy-MM-dd HH:mm:ss} %m %n
上述配置說明,名稱為lemmaXml的appender,是每天形成一個日志文件,日志文件的名稱為
${catalina.home}/logs/lemmaXml.log
日志的格式為
%5p %d{yyyy-MM-dd HH:mm:ss} %m %n
2. 第二種方式(這種方式親測正確)
在類對象中用如下方式定義logger變量
import org.apache.log4j.Logger; private static Logger logger = Logger.getLogger(ExportLemmaManagerService.class);
即,通過類的參數(shù)來獲取logger變量,此時必然也需要在log4j.properties文件有關(guān)于該對象日志文件的輸出配置,當然這里的配置不是針對每個類專門配置,而是針對一個路徑整體配置,即,你可以配置某個目錄下的所有類方式的輸出文件的文件名稱,如下
log4j.rootLogger=info,stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Threshold=DEBUG log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p %d{yyyy-MM-dd HH:mm:ss} %c %m %n log4j.logger.com.soso.baike.service=DEBUG,ServiceLog log4j.appender.ServiceLog=org.apache.log4j.DailyRollingFileAppender log4j.appender.ServiceLog.Threshold=DEBUG log4j.appender.ServiceLog.File=${catalina.home}/logs/service.log log4j.appender.ServiceLog.MaxBackupIndex=10 log4j.appender.ServiceLog.layout=org.apache.log4j.PatternLayout log4j.appender.ServiceLog.layout.ConversionPattern=%5p %d{yyyy-MM-dd HH:mm:ss} %c %m %n
上述配置了目錄com.soso.baike.service下的輸出文件方式,上述ExportLemmaManagerService類就是在該包下,所以用該包下的類方式來定義logger的話,輸出的文件就都在service.log文件中,每天生成一個新的日志文件。
如何隨心所欲地自定義log4j輸出格式
在某種情況下,我們需要在不影響原有代碼的基礎(chǔ)上自定義log4j的輸出格式。
例如這樣的需求,硬性規(guī)定了項目的日志格式為:
日期 日志等級 ClassName:line - [版本號] [請求ip地址] [項目應(yīng)用名稱] [服務(wù)接口模塊] [模塊方法] [業(yè)務(wù)參數(shù)1] [業(yè)務(wù)參數(shù)2] [業(yè)務(wù)參數(shù)3] 日志詳細內(nèi)容(必須為json格式)
示例:
2018-05-10 14:04:50,972 INFO ViolationService:51 - [v1.0.0] [192.168.137.47] [merchant-service.cx580.com] [OrderController] [messageList] [null] [] [] {"body":"訂單狀態(tài)消息列表resp:{\"code\":1000,\"msg\":\"成功\"}"}
其中:
- 版本號是指當前服務(wù)接口實際的版本信息,例如V1.0.1;
- 請求ip地址為用戶真實的請求ip;
- 項目應(yīng)用名稱為項目的名稱或者標識,例如支付服務(wù)定義應(yīng)用名稱為payService;
- 服務(wù)接口模塊是指請求接口對應(yīng)的模塊代碼,例如請求訂單接口,則接口模塊為OrderControlller;
- 模塊方法是指接口對應(yīng)的請求方法,例如下單接口對應(yīng)模塊方法為createOrder;
- 業(yè)務(wù)參數(shù)1可根據(jù)實際情況寫入相應(yīng)的業(yè)務(wù)數(shù)據(jù),錄入訂單號orderId,該參數(shù)可為空;
- 業(yè)務(wù)參數(shù)2同上;
- 業(yè)務(wù)參數(shù)3同上;
- 日志詳細內(nèi)容是指請求接口時需打印出來的描述信息,例如創(chuàng)建訂單異常時,在異常捕捉方法體中描述異常詳細信息,日志內(nèi)容需定義到一個json結(jié)構(gòu)中。
以上是我遇到的場景,這時在不影響原有項目代碼的基礎(chǔ)上,我們做出日志格式的調(diào)整,使用如下方案:
1.通過log4j的占位替換符%X{}配合MDC格式化日志,使用AOP切面在請求線程開始處填充替換符變量
2.繼承l(wèi)og4j的具體appender類,重寫subAppend方法,修改日志輸出的內(nèi)容格式。
此時log4j文件如下
log4j.rootCategory=INFO, stdout, file, errorfile #log4j.category.com.cx=DEBUG log4j.logger.error=errorfile log4j.appender.stdout=com.test.common.GrayLogConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p %c{1}:%L%X{log_version}%X{log_ip}%X{log_item}%X{log_module}%X{log_method}%X{log_req_params} %m%n log4j.appender.file=com.test.common.GrayLogDailyRollingFileAppender log4j.appender.file.file=${log.dir}/${spring.application.name}.log log4j.appender.file.DatePattern='.'yyyy-MM-dd log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p %c{1}:%L%X{log_version}%X{log_ip}%X{log_item}%X{log_module}%X{log_method}%X{log_req_params} %m%n log4j.appender.errorfile=com.test.common.GrayLogDailyRollingFileAppender log4j.appender.errorfile.file=${log.dir}/${spring.application.name}_error.log log4j.appender.errorfile.DatePattern='.'yyyy-MM-dd log4j.appender.errorfile.Threshold = ERROR log4j.appender.errorfile.layout=org.apache.log4j.PatternLayout log4j.appender.errorfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p %c{1}:%L%X{log_version}%X{log_ip}%X{log_item}%X{log_module}%X{log_method}%X{log_req_params} %m%n
在log4j.properties文件,我們做了兩個變動,一個是添加了%X{value}的變量,另一個則是將原本的DailyRollingFileAppender修改成了com.test.common.GrayLogConsoleAppender。
處理log4j的變量,對代碼進行controller切面,在一個http請求java的入口中放入線程變量,該線程變量在當次http請求生命周期內(nèi)生效。
切面代碼如下:
@Around("execution(public * com.test.controller..*.*(..))") public Object aroundController(ProceedingJoinPoint joinPoint) { ServletRequestAttributes attributes =(ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request =attributes.getRequest(); String execIp = request.getHeader("X-Real-IP"); if(StringUtils.isBlank(execIp)){ execIp=request.getRemoteAddr(); } String execClass= joinPoint.getTarget().getClass().getSimpleName(); String execMethod = joinPoint.getSignature().getName(); Map<String,String[]> map = request.getParameterMap(); List<String> paramsList = new ArrayList<>(); for(Map.Entry<String,String []> m : map.entrySet()){ String [] value = m.getValue(); paramsList.add( m.getKey() + "=" + StringUtils.join(value,",")); } String execParams = "[" + StringUtils.join(paramsList,"&") + "] [] []"; MDC.put("log_version"," - [V1.0.0]"); MDC.put("log_item"," [violation-mini]"); MDC.put("log_module"," [" + execClass + "]"); MDC.put("log_method"," [" + execMethod+ "]"); MDC.put("log_req_params"," " + execParams); MDC.put("log_ip"," [" + execIp+ "]"); Object result= null; try { result = joinPoint.proceed(); } catch (Throwable throwable) { LOGGER.error("方法異常:",throwable); } return result; }
至此,格式中的MDC變量都已被放入成功。
下一步,將原本的日志內(nèi)容套上json外套。
新建GrayLogConsoleAppender類繼承具體的appender類
代碼如下:
package com.test.common; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.spi.LoggingEvent; import org.apache.log4j.spi.ThrowableInformation; import java.lang.reflect.Field; /** * @Author: Lxx * @Description: * @Date: Created in 17:29 2018/5/30 */ public class GrayLogConsoleAppender extends ConsoleAppender { @Override protected void subAppend(LoggingEvent event) { try { Class<LoggingEvent> clazz = LoggingEvent.class; Field filed = clazz.getDeclaredField("throwableInfo"); filed.setAccessible(true); Object exception = filed.get(event); JSONObject json = new JSONObject(); if(exception != null){ if(exception instanceof ThrowableInformation){ ThrowableInformation throwableInformation = (ThrowableInformation) exception; String [] details = throwableInformation.getThrowableStrRep(); String error_msg = StringUtils.join(details,"\r\n"); json.put("exception",error_msg); } } filed.set(event,null); boolean flag = false; Field filed1 = clazz.getDeclaredField("message"); filed1.setAccessible(true); Object message = filed1.get(event); if (message instanceof String) { String msg = (String) message; if (message != null) { flag = true; } json.put("body", msg); filed1.set(event, json.toString()); } if(!flag){ Field filed2 = clazz.getDeclaredField("renderedMessage"); filed2.setAccessible(true); Object message2 = filed2.get(event); if (message2 instanceof String) { String msg = (String) message2; json.put("body", msg); filed2.set(event, json.toString()); } } } catch (Exception e) { e.printStackTrace(); } super.subAppend(event); } }
至此,已為日志內(nèi)容套上json外套,并且當有異常日志時,將異常的堆棧信息放入json的exception中輸出出來,不打印堆棧信息。
最終結(jié)果:
2018-06-09 00:48:31,849 INFO LogAspect:65 - [V1.0.0] [223.88.53.135] [violation-mini] [TestController] [queryList] [appName=abc&authType=test&avatar=&nickName=&token=asdfasdfadsfasdf&userId=asdfasdfasdfasdf&userType=aaaaa] [] [] {"body":"結(jié)果為:ResponseResult{code='0', msg='null', errormsg='查詢成功', data={}, successFlag=false}"}
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
完美解決SpringCloud-OpenFeign使用okhttp替換不生效問題
這篇文章主要介紹了完美解決SpringCloud-OpenFeign使用okhttp替換不生效問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02Spring?Boot?集成JWT實現(xiàn)前后端認證的示例代碼
小程序、H5應(yīng)用的快速發(fā)展,使得前后端分離已經(jīng)成為了趨勢,本文主要介紹了Spring?Boot?集成JWT實現(xiàn)前后端認證,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-04-04關(guān)于SpringCloud分布式系統(tǒng)中實現(xiàn)冪等性的幾種方式
這篇文章主要介紹了關(guān)于SpringCloud分布式系統(tǒng)中實現(xiàn)冪等性的幾種方式,冪等函數(shù),或冪等方法,是指可以使用相同參數(shù)重復(fù)執(zhí)行,并能獲得相同結(jié)果的函數(shù),這些函數(shù)不會影響系統(tǒng)狀態(tài),也不用擔心重復(fù)執(zhí)行會對系統(tǒng)造成改變,需要的朋友可以參考下2023-10-10Java?synchornized與ReentrantLock處理并發(fā)出現(xiàn)的錯誤
synchronized機制提供了對每個對象相關(guān)的隱式監(jiān)視器鎖,并強制所有鎖的獲取和釋放都必須在同一個塊結(jié)構(gòu)中。當獲取了多個鎖時,必須以相反的順序釋放。即synchronized對于鎖的釋放是隱式的2023-01-01spring中在xml配置中加載properties文件的步驟
這篇文章主要介紹了在spring中如何在xml配置中加載properties文件,本文分步驟給大家介紹在XML配置中加載properties文件的方法,需要的朋友可以參考下2023-07-07關(guān)于Hystrix的監(jiān)控及可視化面板
這篇文章主要介紹了關(guān)于Hystrix的監(jiān)控及可視化面板,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08Java實現(xiàn)查找算法的示例代碼(二分查找、插值查找、斐波那契查找)
查找就是根據(jù)給定的某個值,在查找表中確定一個其關(guān)鍵字等于給定值的數(shù)據(jù)元素。本文介紹了常見的數(shù)據(jù)查找算法:順序查找、二分查找、插值查找和斐波那契查找等以及相應(yīng)的Java代碼實現(xiàn)。需要的可以參考一下2022-01-01Java如何使用while循環(huán)計算一個整數(shù)的位數(shù)
這篇文章主要介紹了Java使用while循環(huán)計算一個整數(shù)的位數(shù)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01