log4j2的学习

背景:

业务上面的代码,肯定都是需要打印日志的,不论是之后定位问题还是其他什么的。
最基础的诉求就是能够将不同级别的日志打印到不同的文件中,能够按天来区分。这些log4j2都有,并且支持异步处理。虽然我没有用过logback和log4j,但是通过博客中得出的结论就是log4j2有他们都有的优点,也有他们没有的优点。
本文并没有仔细钻研log4j2的底层,暂时只是出于用日志的地步。其实有日志也可以玩出很多玩意,比如小米的lcs,就可以通过log4j2来讲一些数据异步上传到服务器中,来完成一些数据打点。

日志配置

先来一个简单的日志配置
依赖:

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.7</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>

        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>3.4.2</version>
        </dependency>
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="error">
    <!--先定义所有的appender -->
    <appenders>
        <!--这个输出控制台的配置 -->
<!--        onMatch="ACCEPT" 表示匹配该级别及以上-->
<!--        onMatch="DENY" 表示不匹配该级别及以上-->
<!--        onMatch="NEUTRAL" 表示该级别及以上的,由下一个filter处理,如果当前是最后一个,则表示匹配该级别及以上-->
<!--        onMismatch="ACCEPT" 表示匹配该级别以下-->
<!--        onMismatch="NEUTRAL" 表示该级别及以下的,由下一个filter处理,如果当前是最后一个,则不匹配该级别以下的-->
<!--        onMismatch="DENY" 表示不匹配该级别以下的-->

        <Console name="Console" target="SYSTEM_OUT">
            <!--             控制台只输出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"/>
        </Console>

        <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用 -->
        <!--append为TRUE表示消息增加到指定文件中,false表示消息覆盖指定的文件内容,默认值是true -->
        <File name="log" fileName="D:/logs/log4j2.log" append="true">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
        </File>

        <!--添加过滤器ThresholdFilter,可以有选择的输出某个级别以上的类别  onMatch="ACCEPT" onMismatch="DENY"意思是匹配就接受,否则直接拒绝  -->
        <File name="ERROR" fileName="D:/logs/error.log">
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="%d{yyyy.MM.dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
        </File>

        <!--这个会打印出所有的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 -->
        <RollingFile name="RollingFile" fileName="D:/logs/web.log"
                     filePattern="logs/$${date:yyyy-MM}/web-%d{MM-dd-yyyy}-%i.log.gz">
            <PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
            <SizeBasedTriggeringPolicy size="2MB"/>
        </RollingFile>
    </appenders>


    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效 -->
    <loggers>
        <AsyncRoot level="info" includeLocation="true">
            <appender-ref ref="RollingFile"/>
            <appender-ref ref="Console"/>
            <appender-ref ref="log"/>
            <appender-ref ref="ERROR" />
        </AsyncRoot>
    </loggers>
</configuration>

测试程序

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

public class Test {
    private static  Logger logger= LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
    @org.junit.Test
    public void Test(){
        System.out.println(1);
        logger.info("info");
        logger.error("error");
    }
}

=========================================================
上面中重要的配置标签有3个,一个是全局的Configuration, 然后是appender, 然后是logger.
下面这段话是copy过来的。


image.png

Logger是最常使用的类,用于打印日记的接口,通过LogManager类指定名字获得。LogManager定位LoggerContext并从它那获得Logger。如果Logger被创建,Logger会被关联一个LoggerConfig,该LoggerConfig可能与Logger同名,或同父package名或为root LoggerConfig(涉及Level Inheritance)(这边可以结合测试程序来看)。

使用Logger可以打印不同级别的日志,这些日志会被包装为LogEvent。LogEvent会被派送到LoggerConfig上。LoggerConfig在xml配置文件中由Logger元素配置的(我们日志中的应该就是rootlog),LoggerConfig含有日志级别(Log Level)信息,对应0到多个Appender。LoggerConfig根据自己的日志级别配置与LogEvent的级别,决定是否允许进一步的处理。如果NO则丢弃,如果Yes则传给Appender。

LoggerConfig中的日记级别只是一个种定义好了的过滤器(Filter),还可以在Logger元素中为LoggerConfig配置过滤器,进行更细致的控制。
Appender负责将LogEvent派送到目的地,可以有很多目的地,如console,文件、远程服务器或数据库等等。一个loggerConfig可以对应很多Appender,loggerConfig含有父(或祖先)loggerConfig的引用。因此loggerConfig不仅将LogEvent发送给自己的所有Appender,还发给它的父(或祖先)LoggerConfig的appender,然后递归向上,这种行为被称为Additivity。
appender只关心如何将LogEvent送到目的地,而Layout负责格式化LogEvent。log4j中含有不同种Layout,用于不同的用途,如JSON,XML,HTML等等。
每个Logger都有自己的名字,通常使用Logger所处类的全限定名作为Logger的名字。
在log4j中必定存在root LoggerConfig,即使没有手动配置,也会使用默认的,保证Logger能够关联到LoggerConfig。
==================================================
(这边有一个疑惑,logger的日志级别设置和appender中的ThresholdFilter日志级别设置)
问题解答:经过我本地的测试,其实这边消息会经过多种过滤。首先在logger中会根据日志级别将消息传到所有满足的logger上(当有多个logger的时候),然后logger会传送给appender, appender如果设置了ThresholdFilter, 就会筛选出符合对应日志级别的信息,床送到目的地中

学习的有些片面,刚会使用的地步,但是在调用异步的时候,程序开始报错了,明天需要好好探究一下原因。 原因居然是因为少加了一个依赖=-=,也是醉了,算了,明天再写吧

        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>3.4.2</version>
        </dependency>

3个标签:

Configuration

Configuration元素一些重要的属性如下:
status:log4j框架内部要输出到console的LogEvent的级别。可选值:trace", “debug”, “info”, “warn”, “error” and “fatal”。貌似默认warn。
dest:输出到console具体的流。可选值:“err”,“out”,文件或url。默认out。
monitorInterval:多少秒扫描一下配置文件

Appenders

<appenders>主要有3种类型的子标签,console(控制台), File 普通文件, RollingFile 一种可以自动调节的文件
Console节点用来定义输出到控制台的Appender.
File节点用来定义输出到指定位置的文件的Appender.
RollingFile节点用来定义超过指定大小自动删除旧的创建新的的Appender.
其中滚动策略可以做成时间和大小双重滚动,配置如下:

<!--此处举例为每小时滚动一个文件且每500M滚动一个文件,控制每小时最多保留20个文件,总的文件保留3天-->
  <!--具体需要根据应用的日志量和希望保留日志大小以及磁盘空间进行评估-->
  <RollingRandomAccessFile name="APPINFO_APPENDER" fileName="${baseLogDir}/appinfo.log"
      filePattern="${baseLogDir}/appinfo.log.%d{yyyyMMddHH}.%i.gz">
      <PatternLayout>
          <Pattern>${pattern}</Pattern>
      </PatternLayout>
      <Policies>
          <SizeBasedTriggeringPolicy size="500MB" />
          <TimeBasedTriggeringPolicy interval="1" modulate="true" />
      </Policies>
      <Filters>
          <!-- 当前appender只打印info日志,warn及以上日志忽略,由后面的错误日志记录 -->
          <ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL" />
          <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY" />
      </Filters>
      <!-- max=20表示一小时内最多保留20个日志文件 -->
      <DefaultRolloverStrategy max="20">
          <!-- 对于指定的路径下的指定后缀的文件,只保留3天的日志文件,那么最多会有3天*24小时*20个日志文件 -->
          <!-- 注意应用需要根据业务需求和磁盘大小评估需要保留的日志个数,对于500M的日志文件来说,要根据应用日志的情况,观察单个日志压缩后文件大小,并计算总大小需要的空间 -->
          <Delete basePath="${baseLogDir}" maxDepth="1">
              <IfFileName glob="*.gz" />
              <IfLastModified age="3d" />
          </Delete>
      </DefaultRolloverStrategy>
  </RollingRandomAccessFile>

具体标签,看配置文件应该是可以看懂的,之后的例子都可以这样子设置

Logger

Loggers标签中常见同步的有两种:Root和Logger,异步有AsyncRoot ,和 AsyncLogger。
log4j2支持同步,异步,混合模式。
Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出
Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。
自定义的Logger(子Loggger)继承自rootLogger,如果存在继承,那么他们也会继承他们的父类中的appender,这个就是上面所说的这种行为被称为Additivity。

总结:

对目前来说,会用就行,不要搞出都不知道日志打印到了哪边,日志怎么不输出的玩笑出来。

参考博客:

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。