Log4j2的使用,以及Spring 和 SpringBoot 分别使用Slf4j集成Log4j2构建项目日志系统

一、日志框架介绍

        Slf4j(全称是Simple Loging Facade For Java) 是一个为Java程序提供日志输出的统一接口,是对所有日志框架制定的一种规范、标准、接口,并不是一个具体的日志实现方案,因此Slf4j并不能但单独使用,需要和其他具体的日志框架实现配合使用,比如log4j、logback、log4j2等。

        Log4j2是Log4j 1.x和Logback的改进版,它相比其前身Log4j 1.x进行了重大改进,并提供了Logback中可用的许多改进,吸取了优秀的Logback的设计而重新推出的一款新组件,同时修复了Logback架构中的一些固有问题。 从GitHub的更新日志来看,Logback更新活跃度一般,而作为知名组织的Apache下的Log4j2的更新却是非常活跃的,并且流行度也很高,Log4j 1.x 于2015年8月停止维护更新了。

       Log4j2官方网址:http://logging.apache.org/log4j/2.x/

二、为什么需要用Slf4j接口集成Log4j2,而不直接使用其具体实现的Log4j2

        Slf4j接口是一个为Java程序提供日志输出的统一接口,是对所有日志框架制定的一种规范、标准、接口,可以有多个实现,使用时是面向接口的 (导入的包都是slf4j的包而不是具体某个日志框架中的包),即直接和接口交互,不直接使用具体实现,所以可以任意的更换具体实现而不用更改代码中的日志相关代码,这就可以很好的保证我们的日志系统具有良好的兼容性,能够兼容当前常见的几种日志系统,同时使用log4j2而不是log4j是因为Log4j 1.x 在高并发情况下出现死锁导致cpu使用率异常飙升,而Log4j2.0基于LMAX Disruptor的异步日志在多线程环境下性能会远远优于Log4j 1.x和logback (官方数据是10倍以上)。

三、Log4j2的简单使用

1、在pom.xml文件中引入Log4j2的依赖

<properties>
     <log4j.version>2.11.1</log4j.version>
</properties>

<dependencies>

  <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>${log4j.version}</version>
  </dependency>

  <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>${log4j.version}</version>
  </dependency>

</dependencies>

2、编写Log4j2Test测试类,进行简单测试

package com.log4j2.test;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;


public class Log4j2Test {

    //通过根日志名称来获取日志实例
    private static final Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);

    public static void main(String[] args) {

        logger.trace("trace level");
        logger.debug("debug level");
        logger.info("info level");
        logger.warn("warn level");
        logger.error("error level");
        logger.fatal("fatal level");
    }

}

3、其运行结果如下所示

2019-08-17 11:19:11,993 main WARN No Root logger was configured, creating default ERROR-level Root logger with Console appender
11:19:12.118 [main] ERROR  - error level
11:19:12.118 [main] FATAL  - fatal level

结果说明:

     1、在项目中,我们只引入了Log4j2的依赖包并没有引入或者编写其相应的配置文件,在控制台输出了第一句,其解释为:没有配置根记录程序,使用控制台附加程序创建默认的错误级根记录程序

     2、可以看到控制台只输出了error 和 fatal级别的错误信息,其他级别的错误信息并没有被输出,是因为没有配置文件,则默认使用error级别并在控制台输出,所以只有error 或者比 error更高的级别 fatal被输出到控制台

    3、缺省的配置等同于如下其默认的配置 (相关配置会在后面进行统一讲解)

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">

    <Appenders>
       <Console name="Console" target="SYSTEM_OUT">
           <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
       </Console>
    </Appenders>

    <Loggers>
        <Root level="error">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

四、Log4j2的日志名称层次输出规则

1、在com.log4j2包路径下创建一个名为Log4j2类

package com.log4j2;


import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4j2 {

    private static final Logger logger = LogManager.getLogger(Log4j2.class);

    public static void main(String[] args) {

        logger.info("INFO:Log4j2 --- info level");
        logger.warn("WARN:Log4j2 --- warn level");
        logger.error("ERROR:Log4j2 --- error level");
    }
}

2、在src/main/resources目录下,创建一个名为log4j2.xml的配置文件 (注意:配置文件的名称最好为log4j2.xml,因为log4j2默认会去找一个名为log4j2.xml的配置文件)

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">

    <Appenders>
       <Console name="Console" target="SYSTEM_OUT">
           <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
       </Console>
    </Appenders>
    <Loggers>
        <Logger name="com.log4j2.Log4j2" level="info">
            <AppenderRef ref="Console"/>
        </Logger>

        <Logger name="com.log4j2" level="warn">
            <AppenderRef ref="Console"/>
        </Logger>

        <Root level="error">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

3、其运行结果如下所示

14:13:47.526 [main] INFO  com.log4j2.Log4j2 - INFO:Log4j2 --- info level
14:13:47.526 [main] INFO  com.log4j2.Log4j2 - INFO:Log4j2 --- info level
14:13:47.526 [main] INFO  com.log4j2.Log4j2 - INFO:Log4j2 --- info level
14:13:47.529 [main] WARN  com.log4j2.Log4j2 - WARN:Log4j2 --- warn level
14:13:47.529 [main] WARN  com.log4j2.Log4j2 - WARN:Log4j2 --- warn level
14:13:47.529 [main] WARN  com.log4j2.Log4j2 - WARN:Log4j2 --- warn level
14:13:47.529 [main] ERROR com.log4j2.Log4j2 - ERROR:Log4j2 --- error level
14:13:47.529 [main] ERROR com.log4j2.Log4j2 - ERROR:Log4j2 --- error level
14:13:47.529 [main] ERROR com.log4j2.Log4j2 - ERROR:Log4j2 --- error level

结果说明:

     1、在控制台中,我们可以看到,同样的日志信息被输出了三次,细心的小伙伴们就会发现,名为"com.log4j2.Log4j2"的子级Logger的level=info,而名为"com.log4j2"的父级Logger的level=warn 和 顶级Root的level=error,都比子级Logger的level级别要高,那为什么低级别的信息,例如:info信息,还会被父级Logger 和 顶级Root输出打印呢?这是因为<Logger>标签中的additivity默认为true,即允许同时输出日志到父级的Appender中。同时Logger的命名符合命名层次规则,即一个Logger的名称是另一个Logger名称的前缀,则称其为另一个Logger的祖先或者父级,例如:名为com.log4j2的Logger 是名为com.log4j2.Log4j2的Logger的祖先或者父级

     2、同时Logger的命名和类的命名也有关联,当我们在配置文件中配置一个名为com.log4j2.Log4j2的Logger,那么在com.log4j2.Log4j2类中的Logger就会匹配到这一配置

    3、Root是整个命名层次规则的顶层,所以在输出日志时,配置文件中的二个Logger 和 Root对应的Appender会分别被调用

    4、如果你不想日志输出到父级的Appender中,则在<Logger>标签中配置additivity="false"即可

4、修改名为"com.log4j2.Log4j2"的Logger的level,由info 修改为 error,其他配置不变

<Logger name="com.log4j2.Log4j2" level="error">
   <AppenderRef ref="Console"/>
</Logger>

5、其运行结果如下所示

09:41:42.127 [main] ERROR com.log4j2.Log4j2 - ERROR:Log4j2 --- error level
09:41:42.127 [main] ERROR com.log4j2.Log4j2 - ERROR:Log4j2 --- error level
09:41:42.127 [main] ERROR com.log4j2.Log4j2 - ERROR:Log4j2 --- error level

结果说明:

    1、在控制台中,我们可以看到,日志信息只有error级别的被输出了三次,而info 和 warn级别的信息并没有在子级Logger、父级Logger 和 顶级Root中被输出打印,这又是为什么呢?首先info 和 warn级别的信息并没有在子级Logger中被打印,我们可以理解,这是因为我们修改了子级Logger的level=error,所以低于error级别的信息,将不会被子级Logger所打印

    2、父级Logger的level=warn,顶级Root的level=error,那么至少warn级别的信息应该被父级Logger输出打印才对啊,可是为什么并没有被打印呢?这是因为子级Logger的level=error,只有被子级Logger输出打印的信息,才会被父级Logger 和 顶级Root输出打印

五、Log4j2的配置文件详解

1、Log4j2配置文件的名称以及在项目中的存放位置

  log4j 2.0不再支持像1.x中以".properties"后缀的文件配置方式,2.x版本的配置文件后缀名只能以".xml",".json"或者".jsn"结尾。

  在默认情况下,系统选择Log4j2配置文件的优先级如下:

classpath路径下名为 log4j-test.json 或者log4j-test.jsn文件
classpath路径下名为 log4j2-test.xml
classpath路径下名为 log4j.json 或者log4j.jsn文件
classpath路径下名为 log4j2.xml


这里就会有人有疑问,classpath路径在java项目中到底指向哪里?

   1、src不是classpath,WEB-INF/classes、WEB-INF/lib才是classpath,WEB-INF/是资源目录,客户端不能直接访问。
   2、WEB-INF/classes目录用于存放 src目录下java文件编译之后的class文件、xml、properties等资源配置文件,这是一个定位资源的入口。
   3、lib和classes同属classpath,两者的访问优先级为: lib>classes。

2、log4j2.xml配置文件的大致结构,如下图所示

3、相关配置介绍

3.1、<Configuration>标签,根节点

Configuration: 根节点,拥有status 和 monitorInterval等多个属性
  status属性:用于控制log4j2日志框架本身的日志级别,如果将stratus设置为较低的级别就会看到很多关log4j2本身的日志,如加载log4j2配置文件的路径等信息,其取值有:ALL、TRACE、DEBUG、INFO、WARN、ERROR、FATAL、OFF
 
  monitorInterval属性:用于指定log4j2每隔多少秒重新读取配置文件,可以在修改配置时不重启应用的情况,重新读取修改后的配置文件

3.2、<properties>标签,用于定义常量

properties:属性,可包含多个property标签,用于定义常量,以便在配置其他属性时引用该常量,properties属性是可选的,即:可配可不配
  property标签:用于配置常量
      name:property的属性,用于在配置其他属性时引用该常量

3.3、<Appenders>标签,用于定义日志输出

Appenders:输出源,用于定义日志输出的地方,log4j2支持的输出源有很多,常见的有控制台Console、文件File 和 RollingRandomAccessFile

  Console:用于定义将日志打印到控制台上,开发的时候一般都会配置,以便调试
     name属性:用于指定Console的名称
     target属性:取值为SYSTEM_OUT 或者 SYSTEM_ERR,一般设置为SYSTEM_OUT即可
     follow属性:用于标识appender是否通过System.setOut或System.setErr在配置后进行重新分配System.out或System.err
     PatternLayout子节点:用于定义输出格式和字符集,通过pattern设置输出格式,charset指定字符集
  
  File:用于定义将日志输出到指定的文件中,需要配置输出到哪个位置,例如:D:/logs/mylog.log
    name属性:用于指定File的名称
    fileName属性:用于指定输出日志的目的文件,带全路径的文件名,例如:D:/logs/mylog.log
    append属性:用于指定是否以追加的方式添加日志到文件中,默认为true,当设置为false时,每此输入日志都会将之前的文件内容删除
    PatternLayout子节点:用于定义输出格式和字符集,通过pattern设置输出格式,charset指定字符集

  RollingRandomAccessFile:用于定义将日志输出到指定的文件中,相比File更加强大,可以指定当文件达到一定大小(如60MB)时,另起一个文件继续写入日志,另起一个文件就涉及到新文件的名字命名规则,因此需要配置文件命名规则,而这种方式更加实用,因为你不可能一直往一个文件中写,如果一直写,文件过大,打开就会卡死,也不便于查找日志。
    name属性:用于指定RollingRandomAccessFile的名称
    fileName属性:用于指定输出日志的目的文件,带全路径的文件名,例如:D:/logs/mylog.log
    filePattern属性:用于指定当发生Rolling时,即新建一个文件继续写入日志时,新建文件的命名规则
    append属性:用于指定是否以追加的方式添加日志到文件中,默认为true,当设置为false时,每此输入日志都会将之前的文件内容删除
    PatternLayout子节点:用于定义输出格式和字符集,通过pattern设置输出格式,charset指定字符集
    Policies子节点:用于指定滚动日志的策略,即:什么时候进行新建日志文件输出日志
      TimeBasedTriggeringPolicy:Policies的子节点,基于时间的滚动策略,interval属性用来指定多久滚动一次,默认是1小时。同时interval属性需要结合filePattern属性,如果filePattern属性的命名规则是%d{yyyy-MM-dd HH-mm}-%i.log,最小的时间粒度是mm,即:分钟,那么如果指定interval为1的话,就是一分钟生成一个新的文件,如果命名规则改为%d{yyyy-MM-dd HH}-%i.log,最小的时间粒度是HH,即:小时,那么如果指定interval为1的话,就是一小时生成一个新的文件
      SizeBasedTriggeringPolicy:Policies的子节点,基于指定文件大小的滚动策略,size属性用来定义每个日志文件的大小.
    DefaultRolloverStrategy子节点:用来指定同一个文件夹下最多保存多少个日志文件,通过max属性进行设置
    Filters子节点:用于配置日志过滤器
       ThresholdFilter:Filters的子节点,阈值(日志级别) 过滤器
                 level:ThresholdFilter的属性,用于设置日志过滤的级别
               onMatch:ThresholdFilter的属性,默认值是NEUTRAL(中立),其取值有ACCEPT(接受)、DENY(拒绝)、NEUTRAL(中立),onMatch指的是日志级别大于等于level时的操作
            onMismatch:ThresholdFilter的属性,默认值是DENY(拒绝),其取值有ACCEPT(接受)、DENY(拒绝)、NEUTRAL(中立),onMismatch表示日志级别小于level时的操作
                 
                在使用单个过滤器的时候,一般就是使用ACCEPT(接受) 和 DENY(拒绝)这两个值。但是在组合过滤器中,
             如果用接受ACCEPT(接受)的话,日志信息就会直接写入到日志文件中,后续的过滤器将不再对其进行过滤。
             所以,在组合过滤器中,接受时使用NEUTRAL(中立),被第一个过滤器接受的日志信息,会继续使用后面的
             过滤器进行过滤,只有符合所有过滤器条件的日志信息,最终才会被写入到日志文件中。


  PatternLayout输出格式介绍:
      %d/%data{yyyy-MM-dd HH:mm:ss.SSS} 表示输出到毫秒的时间
      %t/%thread 输出当前线程名称
      %-5level 输出日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0
      %logger 输出logger名称,因为Root Logger没有名称,所以没有输出
      %C/%class 输出所在类的全路径名
      %msg 日志文本
      %n 换行

  其他常用的占位符有:
      %F 输出所在的类文件名,如Log4j2.java
      %L 输出行号
      %M 输出所在方法名
      %l 输出语句所在的行数, 包括类名、方法名、文件名、行数

               

3.4、<Loggers>标签,日志器

Loggers:日志器,又分为根日志器Root 和 自定义日志器Logger,如果没有单独指定Logger 或者 根据日志名称获取不到指定的Logger时,就会使用Root作为默认的日志器

  Root:根日志器 / AsyncRoot:异步根日志器
    level属性:用于指定日志输出级别,一共有8个级别,按照从低到高为:all < trace < debug < info < warn < error < fatal < off,不指定level属性时,默认为error
    AppenderRef子节点:用于指定日志输出到哪个Appender中,通过ref属性指定
 
  Logger:自定义日志器 / AsyncLogger:异步自定义日志器
    name属性:Logger的名称,可用包名作为日志的名字,不同的包可配置不同的日志级别
    level属性:用于指定日志输出级别,一共有8个级别,按照从低到高为:all < trace < debug < info < warn < error < fatal < off,不指定level属性时,默认为error
    additivity属性:用于指定是否同时输出日志到父级的Appender中,不指定additivity属性时,默认为true
    AppenderRef子节点:通过ref属性用于指定日志输出到哪个Appender中,level属性用于指定其日志输出级别,如果自定义日志器Logger的level=trace,而其AppenderRef子节点的level=info,那么该AppenderRef子节点引用的Appender只会输出info级别及其以上的日志信息,而不会输出trace级别及其以上的日志信息

六、Spring整合 Slf4j与Log4j2

1、在pom.xml文件中引入Slf4j和Log4j2的相关依赖

<?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.log4j2</groupId>
    <artifactId>Log4j2Project</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <log4j.version>2.11.1</log4j.version>
    </properties>

    <dependencies>

        <!-- slf4j的相关依赖包 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>

        <!-- 桥接:告诉Slf4j使用Log4j2 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>${log4j.version}</version>
        </dependency>

        <!-- log4j2的相关依赖包 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>${log4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>${log4j.version}</version>
        </dependency>

        <!-- log4j2异步输出的依赖包 -->
        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>3.4.2</version>
        </dependency>

    </dependencies>

</project>

2、在src/main/resources目录下,创建一个名为log4j2.xml的配置文件 ,其配置如下所示 (注意:配置文件的名称最好为log4j2.xml,因为log4j2默认会去找一个名为log4j2.xml的配置文件)

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--status="WARN": 用于设置log4j2自身内部日志的信息输出级别,默认是OFF-->
<!--monitorInterval="1800": 间隔秒数,自动检测配置文件的变更和重新配置本身-->
<configuration status="WARN" monitorInterval="1800">

    <Properties>
        <!--自定义一些常量,之后使用${变量名}引用-->
        <Property name="logFilePath">D:/logs</Property>
        <Property name="logFileName">myLog</Property>
    </Properties>

    <!--Appenders: 定义输出内容,输出格式,输出方式,日志保存策略等,常用其下三种标签[console,File,RollingRandomAccessFile]-->
    <Appenders>
        <!--Console: 控制台输出的配置-->
        <console name="Console" target="SYSTEM_OUT" follow="true">
            <!--PatternLayout: 用于定义输出日志的格式-->
            <PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %class{36}.%M() line:%L - %msg%xEx%n"/>
        </console>

        <!--File: 同步输出日志到本地文件-->
        <!--append="false": 根据其下日志策略,每次清空文件重新输入日志,可用于测试-->
        <File name="File" fileName="${logFilePath}/${logFileName}.log" append="false">
            <PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %class{36}.%M() line:%L - %msg%xEx%n"/>
        </File>

        <RollingRandomAccessFile name="RollingRandomAccessFile" fileName="${logFilePath}/logs/info.log"
                     filePattern="${logFilePath}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd HH}-%i.log" append="false">

            <!--  ACCEPT(接受)、DENY(拒绝)、NEUTRAL(中立) -->
            <!--  onMatch >= level  onMatch指的是日志级别大于等于level时的操作 -->
            <!--  onMismatch < level onMismatch表示日志级别小于level时的操作 -->
            <!--  通过添加两组 ThresholdFilter 可以达到一个级别的log 对应一个文件的目的-->
            <Filters>
                <!-- 这里设置只接收info级别的信息 -->
                <!--level="warn": 日志级别,onMatch="DENY": 级别在info之上则拒绝,onMismatch="NEUTRAL": 级别在info之下则中立,即不作任何操作-->
                <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>

            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%t] [%class{36}.%M() line:%L] - %msg%xEx%n"/>

            <!-- Policies: 日志滚动策略-->
            <Policies>
                <!--
                     TimeBasedTriggeringPolicy: 时间滚动策略,默认0点小时产生新的文件,interval="6": 自定义文件滚动时间间隔,
                     每隔6小时产生新文件, modulate="true": 产生文件是否以0点偏移时间,即6点、12点、18点、0点
                 -->
                <TimeBasedTriggeringPolicy interval="6" modulate="true"/>
                <!-- SizeBasedTriggeringPolicy :文件大小滚动策略-->
                <SizeBasedTriggeringPolicy size="60MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了10 -->
            <DefaultRolloverStrategy max="10"/>
        </RollingRandomAccessFile>

    </Appenders>

    <!--然后定义Logger,只有定义了Logger并引入的Appender,Appender才会生效-->
    <Loggers>
        <!-- 过滤掉spring和mybatis的一些无用的DEBUG信息 -->
        <!-- Logger节点用来单独指定日志的形式,name为包路径,比如要为org.springframework包下所有日志指定为error级别 -->
        <!-- 由于additivity属性缺省,则默认为true,其error级别以上(包括error级别)的日志信息都会在顶级Root的Appender中被输出 -->
        <Logger name="org.springframework" level="error"></Logger>
        <Logger name="org.mybatis" level="error"></Logger>

        <!-- Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出 -->
        <Root level="warn">
            <appender-ref ref="Console"/>
            <appender-ref ref="File"/>
            <appender-ref ref="RollingRandomAccessFile"/>
        </Root>

        <!-- AsyncLogger: 异步日志,LOG4J有三种日志模式,全异步日志,混合模式,同步日志,性能从高到底,线程越多效率越高,也可以避免日志卡死线程情况发生 -->
        <!-- additivity="false": additivity设置事件是否在父级logger 和 顶级Root的Appender中输出,为了避免重复输出,可以在Logger标签下设置additivity为"false" -->
        <!-- includeLocation="true": 显示文件行数,方法名等信息,对性能会有影响,能不用的情况下最好不用 -->
        <AsyncLogger name="com.log4j2" level="all" includeLocation="true" additivity="false">
            <appender-ref ref="RollingRandomAccessFile"/>
        </AsyncLogger>
    </Loggers>
</configuration>

3、在com.log4j2包路径下创建一个名为Log4j2Test类,注意:这里导入的包都是slf4j的包而不是具体某个日志框架中的包

package com.log4j2;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Log4j2Test {

    private static final Logger logger = LoggerFactory.getLogger(Log4j2.class);

    public static void main(String[] args) {

        for (int i = 0; i < 10; i++) {

            logger.trace("trace level "+i);
            logger.debug("debug level "+i);
            logger.info("info level "+i);
            logger.warn("warn level "+i);
            logger.error("error level "+i);
           
        }
    }
}

4、其运行结果如下所示

扩展补充:

七、配置log4j2.xml在控制台打印Mybatis执行的SQL语句

1、自定义一个Logger,设置其name属性为项目持久层接口 (与Mybatis配置Sql文件对应接口) 的package (包路径),再设置其level为"debug",最后设置其日志在控制台输出,配置如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--status="WARN": 用于设置log4j2自身内部日志的信息输出级别,默认是OFF-->
<!--monitorInterval="1800": 间隔秒数,自动检测配置文件的变更和重新配置本身-->
<configuration status="WARN" monitorInterval="1800">

    <!--Appenders: 定义输出内容,输出格式,输出方式,日志保存策略等,常用其下三种标签[console,File,RollingRandomAccessFile]-->
    <Appenders>
        <!--Console: 控制台输出的配置-->
        <console name="Console" target="SYSTEM_OUT" follow="true">
            <!--PatternLayout: 用于定义输出日志的格式-->
            <PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %class{36}.%M() line:%L - %msg%xEx%n"/>
        </console>
    </Appenders>

    <!--然后定义Logger,只有定义了Logger并引入的Appender,Appender才会生效-->
    <Loggers>

        <!-- 将与数据库交互的dao/mapper接口的包名填写到Logger的name属性中,并设置Appender为控制台输出即可 -->
         <!-- additivity="false": additivity设置事件是否在父级logger 和 顶级Root的Appender中输出,为了避免重复输出,可以在Logger标签下设置additivity为"false" -->
        <Logger name="com.log4j2.mapper" level="debug" additivity="false">
            <appender-ref ref="Console"/>
        </Logger>

        <!-- Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出 -->
        <Root level="warn">
            <appender-ref ref="Console"/>
        </Root>
    </Loggers>
</configuration>

2、这里的level为debug,只会打印Mybatis执行的sql语句、sql参数和结果条数,其运行结果如下所示:

2019-08-20 10:56:06.775 DEBUG org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug() line:159 - ==>  Preparing: select id, name, sex, age, birthday from user where id=? 
2019-08-20 10:56:06.865 DEBUG org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug() line:159 - ==> Parameters: 1(Integer)
2019-08-20 10:56:06.877 DEBUG org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug() line:159 - <==      Total: 1

3、如果把level设置为trace,则会打印Mybatis执行的sql语句、sql参数、执行的结果和结果条数,其运行结果如下所示:

2019-08-20 10:58:55.825 DEBUG org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug() line:159 - ==>  Preparing: select id, name, sex, age, birthday from user where id=? 
2019-08-20 10:58:55.851 DEBUG org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug() line:159 - ==> Parameters: 1(Integer)
2019-08-20 10:58:55.860 TRACE org.apache.ibatis.logging.jdbc.BaseJdbcLogger.trace() line:165 - <==    Columns: id, name, sex, age, birthday
2019-08-20 10:58:55.861 TRACE org.apache.ibatis.logging.jdbc.BaseJdbcLogger.trace() line:165 - <==        Row: 1, 曾华, 男, 42, 1977-09-01
2019-08-20 10:58:55.864 DEBUG org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug() line:159 - <==      Total: 1

八、配置log4j2.xml把日志写入到数据库中

1、创建日志记录表error_log

DROP TABLE IF EXISTS `error_log`;
CREATE TABLE `error_log` (
  `log_id` int(20) NOT NULL AUTO_INCREMENT,
  `class` varchar(200) DEFAULT NULL COMMENT '类的全路径名',
  `method` varchar(100) DEFAULT NULL COMMENT '方法名',
  `line` int(20) DEFAULT NULL COMMENT '所在行号',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `log_level` varchar(50) DEFAULT NULL COMMENT '日志级别',
  `msg` varchar(4000) DEFAULT NULL COMMENT '日志信息',
  PRIMARY KEY (`log_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2、在log4j2.xml中配置JDBC输出源

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--status="WARN": 用于设置log4j2自身内部日志的信息输出级别,默认是OFF-->
<!--monitorInterval="1800": 间隔秒数,自动检测配置文件的变更和重新配置本身-->
<configuration status="WARN" monitorInterval="1800">

    <!--Appenders: 定义输出内容,输出格式,输出方式,日志保存策略等,常用其下三种标签[console,File,RollingRandomAccessFile]-->
    <Appenders>
        <!--Console: 控制台输出的配置-->
        <console name="Console" target="SYSTEM_OUT" follow="true">
            <!--PatternLayout: 用于定义输出日志的格式-->
            <PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %class{36}.%M() line:%L - %msg%xEx%n"/>
        </console>

        <!--写入数据库配置,在Logger中设置日志级别为error-->
        <JDBC name="databaseAppender" tableName="error_log">
            <ConnectionFactory class="com.log4j2.ConnectionFactoryConfig" method="getDataSourceConnection" />
            <Column name="class" pattern="%c" />
            <Column name="method" pattern="%M" />
            <Column name="line" pattern="%L" />
            <Column name="log_level" pattern="%p" />
            <Column name="msg" pattern="%msg"/>
            <Column name="create_time" pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}"/>
        </JDBC>
    </Appenders>

    <!--然后定义Logger,只有定义了Logger并引入的Appender,Appender才会生效-->
    <Loggers>

        <!-- 将与数据库交互的dao/mapper接口的包名填写到Logger的name属性中,并设置Appender为控制台输出即可 -->
        <!-- additivity="false": additivity设置事件是否在父级logger 和 顶级Root的Appender中输出,为了避免重复输出,可以在Logger标签下设置additivity为"false" -->
        <Logger name="com.log4j2" level="trace" additivity="false">
            <!-- level="debug": 设置只打印debug级别以上的日志信息 -->
            <appender-ref ref="Console" level="debug"/>
            <appender-ref ref="databaseAppender" level="error"/>
        </Logger>

        <!-- Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出 -->
        <Root level="warn">
            <appender-ref ref="Console"/>
        </Root>
    </Loggers>
</configuration>

3、在src/main/resources目录下,创建一个properties文件夹,并在其文件夹中创建一个jdbc.properties文件,用于配置数据库信息

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3316/log4j2?rewriteBatchedStatements=true&allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&&useSSL=false
jdbc.username=root
jdbc.password=123456

4、在pom.xml文件中引入Druid的依赖

<!-- Druid连接池 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.10</version>
</dependency>

5、创建数据库连接类ConnectionFactoryConfig,使用的是druid数据源

package com.log4j2;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

public class ConnectionFactoryConfig {

    private static String driverClassName = null;
    private static String dbUrl = null;
    private static String username = null;
    private static String password = null;
    private DruidDataSource dataSource = null;
    private static ConnectionFactoryConfig instance = null;

    /**
     * 获取数据库连接对象
     * @return
     * @throws SQLException
     */
    public static Connection getDataSourceConnection() throws SQLException {
        if (instance == null) {
            instance = new ConnectionFactoryConfig();
        }
        return instance.getConnection();
    }

    
    /**
     * 取消初始化
     */
    public void uninitialize() {
        try {
            if (dataSource != null && !dataSource.isClosed()) {
                dataSource.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    

    /**
     * 获取数据库连接
     * @return
     * @throws SQLException
     */
    private Connection getConnection() throws SQLException {
        loadPropertiesFile();

        try {
            dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(getProperties());
        } catch (Exception e) {
            e.printStackTrace();
            uninitialize();
        }
        return dataSource.getConnection();
    }


    /**
     * 创建Properties 实例
     * @return Properties 实体类
     */
    private Properties getProperties() {
        Properties properties = new Properties();
        properties.put("driverClassName", driverClassName);
        properties.put("url", dbUrl);
        properties.put("username", username);
        properties.put("password", password);
        return properties;
    }


    /**
     * 加载资源文件
     */
    private void loadPropertiesFile() {
        InputStream resourceAsStream = ConnectionFactoryConfig.class.getClassLoader().getResourceAsStream("properties/jdbc.properties");
        Properties props = new Properties();
        try {
            props.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

        driverClassName = props.getProperty("jdbc.driver");
        dbUrl = props.getProperty("jdbc.url");
        username = props.getProperty("jdbc.username");
        password = props.getProperty("jdbc.password");
    }
    
}

6、在com.log4j2包路径下创建一个名为JdbcLog4j2Test类,注意:这里导入的包都是slf4j的包而不是具体某个日志框架中的包

package com.easyexcel;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class JdbcLog4j2Test {

    private static final Logger logger = LoggerFactory.getLogger(JdbcLog4j2Test.class);

    public static void main(String[] args) {

            logger.trace("trace level");
            logger.debug("debug level");
            logger.info("info level");
            logger.warn("warn level");
            logger.error("error level");
    }

}

7、其运行结果如下所示

九、SpringBoot整合 Slf4j与Log4j2

1、在pom.xml文件中引入Slf4j和Log4j2的相关依赖

        <!-- springboot起步依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>

            <!-- 排除自带的Logback日志工具依赖,为了项目使用Log4j2日志打印工具 -->
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--
             1、log4j2的依赖包
             2、spring-boot-starter-log4j2 中自动依赖了 slf4j-api 和 log4j-slf4j-impl
        -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>

        <!-- log4j2异步输出的依赖包 -->
        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>3.4.2</version>
        </dependency>

2、在src/main/resources目录下,创建一个名为log4j2.xml的配置文件 ,其配置如下所示

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--status="WARN": 用于设置log4j2自身内部日志的信息输出级别,默认是OFF-->
<!--monitorInterval="1800": 间隔秒数,自动检测配置文件的变更和重新配置本身-->
<configuration status="WARN" monitorInterval="1800">


    <!--Appenders: 定义输出内容,输出格式,输出方式,日志保存策略等,常用其下三种标签[console,File,RollingRandomAccessFile]-->
    <Appenders>
        <!--Console: 控制台输出的配置-->
        <console name="Console" target="SYSTEM_OUT" follow="true">
            <!--PatternLayout: 用于定义输出日志的格式-->
            <PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %class{36}.%M() line:%L - %msg%xEx%n"/>
        </console>

    </Appenders>

    <!--然后定义Logger,只有定义了Logger并引入的Appender,Appender才会生效-->
    <Loggers>

        <!-- Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出 -->
        <Root level="info">
            <appender-ref ref="Console"/>
        </Root>

    </Loggers>
</configuration>

3、在com.log4j2包路径下创建一个名为Log4j2Test类,注意:这里导入的包都是slf4j的包而不是具体某个日志框架中的包

package com.log4j2;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Log4j2 {

   private static final Logger logger = LoggerFactory.getLogger(Log4j2.class);

    public static void main(String[] args) {

        logger.info("INFO:Log4j2 --- info level");
        logger.warn("WARN:Log4j2 --- warn level");
        logger.error("ERROR:Log4j2 --- error level");
    }
}

4、其运行结果如下所示

2019-09-26 09:20:33.679 INFO  com.log4j2.Log4j2.main() line:12 - INFO:Log4j2 --- info level
2019-09-26 09:20:33.682 WARN  com.log4j2.Log4j2.main() line:13 - WARN:Log4j2 --- warn level
2019-09-26 09:20:33.682 ERROR com.log4j2.Log4j2.main() line:14 - ERROR:Log4j2 --- error level

                      如果有遇到不懂或者有问题时,可以扫描下方二维码,欢迎进群交流与分享,希望能够跟大家交流学习!

                                                                          

发布了76 篇原创文章 · 获赞 253 · 访问量 43万+

猜你喜欢

转载自blog.csdn.net/qq_39135287/article/details/99683006
今日推荐