Log4j2不同级别的日志写入到不同文件+扩展日志级别实现业务日志分类

目录

前言

1.不同级别的日志输出到不同的文件

1.1    自定义RollingFile配置

1.2    自定义Console

1.3    Async异步输出

1.4    自定义Logger配置

1.5    自定义统一日志类LogUtil.java

1.6    使用说明

1.6.1.1 通过LogUtil创建业务类日志对象

1.6.1.2 使用业务类日志对象

1.7    日志输出样式

2.扩展日志级别

2.1    自定义日志级别

2.2    自定义RollingFile配置

2.3    Async异步输出

2.4    root引入扩展日志级别

2.5    自定义统一日志类LogUtil.java

2.6    StandardLevel

2.7    使用说明

2.7.1 通过LogUtil创建业务类日志对象

2.7.2 使用业务类日志对象

 

2.8    日志输出样式

3.动态Appender

4.扩展log4j2自定义Appender


前言

该篇适合于工程项目已有对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

 

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值