log4j控制日志輸出文件名稱(chēng)的兩種方式小結(jié)
log4j控制日志輸出文件名稱(chēng)
1. 第一種方式
在類(lèi)對(duì)象中用如下方式定義logger變量
private static Logger logger = Logger.getLogger("lemmaXml");
這樣通過(guò)名稱(chēng)的方式獲取logger,需要在log4j.properties文件中定義一個(gè)名稱(chēng)為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
上述配置說(shuō)明,名稱(chēng)為lemmaXml的appender,是每天形成一個(gè)日志文件,日志文件的名稱(chēng)為
${catalina.home}/logs/lemmaXml.log
日志的格式為
%5p %d{yyyy-MM-dd HH:mm:ss} %m %n
2. 第二種方式(這種方式親測(cè)正確)
在類(lèi)對(duì)象中用如下方式定義logger變量
import org.apache.log4j.Logger; private static Logger logger = Logger.getLogger(ExportLemmaManagerService.class);
即,通過(guò)類(lèi)的參數(shù)來(lái)獲取logger變量,此時(shí)必然也需要在log4j.properties文件有關(guān)于該對(duì)象日志文件的輸出配置,當(dāng)然這里的配置不是針對(duì)每個(gè)類(lèi)專(zhuān)門(mén)配置,而是針對(duì)一個(gè)路徑整體配置,即,你可以配置某個(gè)目錄下的所有類(lèi)方式的輸出文件的文件名稱(chēng),如下
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類(lèi)就是在該包下,所以用該包下的類(lèi)方式來(lái)定義logger的話,輸出的文件就都在service.log文件中,每天生成一個(gè)新的日志文件。
如何隨心所欲地自定義log4j輸出格式
在某種情況下,我們需要在不影響原有代碼的基礎(chǔ)上自定義log4j的輸出格式。
例如這樣的需求,硬性規(guī)定了項(xiàng)目的日志格式為:
日期 日志等級(jí) ClassName:line - [版本號(hào)] [請(qǐng)求ip地址] [項(xiàng)目應(yīng)用名稱(chēng)] [服務(wù)接口模塊] [模塊方法] [業(yè)務(wù)參數(shù)1] [業(yè)務(wù)參數(shù)2] [業(yè)務(wù)參數(shù)3] 日志詳細(xì)內(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\":\"成功\"}"}
其中:
- 版本號(hào)是指當(dāng)前服務(wù)接口實(shí)際的版本信息,例如V1.0.1;
- 請(qǐng)求ip地址為用戶真實(shí)的請(qǐng)求ip;
- 項(xiàng)目應(yīng)用名稱(chēng)為項(xiàng)目的名稱(chēng)或者標(biāo)識(shí),例如支付服務(wù)定義應(yīng)用名稱(chēng)為payService;
- 服務(wù)接口模塊是指請(qǐng)求接口對(duì)應(yīng)的模塊代碼,例如請(qǐng)求訂單接口,則接口模塊為OrderControlller;
- 模塊方法是指接口對(duì)應(yīng)的請(qǐng)求方法,例如下單接口對(duì)應(yīng)模塊方法為createOrder;
- 業(yè)務(wù)參數(shù)1可根據(jù)實(shí)際情況寫(xiě)入相應(yīng)的業(yè)務(wù)數(shù)據(jù),錄入訂單號(hào)orderId,該參數(shù)可為空;
- 業(yè)務(wù)參數(shù)2同上;
- 業(yè)務(wù)參數(shù)3同上;
- 日志詳細(xì)內(nèi)容是指請(qǐng)求接口時(shí)需打印出來(lái)的描述信息,例如創(chuàng)建訂單異常時(shí),在異常捕捉方法體中描述異常詳細(xì)信息,日志內(nèi)容需定義到一個(gè)json結(jié)構(gòu)中。
以上是我遇到的場(chǎng)景,這時(shí)在不影響原有項(xiàng)目代碼的基礎(chǔ)上,我們做出日志格式的調(diào)整,使用如下方案:
1.通過(guò)log4j的占位替換符%X{}配合MDC格式化日志,使用AOP切面在請(qǐng)求線程開(kāi)始處填充替換符變量
2.繼承l(wèi)og4j的具體appender類(lèi),重寫(xiě)subAppend方法,修改日志輸出的內(nèi)容格式。
此時(shí)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文件,我們做了兩個(gè)變動(dòng),一個(gè)是添加了%X{value}的變量,另一個(gè)則是將原本的DailyRollingFileAppender修改成了com.test.common.GrayLogConsoleAppender。
處理log4j的變量,對(duì)代碼進(jìn)行controller切面,在一個(gè)http請(qǐng)求java的入口中放入線程變量,該線程變量在當(dāng)次http請(qǐng)求生命周期內(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類(lèi)繼承具體的appender類(lèi)
代碼如下:
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外套,并且當(dāng)有異常日志時(shí),將異常的堆棧信息放入json的exception中輸出出來(lái),不打印堆棧信息。
最終結(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}"}
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
springCloud中的Sidecar多語(yǔ)言支持詳解
這篇文章主要介紹了springCloud中的Sidecar多語(yǔ)言支持詳解,Sidecar是將一組緊密結(jié)合的任務(wù)與主應(yīng)用程序共同放在一臺(tái)主機(jī)Host中,但會(huì)將它們部署在各自的進(jìn)程或容器中,需要的朋友可以參考下2024-01-01
Java通過(guò)JsApi方式實(shí)現(xiàn)微信支付
本文講解了Java如何實(shí)現(xiàn)JsApi方式的微信支付,代碼內(nèi)容詳細(xì),文章思路清晰,需要的朋友可以參考下2015-07-07
Java集合ArrayDeque類(lèi)實(shí)例分析
這篇文章主要介紹了Java集合ArrayDeque類(lèi)實(shí)例分析的相關(guān)資料,需要的朋友可以參考下2017-04-04
SpringBoot密碼加密的實(shí)現(xiàn)示例
本文主要介紹了SpringBoot密碼加密的實(shí)現(xiàn)示例,包括引入依賴(lài)、配置加密工具、生成加密密鑰、加密密碼、配置解密,具有一定的參考價(jià)值,感興趣的可以了解一下2024-08-08

