slf4j日志框架源码阅读

slf4j日志框架源码阅读

private static final Logger log = LoggerFactory.getLogger(App.class);

  1. Logger logger = getLogger(clazz.getName());
    1.1 ILoggerFactory iLoggerFactory = getILoggerFactory(); 参见 getILoggerFactory方法详细解析 章节
    //获取 日志实现框架的 LoggerFactory;这个必须要实现 slf4j 的 org.slf4j.ILoggerFactory 接口
    1.2 return iLoggerFactory.getLogger(name); //获取到 日志实现框架的 logger
  2. DETECT_LOGGER_NAME_MISMATCH
  3. return logger;

getILoggerFactory 方法详细解析

public static ILoggerFactory getILoggerFactory() {
    if (INITIALIZATION_STATE == UNINITIALIZED) { //首次 getILoggerFactory 的话,那么 INITIALIZATION_STATE 一定是 UNINITIALIZED
        synchronized (LoggerFactory.class) {    //这里有 类锁,
            if (INITIALIZATION_STATE == UNINITIALIZED) {    //再次 检查 INITIALIZATION_STATE 状态, 双重检查锁 ( INITIALIZATION_STATE 是 volatile 类型得的)
                INITIALIZATION_STATE = ONGOING_INITIALIZATION; //设置 INITIALIZATION_STATE 为 ONGOING_INITIALIZATION 状态
                performInitialization();  //继续  performInitialization 方法
            }
        }
    }
    switch (INITIALIZATION_STATE) {
    case SUCCESSFUL_INITIALIZATION: //首次绑定成功 或者 第二次 使用 getILoggerFactory 方法,最会 走到这一步 
        return StaticLoggerBinder.getSingleton().getLoggerFactory(); //获取 日志实现框架的 LoggerFactory;这个必须要实现 slf4j 的 org.slf4j.ILoggerFactory 接口 
    case NOP_FALLBACK_INITIALIZATION:
        return NOP_FALLBACK_FACTORY;
    case FAILED_INITIALIZATION:
        throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
    case ONGOING_INITIALIZATION:
        // support re-entrant behavior.
        // See also http://jira.qos.ch/browse/SLF4J-97
        return SUBST_FACTORY;
    }
    throw new IllegalStateException("Unreachable code");
}

performInitialization 方法详细解析

// 初始化 方法
private final static void performInitialization() {
    bind(); //绑定具体的 log 实现框架;继续 bind 方法
    //如果 bind 成功的话,INITIALIZATION_STATE 则已经是 SUCCESSFUL_INITIALIZATION 了
    if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
        versionSanityCheck(); //版本相关检查
    }
}

bind 方法详细解析

private final static void bind() {
    try {
        Set<URL> staticLoggerBinderPathSet = null;
        // skip check under android, see also
        // http://jira.qos.ch/browse/SLF4J-328
        if (!isAndroid()) { //非 android 环境 
            staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet(); //找到classpath 中 所有的 org/slf4j/impl/StaticLoggerBinder.class 路径
            reportMultipleBindingAmbiguity(staticLoggerBinderPathSet); // 如果 找到了 多个 org/slf4j/impl/StaticLoggerBinder.class 那么则会 打印 提示 每个  org/slf4j/impl/StaticLoggerBinder.class 的 路径
        }
        // the next line does the binding
        StaticLoggerBinder.getSingleton(); //注意这个 StaticLoggerBinder 就是 上面找到的 org/slf4j/impl/StaticLoggerBinder.class ,这个需要每个不同的 日志框架自己去实现 
        INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;   // INITIALIZATION_STATE 设为 SUCCESSFUL_INITIALIZATION 状态
        reportActualBinding(staticLoggerBinderPathSet); //如果 找到了 多个 org/slf4j/impl/StaticLoggerBinder.class 那么则会 打印 提示 实际使用的是 那个 框架的 log
    } catch (NoClassDefFoundError ncde) {   //这里是没有找到 日志实现框架的情况 
        String msg = ncde.getMessage();
        if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
            INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
            Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
            Util.report("Defaulting to no-operation (NOP) logger implementation");
            Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details.");
        } else {
            failedBinding(ncde);
            throw ncde;
        }
    } catch (java.lang.NoSuchMethodError nsme) {
        String msg = nsme.getMessage();
        if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
            INITIALIZATION_STATE = FAILED_INITIALIZATION;
            Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
            Util.report("Your binding is version 1.5.5 or earlier.");
            Util.report("Upgrade your binding to version 1.6.x.");
        }
        throw nsme;
    } catch (Exception e) {
        failedBinding(e);
        throw new IllegalStateException("Unexpected initialization failure", e);
    } finally {
        postBindCleanUp();
    }
}

总结

通过上面我们对 slf4j 的源码阅读,我们明白了,slf4j 是如何 找到 具体日志实现 的过程。
也明白了 具体日志实现 需要实现 slf4j 的 一些 特定类 和实现某些接口的。

org/slf4j/impl/StaticLoggerBinder.class 桥接类
org.slf4j.ILoggerFactory logger 接口  

所以我们在使用 具体的 日志实现框架的时候,是需要 引入 其和 slf4j 之间的 桥接包的。

使用 slf4j 需要 引入的 jar 包,也是使用日志 必须引入的 jar 包

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.30</version>
</dependency>

使用 logback 需要 引入的 jar 包

<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-core</artifactId>
  <version>1.2.3</version>
</dependency>
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.2.3</version>
</dependency>

logback-classic 包 依赖 logback-core包,logback-classic 里面就是有 org/slf4j/impl/StaticLoggerBinder.class 桥接类
和 org.slf4j.ILoggerFactory logger 接口 的实现类

日志配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <property name="USER_HOME" value="./log" />

    <appender name="FILE-THREAD" class="ch.qos.logback.classic.sift.ParrelSiftingAppender">

        <!-- This is MDC value -->
        <!-- We will assign a value to 'logFileName' via Java code -->
        <discriminator>
            <key>logFileName</key>
            <defaultValue>head0</defaultValue>
        </discriminator>

        <sift>
            <!-- A standard RollingFileAppender, the log file is based on 'logFileName' at runtime  -->
            <appender name="FILE-${logFileName}"
                      class="ch.qos.logback.core.rolling.RollingFileAppender">
                <file>${USER_HOME}/${logFileName}.log</file>

                <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                    <Pattern>
                        %d{yyyy-MM-dd HH:mm:ss} %mdc [%thread] %level %logger{35} - %msg%n
                    </Pattern>
                </encoder>

                <rollingPolicy
                        class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
                    <FileNamePattern>${USER_HOME}/${logFileName}.%i.log.zip
                    </FileNamePattern>
                    <MinIndex>1</MinIndex>
                    <MaxIndex>10</MaxIndex>
                </rollingPolicy>

                <triggeringPolicy
                        class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
                    <MaxFileSize>10MB</MaxFileSize>
                </triggeringPolicy>

            </appender>

        </sift>
    </appender>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>
                %-5level %logger{36} - %msg%n
            </Pattern>
        </layout>
    </appender>

    <logger name="org.yyb" level="debug"
            additivity="false">
        <appender-ref ref="FILE-THREAD" />
        <appender-ref ref="STDOUT" />
    </logger>

    <root level="error">
        <appender-ref ref="STDOUT" />
    </root>

</configuration>

使用 log4j 需要 引入的 jar 包

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.7.30</version>
</dependency>v
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>

slf4j-log4j12 是桥接包,log4j 是 真正的log实现

日志配置

# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

log4j.rootLogger=INFO, stdout, connectAppender
#log4j.rootLogger=INFO, stdout

# Send the logs to the console.
#
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

# Send the logs to a file, rolling the file at midnight local time. For example, the `File` option specifies the
# location of the log files (e.g. ${kafka.logs.dir}/connect.log), and at midnight local time the file is closed
# and copied in the same directory but with a filename that ends in the `DatePattern` option.
#
log4j.appender.connectAppender=com.smcv.org.apache.log4j.ThreadWriterAppender
log4j.appender.connectAppender.append=org.apache.log4j.DailyRollingFileAppender
log4j.appender.connectAppender.DatePattern='.'yyyy-MM-dd-HH
log4j.appender.connectAppender.fileName=log/$X{threadName}connect.log
log4j.appender.connectAppender.layout=org.apache.log4j.PatternLayout

# The `%X{connector.context}` parameter in the layout includes connector-specific and task-specific information
# in the log message, where appropriate. This makes it easier to identify those log messages that apply to a
# specific connector. Simply add this parameter to the log layout configuration below to include the contextual information.
#
connect.log.pattern=[%d] %p %m (%c:%L)%n
#connect.log.pattern=[%d] %p %X{connector.context}%m (%c:%L)%n

log4j.appender.stdout.layout.ConversionPattern=${connect.log.pattern}
log4j.appender.connectAppender.layout.ConversionPattern=${connect.log.pattern}

log4j.logger.org.apache.zookeeper=ERROR
log4j.logger.org.reflections=ERROR

使用 log4j2 需要 引入的 jar 包

<!--添加log4j2相关jar包-->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-slf4j-impl</artifactId>
  <version>2.7</version>
</dependency>
<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>

log4j-slf4j-impl 是 桥接包

log4j 日志配置转化为 logback 日志配置

使用这个网站

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值