快捷搜索:  汽车  科技

log4j会默认生成配置文件吗(如何随心所欲地自定义log4j输出格式)

log4j会默认生成配置文件吗(如何随心所欲地自定义log4j输出格式)项目应用名称为项目的名称或者标识,例如支付服务定义应用名称为payService; 请求ip地址为用户真实的请求ip;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":"订单状态消息列表resp:{\"code\":1000 \"msg\":\"成功\"}"}其中: 版本号是指当前服务接口实际的版本信息,例如V1.0.1;

在某种情况下,我们需要在不影响原有代码的基础上自定义log4j的输出格式。

例如这样的需求,硬性规定了项目的日志格式为:

日期 日志等级 ClassName:line - [版本号] [请求ip地址] [项目应用名称] [服务接口模块] [模块方法] [业务参数1] [业务参数2] [业务参数3] 日志详细内容(必须为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":"订单状态消息列表resp:{\"code\":1000 \"msg\":\"成功\"}"}

其中:

版本号是指当前服务接口实际的版本信息,例如V1.0.1;

请求ip地址为用户真实的请求ip;

项目应用名称为项目的名称或者标识,例如支付服务定义应用名称为payService;

服务接口模块是指请求接口对应的模块代码,例如请求订单接口,则接口模块为OrderControlller;

模块方法是指接口对应的请求方法,例如下单接口对应模块方法为createOrder;

业务参数1可根据实际情况写入相应的业务数据,录入订单号orderId,该参数可为空;

业务参数2同上;

业务参数3同上;

日志详细内容是指请求接口时需打印出来的描述信息,例如创建订单异常时,在异常捕捉方法体中描述异常详细信息,日志内容需定义到一个json结构中。

以上是我遇到的场景,这时在不影响原有项目代码的基础上,我们做出日志格式的调整,使用如下方案:

1.通过log4j的占位替换符%X{}配合MDC格式化日志,使用AOP切面在请求线程开始处填充替换符变量

2.继承log4j的具体appender类,重写subAppend方法,修改日志输出的内容格式。

此时log4j文件如下


  1. log4j.rootCategory=INFO stdout file errorfile
  2. #log4j.category.com.cx=DEBUG
  3. log4j.logger.error=errorfile
  4. log4j.appender.stdout=com.test.common.GrayLogConsoleAppender
  5. log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
  6. 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
  7. log4j.appender.file=com.test.common.GrayLogDailyRollingFileAppender
  8. log4j.appender.file.file=${log.dir}/${spring.application.name}.log
  9. log4j.appender.file.DatePattern='.'yyyy-MM-dd
  10. log4j.appender.file.layout=org.apache.log4j.PatternLayout
  11. 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
  12. log4j.appender.errorfile=com.test.common.GrayLogDailyRollingFileAppender
  13. log4j.appender.errorfile.file=${log.dir}/${spring.application.name}_error.log
  14. log4j.appender.errorfile.DatePattern='.'yyyy-MM-dd
  15. log4j.appender.errorfile.Threshold = ERROR
  16. log4j.appender.errorfile.layout=org.apache.log4j.PatternLayout
  17. 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请求生命周期内生效。

切面代码如下:


  1. @Around("execution(public * com.test.controller..*.*(..))")
  2. public Object aroundController(ProceedingJoinPoint joinPoint) {
  3. ServletrequestAttributes attributes =(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  4. HttpServletRequest request =attributes.getRequest();
  5. String execIp = request.getHeader("X-Real-IP");
  6. if(StringUtils.isBlank(execIp)){
  7. execIp=request.getRemoteAddr();
  8. }
  9. String execClass= joinPoint.getTarget().getClass().getSimpleName();
  10. String execMethod = joinPoint.getSignature().getName();
  11. Map<String String[]> map = request.getParameterMap();
  12. List<String> paramsList = new ArrayList<>();
  13. for(Map.Entry<String String []> m : map.entrySet()){
  14. String [] value = m.getValue();
  15. paramsList.add( m.getKey() "=" StringUtils.join(value " "));
  16. }
  17. String execParams = "[" StringUtils.join(paramsList "&") "] [] []";
  18. MDC.put("log_version" " - [V1.0.0]");
  19. MDC.put("log_item" " [violation-mini]");
  20. MDC.put("log_module" " [" execClass "]");
  21. MDC.put("log_method" " [" execMethod "]");
  22. MDC.put("log_req_params" " " execParams);
  23. MDC.put("log_ip" " [" execIp "]");
  24. Object result= null;
  25. try {
  26. result = joinPoint.proceed();
  27. } catch (Throwable throwable) {
  28. LOGGER.error("方法异常:" throwable);
  29. }
  30. return result;
  31. }

至此,格式中的MDC变量都已被放入成功。

下一步,将原本的日志内容套上json外套。

新建GrayLogConsoleAppender类继承具体的appender类

代码如下:


  1. package com.test.common;
  2. import net.sf.json.JSONObject;
  3. import org.apache.commons.lang.StringUtils;
  4. import org.apache.log4j.ConsoleAppender;
  5. import org.apache.log4j.spi.LoggingEvent;
  6. import org.apache.log4j.spi.ThrowableInformation;
  7. import java.lang.reflect.Field;
  8. /**
  9. * @Author: Lxx
  10. * @Description:
  11. * @Date: Created in 17:29 2018/5/30
  12. */
  13. public class GrayLogConsoleAppender extends ConsoleAppender {
  14. @Override
  15. protected void subAppend(LoggingEvent event) {
  16. try {
  17. Class<LoggingEvent> clazz = LoggingEvent.class;
  18. Field filed = clazz.getDeclaredField("throwableInfo");
  19. filed.setAccessible(true);
  20. Object exception = filed.get(event);
  21. JSONObject json = new JSONObject();
  22. if(exception != null){
  23. if(exception instanceof ThrowableInformation){
  24. ThrowableInformation throwableInformation = (ThrowableInformation) exception;
  25. String [] details = throwableInformation.getThrowableStrRep();
  26. String error_msg = StringUtils.join(details "\r\n");
  27. json.put("exception" error_msg);
  28. }
  29. }
  30. filed.set(event null);
  31. boolean flag = false;
  32. Field filed1 = clazz.getDeclaredField("message");
  33. filed1.setAccessible(true);
  34. Object message = filed1.get(event);
  35. if (message instanceof String) {
  36. String msg = (String) message;
  37. if (message != null) {
  38. flag = true;
  39. }
  40. json.put("body" msg);
  41. filed1.set(event json.toString());
  42. }
  43. if(!flag){
  44. Field filed2 = clazz.getDeclaredField("renderedMessage");
  45. filed2.setAccessible(true);
  46. Object message2 = filed2.get(event);
  47. if (message2 instanceof String) {
  48. String msg = (String) message2;
  49. json.put("body" msg);
  50. filed2.set(event json.toString());
  51. }
  52. }
  53. } catch (Exception e) {
  54. e.printStackTrace();
  55. }
  56. super.subAppend(event);
  57. }
  58. }

至此,已为日志内容套上json外套,并且当有异常日志时,将异常的堆栈信息放入json的exception中输出出来,不打印堆栈信息。

最终结果:

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":"结果为:ResponseResult{code='0' msg='null' errormsg='查询成功' data={} successFlag=false}"}

log4j会默认生成配置文件吗(如何随心所欲地自定义log4j输出格式)(1)

猜您喜欢: