6. Spring Boot 2.x With Apache Log4j2

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/hadues/article/details/100856124


这篇博文讲解Spring Boot 项目如何集成 Log4j 2.x

0x01 前言

Spring Boot 不仅支持默认的Logback 日志框架的集成,而且也支持Log4j 2.x 的支持。

Apache Log4j 2.x 官网宣称:

Apache Log4j 2是对Log4j的升级,它比其前身Log4j1.x提供了重大改进,并提供了Logback中可用的许多改进,同时修复了Logback架构中的一些固有问题。

简单来说就是 Log4J 2.x 比logback 要更好更强大

0x02 依赖说明

  • 下面是log4j 2.x 的一些依赖说明
组件 描述
log4j-api 应用程序应使用和编码的接口
log4j-core 标准实现,也称为Log4j 2 Core,包含Appender,Filters等。
log4j-iostreams 用于处理旧API的额外类,这些API期望来自java.io的类用于记录
log4j-taglib 使用Log4j 2在JavaServer Pages™中实现无Java登录的标记库。
JSP Tag Library (TLD Doc) Log4j 2 JSP标记库的特殊类Javadoc标记库文档。
  • 兼容组件依赖库
组件 描述
Commons Logging Bridge 允许针对Apache Commons Logging API编写的应用程序使用Log4j 2进行日志记录的桥接器。Common Logging API+ Log4j 2 Core
SLF4J Binding 允许针对SLF4J API编写的应用程序使用Log4j 2进行日志记录的桥接器 SLF4j+Log4j2 Core
Java Util Logging Adapter 允许针对java.util.logging API编写的应用程序使用Log4j 2进行日志记录的桥梁。java.util.logging API+Log4j 2 Core
Log4j 1.2 API Bridge 允许针对Log4j 1.2.x API编写的应用程序使用Log4j 2进行日志记录的桥梁。Log4j 1.2.x API—>Log4j 2
Log4j 2 to SLF4J Adapter 允许针对Log4j 2 API编写的应用程序使用SLF4J进行日志记录的适配器。 Log4j2 —>SLF4j
Log4j JMX GUI 基于Java Swing的客户端,用于远程查看状态记录器和编辑Log4j配置。
Log4j JPA Apache Log4j Java Persistence API Appender

0x03 添加Log4j 2.x 依赖

  • 对于一个新的Spring Boot 项目,我们只需要添加如下依赖即可
<!--排除默认的logback日志-->
<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>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-log4j2 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

如果你正在寻找 Log4j 1.x如何升级到Log4J 2.x 的内容, 请参考我的另外一篇博文 关于Log4j 1.x 升级Log4j 2.x那些事

0x04 XML配置篇

4.1 配置文件加载顺序

  • 1.查看系统属性log4j.configurationFile是否配置了,如果配置了使用ConfigurationFactory
  • 2.如果没有配置,那么查找log4j2-test.properties
  • 3.如果也没找到,那么检查是否有log4j2-test.yaml或者log4j2-test.yml
  • 4.如果也没找到,那么检查是否有log4j2-test.json或者log4j2-test.jsn
  • 5.如果也没找到,那么检查是否有log4j2-test.xml
  • 6.如果也没找到,那么检查是否有log4j2.properties
  • 7.如果也没找到,那么检查是否有log4j2.yaml或log4j2.yml
  • 8.如果也没找到,那么检查是否有log4j2.json或log4j2.jsn
  • 9.如果也没找到,那么检查是否有log4j2.xml
  • 10.如果也没知道,那么使用默认的DefaultConfiguration,只输出到控制台

4.2 log4j2.xml 经典配置

  • log4j2.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<!--从版本2.9开始,出于安全原因,Log4j不处理XML文件中的DTD-->
<!--monitorInterval="30" 每隔30秒检查下当前配置文件是否有改动如果有重新自动加载,最小间隔为5秒。 -->
<!-- status可选值"trace", "debug", "info", "warn", "error" and "fatal" log4j进行故障排除使用-->
<!--是否严格检查log4j2.xml配置文件格式 strict="false"-->
<!--verbose="" 加载插件时启用诊断信息。-->
<configuration name="log4j2Config" monitorInterval="30" status="WARN" strict="false" >
    <Properties>
        <property name="appName" value="myApp"/>
        <Property name="baseDir" value="/opt/applog/${appName}/log"></Property>
        <Property name="logName" value="${baseDir}/${appName}.log"></Property>
        <Property name="logArchive" value="${baseDir}/$${date:yyyy-MM-dd}/${appName}-archive-%d{yyyy-MM-dd}-%i.log.gz"/>
    </Properties>
    <Appenders>
        <!-- 输出到控制台 -->
        <Console name="STDOUT" target="SYSTEM_OUT" ignoreExceptions="false">
            <PatternLayout charset="GB18030" pattern="%d{yyyy/MM/dd HH:mm:ss,SSS} %-5level %l %n%msg%n"/>
        </Console>
        <!-- 滚动输出到文件 -->
        <RollingFile name="rollingFile"
                     append="true"
                     bufferedIO="true"
                     bufferSize="8192"
                     createOnDemand="false"
                     immediateFlush="true"
                     fileName="${logName}"
                     filePattern="${logArchive}"
                     ignoreExceptions="true"
        >
            <PatternLayout>
                <charset>UTF-8</charset>
                <Pattern>%d{yyyy/MM/dd HH:mm:ss,SSS} %-5level %l %n%msg%n</Pattern>
            </PatternLayout>
            <Policies>
                <!--每次启动是否归档滚动 -->
                <!--<OnStartupTriggeringPolicy minSize="1"/>-->
                <!-- 滚动策略根据大小进行滚动 -->
                <SizeBasedTriggeringPolicy size="1GB"  />
                <!-- 滚动策略根据日期进行滚动 每天压缩一次
                 maxRandomDelay表示随机延迟翻转的最大秒数。 默认情况下,该值为0表示没有延迟-->
                <TimeBasedTriggeringPolicy interval="1" modulate="false" maxRandomDelay="0"/>
            </Policies>
            <!--最多保留100个归档-->
            <DefaultRolloverStrategy max="20" min="1">
                <!--删除策略 -->
                <Delete basePath="${baseDir}" maxDepth="8">
                    <IfFileName glob="*/app-*.log.gz">
                        <!--会保留大小10G或者最近的10个文件-->
                        <IfLastModified age="30d">
                            <IfAny>
                                <IfAccumulatedFileSize exceeds="10GB" />
                                <IfAccumulatedFileCount exceeds="100" />
                            </IfAny>
                        </IfLastModified>
                    </IfFileName>
                </Delete>
            </DefaultRolloverStrategy>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Logger name="com.xingyun" level="DEBUG" additivity="true"/>
        <root level="ERROR">
            <AppenderRef ref="STDOUT" />
            <AppenderRef ref="rollingFile" />
        </root>
    </Loggers>
</configuration>

关于日志格式可参考 log4j2配置详解(节点和输出格式)

0x05 代码中自定义线程日志

5.1 Log4J内置标准日志级别

  • Log4J内置标准日志级别如下:
Standard Level intLevel
OFF 0
FATAL 100
ERROR 200
WARN 300
INFO 400
DEBUG 500
TRACE 600
ALL Integer.MAX_VALUE
  • 自定义日志级别
  • Log4J 2 支持自定义日志级别,可以使用Level.forName() 定义不同的日志级别.

5.2 代码中自定义线程日志工具类

如果想在代码中自定义线程日志工具类,我们需要先来看下设计架构图

log4j 2设计架构图
在这里插入图片描述

在设计这个的时候,刚开始走了不少坑,主要原因在于这里有一个很大的误区:

  • 当我们自定义线程日志的时候很容易不小心引错包,
    • 我们很容看到有两个包,这俩有啥区别呢?
      org.apache.logging.log4j.Loggerorg.apache.logging.log4j.core.Logger
  • log4j1.x 升级到log4j 2.x之后发生了一个改变,那就是实现和接口分离。
  • org.apache.logging.log4j.Logger 是log4j2 API 日志门面,只是日志接口类,位于log4j-api.jar
  • org.apache.logging.log4j.core.Logger 是Log4j2 日志接口实现类,位于log4j-core.jar

我们如果想要自定义一个线程日志类,注意事项:

  1. 使用日志实现类 org.apache.logging.log4j.core.Logger 这个类才有logger.addAppender(appender);方法
  2. 设置继承为false, logger.setAdditive(false); 这样设置可以保持独立和log4j.xml 配置互相不冲突

工具类源码封装如下:

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.Logger;
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.config.Configuration;
import org.apache.logging.log4j.core.layout.PatternLayout;

import java.io.File;
import java.nio.charset.Charset;
/**
 * @author 星云
 * @功能 线程日志工具类 使用Log4j2自定义线程日志类
 * @date 9/17/2019 7:09 AM
 */
public class ThreadCustomLogger{

	/**
     * 配置方法
	 *日志输出文件夹
	 */
	private static String logBasePath="/opt/applog/myApp/log/customThread";
    /**
     * 日志文件后缀
     */
	private static String logFileSuffix=".log";
    /**
     * 单个文件最大大小
     */
    private static String maxFileSize="1GB";
    /**
     * 是否使用缓冲区
     */
    private static Boolean bufferedIo=true;
    /**
     * 缓冲区大小
     */
    private static Integer bufferSize=8192;
    /**
     * 是否继承Root节点配置
     */
    private static Boolean additive=false;
    ////////////////////////////////////不经常修改/////////////////////////////////////////////////////
    /**
     *  滚动追加器名称
     */
    private static final String name="customRollingFileAppenderBuilder";
    /**
     * 字符集
     */
    private static final String charSet="UTF-8";
	/**
	 * 日志文件中日志输出格式
	 */
	private static String logOutFileFormat="%d{yyyy/MM/dd HH:mm:ss,SSS} %X{path} %X{ip} %-5level %l %n%msg%n";
	/**
	 * 滚动压缩后日志文件路径和名称
	 */
	private static String filePatternValue=logBasePath+File.separator+"customThread-archive-%d{yyyy-MM-dd}-%i.log.gz";

	/**
	 * 是否使用日志追加模式
	 */
	private static final Boolean appendEnable=true;

	/**
	 * 是否忽略异常继续写入
	 */
	private static final Boolean ignoreExceptions=true;

	/**
	 * 是否立即写入
	 */
	private static final Boolean immediateFlush=true;

	/**
	 * 默认为false,该appender按需创建文件,当日志事件通过所有的filters并且通过路由指向了该appender,该appender仅仅创建该文件
	 */
	private static final Boolean createOnDemand=false;

	/**
	 * 获取日志上下文
	 */
	private static LoggerContext loggerContext= (LoggerContext) LogManager.getContext(false);
	/**
	 * 获取日志配置文件实例
	 */
	private static Configuration loggerConfiguration=loggerContext.getConfiguration();

    /**
     * 日志输出文件名称
     */
	private static StringBuffer loggerFullFileName=null;
    /**
     * 日志
     */
	private static Logger logger=null;
    /**
     * 滚动日志追加Appender Builder 对象
     */
    private static RollingFileAppender.Builder rollingFileAppenderBuilder= null;
    /**
     * 滚动日志追加Appender对象
     */
    private static RollingFileAppender rollingFileAppender= null;
    /**
     * 设计思路
     * 1.log4j2.xml 根节点是configuration
     * 2.Appenders 可以多个追加方法,比如追加到控制台,追加到文件等
     * 3.代码中获取上面的配置,然后动态修改追加到文件的Appender
	 * @param threadFolderName
	 * @param loggerName
	 * @return
	 */
	public static  Logger getLogger(String threadFolderName, String loggerName) {
        //返回实例
        logger =(Logger)LogManager.getLogger(loggerName);

		//日志分割文件夹路径
		loggerFullFileName=new StringBuffer();
		loggerFullFileName.append(logBasePath);
		loggerFullFileName.append(File.separator);
		loggerFullFileName.append(threadFolderName);
		//设置文件名称
		loggerFullFileName.append(File.separator);
		//设置日志的名称
		loggerFullFileName.append(loggerName);
		//日志文件后缀类型 默认.log
		loggerFullFileName.append(logFileSuffix);

		//不需要重复创建公共的配置过程
		if(null==rollingFileAppenderBuilder){
            rollingFileAppenderBuilder=initCustomRollingFileAppender();
        }

		//添加Appender
        rollingFileAppender=rollingFileAppenderBuilder.withFileName(loggerFullFileName.toString()).build();
        rollingFileAppender.start();
        //设置追加器
        logger.addAppender(rollingFileAppender);
        //是否继承自log4j2.xml中Root节点的配置
        logger.setAdditive(additive);
        //设置拦截的日志级别
        logger.setLevel(Level.DEBUG);
		return logger;
	}


    /**
     * 初始化
     * @return
     */
    private static RollingFileAppender.Builder initCustomRollingFileAppender(){

        //日志打印输出布局
        Layout layout= PatternLayout.newBuilder()
                //设置字符集
                .withCharset(Charset.forName(charSet))
                //加载配置
                .withConfiguration(loggerConfiguration)
                //输出布局
                .withPattern(logOutFileFormat).build();


        //设置默认值
        DefaultRolloverStrategy defaultRolloverStrategy= DefaultRolloverStrategy.newBuilder()
                //从2.8版本开始,如果fileIndex属性设置为nomax,那么最大和最小值,都将会被忽略掉
                .withFileIndex("noMax")
                //.withMin("1")
                //.withMax("20")
                //设置压缩级别0-9,其中0=无,1=最佳速度,通过9=最佳压缩。只适用于ZIP文件。
                //.withCompressionLevelStr("9")
                .withConfig(loggerConfiguration)
                .build();

        //根据大小触发滚动策略 当文件大小增加到1GB时候触发该策略
        SizeBasedTriggeringPolicy sizeBasedTriggeringPolicy= SizeBasedTriggeringPolicy.createPolicy(maxFileSize==null?null:maxFileSize);

        //根据日期和时间触发滚动策略
        TimeBasedTriggeringPolicy timeBasedTriggeringPolicy= TimeBasedTriggeringPolicy.newBuilder()
                //根据日期格式中最具体的时间单位来决定应该多久发生一次rollover
                //例如,在日期模式中小时为具体的时间单位,那么每4小时会发生4次rollover,默认值为1
                //如果是yyyy-MM-dd 那么1表示一天 如果是yyyy-MM 那么1表示一个月
                .withInterval(1)
                //表示是否调整时间间隔以使在时间间隔边界发生下一个rollover
                //假设小时为具体的时间单元,当前时间为上午3点,时间间隔为4,第一次发送rollover是在上午4点,接下来是上午8点,接着是中午,接着是下午4点等发生
                .withModulate(false)
                //默认值 0 单位:秒,下一次触发时间会在interval基础上,增加一个随机的毫秒数Random.nextLong(0, 1+maxRandomDelay*1000)
                .withMaxRandomDelay(0)
                .build();

        //日志滚动追加器
        RollingFileAppender.Builder defaultConfigRollingFileAppenderBuilder = RollingFileAppender.newBuilder()
                .withName(name)
                //日志是否追加模式
                .withAppend(appendEnable)
                //是否使用缓冲区
                .withBufferedIo(bufferedIo)
                //缓冲区大小
                .withBufferSize(bufferSize)
                .withCreateOnDemand(createOnDemand)
                //是否立即刷新
                .withImmediateFlush(immediateFlush)
                //log4j 2.9.1 以上版本使用.setIgnoreExceptions(true)替换
                .withIgnoreExceptions(ignoreExceptions)
                //日志输出格式 log4j 2.9.1 以上版本使用.setLayout(layout)替换
                .withLayout(layout)
                //滚动输出归档压缩包文件命名格式
                .withFilePattern(filePatternValue)
                //用于决定是否发生rollover的策略 根据大小和日志进行滚动
                .withPolicy(CompositeTriggeringPolicy.createPolicy(sizeBasedTriggeringPolicy,timeBasedTriggeringPolicy))
                //用于决定压缩文件的名称和路径 单个文件最大1G
                .withStrategy(defaultRolloverStrategy);

        return defaultConfigRollingFileAppenderBuilder;
    }
}

0x06 日志调用方法汇总

在代码中调用方式如下:

import com.xingyun.springbootwithlog4j2sample.util.ThreadCustomLogger;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 星云
 * @功能
 * @date 9/15/2019 3:45 PM
 */
@Slf4j
@RestController
public class LogController {

    private static final org.slf4j.Logger LOGGER_SLF4J= LoggerFactory.getLogger(LogController.class);
    private static final Logger LOGGER_LOG4J2= LogManager.getLogger(LogController.class);
    private static Logger loggerCustom= null;

    @GetMapping(value = "/log.do")
    public String log(){

        //第一种日志使用SLF4J 日志门面调用
        LOGGER_SLF4J.debug("this is debug message with slf4j");
        LOGGER_SLF4J.info("this is info message with slf4j");
        LOGGER_SLF4J.warn("this is warn message with slf4j");
        LOGGER_SLF4J.error("this is error message with slf4j");

        //第二种方式使用log4j API 日志门面调用
        LOGGER_LOG4J2.debug("this is debug message with log4j2");
        LOGGER_LOG4J2.info("this is info message with log4j2");
        LOGGER_LOG4J2.warn("this is warn message with log4j2");
        LOGGER_LOG4J2.error("this is error message with log4j2");

        //第三种方式 配合lombok @Slf4j注解使用
        log.debug("this is debug message with lombok");
        log.info("this is  info message  with lombok");
        log.warn("this is  warn message with debug");
        log.error("this is error message with lombok");

        //第四种方式自定义线程日志
        if(null==loggerCustom){
            loggerCustom= ThreadCustomLogger.getLogger("myThread",LogController.class.getSimpleName());
        }
        loggerCustom.debug("this is custom debug message");
        loggerCustom.info("this is custom info message");
        loggerCustom.warn("this is custom warn message");
        loggerCustom.error("this is custom error message");
        return "log test finished,please check console message";
    }
}

0x07 源码下载

0x08 参考资料


本篇完,喜欢我的博文,欢迎点赞,关注 ~

猜你喜欢

转载自blog.csdn.net/hadues/article/details/100856124