log4j2自定义动态配置日志

Posted 梦江黎

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了log4j2自定义动态配置日志相关的知识,希望对你有一定的参考价值。


项目需求

1.平台日志:Spring Boot项目中接入log4j2日志,公共配置作平台日志。
2.业务日志:为了方便定位业务问题自定义动态日志,根据不同业务输出不同名称的日志文件。

思路分析

平台日志

首先配置 pom.xml 文件,将原有的 logback 移除,改用 log4j2

 
   
   
 
  1. <dependency>

  2. <groupId>org.springframework.boot</groupId>

  3. <artifactId>spring-boot-starter</artifactId>

  4. <exclusions>

  5. <exclusion>

  6. <groupId>org.springframework.boot</groupId>

  7. <artifactId>spring-boot-starter-logging</artifactId>

  8. </exclusion>

  9. </exclusions>

  10. </dependency>

添加 log4j 的包

 
   
   
 
  1. <dependency>

  2. <groupId>org.springframework.boot</groupId>

  3. <artifactId>spring-boot-starter-log4j2</artifactId>

  4. </dependency>

添加 log4j2.xml 配置文件

 
   
   
 
  1. <?xml version="1.0" encoding="UTF-8"?>

  2. <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->

  3. <!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->

  4. <!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->

  5. <configuration>

  6. <!-- 全局参数 -->

  7. <properties>

  8. <Property name="displayName">platform</Property>

  9. <Property name="log_path">logs/${displayName}</Property>

  10. <Property name="pattern">%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %C %c{1}:%L -%m%n</Property>


  11. </properties>

  12. <!--先定义所有的appender-->

  13. <appenders>

  14. <!--这个输出控制台的配置-->

  15. <console name="Console" target="SYSTEM_OUT" follow="true">

  16. <PatternLayout>

  17. <pattern>${pattern}</pattern>

  18. </PatternLayout>

  19. </console>

  20. <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用-->

  21. <File name="log" fileName="logs/platform.log" append="false">

  22. <!-- 指定error 级别的日志 -->

  23. <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>

  24. <PatternLayout>

  25. <pattern>${pattern}</pattern>

  26. </PatternLayout>

  27. </File>

  28. <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->

  29. <RollingFile name="RollingFileInfo" fileName="${log_path}/info.log"

  30. filePattern="${log_path}/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">

  31. <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->

  32. <Filters>

  33. <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>

  34. <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY" />

  35. </Filters>

  36. <PatternLayout>

  37. <pattern>${pattern}</pattern>

  38. </PatternLayout>

  39. <Policies>

  40. <TimeBasedTriggeringPolicy/>

  41. <SizeBasedTriggeringPolicy size="100 MB"/>

  42. </Policies>

  43. <DefaultRolloverStrategy max="100000"/>

  44. </RollingFile>

  45. <!-- <RollingFile name="RollingFileWarn" fileName="${log_path}/warn.log"

  46. filePattern="${log_path}/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">

  47. <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>

  48. <PatternLayout>

  49. <pattern>${pattern}</pattern>

  50. </PatternLayout>

  51. <Policies>

  52. <TimeBasedTriggeringPolicy/>

  53. <SizeBasedTriggeringPolicy size="100 MB"/>

  54. </Policies>

  55. &lt;!&ndash; DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 &ndash;&gt;

  56. <DefaultRolloverStrategy max="20"/>

  57. </RollingFile>-->

  58. <RollingFile name="RollingFileError" fileName="${log_path}/error.log"

  59. filePattern="${log_path}/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">

  60. <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>

  61. <PatternLayout>

  62. <pattern>${pattern}</pattern>

  63. </PatternLayout>

  64. <Policies>

  65. <TimeBasedTriggeringPolicy/>

  66. <SizeBasedTriggeringPolicy size="100 MB"/>

  67. </Policies>

  68. <DefaultRolloverStrategy max="100000"/>

  69. </RollingFile>

  70. </appenders>

  71. <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->

  72. <loggers>

  73. <!--过滤掉spring的一些无用的DEBUG信息-->

  74. <logger name="org.springframework" level="WARN"></logger>

  75. <logger name="org.hibernate" level="WARN"/>

  76. <!--<logger name="com.lottery" level="DEBUG"></logger>-->

  77. <Root level="INFO">

  78. <appender-ref ref="Console"/>

  79. <appender-ref ref="RollingFileInfo"/>

  80. <!--<appender-ref ref="RollingFileWarn"/>-->

  81. <appender-ref ref="RollingFileError"/>

  82. </Root>

  83. </loggers>

  84. </configuration>

业务日志

直接贴代码了,根据输入的业务名称参数调用LogUtil工具方法生成不同名称的业务日志。为了规范化和方便管理,我将业务名称参数定义成枚举量,这样添加某业务日志需要去枚举类里面添加。

 
   
   
 
  1. import com.lottery.enums.BusinessEnum;

  2. import org.apache.logging.log4j.Level;

  3. import org.apache.logging.log4j.LogManager;

  4. import org.apache.logging.log4j.Logger;

  5. import org.apache.logging.log4j.core.Filter;

  6. import org.apache.logging.log4j.core.Layout;

  7. import org.apache.logging.log4j.core.LoggerContext;

  8. import org.apache.logging.log4j.core.appender.RollingFileAppender;

  9. import org.apache.logging.log4j.core.appender.rolling.*;

  10. import org.apache.logging.log4j.core.config.AppenderRef;

  11. import org.apache.logging.log4j.core.config.Configuration;

  12. import org.apache.logging.log4j.core.config.LoggerConfig;

  13. import org.apache.logging.log4j.core.filter.ThresholdFilter;

  14. import org.apache.logging.log4j.core.layout.PatternLayout;

  15. import java.io.File;

  16. import static org.apache.logging.log4j.core.Filter.Result.DENY;


  17. public class LogUtil {

  18. /**

  19. * 日志打印的目录

  20. */

  21. private static final String datalogDir = "logs" + File.separator + "business";


  22. private static final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);

  23. private static final Configuration config = ctx.getConfiguration();

  24. private static final String INFO = "info";

  25. private static final String ERROR = "error";



  26. private LogUtil() {

  27. }


  28. /**

  29. * 启动一个动态的logger

  30. */

  31. private static void start(String loggerName) {


  32. //创建一个展示的样式:PatternLayout, 还有其他的日志打印样式。

  33. Layout layout = PatternLayout.newBuilder()

  34. .withConfiguration(config).withPattern("%d{yyyy-MM-dd HH:mm:ss,SSS} %t %-5p %C %c{1}:%L -%m%n").build();


  35. //单个日志文件大小

  36. TimeBasedTriggeringPolicy tbtp = TimeBasedTriggeringPolicy.createPolicy(null, null);

  37. TriggeringPolicy tp = SizeBasedTriggeringPolicy.createPolicy("100 MB");// 大小分割

  38. CompositeTriggeringPolicy policyComposite = CompositeTriggeringPolicy.createPolicy(tbtp, tp);


  39. String loggerDir = datalogDir + File.separator + loggerName;

  40. //删除日志的条件

  41. /* IfFileName ifFileName = IfFileName.createNameCondition(null, loggerName + "\\.\\d{4}-\\d{2}-\\d{2}.*");

  42. IfLastModified ifLastModified = IfLastModified.createAgeCondition(Duration.parse("1d"));

  43. DeleteAction deleteAction = DeleteAction.createDeleteAction(

  44. loggerDir, false, 1, false, null,

  45. new PathCondition[]{ifLastModified, ifFileName}, null, config);

  46. Action[] actions = new Action[]{deleteAction};*/


  47. DefaultRolloverStrategy strategy = DefaultRolloverStrategy.createStrategy(

  48. "100000", "1", null, null, null, false, config);

  49. ThresholdFilter filter = ThresholdFilter.createFilter(Level.INFO, Filter.Result.ACCEPT, DENY);

  50. ThresholdFilter denyWarnFilter = ThresholdFilter.createFilter(Level.WARN, DENY, Filter.Result.NEUTRAL);

  51. RollingFileAppender appender = RollingFileAppender.newBuilder()

  52. .withFilter(filter)

  53. .withFilter(denyWarnFilter)

  54. .withFileName(loggerDir + "-info.log")

  55. .withFilePattern(datalogDir + File.separator + "${date:yyyy-MM}" + File.separator + "info-%d{yyyy-MM-dd}-" + loggerName + ".%i.log")

  56. .withAppend(true)

  57. .withStrategy(strategy)

  58. .withName(loggerName + INFO)

  59. .withPolicy(policyComposite)

  60. .withLayout(layout)

  61. .withConfiguration(config)

  62. .build();

  63. appender.start();

  64. config.addAppender(appender);

  65. ThresholdFilter filterError = ThresholdFilter.createFilter(Level.ERROR, Filter.Result.ACCEPT, DENY);

  66. RollingFileAppender appenderError = RollingFileAppender.newBuilder()

  67. .withFilter(filterError)

  68. .withFileName(loggerDir + "-error.log")

  69. .withFilePattern(datalogDir + File.separator + "${date:yyyy-MM}" + File.separator + "error-%d{yyyy-MM-dd}-" + loggerName + ".%i.log")

  70. .withAppend(true)

  71. .withStrategy(strategy)

  72. .withName(loggerName + ERROR)

  73. .withPolicy(policyComposite)

  74. .withLayout(layout)

  75. .withConfiguration(config)

  76. .build();

  77. appenderError.start();

  78. config.addAppender(appenderError);


  79. AppenderRef refInfo = AppenderRef.createAppenderRef(loggerName + INFO, null, null);

  80. AppenderRef refError = AppenderRef.createAppenderRef(loggerName + ERROR, null, null);

  81. AppenderRef[] refs = new AppenderRef[]{refInfo, refError};

  82. LoggerConfig loggerConfig = LoggerConfig.createLogger(false,

  83. Level.INFO, loggerName, "true", refs, null, config, null);

  84. loggerConfig.addAppender(appender, Level.INFO, null);

  85. loggerConfig.addAppender(appenderError, Level.ERROR, null);

  86. config.addLogger(loggerName, loggerConfig);

  87. ctx.updateLoggers();

  88. }


  89. /**

  90. * 使用完之后记得调用此方法关闭动态创建的logger,避免内存不够用或者文件打开太多

  91. */

  92. public static void stop(BusinessEnum businessEnum) {

  93. String loggerName = businessEnum.getMessage();

  94. synchronized (config) {

  95. config.getAppender(loggerName + INFO).stop();

  96. config.getAppender(loggerName + ERROR).stop();

  97. config.getLoggerConfig(loggerName).removeAppender(loggerName + INFO);

  98. config.getLoggerConfig(loggerName).removeAppender(loggerName + ERROR);

  99. config.removeLogger(loggerName);

  100. ctx.updateLoggers();

  101. }

  102. }


  103. /**

  104. * 获取Logger

  105. */

  106. public static Logger getLogger(BusinessEnum lotteryType) {

  107. String loggerName = lotteryType.getMessage();

  108. synchronized (config) {

  109. if (!config.getLoggers().containsKey(loggerName)) {

  110. start(loggerName);

  111. }

  112. }

  113. return LogManager.getLogger(loggerName);

  114. }

  115. }

  116. // LotteryType 是一个枚举类,用来获取不同名称的日志,做业务分割

代码动态配置

 
   
   
 
  1. 1. 系统逻辑日志

  2. private static Logger log = LogManager.getLogger(BranchController.class);// 传入类class


  3. 2.自定义业务日志

  4. private static Logger log = LogUtil.getLogger(LotteryType.FIVE);// lottryType为业务名称枚举,根据需求添加

代码中具体使用

 
   
   
 
  1. import org.apache.logging.log4j.LogManager;

  2. import org.apache.logging.log4j.Logger;


  3. private static Logger log = LogManager.getLogger(CapitalDetailService.class);



  4. log.info("日志信息")

参考资料

 
Log4j2官方文档
http://logging.apache.org/log4j/2.x/manual/architecture.html 
Log4j2官方文档翻译
https://blog.csdn.net/liyiming2017/article/details/82781030 

更多文章


 

长按二维码关注,阅读我的程序员故事

以上是关于log4j2自定义动态配置日志的主要内容,如果未能解决你的问题,请参考以下文章

2019-07-03 log4j2 自定义ElasticSearch Appender

log4j2如何实现日志自脱敏?山人自有妙计!

log4j2自定义Appender(输出到文件/RPC服务中)

Spring Boot + Log4j2 日志框架配置 (Maven)

教你打印自己的日志 -- 如何自定义 log4j2 各组件

利用 Log4j2 异步保存日志到 MongoDB 中