log4j2自定义动态配置日志
Posted 梦江黎
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了log4j2自定义动态配置日志相关的知识,希望对你有一定的参考价值。
项目需求
1.平台日志:Spring Boot项目中接入log4j2日志,公共配置作平台日志。
2.业务日志:为了方便定位业务问题自定义动态日志,根据不同业务输出不同名称的日志文件。
思路分析
平台日志
首先配置 pom.xml 文件,将原有的 logback 移除,改用 log4j2
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
添加 log4j 的包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
添加 log4j2.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration>
<!-- 全局参数 -->
<properties>
<Property name="displayName">platform</Property>
<Property name="log_path">logs/${displayName}</Property>
<Property name="pattern">%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %C %c{1}:%L -%m%n</Property>
</properties>
<!--先定义所有的appender-->
<appenders>
<!--这个输出控制台的配置-->
<console name="Console" target="SYSTEM_OUT" follow="true">
<PatternLayout>
<pattern>${pattern}</pattern>
</PatternLayout>
</console>
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用-->
<File name="log" fileName="logs/platform.log" append="false">
<!-- 指定error 级别的日志 -->
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout>
<pattern>${pattern}</pattern>
</PatternLayout>
</File>
<!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="RollingFileInfo" fileName="${log_path}/info.log"
filePattern="${log_path}/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<Filters>
<ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY" />
</Filters>
<PatternLayout>
<pattern>${pattern}</pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<DefaultRolloverStrategy max="100000"/>
</RollingFile>
<!-- <RollingFile name="RollingFileWarn" fileName="${log_path}/warn.log"
filePattern="${log_path}/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout>
<pattern>${pattern}</pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<!– DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 –>
<DefaultRolloverStrategy max="20"/>
</RollingFile>-->
<RollingFile name="RollingFileError" fileName="${log_path}/error.log"
filePattern="${log_path}/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout>
<pattern>${pattern}</pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<DefaultRolloverStrategy max="100000"/>
</RollingFile>
</appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
<loggers>
<!--过滤掉spring的一些无用的DEBUG信息-->
<logger name="org.springframework" level="WARN"></logger>
<logger name="org.hibernate" level="WARN"/>
<!--<logger name="com.lottery" level="DEBUG"></logger>-->
<Root level="INFO">
<appender-ref ref="Console"/>
<appender-ref ref="RollingFileInfo"/>
<!--<appender-ref ref="RollingFileWarn"/>-->
<appender-ref ref="RollingFileError"/>
</Root>
</loggers>
</configuration>
业务日志
直接贴代码了,根据输入的业务名称参数调用LogUtil工具方法生成不同名称的业务日志。为了规范化和方便管理,我将业务名称参数定义成枚举量,这样添加某业务日志需要去枚举类里面添加。
import com.lottery.enums.BusinessEnum;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.RollingFileAppender;
import org.apache.logging.log4j.core.appender.rolling.*;
import org.apache.logging.log4j.core.config.AppenderRef;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.filter.ThresholdFilter;
import org.apache.logging.log4j.core.layout.PatternLayout;
import java.io.File;
import static org.apache.logging.log4j.core.Filter.Result.DENY;
public class LogUtil {
/**
* 日志打印的目录
*/
private static final String datalogDir = "logs" + File.separator + "business";
private static final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
private static final Configuration config = ctx.getConfiguration();
private static final String INFO = "info";
private static final String ERROR = "error";
private LogUtil() {
}
/**
* 启动一个动态的logger
*/
private static void start(String loggerName) {
//创建一个展示的样式:PatternLayout, 还有其他的日志打印样式。
Layout layout = PatternLayout.newBuilder()
.withConfiguration(config).withPattern("%d{yyyy-MM-dd HH:mm:ss,SSS} %t %-5p %C %c{1}:%L -%m%n").build();
//单个日志文件大小
TimeBasedTriggeringPolicy tbtp = TimeBasedTriggeringPolicy.createPolicy(null, null);
TriggeringPolicy tp = SizeBasedTriggeringPolicy.createPolicy("100 MB");// 大小分割
CompositeTriggeringPolicy policyComposite = CompositeTriggeringPolicy.createPolicy(tbtp, tp);
String loggerDir = datalogDir + File.separator + loggerName;
//删除日志的条件
/* IfFileName ifFileName = IfFileName.createNameCondition(null, loggerName + "\\.\\d{4}-\\d{2}-\\d{2}.*");
IfLastModified ifLastModified = IfLastModified.createAgeCondition(Duration.parse("1d"));
DeleteAction deleteAction = DeleteAction.createDeleteAction(
loggerDir, false, 1, false, null,
new PathCondition[]{ifLastModified, ifFileName}, null, config);
Action[] actions = new Action[]{deleteAction};*/
DefaultRolloverStrategy strategy = DefaultRolloverStrategy.createStrategy(
"100000", "1", null, null, null, false, config);
ThresholdFilter filter = ThresholdFilter.createFilter(Level.INFO, Filter.Result.ACCEPT, DENY);
ThresholdFilter denyWarnFilter = ThresholdFilter.createFilter(Level.WARN, DENY, Filter.Result.NEUTRAL);
RollingFileAppender appender = RollingFileAppender.newBuilder()
.withFilter(filter)
.withFilter(denyWarnFilter)
.withFileName(loggerDir + "-info.log")
.withFilePattern(datalogDir + File.separator + "${date:yyyy-MM}" + File.separator + "info-%d{yyyy-MM-dd}-" + loggerName + ".%i.log")
.withAppend(true)
.withStrategy(strategy)
.withName(loggerName + INFO)
.withPolicy(policyComposite)
.withLayout(layout)
.withConfiguration(config)
.build();
appender.start();
config.addAppender(appender);
ThresholdFilter filterError = ThresholdFilter.createFilter(Level.ERROR, Filter.Result.ACCEPT, DENY);
RollingFileAppender appenderError = RollingFileAppender.newBuilder()
.withFilter(filterError)
.withFileName(loggerDir + "-error.log")
.withFilePattern(datalogDir + File.separator + "${date:yyyy-MM}" + File.separator + "error-%d{yyyy-MM-dd}-" + loggerName + ".%i.log")
.withAppend(true)
.withStrategy(strategy)
.withName(loggerName + ERROR)
.withPolicy(policyComposite)
.withLayout(layout)
.withConfiguration(config)
.build();
appenderError.start();
config.addAppender(appenderError);
AppenderRef refInfo = AppenderRef.createAppenderRef(loggerName + INFO, null, null);
AppenderRef refError = AppenderRef.createAppenderRef(loggerName + ERROR, null, null);
AppenderRef[] refs = new AppenderRef[]{refInfo, refError};
LoggerConfig loggerConfig = LoggerConfig.createLogger(false,
Level.INFO, loggerName, "true", refs, null, config, null);
loggerConfig.addAppender(appender, Level.INFO, null);
loggerConfig.addAppender(appenderError, Level.ERROR, null);
config.addLogger(loggerName, loggerConfig);
ctx.updateLoggers();
}
/**
* 使用完之后记得调用此方法关闭动态创建的logger,避免内存不够用或者文件打开太多
*/
public static void stop(BusinessEnum businessEnum) {
String loggerName = businessEnum.getMessage();
synchronized (config) {
config.getAppender(loggerName + INFO).stop();
config.getAppender(loggerName + ERROR).stop();
config.getLoggerConfig(loggerName).removeAppender(loggerName + INFO);
config.getLoggerConfig(loggerName).removeAppender(loggerName + ERROR);
config.removeLogger(loggerName);
ctx.updateLoggers();
}
}
/**
* 获取Logger
*/
public static Logger getLogger(BusinessEnum lotteryType) {
String loggerName = lotteryType.getMessage();
synchronized (config) {
if (!config.getLoggers().containsKey(loggerName)) {
start(loggerName);
}
}
return LogManager.getLogger(loggerName);
}
}
// LotteryType 是一个枚举类,用来获取不同名称的日志,做业务分割
代码动态配置
1. 系统逻辑日志
private static Logger log = LogManager.getLogger(BranchController.class);// 传入类class
2.自定义业务日志
private static Logger log = LogUtil.getLogger(LotteryType.FIVE);// lottryType为业务名称枚举,根据需求添加
代码中具体使用
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
private static Logger log = LogManager.getLogger(CapitalDetailService.class);
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自定义Appender(输出到文件/RPC服务中)