SpringBoot系列:日志框架-Log4j2

目标

  • 异步输出日志
  • 根据业务不同分类输入到不同的日志文件中
  • 自动压缩日志文件并归档
  • 动态修改日志等级

开发环境

  • spring boot 2.0.4.RELEASE
  • JDK 1.8

Log4j2介绍

Log4j2详解

创建spring boot

SpringBoot系列:快速入门

添加maven依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itxiaoer</groupId>
    <artifactId>demo-spring-cloud-log4j2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>demo-spring-cloud-log4j2</name>
    <description>Demo log4j2 for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--去掉默认的日志框架-->
        <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>

        <!--引入log4j2-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>


        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!--异步支持-->
        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>3.3.6</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

添加log4j2.xml文件

在resources下创建log4j2.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="TRACE" monitorinterval="5">
    <!--配置常量-->
    <Properties>
        <Property name="baseDir">logs</Property>
    </Properties>

    <Appenders>
        <!--这个输出控制台的配置-->
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
        <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用-->
        <!--append = true : 保留启动前的日志,追加新日志,false: 清空原有日志-->
        <File name="log" fileName="${baseDir}/all.log" append="true">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
        </File>

        <RollingFile name="RollingFile_1" fileName="${baseDir}/logs_1/info.log"
                     filePattern="${baseDir}/logs_1/$${date:yyyy-MM}/info-%d{yyyy-MM-dd HH-mm}-%i.log.gz">
            <!--只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
            <Policies>
                <!--4分钟rollover一次-->
                <TimeBasedTriggeringPolicy interval="4"/>
                <!--文件大小-->
                <SizeBasedTriggeringPolicy size="1 MB"/>
            </Policies>
            <!--文件数量-->
            <DefaultRolloverStrategy max="3"/>

        </RollingFile>

        <!--删除历史日志文件-->
        <RollingRandomessFile name="RollingFile_2" fileName="${baseDir}/logs_2/info.log"
                              filePattern="${baseDir}/logs_2/$${date:yyyy-MM}/info-%d{yyyy-MM-dd HH-mm}-%i.log.gz">
            <!--只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
            <Policies>
                <!--1分钟rollover一次-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <!--文件大小-->
                <SizeBasedTriggeringPolicy size="1 MB"/>
            </Policies>
            <!--文件数量-->
            <DefaultRolloverStrategy>
                <Delete basePath="${baseDir}/logs_2" maxDepth="2">
                    <IfFileName glob="*/info-*.log.gz"/>
                    <IfLastModified age="5m"/>
                </Delete>
            </DefaultRolloverStrategy>
        </RollingRandomessFile>
        <!--pay-->
        <RollingFile name="payRollingFile" fileName="${baseDir}/pay/info.log"
                     filePattern="${baseDir}/pay/$${date:yyyy-MM}/info-%d{yyyy-MM-dd HH-mm}-%i.log.gz">
            <!--只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
            <Policies>
                <!--1分钟rollover一次-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <!--文件大小-->
                <SizeBasedTriggeringPolicy size="1 MB"/>
            </Policies>
            <!--文件数量-->
            <DefaultRolloverStrategy>
                <Delete basePath="${baseDir}/pay" maxDepth="2">
                    <IfFileName glob="*/info-*.log.gz"/>
                    <IfLastModified age="5m"/>
                </Delete>
            </DefaultRolloverStrategy>
        </RollingFile>

        <!--pay-->
        <RollingFile name="bizRollingFile" fileName="${baseDir}/biz/info.log"
                     filePattern="${baseDir}/biz/$${date:yyyy-MM}/info-%d{yyyy-MM-dd HH-mm}-%i.log.gz">
            <!--只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
            <Policies>
                <!--1分钟rollover一次-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <!--文件大小-->
                <SizeBasedTriggeringPolicy size="1 MB"/>
            </Policies>
            <!--文件数量-->
            <DefaultRolloverStrategy>
                <Delete basePath="${baseDir}/biz" maxDepth="2">
                    <IfFileName glob="*/info-*.log.gz"/>
                    <IfLastModified age="5m"/>
                </Delete>
            </DefaultRolloverStrategy>
        </RollingFile>

    </Appenders>
    <Loggers>
        <!--pay包下的日志输入到pay目录下-->
        <Logger name="com.itxiaoer.logging.pay" level="trace" additivity="false">
            <AppenderRef ref="payRollingFile"/>
        </Logger>
        <!--biz包下的日志输入到biz目录下-->
        <AsyncLogger name="com.itxiaoer.logging.biz" level="trace" additivity="false" includeLocation="true">
            <AppenderRef ref="bizRollingFile"/>
        </AsyncLogger>
        <Root level="info">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="log"/>
            <AppenderRef ref="RollingFile_1"/>
            <AppenderRef ref="RollingFile_2"/>
        </Root>
    </Loggers>
</Configuration>

启用定时任务

package com.itxiaoer.logging;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

/**
 * @author : liuyk
 */
@EnableScheduling
@SpringBootApplication
public class DemoSpringCloudLog4j2Application {

    public static void main(String[] args) {
        SpringApplication.run(DemoSpringCloudLog4j2Application.class, args);
    }
}

创建定时任务类

  • PayJob
package com.itxiaoer.logging.pay;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * @author : liuyk
 */
@Slf4j
@Component
public class PayJob {

    /**
     * 2秒钟执行1次
     */
    @Scheduled(fixedRate = 5 * 1000)
    public void logging() {
        String now = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(LocalDateTime.now());
        log.info(now);
        log.debug(now);
        log.error(now);
    }
}

  • BizJob
package com.itxiaoer.logging.biz;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * @author : liuyk
 */
@Slf4j
@Component
public class BizJob {

    /**
     * 2秒钟执行1次
     */
    @Scheduled(fixedRate = 2 * 1000)
    public void logging() {
        String now = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(LocalDateTime.now());
        log.trace(now);
        log.debug(now);
        log.info(now);
        log.debug(now);
        log.error(now);

    }

}

动态修改日志等级

添加maven依赖


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

说明

spring-boot-actuator模块提供了一个监控和管理生产环境的模块,可以使用http、jmx、ssh、telnet等来管理和监控应用。审计(Auditing)、 健康(health)、数据采集(metrics gathering)会自动加入到应用里面。

修改application.yml


management:
  endpoints:
    web:
      exposure:
        include: loggers

启动,查看日志

16:57:30.850 [main] INFO  org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping - Mapped "{[/actuator/loggers],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
16:57:30.851 [main] INFO  org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping - Mapped "{[/actuator/loggers/{name}],methods=[POST],consumes=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
16:57:30.852 [main] INFO  org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping - Mapped "{[/actuator/loggers/{name}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)
16:57:30.852 [main] INFO  org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping - Mapped "{[/actuator],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}" onto protected java.util.Map<java.lang.String, java.util.Map<java.lang.String, org.springframework.boot.actuate.endpoint.web.Link>> org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping.links(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)


访问

http://127.0.0.1:8080/actuator/loggers

{
    "levels": ["OFF", "FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"],
    "loggers": {
        "ROOT": {
            "configuredLevel": "INFO",
            "effectiveLevel": "INFO"
        },
        "com.itxiaoer.logging.biz": {
            "configuredLevel": "TRACE",
            "effectiveLevel": "TRACE"
        },
        "com.itxiaoer.logging.pay": {
            "configuredLevel": "TRACE",
            "effectiveLevel": "TRACE"
        }
    }
}

修改日志级别

  • 请求地址 http://127.0.0.1:8080/actuator/loggers/{name}

说明

  • 修改请求方式为POST
  • name为要修改的日志名称,比如com.itxiaoer.logging.biz
  • 修改请求体格式为 {"configuredLevel": "DEBUG"}
  • curl方式修改
curl -H "Content-Type: application/json" -X POST --data "{\"configuredLevel\": \"DEBUG\"}" http://127.0.0.1:8080/actuator/loggers/com.itxiaoer.logging.biz

  • PostMan工具修改

avatar

查看日志级别

http://127.0.0.1:8080/actuator/loggers/{name}

说明

  • 查看请求方式为GET
  • name为要查看的日志名称,比如com.itxiaoer.logging.biz
{
    "configuredLevel": "INFO",
    "effectiveLevel": "INFO"
}