目录
前言
该篇适合于工程项目已有对info、warn、error等不同级别日志的统一拦截,仍需要根据业务将不同业务的日志分级别输出到不同的文件中,这些日志文件只有该业务的日志,不含有系统其它业务、框架等的日志;网上很多都是千篇一律的东西,内容老旧,错误百出,不能解决问题,该篇文章是将实际的集成过程整理,并将遇到的问题进行了说明,另外java代码实现动态日志附加器是根据log4j2的新方法实现,不是网上都在转的log4j(很多方法会显示已启用),可供大家参考。
设计思路:
- 首先定义业务名称,日志输出的时候带有该业务的名称(日志格式可以灵活设置)
- 其次通过统一的LogUtil工具类进行输出,并将logger标签配置到LogUtil,这样做的目的是避免系统已存在的日志附加器RollingFile配置输出的日志写入到业务日志文件,造成日志混乱,
- 统一的LogUtil类,通过集成原生的ExtendedLoggerWrapper既实现了日志级别的扩展,也可以在业务类使用LogUtil的时候通过工厂模式获取到当前业务类的日志对象,从而通过该对象获取到业务类的全路径,方便日志输出的灵活改造。
- 最后扩展日志级别,扩展的日志级别主要用于补充业务场景,更多的用在公共组件、中间件内部进行输出(根据实际情况和要求设计)。
这里再说一下,如果对整个系统实现不同级别的日志写入到不同的文件,以下的配置修改一下即可,而且不需要创建LogUtil,也不需要扩展日志级别,配置好RollingFile,Console,直接引入到root(实际上是使用了默认的日志检测路径)就可以实现。
1.不同级别的日志输出到不同的文件
1.1 自定义RollingFile配置
自定义Customer类型、INFO级别日志输出
名称:CustomerInfoLog
存储路径:项目配置路径+ /logs/Customer/CustomerInfo.log
日志输出格式:%d{yyyy-MM-dd HH:mm:ss.SSS} |-%-5level [%thread] Customer [%L] %msg%n"
其他内容参考以下配置:LOG_HOME是自己配置的项目日志路径property;FILE_DEADLINE是自己配置的保存时间property。
<!-- 自定义Customer类型INFO级别日志附加器 -->
<!-- 滚动文件生成 fileName日志文件名称,filePattern压缩文件路径 -->
<RollingFile name="CustomerInfoLog" fileName="${LOG_HOME}/logs/Customer/CustomerInfo.log"
filePattern="${LOG_HOME}/logs/Customer/CustomerInfo/CustomerInfo-%d{MM-dd-yyyy}-%i.log.gz">
<!-- 过滤日志级别 info及以上 -->
<Filters>
<ThresholdFilter onMismatch="NEUTRAL" onMatch="DENY" level="WARN"/>
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY" />
</Filters>
<!-- 指定日志输出格式 -->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} |-%-5level [%thread] Customer [%L] %msg%n" />
<!-- 新文件生成策略,每100M生成一个新文件 -->
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="100 MB" />
</Policies>
<!-- 默认过度策略,不设置,则默认为最多同一文件夹下7个文件 大于此值会删除旧的日志文件 -->
<DefaultRolloverStrategy>
<!-- 文件删除策略,basePath扫描路径,maxDepth目录的最大级别数,IfFileName文件名称,IfLastModified指定持续时间(7天前)-->
<Delete basePath="${LOG_HOME}" maxDepth="4">
<IfFileName glob="*/Customer/CustomerInfo/CustomerInfo-*.log.gz" />
<IfLastModified age="${FILE_DEADLINE}" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
自定义Customer类型、WARN级别日志输出
名称:CustomerWarnLog
存储路径:项目配置路径+ /logs/Customer/CustomerWarn.log
日志输出格式:%d{yyyy-MM-dd HH:mm:ss.SSS} |-%-5level [%thread] Customer [%L] %msg%n"
其他内容参考以下配置:LOG_HOME是自己配置的项目日志路径property;FILE_DEADLINE是自己配置的保存时间property。
<!-- 自定义WARN级别日志附加器 -->
<!-- 滚动文件生成 fileName日志文件名称,filePattern压缩文件路径 -->
<RollingFile name="CustomerWarnLog" fileName="${LOG_HOME}/logs/Customer/CustomerWarn.log"
filePattern="${LOG_HOME}/logs/Customer/CustomerWarn/CustomerWarn-%d{MM-dd-yyyy}-%i.log.gz">
<!-- 过滤日志级别 info及以上 -->
<Filters>
<ThresholdFilter onMismatch="NEUTRAL" onMatch="DENY" level="ERROR"/>
<ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY" />
</Filters>
<!-- 指定日志输出格式 -->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} |-%-5level [%thread] Customer [%L] %msg%n" />
<!-- 新文件生成策略,每100M生成一个新文件 -->
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="100 MB" />
</Policies>
<!-- 默认过度策略,不设置,则默认为最多同一文件夹下7个文件 大于此值会删除旧的日志文件 -->
<DefaultRolloverStrategy>
<!-- 文件删除策略,basePath扫描路径,maxDepth目录的最大级别数,IfFileName文件名称,IfLastModified指定持续时间(7天前)-->
<Delete basePath="${LOG_HOME}" maxDepth="4">
<IfFileName glob="*/Customer/CustomerWarn/CustomerWarn-*.log.gz" />
<IfLastModified age="${FILE_DEADLINE}" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
自定义Customer类型、ERROR级别日志输出
名称:CustomerErrorLog
存储路径:项目配置路径+ /logs/Customer/CustomerError.log
日志输出格式:%d{yyyy-MM-dd HH:mm:ss.SSS} |-%-5level [%thread] Customer [%L] %msg%n"
其他内容参考以下配置:LOG_HOME是自己配置的项目日志路径property;FILE_DEADLINE是自己配置的保存时间property。
<!-- 自定义ERROR级别日志附加器 -->
<!-- 滚动文件生成 fileName日志文件名称,filePattern压缩文件路径 -->
<RollingFile name="CustomerErrorLog" fileName="${LOG_HOME}/logs/Customer/CustomerError.log"
filePattern="${LOG_HOME}/logs/Customer/CustomerError/CustomerError-%d{MM-dd-yyyy}-%i.log.gz">
<!-- 过滤日志级别 info及以上 -->
<Filters>
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY" />
</Filters>
<!-- 指定日志输出格式 -->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} |-%-5level [%thread] Customer [%L] %msg%n" />
<!-- 新文件生成策略,每100M生成一个新文件 -->
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="100 MB" />
</Policies>
<!-- 默认过度策略,不设置,则默认为最多同一文件夹下7个文件 大于此值会删除旧的日志文件 -->
<DefaultRolloverStrategy>
<!-- 文件删除策略,basePath扫描路径,maxDepth目录的最大级别数,IfFileName文件名称, IfLastModified指定持续时间(7天前)-->
<Delete basePath="${LOG_HOME}" maxDepth="4">
<IfFileName glob="*/Customer/CustomerError/CustomerError-*.log.gz" />
<IfLastModified age="${FILE_DEADLINE}" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
1.2 自定义Console
自定义Customer类型、CONSOLE(控制台)日志输出
名称:CustomerCONSOLE
日志输出格式:%d{yyyy-MM-dd HH:mm:ss.SSS} |-%-5level [%thread] Customer [%L] %msg%n"
<Console name="CustomerCONSOLE" target="system_out">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} |-%-5level [%thread] Customer [%L] %msg%n" />
</Console>
日志输出格式如下:
2020-11-22 20:07:06.927 |-INFO [http-nio-9991-exec-1] Customer [] com.xxx.controller.TestController -| xxxxxxxxx
1.3 Async异步输出
将Customer日志类型的INFO、WARN、ERROR的RollingFile以及自定义的CONSOLE引入
<Async name="AsyncCustomerInfoLog">
<AppenderRef ref="CustomerInfoLog"/>
</Async>
<Async name="AsyncCustomerWarnLog">
<AppenderRef ref="CustomerWarnLog"/>
</Async>
<Async name="AsyncCustomerErrorLog">
<AppenderRef ref="CustomerErrorLog"/>
</Async>
<Async name="AsyncCustomerCONSOLE">
<AppenderRef ref="CustomerCONSOLE"/>
</Async>
1.4 自定义Logger配置
name:com.xxx.util.LogUtil 指向统一日志类,由该类统一输出日志
level:info 级别不能高于引入的附加器(appender)的最低等级
<!-- 使用LogUtil输出日志的,写入到指定的文件 -->
<Logger name="com.xxx.util.LogUtil" level="info" additivity="false">
<AppenderRef ref="AsyncCustomerCONSOLE" />
<AppenderRef ref="AsyncCustomerInfoLog"/>
<AppenderRef ref="AsyncCustomerWarnLog"/>
<AppenderRef ref="AsyncCustomerErrorLog"/>
</Logger>
注意:自定义的异步RollingFile和Console不能引入到root中,否则,导致其他路径下的同级别日志也会写入到自定义的文件中。
loggers配置说明
- name:用来指定该Logger所适用的类或者类所在的包全路径,继承自Root节点。一般是项目包名或者框架的包名
- level:日志输出级别,未指定级别,则默认为 ERROR
- AppenderRef:Logger的子节点,用来指定该日志输出到哪个Appender,如果没有指定,就会默认继承自Root。如果指定了,那么会在指定的这个Appender和Root的Appender中都会输出,此时我们可以设置Logger的additivity="false"只在自定义的Appender中进行输出
1.5 自定义统一日志类LogUtil.java
关键代码:
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.MessageFactory;
import org.apache.logging.log4j.spi.AbstractLogger;
import org.apache.logging.log4j.spi.ExtendedLoggerWrapper;
import org.apache.logging.log4j.util.MessageSupplier;
import org.apache.logging.log4j.util.Supplier;
/**
* 自定义日志工具类.
*
* @Author: gaoxd
* @Date: 2020/11/11 15:17
* @Version: 1.0
**/
public class LogUtil extends ExtendedLoggerWrapper {
private final Logger log = LogManager.getLogger(LogUtil.class);
private final ExtendedLoggerWrapper logger;
private static final String FQCN = LogUtil.class.getName();
private LogUtil(final Logger logger) {
super((AbstractLogger) logger, logger.getName(), logger.getMessageFactory());
this.logger = this;
}
public static LogUtil getLogger() {
final Logger wrapped = LogManager.getLogger();
return new LogUtil(wrapped);
}
public static LogUtil getLogger(final Class<?> loggerName) {
final Logger wrapped = LogManager.getLogger(loggerName);
return new LogUtil(wrapped);
}
public static LogUtil getLogger(final Class<?> loggerName, final MessageFactory factory) {
final Logger wrapped = LogManager.getLogger(loggerName, factory);
return new LogUtil(wrapped);
}
public static LogUtil getLogger(final Object value) {
final Logger wrapped = LogManager.getLogger(value);
return new LogUtil(wrapped);
}
public static LogUtil getLogger(final Object value, final MessageFactory factory) {
final Logger wrapped = LogManager.getLogger(value, factory);
return new LogUtil(wrapped);
}
public static LogUtil getLogger(final String name) {
final Logger wrapped = LogManager.getLogger(name);
return new LogUtil(wrapped);
}
public static LogUtil getLogger(final String name, final MessageFactory factory) {
final Logger wrapped = LogManager.getLogger(name, factory);
return new LogUtil(wrapped);
}
/**
* 改写slf4j2 info级别部分方法
* 可以自行添加
* 为了防止调用超出该类实现的info方法,名字后加了Util跟原info方法区分,
* 如果将slf4j2的info方法全部重写,可以在该类中将方法命名为info.
*/
public void infoUtil(final String message) {
log.info(logger.getName() + " -| " + message);
}
public void infoUtil(final String message, final Object... params) {
log.info(logger.getName() + " -| " + message, params);
}
public void infoUtil(final String message, final Throwable t) {
log.info(logger.getName() + " -| " + message, t);
}
public void infoUtil(final String message, final Supplier<?>... paramSuppliers) {
log.info(logger.getName() + " -| " + message, paramSuppliers);
}
/**
* 改写slf4j2 warn级别部分方法
* 可以自行添加
* 为了防止调用超出该类实现的warn方法,名字后加了Util跟原warn方法区分,
* 如果将slf4j2的warn方法全部重写,可以在该类中将方法命名为warn.
*/
public void warnUtil(final String message) {
log.warn(logger.getName() + " -| " + message);
}
public void warnUtil(final String message, final Object... params) {
log.warn(logger.getName() + " -| " + message, params);
}
public void warnUtil(final String message, final Throwable t) {
log.warn(logger.getName() + " -| " + message, t);
}
public void warnUtil(final String message, final Supplier<?>... paramSuppliers) {
log.warn(logger.getName() + " -| " + message, paramSuppliers);
}
/**
* 改写slf4j2 error级别部分方法
* 可以自行添加
* 为了防止调用超出该类实现的error方法,名字后加了Util跟原error方法区分,
* 如果将slf4j2的error方法全部重写,可以在该类中将方法命名为error.
*/
public void errorUtil(final String message) {
log.error(logger.getName() + " -| " + message);
}
public void errorUtil(final String message, final Object... params) {
log.error(logger.getName() + " -| " + message, params);
}
public void errorUtil(final String message, final Throwable t) {
log.error(logger.getName() + " -| " + message, t);
}
public void errorUtil(final String message, final Supplier<?>... paramSuppliers) {
log.error(logger.getName() + " -| " + message, paramSuppliers);
}
}
特殊说明:
1. 继承ExtendedLoggerWrapper类是为了使用log4j2的原生获取logger对象的方式,从而通过logger.getName获取使用LogUtil的业务类的class全路径,方便对日志输入改写;应该还有其他的方式,比如:动态代理的方式,这里没有采用,有兴趣可以尝试。
2. 2020-11-22 20:07:06.927 |-INFO [http-nio-9991-exec-1] Customer [] com.xxx.controller.TestController -| xxxx 日志中的com.xxx.controller.TestController(调用LogUtil的业务类的全路径)并非通过配置文件加进去,而是跟msg拼接加入进去。
3. private final Logger log = LogManager.getLogger(LogUtil.class); 使用log4j2原生方式创建LogUtil类自己的日志对象,以使用对象的原生方法info/warn/error,从而与Logger配置的name对应。当前只扩展了部分方法,而且为了跟原生方法区分,防止调用到原生方法,方法名称定义为infoUtil/warnUtil/errorUtil。
1.6 使用说明
1.6.1.1 通过LogUtil创建业务类日志对象
private final LogUtil LOGGER = LogUtil.getLogger(TestController.class);
1.6.1.2 使用业务类日志对象
LOGGER.infoUtil("TestController:infoUtil");
LOGGER.warnUtil("TestController:warnUtil");
LOGGER.errorUtil("TestController:errorUtil");
关键代码:
import com.xxx.util.LogUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 文件描述
* @Author: gaoxd
* @Date: 2020/11/11 18:43
* @Version: 1.0
**/
@RestController
@Slf4j
public class TestController {
private final LogUtil LOGGER = LogUtil.getLogger(TestController.class);
@GetMapping("test1")
public Object test1() {
LOGGER.infoUtil("TestController:infoUtil");
LOGGER.info("TestController:info");
LOGGER.warnUtil("TestController:warnUtil");
LOGGER.warn("TestController:warn");
LOGGER.errorUtil("TestController:errorUtil");
LOGGER.error("TestController:error");
return "test1";
}
}
特殊说明:用LogUtil对象调用原生的info/warn/error方法会在控制台输出,不能输出到指定的业务日志文件中;如果我们在LogUtil中将方法名称定义跟log4j2原生的方法一样并且全部覆盖,那么LOGGER.info()的调用方式既可以输出到控制台,也可以输出到指定文件。
1.7 日志输出样式
- 2020-11-22 20:07:06.927 |-INFO [http-nio-9991-exec-1] Customer [] com.xxx.controller.TestController -| TestController:infoUtil
- 2020-11-22 20:07:06.927 |-WARN [http-nio-9991-exec-1] Customer [] com.xxx.controller.TestController -| TestController:warnUtil
- 2020-11-22 20:07:06.927 |-ERROR [http-nio-9991-exec-1] Customer [] com.xxx.controller.TestController -| TestController:errorUtil
2.扩展日志级别
2.1 自定义日志级别
<!-- 自定义日志级别 -->
<CustomLevels>
<CustomLevel name="EXCEPTION" intLevel="70" />
<CustomLevel name="BUSINESS" intLevel="80" />
<CustomLevel name="MIDDLE" intLevel="90" />
</CustomLevels>
特说说明:该处可以不配置,不影响使用,只是服务启动的时候会报错误日志,没有默认值;
2.2 自定义RollingFile配置
自定义BUSINESS级别日志输出
名称:BusinessLog
存储路径:项目配置路径+ /logs/ business.log
其他内容参考以下配置
<!-- 滚动文件生成 fileName日志文件名称,filePattern压缩文件路径 -->
<RollingFile name="BusinessLog" fileName="${LOG_HOME}/logs/business.log"
filePattern="${LOG_HOME}/logs/business/business-%d{MM-dd-yyyy}-%i.log.gz">
<!-- 过滤日志级别 info及以上 -->
<Filters>
<ThresholdFilter onMismatch="NEUTRAL" onMatch="DENY" level="EXCEPTION"/>
<ThresholdFilter level="BUSINESS" onMatch="ACCEPT" onMismatch="DENY" />
</Filters>
<!-- 指定日志输出格式 -->
<PatternLayout pattern="${LOG_PATTERN}" />
<!-- 新文件生成策略,每100M生成一个新文件 -->
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="100 MB" />
</Policies>
<!-- 默认过度策略,不设置,则默认为最多同一文件夹下7个文件 大于此值会删除旧的日志文件 -->
<DefaultRolloverStrategy>
<!-- 文件删除策略,basePath扫描路径,maxDepth目录的最大级别数,IfFileName文件名称, IfLastModified指定持续时间(7天前)-->
<Delete basePath="${LOG_HOME}" maxDepth="4">
<IfFileName glob="*/business/business-*.log.gz" />
<IfLastModified age="${FILE_DEADLINE}" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
自定义MIDDLE级别日志输出
名称:MiddleLog
存储路径:项目配置路径+ /logs/middle.log
其他内容参考以下配置
<!-- 滚动文件生成 fileName日志文件名称,filePattern压缩文件路径 -->
<RollingFile name="MiddleLog" fileName="${LOG_HOME}/logs/middle.log"
filePattern="${LOG_HOME}/logs/middle/middle-%d{MM-dd-yyyy}-%i.log.gz">
<!-- 过滤日志级别 info及以上 -->
<Filters>
<ThresholdFilter onMismatch="NEUTRAL" onMatch="DENY" level="BUSINESS"/>
<ThresholdFilter level="MIDDLE" onMatch="ACCEPT" onMismatch="DENY" />
</Filters>
<!-- 指定日志输出格式 -->
<PatternLayout pattern="${LOG_PATTERN}" />
<!-- 新文件生成策略,每100M生成一个新文件 -->
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="100 MB" />
</Policies>
<!-- 默认过度策略,不设置,则默认为最多同一文件夹下7个文件 大于此值会删除旧的日志文件 -->
<DefaultRolloverStrategy>
<!-- 文件删除策略,basePath扫描路径,maxDepth目录的最大级别数,IfFileName文件名称,IfLastModified指定持续时间(7天前)-->
<Delete basePath="${LOG_HOME}" maxDepth="4">
<IfFileName glob="*/middle/middle-*.log.gz" />
<IfLastModified age="${FILE_DEADLINE}" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
自定义EXCEPTION级别日志输出
名称:ExceptionLog
存储路径:项目配置路径+ /logs/exception.log
其他内容参考以下配置
<!-- 滚动文件生成 fileName日志文件名称,filePattern压缩文件路径 -->
<RollingFile name="ExceptionLog" fileName="${LOG_HOME}/logs/exception.log"
filePattern="${LOG_HOME}/logs/exception/exception-%d{MM-dd-yyyy}-%i.log.gz">
<!-- 过滤日志级别 info及以上 -->
<Filters>
<ThresholdFilter level="EXCEPTION" onMatch="ACCEPT" onMismatch="DENY" />
</Filters>
<!-- 指定日志输出格式 -->
<PatternLayout pattern="${LOG_PATTERN}" />
<!-- 新文件生成策略,每100M生成一个新文件 -->
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="100 MB" />
</Policies>
<!-- 默认过度策略,不设置,则默认为最多同一文件夹下7个文件 大于此值会删除旧的日志文件 -->
<DefaultRolloverStrategy>
<!-- 文件删除策略,basePath扫描路径,maxDepth目录的最大级别数,IfFileName文件名称,IfLastModified指定持续时间(7天前)-->
<Delete basePath="${LOG_HOME}" maxDepth="4">
<IfFileName glob="*/exception/exception-*.log.gz" />
<IfLastModified age="${FILE_DEADLINE}" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
2.3 Async异步输出
<Async name="AsyncBusinessLog">
<AppenderRef ref="BusinessLog"/>
</Async>
<Async name="AsyncExceptionLog">
<AppenderRef ref="ExceptionLog"/>
</Async>
<Async name="AsyncMiddleLog">
<AppenderRef ref="MiddleLog"/>
</Async>
2.4 root引入扩展日志级别
特殊说明:对项目路径中使用到的扩展日志输出进行写入到文件;如需配置到指定路径下,需要配置logger。
<loggers>
<root level="${LOG_LEVEL}">
<!-- 引入扩展日志级别的Appender,对项目路径中使用到的扩展日志输出进行写入到文件 -->
<AppenderRef ref="AsyncBusinessLog"/>
<AppenderRef ref="AsyncExceptionLog"/>
<AppenderRef ref="AsyncMiddleLog"/>
</root>
</loggers>
2.5 自定义统一日志类LogUtil.java
关键代码:只实现的部分日志输出的方法,可以自行扩展
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.MessageFactory;
import org.apache.logging.log4j.spi.AbstractLogger;
import org.apache.logging.log4j.spi.ExtendedLoggerWrapper;
import org.apache.logging.log4j.util.MessageSupplier;
import org.apache.logging.log4j.util.Supplier;
public class LogUtil extends ExtendedLoggerWrapper {
private final ExtendedLoggerWrapper logger;
private static final String FQCN = LogUtil.class.getName();
/**
* 异常日志级别.
*/
private static final Level EXCEPTION = Level.forName("EXCEPTION", 70);
/**
* 业务日志级别.
*/
private static final Level BUSINESS = Level.forName("BUSINESS", 80);
/**
* 中间结果级别.
*/
private static final Level MIDDLE = Level.forName("MIDDLE", 90);
private LogUtil(final Logger logger) {
super((AbstractLogger) logger, logger.getName(), logger.getMessageFactory());
this.logger = this;
}
public static LogUtil getLogger() {
final Logger wrapped = LogManager.getLogger();
return new LogUtil(wrapped);
}
public static LogUtil getLogger(final Class<?> loggerName) {
final Logger wrapped = LogManager.getLogger(loggerName);
return new LogUtil(wrapped);
}
public static LogUtil getLogger(final Class<?> loggerName, final MessageFactory factory) {
final Logger wrapped = LogManager.getLogger(loggerName, factory);
return new LogUtil(wrapped);
}
public static LogUtil getLogger(final Object value) {
final Logger wrapped = LogManager.getLogger(value);
return new LogUtil(wrapped);
}
public static LogUtil getLogger(final Object value, final MessageFactory factory) {
final Logger wrapped = LogManager.getLogger(value, factory);
return new LogUtil(wrapped);
}
public static LogUtil getLogger(final String name) {
final Logger wrapped = LogManager.getLogger(name);
return new LogUtil(wrapped);
}
public static LogUtil getLogger(final String name, final MessageFactory factory) {
final Logger wrapped = LogManager.getLogger(name, factory);
return new LogUtil(wrapped);
}
/**自定义级别:business*/
public void business(final Marker marker, final Message msg) {
logger.logIfEnabled(FQCN, BUSINESS, marker, msg, (Throwable) null);
}
public void business(final Marker marker, final Message msg, final Throwable t) {
logger.logIfEnabled(FQCN, BUSINESS, marker, msg, t);
}
public void business(final Marker marker, final Object message) {
logger.logIfEnabled(FQCN, BUSINESS, marker, message, (Throwable) null);
}
/**自定义级别:exception*/
public void exception(final Marker marker, final Message msg) {
logger.logIfEnabled(FQCN, EXCEPTION, marker, msg, (Throwable) null);
}
public void exception(final Marker marker, final Message msg, final Throwable t) {
logger.logIfEnabled(FQCN, EXCEPTION, marker, msg, t);
}
public void exception(final Marker marker, final Object message) {
logger.logIfEnabled(FQCN, EXCEPTION, marker, message, (Throwable) null);
}
/**自定义级别:middle*/
public void middle(final Marker marker, final Message msg) {
logger.logIfEnabled(FQCN, MIDDLE, marker, msg, (Throwable) null);
}
public void middle(final Marker marker, final Message msg, final Throwable t) {
logger.logIfEnabled(FQCN, MIDDLE, marker, msg, t);
}
public void middle(final Marker marker, final Object message) {
logger.logIfEnabled(FQCN, MIDDLE, marker, message, (Throwable) null);
}
}
特殊说明:目前参考示例扩展了3个日志级别BUSINESS/MIDDLE/EXCEPTION,对应的int值分别是80/70/90,数字越小日志级别越高
2.6 StandardLevel
org.apache.logging.log4j.spi. StandardLevel log4j2原生的日志级别类
数字越小级别越高
/**
* No events will be logged.
*/
OFF(0),
/**
* A severe error that will prevent the application from continuing.
*/
FATAL(100),
/**
* An error in the application, possibly recoverable.
*/
ERROR(200),
/**
* An event that might possible lead to an error.
*/
WARN(300),
/**
* An event for informational purposes.
*/
INFO(400),
/**
* A general debugging event.
*/
DEBUG(500),
/**
* A fine-grained debug message, typically capturing the flow through the application.
*/
TRACE(600),
/**
* All events should be logged.
*/
ALL(Integer.MAX_VALUE);
2.7 使用说明
2.7.1 通过LogUtil创建业务类日志对象
private final LogUtil LOGGER = LogUtil.getLogger(TestController.class);
2.7.2 使用业务类日志对象
LOGGER.middle("middle");
LOGGER.business("business");
LOGGER.exception("exception");
关键代码
import com.xxx.util.LogUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class TestController {
private final LogUtil LOGGER = LogUtil.getLogger(TestController.class);
@GetMapping("test1")
public Object test1() {
LOGGER.middle("middle");
LOGGER.business("business");
LOGGER.exception("exception");
return "test1";
}
}
2.8 日志输出样式
2020-11-22 20:07:43.000 |-BUSINESS [http-nio-9991-exec-1] com.xxx.controller.TestController [] -| business
2020-11-22 20:07:43.000 |-MIDDLE [http-nio-9991-exec-1] com.xxx.controller.TestController [] -| middle
2020-11-22 20:07:43.000 |-EXCEPTION [http-nio-9991-exec-1] com.xxx.controller.TestController [] -| exception
3.动态Appender
主要代码是start方法中的,采用log4j2的新方法,个人任务这种方式不如直接配置了
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.LoggerContext;
import org.apache.logging.log4j.core.appender.RollingFileAppender;
import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy;
import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy;
import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy;
import org.apache.logging.log4j.core.appender.rolling.action.*;
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;
/**
* 自定义日志类.
*
* @Author: gaoxd
* @Date: 2020/11/10 20:09
* @Version: 1.0
**/
public class LogUtilTest {
private final static Logger logger = LogManager.getLogger(LogUtilTest.class);
public static void info (String msg) {
logger.info(msg);
}
// todo:实现info的多个方法
/**
* 日志打印的目录
*/
private static final String DARALOGDIR = "log" + File.separator + "logs";
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 LogUtilTest() {
}
/**
* 启动一个动态的logger
*/
private static void start(String loggerName) {
// 日志展示的样式:PatternLayout
PatternLayout patternLayout = PatternLayout.newBuilder()
.withConfiguration(CONFIG).withPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} |-%-5level [%thread] %c [%L] -| %msg%n").build();
// 根据时间滚动文件策略 默认interval是1 每一个时间粒度生成一个新文件,modulate默认false 不以0点为偏移
TimeBasedTriggeringPolicy timeBasedTriggeringPolicy = TimeBasedTriggeringPolicy.newBuilder().build();
// 根据文件大小滚动策略 每100M生成一个新文件
SizeBasedTriggeringPolicy sizeBasedTriggeringPolicy = SizeBasedTriggeringPolicy.createPolicy("100 MB");
// 包含其他触发策略的触发策略
CompositeTriggeringPolicy compositeTriggeringPolicy =
CompositeTriggeringPolicy.createPolicy(timeBasedTriggeringPolicy, sizeBasedTriggeringPolicy);
// log文件名称
String loggerDir = DARALOGDIR + File.separator + loggerName;
// todo:删除日志的策略
IfFileName ifFileName = IfFileName.createNameCondition("*/customize/customize-*.log.gz", null);
// 日志保留7天
IfLastModified ifLastModified = IfLastModified.createAgeCondition(Duration.parse("7d"));
DeleteAction deleteAction = DeleteAction.createDeleteAction(
loggerDir, false, 4, false, null,
new PathCondition[]{ifLastModified, ifFileName}, null, CONFIG);
Action[] actions = new Action[]{deleteAction};
DefaultRolloverStrategy defaultRolloverStrategy =
DefaultRolloverStrategy.newBuilder().withConfig(CONFIG).withCustomActions(actions).build();
ThresholdFilter filter = ThresholdFilter.createFilter(Level.INFO, Filter.Result.ACCEPT, DENY);
RollingFileAppender appender = RollingFileAppender.newBuilder()
.setFilter(filter)
.withFileName(loggerDir + ".log")
// todo:
.withFilePattern(DARALOGDIR + File.separator + loggerName + "-%d{yyyy-MM-dd}-.%i.log")
.withAppend(true)
.withStrategy(defaultRolloverStrategy)
// todo:
.setName(loggerName + INFO)
.withPolicy(compositeTriggeringPolicy)
.setLayout(patternLayout)
.setConfiguration(CONFIG)
.build();
appender.start();
CONFIG.addAppender(appender);
// todo:
AppenderRef refInfo = AppenderRef.createAppenderRef(loggerName + INFO, null, null);
AppenderRef[] refs = new AppenderRef[]{refInfo};
LoggerConfig loggerConfig = LoggerConfig.createLogger(false,
Level.INFO, loggerName, "true", refs, null, CONFIG, null);
loggerConfig.addAppender(appender, Level.INFO, null);
CONFIG.addLogger(loggerName, loggerConfig);
LoggerConfig rootLogger = CONFIG.getRootLogger();
rootLogger.addAppender(appender, Level.INFO, null);
CTX.updateLoggers();
}
public void stop(String loggerName) {
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 getCustomizeLogger(final Class<?> clazz) {
synchronized
(CONFIG) {
if (!CONFIG.getLoggers().containsKey(CONFIG)) {
start("customize");
}
}
return LogManager.getLogger(clazz);
}
}
4.扩展log4j2自定义Appender
写在这篇底部内容会很长,所以新写了一篇,请参考:
https://blog.csdn.net/gxdogjava/article/details/110749120