JDK日志框架源码分析

Posted LackMemory

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK日志框架源码分析相关的知识,希望对你有一定的参考价值。

接上篇,本文开始分析addLoggers方法的源码,这个方法的逻辑较多,但是足够清晰,先看第一部分:

        final String name = logger.getName();
        if (name == null) 
            throw new NullPointerException();
        

        // cleanup some Loggers that have been GC'ed
        drainLoggerRefQueueBounded();

        LoggerWeakRef ref = namedLoggers.get(name);
        if (ref != null) 
            if (ref.get() == null) 
                // It's possible that the Logger was GC'ed after the
                // drainLoggerRefQueueBounded() call above so allow
                // a new one to be registered.
                namedLoggers.remove(name);
             else 
                // We already have a registered logger with the given name.
                return false;
            
        

        // We're adding a new logger.
        // Note that we are creating a weak reference here.
        ref = new LoggerWeakRef(logger);
        namedLoggers.put(name, ref);
这部分的逻辑就是先根据logger的name去hashtable中查询,如果查到了直接返回false,表示不必添加;否则根据logger对象创建一个LoggerWeakRef对象,然后以name为key把这个对象放进namedLoggers中。
这里最重要的点是,为什么namedLoggers不直接以Logger类型的对象为value,而是要用LoggerWeakRef包装一层呢?看一下这个内部类:

final class LoggerWeakRef extends WeakReference<Logger> 
        private String                name;       // for namedLoggers cleanup
        private LogNode               node;       // for loggerRef cleanup
        private WeakReference<Logger> parentRef;  // for kids cleanup

        LoggerWeakRef(Logger logger) 
            super(logger, loggerRefQueue);

            name = logger.getName();  // save for namedLoggers cleanup
        
        .......
我们看到它是WeakReference的子类,意味着这是一个弱引用类型,方便垃圾收集器回收。WeakReference这个坑太大,暂时先不挖了。drainLoggerRefQueueBounded();和namedLoggers.remove(name);这两行代码也和GC有关。

然后看第二部分:

// Apply any initial level defined for the new logger.
        Level level = getLevelProperty(name+".level", null);
        if (level != null) 
            doSetLevel(logger, level);
        

        // Do we have a per logger handler too?
        // Note: this will add a 200ms penalty
        loadLoggerHandlers(logger, name, name+".handlers");
        processParentHandlers(logger, name);
这里终于到了那个全局配置文件了,默认情况下是JAVA_HOME/lib/logging.properties,具体内容见这里
先看getLevelProperty方法:

Level getLevelProperty(String name, Level defaultValue) 
        String val = getProperty(name);
        if (val == null) 
            return defaultValue;
        
        try 
            return Level.parse(val.trim());
         catch (Exception ex) 
            return defaultValue;
        
    
它调用了getProperty方法:

public String getProperty(String name) 
        return props.getProperty(name);
    
这个props变量是个成员变量:

private Properties props = new Properties();
但是刚才我们顺着static块的初始化代码一路走来,并没有其他和props有关的代码,也就是说getLevelProperty方法返回的值是null。此时尚未加载配置文件,希望落空。

接着是loadLoggerHandlers方法,这里传入的三个参数分别是:rootLogger、“”和".handlers",这个方法也会用到props变量,但是此时这个变量还没有内容,所以实际上这个方法也是啥都没干。

而传给processParentHandlers方法的第二个参数是rootLogger的名字“”,所以该方法内部的for 循环会马上停止,这个方法最后也是啥都没干。


好了,最后一部分:

        LogNode node = findNode(name);
        node.loggerRef = ref;
        Logger parent = null;
        LogNode nodep = node.parent;
        while (nodep != null) 
            LoggerWeakRef nodeRef = nodep.loggerRef;
            if (nodeRef != null) 
                parent = nodeRef.get();
                if (parent != null) 
                    break;
                
            
            nodep = nodep.parent;
        

        if (parent != null) 
            doSetParent(logger, parent);
        
        // Walk over the children and tell them we are their new parent.
        node.walkAndSetParent(logger);

        // new LogNode is ready so tell the LoggerWeakRef about it
        ref.setNode(node);
由于传给findNode方法的参数是一个空串,所以它会直接返回成员变量root并赋值给node变量:

private LogNode root = new LogNode(null);
new的时候传入null,导致root的parent成员变量是null,所以代码可以直接跳到node.walkAndSetParent(logger);

这个方法仍然是直接返回了,因为node的children变量是null。
而node.loggerRef = ref;和ref.setNode(node);只不过是相互引用了一下。

至此,addLogger方法走读完毕,可以发现,在类加载阶段,把rootLogger传递给它实际上做不了什么。


addLogger方法之后还有两行不重要的代码,类的初始化就完成了:

Logger.global.setLogManager(manager);
                    manager.addLogger(Logger.global);


以上是关于JDK日志框架源码分析的主要内容,如果未能解决你的问题,请参考以下文章

0006JDK源码分析之服务提供者框架

JDK源码分析之 Reference 实现和应用

Android 高级进阶(源码剖析篇) 便于性能分析的日志框架 hugo

Java日志框架学习--日志门面--中

04 | Android 高级进阶(源码剖析篇) 优美的日志框架 logger

Android 高级进阶(源码剖析篇) 小而美的日志框架 timber(下)