前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring周边:日志——下

Spring周边:日志——下

作者头像
WEBJ2EE
修改2019-11-25 18:29:15
4660
修改2019-11-25 18:29:15
举报
文章被收录于专栏:WebJ2EEWebJ2EE

有日志么?

日志有价值么?

日志框架了解么?

“日志”这个词最早见于航海领域,是记录航行主要情况的载体文件,内容包括操作指令、气象、潮流、航向、航速、旅客、货物等,是处理海事纠纷或者海难的原始依据之一。尔后延伸到航空领域,黑匣子就是一个重要的航空日志载体,调查空难原因时第一反应是找到黑匣子,并通过解析其中的日志信息来还原空难的事实真相 码出高效:Java开发手册

门面设计模式是面面向对象设计模式中的一种,日志框架采用的就是这种模式,类似JDBC的设计理念。它只提供一套接口规范,自身不负责日志功能的实现,目的是让使用者不需要关注底层具体是哪个日志库来负责日志打印及具体的使用细节。目前用的最广泛的日志门面有两种:SLF4J 和 Apache Commons Logging(JCL)。

本期内容提要

SLF4J

JUL、Log4j1、Log4j2、Logback

的集成原理

1. SLF4J 快速上手

SLF4J(Simple Logging Facade for Java),简单日志门面。它提供了 Java 中所有日志框架的简单抽象。因此,它使用户能够使用单个依赖项处理任何日志框架,例如:JUL(Java util logging)、Log4j、Log4j2 和 Logback。

1.1. SLF4J mandatory 依赖

Note that SLF4J-enabling your library implies the addition of only a single mandatory dependency, namely slf4j-api.jar. http://www.slf4j.org/

代码语言:javascript
复制
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.29</version>
</dependency>

1.2. SLF4J bindings 依赖

As mentioned previously, SLF4J supports various logging frameworks. The SLF4J distribution ships with several jar files referred to as "SLF4J bindings", with each binding corresponding to a supported framework. http://www.slf4j.org/manual.html

1.3. SLF4J bridging 依赖

Often, some of the components you depend on rely on a logging API other than SLF4J. To deal with such circumstances, SLF4J ships with several bridging modules which redirect calls made to log4j, JCL and java.util.logging APIs to behave as if they were made to the SLF4J API instead. http://www.slf4j.org/legacy.html

注1:jcl-over-slf4j.jar 与 slf4j-jcl.jar 对比;

JCL-over-SLF4J, i.e. jcl-over-slf4j.jar, comes in handy in situations where JCL needs to be supported for backward compatibility reasons. It can be used to fix problems associated with JCL, without necessarily adopting the SLF4J API, a decision which can be deferred to a later time.

On the other hand, slf4j-jcl.jar is useful after you have already adopted the SLF4J API for your component which needs to be embedded in a larger application environment where JCL is a formal requirement. Your software component can still use SLF4J API without disrupting the larger application. Indeed, slf4j-jcl.jar will delegate all logging decisions to JCL so that the dependency on SLF4J API by your component will be transparent to the larger whole.

Please note that jcl-over-slf4j.jar and slf4j-jcl.jar cannot be deployed at the same time. The former jar file will cause JCL to delegate the choice of the logging system to SLF4J and the latter jar file will cause SLF4J to delegate the choice of the logging system to JCL, resulting in an infinite loop.

注2:log4j-over-slf4j.jar 与 slf4j-log4j12.jar 对比;

The presence of slf4j-log4j12.jar, that is the log4j binding for SLF4J, will force all SLF4J calls to be delegated to log4j. The presence of log4j-over-slf4j.jar will in turn delegate all log4j API calls to their SLF4J equivalents. If both are present simultaneously, slf4j calls will be delegated to log4j, and log4j calls redirected to SLF4j, resulting in an endless loop.

POM 依赖:slf4j-api 通过 slf4j-log4j12 绑定到 log4j1.2;

代码语言:javascript
复制

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.29</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.29</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

测试程序:

代码语言:javascript
复制

package webj2ee;

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

public class SLFJ {
    private static Logger logger = LoggerFactory.getLogger(SLFJ.class);

    public static void main(String[] args) {
        logger.debug("From slf4j's debug msg!");
        logger.info("From slf4j's info msg!");
        logger.warn("From slf4j's warn msg!");
        logger.error("From slf4j's error msg!");
    }
}

运行结果:

POM 依赖:slf4j-api 通过 log4j-slf4j-impl 绑定到 log4j2;

代码语言:javascript
复制
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.29</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.12.1</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.12.1</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.12.1</version>
</dependency>

测试程序:

代码语言:javascript
复制

package webj2ee;

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

public class SLFJ {
    private static Logger logger = LoggerFactory.getLogger(SLFJ.class);

    public static void main(String[] args) {
        logger.debug("From slf4j's debug msg!");
        logger.info("From slf4j's info msg!");
        logger.warn("From slf4j's warn msg!");
        logger.error("From slf4j's error msg!");
    }
}

运行结果:

POM 依赖:logback 自身就实现了 slf4j,不需要再提供额外的绑定依赖包;

代码语言:javascript
复制

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.29</version>
</dependency
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
    <version>1.2.3</version>
</dependency>

测试程序:

代码语言:javascript
复制

package webj2ee;

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

public class SLFJ {
    private static Logger logger = LoggerFactory.getLogger(SLFJ.class);

    public static void main(String[] args) {
        logger.debug("From slf4j's debug msg!");
        logger.info("From slf4j's info msg!");
        logger.warn("From slf4j's warn msg!");
        logger.error("From slf4j's error msg!");
    }
}

运行结果:

POM 依赖:将 JUL、JCL 的调用都通过 SLF4J 转发给 Log4j2;

代码语言:javascript
复制
<!-- jcl's api call redirect to slf4j -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.7.29</version>
</dependency>
<!-- jul's api call redirect to slf4j -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
    <version>1.7.29</version>
</dependency>
<!-- slf4j api -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.29</version>
</dependency>
<!-- slf4j's api call redirect to log4j2 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.12.1</version>
</dependency>
<!-- log4j2 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.12.1</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.12.1</version>
</dependency>

D:\jul.properties:注册 SLF4JBridgeHandler

代码语言:javascript
复制
handlers=org.slf4j.bridge.SLF4JBridgeHandler
.level= INFO

java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

java.util.logging.SimpleFormatter.format=[JUL] %4$s: %5$s [%1$tc]%n

jvm 启动参数:启用 D:\jul.properties

代码语言:javascript
复制

-Djava.util.logging.config.file=D:/jul.properties

测试程序:

代码语言:javascript
复制

package webj2ee;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SLFJ {
    private static Logger logger = LoggerFactory.getLogger(SLFJ.class);
    private static java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger(SLFJ.class.getName());
    private static Log jclLogger = LogFactory.getLog(JCL.class);

    public static void main(String[] args) {
        logger.info("Call from slf4j's api ---- an info msg!");
        logger.warn("Call from slf4j's api ---- an warn msg!");

        julLogger.info("Call from jul's api ---- an info msg!");
        julLogger.warning("Call from jul's api ---- an warning msg!");

        jclLogger.info("Call from jcl's api ---- an info msg!");
        jclLogger.warn("Call from jcl's api ---- an warning msg!");
    }
}

运行结果:

2. SLF4J 深度分析

2.1. POM 依赖

代码语言:javascript
复制
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.29</version>
</dependency>

2.2. 核心接口

SLF4J 日志门面对外暴露两个接口:

代码语言:javascript
复制

package webj2ee;

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

public class SLFJ {
    private static Logger logger = LoggerFactory.getLogger(SLFJ.class);

    public static void main(String[] args) {
        logger.info("Call from slf4j's api ---- an info msg!");
        logger.warn("Call from slf4j's api ---- an warn msg!");
    }
}

SLF4J 日志对象接口,也定义了日志操作的 5 个级别:

TRACE< DEBUG < INFO < WARN < ERROR

以 debug 级日志输出接口为例,比 JCL 门面丰富的多:

2.3. 源码总体构成

2.4. 源码分析——LoggerFactory

2.4.1. LogFactory 最常用方法

上述获取 Log 过程分2个阶段:

  • 通过 getILoggerFactory 方法获取 ILoggerFactory 的过程;
  • 根据 ILoggerFactory.getLogger 接口获取 Logger 的过程

2.4.2. getILoggerFactory() 方法解析

SLF4J API is designed to bind with one and only one underlying logging framework at a time. If more than one binding is present on the class path, SLF4J will emit a warning, listing the location of those bindings. Multiple bindings were found on the class path

The warning emitted by SLF4J is just that, a warning. Even when multiple bindings are present, SLF4J will pick one logging framework/implementation and bind with it. The way SLF4J picks a binding is determined by the JVM and for all practical purposes should be considered random. Multiple bindings were found on the class path

参考:

《码出高效:Java 开发手册》 《Java 开发手册 1.5.0》 SLF4J 官网: http://www.slf4j.org/ Bridging legacy APIs: http://www.slf4j.org/legacy.html SLF4JBridgeHandler‘s API doc: http://www.slf4j.org/api/org/slf4j/bridge/SLF4JBridgeHandler.html Multiple bindings were found on the class path: http://www.slf4j.org/codes.html#multiple_bindings Taxonomy of class loader problems encountered when using Jakarta Commons Logging: http://articles.qos.ch/classloader.html

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-11-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WebJ2EE 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
日志服务
日志服务(Cloud Log Service,CLS)是腾讯云提供的一站式日志服务平台,提供了从日志采集、日志存储到日志检索,图表分析、监控告警、日志投递等多项服务,协助用户通过日志来解决业务运维、服务监控、日志审计等场景问题。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档