zl程序教程

您现在的位置是:首页 >  其他

当前栏目

最通俗易懂的 JAVA slf4j,log4j,log4j2,logback 关系与区别以及完整集成案例

2023-03-14 22:47:24 时间

slf4j 于log4j,log4j2,logback 是什么关系,有何区别

什么是 slf4j

slf4j不是一个真正意义的可用应用程序,他是一个接口层

就像java 的接口性质一样,单纯集成了slf4j 的话,执行比如log.info log.debug等方法,只能在控制台打印日志,并不会在持久化到文件,或者其他自定义介质上,slf4j至少需要一个实现层框架,比如log4j,logback等

什么是 log4j,log4j2,logback​

log4j,log4j2(log4j的2.0+版本),logback都是log日志的实现框架,作者都是Ceki Gülcü或者说是他的团队,最早开发的是log4j,并基于slf4j和log4j,优化开发了logback,再后来又用全新的技术disruptor框架

重构log4j底层并将logback取其精华重新搭建了一个全新的框架log4j2

也就是log4j的2.0+版本,可以这么理解,logback是log4j的升级版,但并没有用log4j那套接口 log4j2

推翻了log4j和logbak所有底层实现,但是复用了log4j的几大模块和接口

slf4j,log4j,log4j2,logback的关系​

前面说了slf4j只是接口层,必须依赖实现层的框架实现接口层才能真正实现日志记录 log4j(与slf4j接口一致无需适配),log4j2,logback(本身内嵌了slf4j

转换器)都是实现层的框架,但是其实都是有自己独立的接口层的,但是这就有一个问题

一个应用程序用了独立用了log4j款就改,如果哪天想要切换成logback框架的话,所有的log初始化以及log

debug,info等语句都要替换

那么工作量太大了,几乎就是整个项目级别的日志语法替换,所以就需要以后所有开发的应用程序都基于接口层统一提供log初始化以及log.debug,log.info语法记录

基于这一点log4j的作者Ceki Gülcü,独立开发了一个slf4j层,专门用来适配底层的实现

就算你这个日志实现框架比如log4j2,logback

目前提供的接口不兼容slf4j也没关系,再开发一个xxx-slf4j-impl实现就slf4j的接口就可以了,相当于做了一个adapter

比如应用程序的底层实现框架用的是log4j2,但是接口层我想要用slf4j接口层,那么就引入一个log4j-slf4j-impl.jar等就可以了

slf4j,log4j,log4j​2,logback 时间线

log4j ->slf4j ->logback ->log4j2

slf4j,log4j,log4j2,log​back 关系图




23e56a4dd7504ee3803c513e23d91aae.png

日志框架完整集成例子

首先设置资源目录

image

logback 日志框架完​整集成例子

 <!--slf4j 依赖-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <!-- logback依赖 -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!--通用格式配置-->
    <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L %thread %m%n"/>
    <!-- 配置文件的输出路径 -->
    <property name="logDir" value="E:/code/log"/>

    <!-- 配置文件的appender 普通文件-->
    <appender name="fileAppender" class="ch.qos.logback.core.FileAppender">
        <!-- 引入文件位置 -->
        <file>${logDir}/test_logback.log</file>
        <!-- 配置日志输出格式 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!-- 格式引用通用属性配置 -->
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <!-- 配置控制台appender -->
    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <!--默认:System.out 表示以黑色字体输出日志-->
        <!--设置:System.err 表示以红色字体输出日志-->
        <target>
            System.err
        </target>
        <!--配置日志输出格式-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!-- 格式引用通用属性配置 -->
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>
    <!--
        日志记录器
        配置root logger
        level:配置日志级别
        可以同时配置多个appender,做日志的多方向输出
    -->
    <root level="ALL">
        <!-- 引入控制台appender -->
        <appender-ref ref="consoleAppender"/>
        <!-- 引入普通文件appender -->
        <appender-ref ref="fileAppender"/>
    </root>
</configuration>

logback demo code

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

/**
 * @Author alan.wang
 */
public class LogTester {

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

    public static void main(String[] args){
        logger.debug("这是Log4j2 debug");
        logger.info("这是Log4j2 info");
        logger.warn("这是Log4j2 warn");
        logger.error("这是Log4j2 error");
    }

}

logback日志执行结果

我们看一下logback.xml设置的文件位置以及输出内容

image

image

image

log4j2 日志框架完整集成例子


 <!--log4j2 依赖-->
        <!-- Slf4j的核心包,只有日志的接口,整合log4j2并没有实现 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <!-- 桥接器,将JCL的日志输出重定向到Slf4j中 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.25</version>
            <scope>runtime</scope>
        </dependency>
        <!-- 导入日志框架核心包与接口包 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.9.1</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.9.1</version>
            <scope>runtime</scope>
        </dependency>
        <!--用于slf4j与log4j2保持桥接 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.9.1</version>
            <scope>runtime</scope>
        </dependency>
        <!--用于解决web环境下关闭服务器时可能出现的log4j线程无法及时关闭的warn,web工程需要包含log4j-web,非web工程不需要 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-web</artifactId>
            <version>2.9.1</version>
            <scope>runtime</scope>
        </dependency>
        <!--使用log4j2的AsyncLogger时需要包含disruptor -->
        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <scope>runtime</scope>
            <version>3.4.2</version>
        </dependency>

log4j2.xml

<Configuration status="warn" monitorInterval="30">

    <Properties>
        <!-- 需要配置日志目录 -->
        <Property name="baseDir">E:/code/log</Property>
        <Property name="appName">test_log4j2</Property>
        <Property name="pattern">%d %-5p [%t] [%c{1.}] %.-300m%n</Property>
    </Properties>

    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="${pattern}"/>
        </Console>

        <RollingFile name="File" fileName="${baseDir}/${appName}/${appName}.log" filePattern="${baseDir}/${appName}/%d{yyyyMMdd}-%i.${appName}.log">
            <PatternLayout pattern="${pattern}"/>

            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="2048 MB"/>
            </Policies>

            <DefaultRolloverStrategy max="30">
                <Delete basePath="${baseDir}/${appName}" maxDepth="1">
                    <IfFileName glob="*/*.log"/>
                    <IfLastModified age="20d"/>
                </Delete>
            </DefaultRolloverStrategy>
        </RollingFile>

    </Appenders>

    <Loggers>

        <Root level="debug" includeLocation="false" additivity="false">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="File"/>
        </Root>
    </Loggers>
</Configuration>

logback demo code

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

/**
 * @Author alan.wang
 */
public class LogTester {

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

    public static void main(String[] args){
        logger.debug("这是Log4j2 debug");
        logger.info("这是Log4j2 info");
        logger.warn("这是Log4j2 warn");
        logger.error("这是Log4j2 error");
    }

}

log4j2日志执行结果

我们看一下log4j2.xml设置的文件位置以及输出内容

image

image

image

logback,log4j2 同时框架同时存在时,slf4j会用哪个实现

有些时候我们引入第三方或者自己多个模块引入时会不小心同时集成了多个日志框架比如同时继承了logback和log4j2 ,这时候他会报错还是会用哪个呢,我们测试结果如下:

首先他会输出提示提醒你发现了哪些可以绑定的实现框架

image

然后会告诉你最终用了哪个

image