ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Log4j2 + SLF4j打造日志系统

2022-01-26 19:33:25  阅读:207  来源: 互联网

标签:输出 Log4j2 打印 指定 SLF4j 日志 log4j2 log4j


原创:Log4j2 + SLF4j打造日志系统 - 云+社区 - 腾讯云

2019-01-15阅读 2.3K0

目录

  • 一:前言
  • 二:添加依赖
    • 2.1:去除直接和间接依赖的log4j1和SLF4j
    • 2.2:添加依赖
  • 三:xml配置
    • 3.1:log4j2.xml常用demo
    • 3.2:demo的优点
    • 3.3:内容详解
    • 3.4:demo变形
      • 3.4.1:同步打印日志
      • 3.4.2:全部异步打印日志
      • 3.4.3:混合模式打印日志
  • 四:其他
    • 4.1:Log日志level
    • 4.2:Log4j2与logback速度对比

一:前言

近期做一个项目打造项目的日志系统时,发现没有一个系统的学习,故准备系统学习一下日志系统,后续会有关于日志系统的其他介绍与总结,比如log4j2为什么这么快,其底层实现原理等。

java 界里有许多实现日志功能的工具,最早得到广泛使用的是 log4j,许多应用程序的日志部分都交给了 log4j,不过作为组件开发者,他们希望自己的组件不要紧紧依赖某一个工具,毕竟在同一个时候还有很多其他很多日志工具,假如一个应用程序用到了两个组件,恰好两个组件使用不同的日志工具,那么应用程序就会有两份日志输出了。

为了解决这个问题,JCL和SLF4j就出现了,JCL只提供 log 接口,具体的实现则在运行时动态寻找。这样一来组件开发者只需要针对JCL或者slf4j的接口开发,而调用组件的应用程序则可以在运行时搭配自己喜好的日志实践工具。跟 JCL 一样,SLF4J 也是只提供 log 接口,具体的实现是在打包应用程序时所放入的绑定器(名字为 slf4j-XXX-version.jar)来决定,XXX 可以是 log4j12, jdk14, jcl, nop 等,他们实现了跟具体日志工具(比如 log4j)的绑定及代理工作。举个例子:如果一个程序希望用 log4j 日志工具,那么程序只需针对 slf4j-api 接口编程,然后在打包时再放入 slf4j-log4j12-version.jar 和 log4j.jar 就可以了。

项目中我们选择了SLF4j+Log4j2来打造日志系统,log4j2的性能还是比Logback好一些的,下面有对比。

如果转载此博文,请附上本文链接:使用 Log4j2 + SLF4j 打造日志系统的全方位教程_李洋洋-CSDN博客_log4j2 over slf4j 谢谢合作~

二:添加依赖

2.1:去除直接和间接依赖的log4j1和SLF4j

首先我们应该先删除项目已经依赖的其他日志组件,这里指的是没有用到的日志组件,例如janusgraph会间接依赖log4j1的组件,这个组件删除就会报错,所以我们只要删除没有使用的日志组件,这样可以使项目更加干净~ 方法:我们可以观察项目目录下的External Libraries下的依赖文件,如果有log4j1或者其他日志依赖,我们将他们在pom文件中找到删除即可。

如果依赖中有但是pom文件中找不到,就是被间接依赖进来的了,我们在pom 文件中右击鼠标,选中Diagrams->show dependences就可以看到整个项目的依赖图,在其中找到对应的log依赖,选中右击Exclude即可。

2.2:添加依赖

添加的所有依赖都是截止2018.11.22日最新的稳定版本

<!--slf4j-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.7.25</version>
    <scope>runtime</scope>
</dependency>
<!--log4j2-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.11.1</version>
</dependency> 
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.11.1</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-web</artifactId>
    <version>2.11.1</version>
    <scope>runtime</scope>
</dependency>
<!--log4j2+slf4j-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.11.1</version>
</dependency>

上述的jcl-over-scf4j的作用以及原因: 即使现在你仍会看到很多程序应用 JCL + log4j 这种搭配,不过当程序规模越来越庞大时,JCL的动态绑定并不是总能成功,具体原因大家可以 Google 一下,这里就不再赘述了。解决方法之一就是在程序部署时静态绑定指定的日志工具,这也是 SLF4J 产生的原因。

现在还有一个问题,假如你正在开发应用程序所调用的组件当中已经使用了 JCL 的,还有一些组建可能直接调用了 java.util.logging,这时你需要一个桥接器(名字为 XXX-over-slf4j.jar)把他们的日志输出重定向到 SLF4J,所谓的桥接器就是一个假的日志实现工具,比如当你把 jcl-over-slf4j.jar 放到 CLASS_PATH 时,即使某个组件原本是通过 JCL 输出日志的,现在却会被 jcl-over-slf4j “骗到”SLF4J 里,然后 SLF4J 又会根据绑定器把日志交给具体的日志实现工具,这样就可以实现日志的统一了。如果你的项目没有使用jcl那么就不必添加这个。

上述的log4j-web是在开发web项目的时候需要的,如果你不是web项目,可以酌情删除

三:xml配置

3.1:log4j2.xml常用demo

在类路径下新建文件:log4j2.xml ,注意“2”不要缺少,位置放的正确并且文件名符合要求的话,项目会自动扫描到该配置文件。

Log4j2能够在初始化期间自动配置自身。当Log4j2启动时,它将找到所有ConfigurationFactory插件并按加权顺序从最高到最低排列。在交付时,Log4j包含四个ConfigurationFactory实现:一个用于JSON,一个用于YAML,一个用于 properties,一个用于XML,下面为查找加载顺序:

  1. Log4j2将检查“log4j.configurationFile”系统属性,如果设置,将尝试使用与文件扩展名匹配的ConfigurationFactory加载配置。
  2. 如果未设置系统属性,则ConfigurationFactory属性将在类路径中查找 log4j2-test.properties。
  3. 如果没有找到这样的文件,YAML ConfigurationFactory将在类路径中查找 log4j2-test.yaml或log4j2-test.yml。
  4. 如果没有找到这样的文件,JSON ConfigurationFactory将在类路径中查找 log4j2-test.json或log4j2-test.jsn。
  5. 如果找不到这样的文件,XML ConfigurationFactory将在类路径中查找 log4j2-test.xml。
  6. 如果找不到测试文件,ConfigurationFactory属性将在类路径上查找 log4j2.properties。
  7. 如果找不到属性文件,YAML ConfigurationFactory将在类路径上查找 log4j2.yaml或log4j2.yml。
  8. 如果找不到YAML文件,JSON ConfigurationFactory将在类路径上查找 log4j2.json或log4j2.jsn。
  9. 如果找不到JSON文件,XML ConfigurationFactory将尝试在类路径上找到 log4j2.xml。
  10. 如果找不到配置文件,则将使用DefaultConfiguration。这将导致所有日志记录输出转到控制台

log4j2.xml 文件内容:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO" monitorInterval="30">
    <properties>
        <!--设置容器日志在硬盘上输出的目录-->
        <property name="logPath">/opt/logs/renetwork/</property>
        <!--设置项目日志在硬盘上输出的目录-->
        <property name="logPathForProject">/opt/logs/renetwork/project/</property>
    </properties>

    <Appenders>
        <!--=====容器日志配置=====-->
        <!--设置在控制台打印日志-->
        <Console name="Console" target="SYSTEM_OUT">
            <!--设置输出格式-->
            <PatternLayout pattern="[%-5p]:%d{YYYY-MM-dd HH:mm:ss} [%t] %c{1}:%L - %msg%n" />
        </Console>

        <!--设置级别为INFO日志输出到info.log中,filename为输出日志的目录,filepattern为压缩文件的命名规范与目录 -->
        <RollingFile name="INFO" filename="${logPath}/info.log"
                     filepattern="${logPath}/%d{YYYYMMdd}-%i-info.log.zip">
            <!--设置日志级别-->
            <Filters>
                <ThresholdFilter level="INFO"/>
            </Filters>
            <!--输出日志的格式-->
            <PatternLayout pattern="[ %-5p]:%d{YYYY-MM-dd HH:mm:ss} [%t] %c{1}:%L - %msg%n" />
            <Policies>
                <!--设置每天打包日志一次-->
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                <!--设置日志文件满50MB后打包-->
                <SizeBasedTriggeringPolicy size="50MB" />
            </Policies>
            <!--设置最多保存20个日志文件,默认为7个-->
            <DefaultRolloverStrategy max="20" />
        </RollingFile>

        <!--设置级别为WARN日志输出到warn.log中-->
        <RollingFile name="WARN" filename="${logPath}/warn.log"
                     filepattern="${logPath}/%d{YYYYMMdd}-%i-warn.log.zip">
            <Filters>
                <!--设置只输出级别为WARN的日志-->
                <ThresholdFilter level="WARN"/>
                <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
            </Filters>
            <PatternLayout pattern="[ %-5p]:%d{YYYY-MM-dd HH:mm:ss} [%t] %c{1}:%L - %msg%n" />
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                <SizeBasedTriggeringPolicy size="50MB" />
            </Policies>
            <DefaultRolloverStrategy max="20" />
        </RollingFile>

        <!--设置级别为ERROR日志输出到error.log中-->
        <RollingFile name="ERROR" filename="${logPath}/error.log"
                     filepattern="${logPath}/%d{YYYYMMdd}-%i-error.log.zip">
            <!--设置只输出级别为ERROR的日志-->
            <ThresholdFilter level="ERROR"/>
            <PatternLayout pattern="[ %-5p]:%d{YYYY-MM-dd HH:mm:ss} [%t] %c{1}:%L - %msg%n" />
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                <SizeBasedTriggeringPolicy size="50MB" />
            </Policies>
            <DefaultRolloverStrategy max="20" />
        </RollingFile>


        <!--=====项目日志配置=====-->
        <Console name="ConsolePro" target="SYSTEM_OUT">
            <PatternLayout pattern="[%-5p]:%d{YYYY-MM-dd HH:mm:ss} [%t] %c{1}:%L - %msg%n" />
        </Console>

        <RollingFile name="INFOPro" filename="${logPathForProject}/info.log"
                     filepattern="${logPathForProject}/%d{YYYYMMdd}-%i-info.log.zip">
            <Filters>
                <ThresholdFilter level="INFO"/>
            </Filters>
            <PatternLayout pattern="[ %-5p]:%d{YYYY-MM-dd HH:mm:ss} [%t] %c{1}:%L - %msg%n" />
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                <SizeBasedTriggeringPolicy size="50MB" />
            </Policies>
            <DefaultRolloverStrategy max="20" />
        </RollingFile>

        <RollingFile name="WARNPro" filename="${logPathForProject}/warn.log"
                     filepattern="${logPathForProject}/%d{YYYYMMdd}-%i-warn.log.zip">
            <Filters>
                <ThresholdFilter level="WARN"/>
                <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
            </Filters>
            <PatternLayout pattern="[ %-5p]:%d{YYYY-MM-dd HH:mm:ss} [%t] %c{1}:%L - %msg%n" />
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                <SizeBasedTriggeringPolicy size="50MB" />
            </Policies>
            <DefaultRolloverStrategy max="20" />
        </RollingFile>

        <RollingFile name="ERRORPro" filename="${logPathForProject}/error.log"
                     filepattern="${logPathForProject}/%d{YYYYMMdd}-%i-error.log.zip">
            <ThresholdFilter level="ERROR"/>
            <PatternLayout pattern="[ %-5p]:%d{YYYY-MM-dd HH:mm:ss} [%t] %c{1}:%L - %msg%n" />
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                <SizeBasedTriggeringPolicy size="50MB" />
            </Policies>
            <DefaultRolloverStrategy max="20" />
        </RollingFile>
    </Appenders>
    
    <Loggers>
        <!--
            添加项目日志
            配置项目日志只输出com.bj58.renetwork下面的日志
            additivity="false" 表示只将该日志输出到该配置的路径下面,并不会再重复输出到root也就是容器日志中
        -->
        <logger name="com.bj58.renetwork" level="debug" additivity="false">
            <appender-ref ref = "ConsolePro"/>
            <appender-ref ref = "INFOPro"/>
            <appender-ref ref = "WARNPro" />
            <appender-ref ref = "ERRORPro" />
        </logger>
        <!--添加容器日志-->
        <root level="INFO">
            <appender-ref ref = "Console"/>
            <appender-ref ref = "INFO" />
            <appender-ref ref = "WARN" />
            <appender-ref ref = "ERROR" />
        </root>
    </Loggers>
</Configuration>

注释我写的应该比较清楚了,如果你还是不太明白,下面我会详细介绍一下。

3.2:demo的优点

  • 将项目的日志和容器的日志分开打印到不同的文件夹中,这样便于查看与管理。比如,一个容器中部署了多个项目,如果不分开打印log的话所有的log都打印到容器的log中,所有项目和容器的log在一个文件中管理和查看的难度可以想象出来。如果每个项目一个对应的文件夹,所有的项目和容器都相互分开,将自己的日志打印到自己对应的日志文件中,简洁、方便查看、便于管理
  • 将日志的info、warn、error级别的日志分开单独打印,INFO包含info\warn\error所有的日志,WARN使其只包含warn的日志,ERROR使其只包含error的日志,这样在发现错误和异常更加便利
  • 将日志文件压缩存储,减少资源消耗
  • 控制日志文件数量,在保证日志可追溯许可的范围下删除过早的日志文件,减少资源消耗

3.3:内容详解

1: 根节点Configuration有两个属性:status和monitorinterval

  • status用来指定log4j2本身的打印日志的级别
  • monitorinterval用于指定log4j自动重新配置的监测间隔时间,单位是s,最小是5s

2:根节点下的子节点properties,用于定义变量和修改变量,这里我只定义了两个路径变量,一个是容器log路径,一个是项目log路径 3:根节点下的子节点Appenders,主要用于定义Appender,常见的有三种子节点:Console、RollingFile、File

log4j组件提供了好多种appender供我们使用,介绍看官网吧特别详细: Log4j – Log4j 2 Appenders

  • Console节点用来定义输出到控制台的Appender.
    • name:指定Appender的名字.
    • target:SYSTEM_OUT(输出所有) 或 SYSTEM_ERR(只输出错误),一般只设置默认:SYSTEM_OUT.
    • PatternLayout:输出格式,不设置默认为:%m%n.
  • File节点用来定义输出到指定位置的文件的Appender.
    • name:指定Appender的名字.
    • fileName:指定输出日志的目的文件带全路径的文件名.
    • PatternLayout:输出格式,不设置默认为:%m%n.
  • RollingFile节点用来定义超过指定大小自动删除旧的创建新的的Appender并且可以压缩文件.
    • name:指定Appender的名字.
    • fileName:指定输出日志的目的文件带全路径的文件名.
    • PatternLayout:输出格式,不设置默认为:%m%n.
    • filePattern:指定新建日志文件的名称格式.
    • Policies:指定滚动日志的策略,就是什么时候进行新建日志文件输出日志.
    • TimeBasedTriggeringPolicy:Policies子节点,基于时间的滚动策略,interval属性用来指定多久滚动一次,默认是1 hour。modulate=true用来调整时间:比如现在是早上3am,interval是4,那么第一次滚动是在4am,接着是8am,12am…而不是7am.
    • SizeBasedTriggeringPolicy:Policies子节点,基于指定文件大小的滚动策略,size属性用来定义每个日志文件的大小.
    • DefaultRolloverStrategy:用来指定同一个文件夹下最多有几个日志文件时开始删除最旧的,创建新的(通过max属性)。

其中:fileName和filePattern不同的作用: fileName指定的是当天日志输出的日志输出位置 filePattern指的根据配置,对每天的日志文件进行压缩存储的时候的文件名,也就是新建的文件名

4:根节点下的子节点Loggers,用于配置上述添加的appender,两种子节点:Root、Logger

  • Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出
    • level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF.
    • AppenderRef(appender-ref):Root的子节点,用来指定该日志输出到哪个Appender.
  • Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等
    • level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF.
    • additivity : 设置是否继承,也就是是否将log也打印到Root下,“false”为不打印到Root下
    • name:用来指定该Logger所适用的类或者类所在的包全路径,继承自Root节点.
    • AppenderRef(appender-ref):Logger的子节点,用来指定该日志输出到哪个Appender,如果没有指定,就会默认继承自Root.如果指定了,那么会在指定的这个Appender和Root的Appender中都会输出,此时我们可以设置Logger的additivity="false"只在自定义的Appender中进行输出。

5:输出格式相关:

  • %t:线程名称
  • %p:日志级别
  • %c:日志消息所在类名
  • %m:消息内容
  • %M:输出执行方法
  • %d:发生时间,%d{yyyy-MM-dd HH:mm:ss,SSS},输出类似:2011-10-18 22:10:28,921
  • %x::输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。
  • %L:代码中的行数
  • %n:换行
  • %c{*}系列:显示LoggerName的格式

我平常使用的就是:[%-5p]:%d{YYYY-MM-dd HH:mm:ss} [%t] %c{2}:%L - %msg%n

3.4:demo变形

3.4.1:同步打印日志

同步打印日志是最消耗资源的方式,我们在开发的时候,可以选择使用全同步方式打印日志,这样便于我们debug。或者项目并发度不高的情况下也可以使用这种方式。但是,当并发量比较大、对项目响应速度敏感时并且对日志不是强实时性要求的话,最好还是使用全部异步或者混合方式。

上述的demo便是全部同步的案例。在此不再赘述。

3.4.2:全部异步打印日志

全部异步打印日志是对项目请求速度最理想的方式,在500个线程的情况下速度几乎是全同步打印log的10倍,是混合打印的2倍。下面是官网的比较图,可以对照着看一下:

异步Logger是让业务逻辑把日志信息放入Disruptor队列后可以直接返回,具有更高吞吐、调用log方法更低的延迟。但也有一些缺点比如:异常处理麻烦、 可变日志消息问题、更大的CPU开销、需要等待“最慢的Appender”消费完成。

所以我们在并发量高、日志实时性要求不高,并且所暴漏的缺点都可以容忍的情况下最好还是选用全部异步打印日志,这样可以获得更快的响应,也会给用户更好的体验。 异步打印配置有几种方式: 1:在你的classpath下面添加个log4j2.component.properties文件,并且添加以下内容:

这种方式不需要修改原来的log4j2.xml文件

Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

2:使用异步标签,修改上面的demo的部分:

如果想要全部异步log的话,一定要所有的相关标签都是用异步标签

    <Loggers>
        <!--
            添加项目日志
            配置项目日志只输出com.bj58.renetwork下面的日志
            additivity="false" 表示只将该日志输出到该配置的路径下面,并不会再重复输出到root也就是容器日志中
        -->
        <asyncLogger name="com.bj58.renetwork" level="debug" additivity="false">
            <appender-ref ref = "ConsolePro"/>
            <appender-ref ref = "INFOPro"/>
            <appender-ref ref = "WARNPro" />
            <appender-ref ref = "ERRORPro" />
        </asyncLogger>
        <!--添加容器日志-->
        <asyncRoot level="INFO">
            <appender-ref ref = "Console"/>
            <appender-ref ref = "INFO" />
            <appender-ref ref = "WARN" />
            <appender-ref ref = "ERROR" />
        </asyncRoot>
    </Loggers>

3:JVM启动参数(boot.ini)加上:

-DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

3.4.3:混合模式打印日志

混合模式就是既有一异步又有同步日志打印,那些部分需要同步或者异步,这需要根据具体对项目该部分的需求来定了.

下面我设置了项目日志同步打印,容器日志异步打印

    <Loggers>
        <!--
            添加项目日志
            配置项目日志只输出com.bj58.renetwork下面的日志
            additivity="false" 表示只将该日志输出到该配置的路径下面,并不会再重复输出到root也就是容器日志中
        -->
        <logger name="com.bj58.renetwork" level="debug" additivity="false">
            <appender-ref ref = "ConsolePro"/>
            <appender-ref ref = "INFOPro"/>
            <appender-ref ref = "WARNPro" />
            <appender-ref ref = "ERRORPro" />
        </logger>
        <!--添加容器日志-->
        <asyncRoot level="INFO">
            <appender-ref ref = "Console"/>
            <appender-ref ref = "INFO" />
            <appender-ref ref = "WARN" />
            <appender-ref ref = "ERROR" />
        </asyncRoot>
    </Loggers>

四:其他

4.1:Log日志level

级别只输出“大于等于”自身级别的log,7中level的级别关系如下: OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL  1. DEBUG :     DEBUG Level指出细粒度信息事件对调试应用程序是非常有帮助的。  2. INFO     INFO level表明 消息在粗粒度级别上突出强调应用程序的运行过程。  3.WARN     WARN level表明会出现潜在错误的情形。  4.ERROR     ERROR level指出虽然发生错误事件,但仍然不影响系统的继续运行。  5.FATAL     FATAL level指出每个严重的错误事件将会导致应用程序的退出。  6.ALL     ALL Level是最低等级的,用于打开所有日志记录。  7.OFF     OFF Level是最高等级的,用于关闭所有日志记录。

4.2:Log4j2与logback速度对比

Log4j2和logback都是日志组件,logback就是为了替代log4j1出现的,log4j2是log4j1的升级版,几乎相当于重构了log4j1。 log4j2的效率可以在多线程时,在线程数量大的情况下,超过logback10倍左右!下面是官网提供的数据对比: 速度对比图(来自官网):

在Solaris和windows操作系统上的数据对比(来自官网):

标签:输出,Log4j2,打印,指定,SLF4j,日志,log4j2,log4j
来源: https://blog.csdn.net/zhaofuqiangmycomm/article/details/122707439

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有