深入理解日志框架門面slf4j

Log日志框架學(xué)習(xí)-Slf4j

什么是slf4j

The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging, logback, log4j) allowing the end user to plug in the desired logging framework at deployment time.

slf4j的全稱是Simple Logging Facade for Java, 也就是簡單的日志門面框架,只提供接口,沒有具體的實(shí)現(xiàn)。具體的日志功能由具體的日志框架去實(shí)現(xiàn),比如java.util.logging, logback, log4j,slf4j-simple等。

使用Slf4j有一個(gè)很大的好處是,當(dāng)你想切換其他日志框架的時(shí)候,原來的代碼是幾乎不用改的

看一個(gè)簡單的例子

引入相關(guān)的jar包,slf4j-api-xxx.jar 和 slf4j-simple-xxx.jar (這個(gè)是slf4j最簡單的日志框架實(shí)現(xiàn)了)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zzh.log</groupId>
    <artifactId>log</artifactId>
    <version>1.0-SNAPSHOT</version>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.25</version>
        </dependency>
    </dependencies>
</project>
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Test {
    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(Test.class);
        logger.info("hahaahah");
    }
}

上面就是最簡單的例子了。

認(rèn)識一些基礎(chǔ)的類
  1. Logger
    Logger是一個(gè)接口,包含了所有的打印方法,像trace()、debug()、info()、warn()、error()等,是日志框架的主要入口。
    LoggerLevel包括ERROR、WARN、INFO、DEBUG、TRACE
    LoggerLevel有什么作用呢,通常默認(rèn)是日志級別是DEBUG,那就會過濾logger.trace()的日志輸出;如果就日志級別設(shè)置為INFO,那就會過濾logger.debug()和logger.trace()的日志輸出,一次類推,ERROR是最高級別,將會過濾出logger.error()所有的日志打印。
  2. ILoggerFactory
public interface ILoggerFactory {
    public Logger getLogger(String name);
}

實(shí)現(xiàn)ILoggerFactory這個(gè)接口,用來生成具體的Logger實(shí)現(xiàn)。

  1. LoggerFactory
    這個(gè)類會去類目錄下去查找這個(gè)org/slf4j/impl/StaticLoggerBinder,并獲得對應(yīng)的ILoggerFactory, 再由ILoggerFactory去拿到對應(yīng)的Logger。
  2. StaticLoggerBinder
    所有實(shí)現(xiàn)slf4j日志門面的日志框架必須實(shí)現(xiàn)這個(gè)類,因?yàn)樵贚oggerFactory這個(gè)類中寫死從StaticLoggerBinder這個(gè)類中拿到ILoggerFactory。
  3. Marker 中文不好翻譯,Marker是日志輸出更加細(xì)粒度的控制,個(gè)人理解,其實(shí)Marker就是一個(gè)過濾器,在日志級別前,將一些不需要的日志輸出給過濾掉。
    如果日志框架需要實(shí)現(xiàn)Marker的功能,需要實(shí)現(xiàn)MarkerFactoryBinder
  4. MDC全稱是"Mapped Diagnostic Context“,線程映射表。實(shí)際是日志框架維護(hù)的一個(gè)全局的map,存儲key-value,可以將鍵值的信息插入到日志信息中。
public class SimpleMDC {
  static public void main(String[] args) throws Exception {

    // You can put values in the MDC at any time. Before anything else
    // we put the first name
    MDC.put("first", "Dorothy");
    Logger logger = LoggerFactory.getLogger(SimpleMDC.class);
    
   }

}

源碼分析

getLogger() :入口

LoggerFactory.getLogger(Test.class); 
    public static Logger getLogger(Class<?> clazz) {
        //1.獲取對應(yīng)的LOgger,接著進(jìn)去看看
        Logger logger = getLogger(clazz.getName());
        //2. 檢查日志器的名稱是否匹配
        if (DETECT_LOGGER_NAME_MISMATCH) {
            Class<?> autoComputedCallingClass = Util.getCallingClass();
            if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
                Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),
                                autoComputedCallingClass.getName()));
                Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
            }
        }
        return logger;
    }

getLogger :獲取Logger

    public static Logger getLogger(String name) {
        ILoggerFactory iLoggerFactory = getILoggerFactory(); //進(jìn)去繼續(xù)看
        return iLoggerFactory.getLogger(name);
    }

getILoggerFactory() : 獲得ILoggerFactory

    public static ILoggerFactory getILoggerFactory() {
        // INITIALIZATION_STATE 初始化的狀態(tài)
        // UNINITIALIZED還沒初始化,就開始初始化
        if (INITIALIZATION_STATE == UNINITIALIZED) {
            synchronized (LoggerFactory.class) {
                if (INITIALIZATION_STATE == UNINITIALIZED) {
                    // 把INITIALIZATION_STATE狀態(tài)設(shè)為正在初始化中
                    INITIALIZATION_STATE = ONGOING_INITIALIZATION;
                    // 初始化工作
                    performInitialization();
                }
            }
        }
        switch (INITIALIZATION_STATE) {
        case SUCCESSFUL_INITIALIZATION:
            //如果初始化成功,則通過StaticLoggerBinder這個(gè)類獲取對應(yīng)的ILoggerFactory,這也是上面說到為什么一定要實(shí)現(xiàn)StaticLoggerBinder這個(gè)接口
            return StaticLoggerBinder.getSingleton().getLoggerFactory();
        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():進(jìn)行初始化工作

    private final static void performInitialization() {
        //綁定
        bind();
        //如果初始化成功則進(jìn)行版本檢查
        if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
            versionSanityCheck();
        }
    }

bind() : 綁定

    private final static void bind() {
        try {
            //存儲所有實(shí)現(xiàn)了staticLoggerBinder的路徑
            Set<URL> staticLoggerBinderPathSet = null;
            // skip check under android, see also
            // http://jira.qos.ch/browse/SLF4J-328
            if (!isAndroid()) {
                //查找實(shí)現(xiàn)了staticLoggerBinder的路徑
                staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
                //打印是否有多個(gè)實(shí)現(xiàn)
                reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
            }
            // the next line does the binding
            StaticLoggerBinder.getSingleton();
            // 將初始化狀態(tài)設(shè)為成功
            INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
            // 打印出來真實(shí)是將哪一個(gè)StaticLoggerBinder實(shí)現(xiàn)進(jìn)行綁定
            reportActualBinding(staticLoggerBinderPathSet);
            fixSubstituteLoggers();
            replayEvents();
            // release all resources in SUBST_FACTORY
            SUBST_FACTORY.clear();
        } catch (NoClassDefFoundError ncde) {
            //綁定失敗的情況,將錯(cuò)誤日志打印出來
            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) {
            //這里的異常是找不到StaticLoggerBinder的getSingleton()方法
            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);
        }
    }

** versionSanityCheck()** : 版本檢查

    private final static void versionSanityCheck() {
        try {
            //要求的版本
            String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
            // 初始版本不匹配
            boolean match = false;
            //static private final String[] API_COMPATIBILITY_LIST = new String[] { "1.6", "1.7" }; 可以匹配的版本
            for (String aAPI_COMPATIBILITY_LIST : API_COMPATIBILITY_LIST) {
                //說明版本匹配成功
                if (requested.startsWith(aAPI_COMPATIBILITY_LIST)) {
                    match = true;
                }
            }
            //匹配不成功,打印錯(cuò)誤日志
            if (!match) {
                Util.report("The requested version " + requested + " by your slf4j binding is not compatible with "
                                + Arrays.asList(API_COMPATIBILITY_LIST).toString());
                Util.report("See " + VERSION_MISMATCH + " for further details.");
            }
        } catch (java.lang.NoSuchFieldError nsfe) {
            // given our large user base and SLF4J's commitment to backward
            // compatibility, we cannot cry here. Only for implementations
            // which willingly declare a REQUESTED_API_VERSION field do we
            // emit compatibility warnings.
        } catch (Throwable e) {
            // we should never reach here
            Util.report("Unexpected problem occured during version sanity check", e);
        }
    }

findPossibleStaticLoggerBinderPathSet() :查找實(shí)現(xiàn)了taticLoggerBinder的路徑集合

    static Set<URL> findPossibleStaticLoggerBinderPathSet() {
        // use Set instead of list in order to deal with bug #138
        // LinkedHashSet appropriate here because it preserves insertion order
        // during iteration
        // 用來存儲staticLoggerBinder路徑的集合
        Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
        try {
            //拿到類加載器
            ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
            Enumeration<URL> paths;
            //STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
            if (loggerFactoryClassLoader == null) {
            //如果為空,說明為當(dāng)前類加載器為啟動(dòng)類加載器
                paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
            } else {
                //在類路徑下去查找這個(gè)類
                paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
            }
            //遍歷
            while (paths.hasMoreElements()) {
                // 獲取對應(yīng)的URL資源
                URL path = paths.nextElement();
                // 將URL放入集合LinkedHashSet中
                staticLoggerBinderPathSet.add(path);
            }
        } catch (IOException ioe) {
            Util.report("Error getting resources from path", ioe);
        }
        return staticLoggerBinderPathSet;
    }

reportMultipleBindingAmbiguity() :報(bào)告是否有多個(gè)綁定沖突

    private static void reportMultipleBindingAmbiguity(Set<URL> binderPathSet) {
        //判斷Set集合里是否有多個(gè)StaticLoggerBinder類的URL
        if (isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
            Util.report("Class path contains multiple SLF4J bindings.");
            for (URL path : binderPathSet) {
                Util.report("Found binding in [" + path + "]");
            }
            Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
        }
    }

reportActualBinding() : 報(bào)告實(shí)際綁定的StaticLoggerBinder

    private static void reportActualBinding(Set<URL> binderPathSet) {
        // binderPathSet can be null under Android
        if (binderPathSet != null && isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
            Util.report("Actual binding is of type [" + StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr() + "]");
        }
    }

哈,就這樣子啦,不正之處,多多指正。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 最近,新開發(fā)的一個(gè)項(xiàng)目遇到了一個(gè)log4j的配置問題,之前一直沒怎么關(guān)注過日志框架,借助這個(gè)機(jī)會,好好了解下Jav...
    漂泊的胡蘿卜閱讀 1,620評論 2 24
  • 在項(xiàng)目開發(fā)過程中,我們可以通過 debug 查找問題。而在線上環(huán)境我們查找問題只能通過打印日志的方式查找問題。因此...
    Java架構(gòu)閱讀 3,572評論 2 41
  • 作為Java開發(fā)人員,對于日志記錄框架一定非常熟悉。而且?guī)缀踉谒袘?yīng)用里面,一定會用到各種各樣的日志框架用來記錄程...
    意識流丶閱讀 14,455評論 0 13
  • 對于Java的日志框架,你也許會經(jīng)??吹竭@些名詞: Log4j、Log4j2 Logback Slf4j JCL ...
    NoahU閱讀 4,122評論 0 15
  • 1.SLF4J SLF4J全稱 Simple Logging Facade for Java,它為Java下的日志...
    近路閱讀 2,775評論 0 3

友情鏈接更多精彩內(nèi)容