源碼閱讀-logback的LoggerContext的設計以及如何創建Logger


上一篇博客介紹了StaticLoggerBinder如何提供ILoggerFactory的實現類,首先擁有一個默認的LoggerContext的實例,再去初始化該實例(可配置化,可通過配置文件或者java類或者使用默認配置),ContextInitializer是如何初始化LoggerContext的我們后續的博客再講述。我們先來看看LoggerContext是如何創建核心類Logger實例的。

 從圖中可以看到,LoggerContext類除了ILoggerFactory接口之外,還實現了LifeCycle接口,並繼承自ContextBase類。同時擁有LoggerContextListener和根Logger兩個比較重要的成員變量。

  我們先大致介紹一下LoggerContext的字段和方法。

/**
 *
 * LoggerContext繼承ContextBase並實現ILoggerFactory和LifeCycle
 * LoggerContext將許多logback經典組件粘在一起。
 * 原則上,每個logback經典組件實例都直接或間接地附加到LoggerContext實例。
 * 同樣重要的是,LoggerContext實現了{@link ILoggerFactory}作為{@link Logger}實例的制造源。
 * @author Ceki Gulcu
 */
public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle {

    /** Default setting of packaging data in stack traces */
    public static final boolean DEFAULT_PACKAGING_DATA = false;

    /** 根Logger */
    final Logger root;
    /** size用來表示LoggerContext一共創建了幾個Logger*/
    private int size;

    /**
     * 沒有Appender警告計數器 原則上只警告一次
     */
    private int noAppenderWarning = 0;
    /**
     * 觀察者模式  被觀察者(主題):LoggerContext 觀察者:多個LoggerContextListener
     * LoggerContext監聽器
     */
    final private List<LoggerContextListener> loggerContextListenerList = new ArrayList<LoggerContextListener>();

    /** loggerCache 緩存 所有LoggerContext創建的logger都放在這里*/
    private Map<String, Logger> loggerCache;
    /**loggerContextRemoteView是一個LoggerContext的VO對象,保存了LoggerContext的一些值 */
    private LoggerContextVO loggerContextRemoteView;
    /**
     * 快速過濾器鏈
     * 通過封裝一個TurboFilterList對象(繼承一個集合對象),可以將 獲取過濾器鏈結果 方法封裝到該對面里面 方法復用
     *
     * 我認為TurboFilterList實例不應放在LoggerContext作為一個成員變量
     * 目前看來 TurboFilterList用來Logger對象的過濾 且LoggerContext沒有用到TurboFilterList
     * 反而后續 Logger對象需要維持一個LoggerContext的成員變量的引用(為了用TurboFilterList組件)
     * 我認為應該在Logger類中擁有一個靜態的TurboFilterList實例對象
     */
    private final TurboFilterList turboFilterList = new TurboFilterList();
    private boolean packagingDataEnabled = DEFAULT_PACKAGING_DATA;

    private int maxCallerDataDepth = ClassicConstants.DEFAULT_MAX_CALLEDER_DATA_DEPTH;
    /** resetCount是用來統計該LoggerContext調用過幾次reset()方法*/
    int resetCount = 0;
    private List<String> frameworkPackages;

 

 構造方法:這個方法基本上是只會調用一次的,在StaticLoggerBinder里

  public LoggerContext() {
        super();
        this.loggerCache = new ConcurrentHashMap<String, Logger>();

        this.loggerContextRemoteView = new LoggerContextVO(this);
        this.root = new Logger(Logger.ROOT_LOGGER_NAME, null, this);
        this.root.setLevel(Level.DEBUG);
        loggerCache.put(Logger.ROOT_LOGGER_NAME, root);
        initEvaluatorMap();
        size = 1;
        this.frameworkPackages = new ArrayList<String>();
    }

 

 方法:

private void incSize() {
        size++;
    }

    int size() {
        return size;
    }
/**
     */
    public Logger exists(String name) {
        return (Logger) loggerCache.get(name);

final void noAppenderDefinedWarning(final Logger logger) {
        if (noAppenderWarning++ == 0) {
            getStatusManager().add(new WarnStatus("No appenders present in context [" + getName() + "] for logger [" + logger.getName() + "].", logger));
        }
    }

/**
     * 獲取所有創建的Loggers
     * @return
     */
    public List<Logger> getLoggerList() {
        Collection<Logger> collection = loggerCache.values();
        List<Logger> loggerList = new ArrayList<Logger>(collection);
        Collections.sort(loggerList, new LoggerComparator());
        return loggerList;
    }


/**
     * 重置LoggerContxet
     */
    @Override
    public void reset() {
        resetCount++;
        super.reset();
        initEvaluatorMap();
        initCollisionMaps();
        root.recursiveReset();
        resetTurboFilterList();
        cancelScheduledTasks();
        fireOnReset();
        resetListenersExceptResetResistant();
        resetStatusListeners();
    }


 /**
     * 一層薄封裝 我覺得沒必要
     * @param newFilter
     */
    public void addTurboFilter(TurboFilter newFilter) {
        turboFilterList.add(newFilter);
    }

    /**
     * First processPriorToRemoval all registered turbo filters and then clear the registration
     * list.
     * 重置快速過濾器鏈表
     * 我認為這個方法應該 封裝在TurboFilterList類中
     */
    public void resetTurboFilterList() {
        for (TurboFilter tf : turboFilterList) {
            tf.stop();
        }
        turboFilterList.clear();
    }

    /**
     * getTurboFilterChainDecision_0_3OrMore
     * getTurboFilterChainDecision_1
     * getTurboFilterChainDecision_2
     * 這一系列奇怪的方法是對TurboFilterList中getTurboFilterChainDecision()的薄封裝
     * 至於目的 據作者自己說是為了提高性能
     * @param marker
     * @param logger
     * @param level
     * @param format
     * @param params
     * @param t
     * @return
     */
    final FilterReply getTurboFilterChainDecision_0_3OrMore(final Marker marker, final Logger logger, final Level level, final String format,
                    final Object[] params, final Throwable t) {
        if (turboFilterList.size() == 0) {
            return FilterReply.NEUTRAL;
        }
        return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, params, t);
    }

    final FilterReply getTurboFilterChainDecision_1(final Marker marker, final Logger logger, final Level level, final String format, final Object param,
                    final Throwable t) {
        if (turboFilterList.size() == 0) {
            return FilterReply.NEUTRAL;
        }
        return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, new Object[] { param }, t);
    }

    final FilterReply getTurboFilterChainDecision_2(final Marker marker, final Logger logger, final Level level, final String format, final Object param1,
                    final Object param2, final Throwable t) {
        if (turboFilterList.size() == 0) {
            return FilterReply.NEUTRAL;
        }
        return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, new Object[] { param1, param2 }, t);
    }

    // === start listeners ==============================================

    /**
     * 觀察者模式
     * 添加一個監聽者
     * @param listener
     */
    public void addListener(LoggerContextListener listener) {
        loggerContextListenerList.add(listener);
    }

    /**
     * 移除一個監聽者
     * @param listener
     */
    public void removeListener(LoggerContextListener listener) {
        loggerContextListenerList.remove(listener);
    }


 /**
     * 清空所有的監聽者
     */
    private void resetAllListeners() {
        loggerContextListenerList.clear();
    }


void fireOnLevelChange(Logger logger, Level level) {
        for (LoggerContextListener listener : loggerContextListenerList) {
            listener.onLevelChange(logger, level);
        }
    }

    /**
     * 當上下文重置時 通知上下文監聽者們
     * 采用“拉”的方式 將自己的引用傳遞過去
     */
    private void fireOnReset() {
        for (LoggerContextListener listener : loggerContextListenerList) {
            listener.onReset(this);
        }
    }

    /**
     * 當啟動上下文時 通知上下文監聽者們
     */
    private void fireOnStart() {
        for (LoggerContextListener listener : loggerContextListenerList) {
            listener.onStart(this);
        }
    }

    /**
     * 當停止上下文時 通知上下文監聽者們
     */
    private void fireOnStop() {
        for (LoggerContextListener listener : loggerContextListenerList) {
            listener.onStop(this);
        }
    }

    // === end listeners ==============================================

    /**
     * 啟動LoggerContxet
     */
    public void start() {
        super.start();
        fireOnStart();
    }

    /**
     * 停止LoggerContxet
     */
    public void stop() {
        reset();
        fireOnStop();
        resetAllListeners();
        super.stop();
    }

 

 接下來我們重點介紹一個核心方法getLogger(String name),LoggerContext如何創建Logger

1.如果name是null,拋出異常。

2.如果請求的是ROOT Logger,那么就直接返回root

3.然后檢查一下請求的Logger是否已經創建過了,如果已經創建過,就直接從loggerCache中返回

4.如果還沒創建過,那就開始逐層創建,比如請求的Logger的name是com.company.package.ClassName,那么一共會創建4個Logger,分別是Logger[com]、Logger[com.company]、Logger[com.company.package]、Logger[com.company.package.ClassName]

public final Logger getLogger(final Class<?> clazz) {
        return getLogger(clazz.getName());
    }

    @Override
    public final Logger getLogger(final String name) {

        if (name == null) {
            throw new IllegalArgumentException("name argument cannot be null");
        }

        // if we are asking for the root logger, then let us return it without
        // wasting time
        //若是獲取根logger直接返回
        if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
            return root;
        }

        int i = 0;
        Logger logger = root;

        // check if the desired logger exists, if it does, return it
        // without further ado.
        //查看loggerCache容器里面是否存在期望的logger,有則直接返回
        Logger childLogger = (Logger) loggerCache.get(name);
        // if we have the child, then let us return it without wasting time
        if (childLogger != null) {
            return childLogger;
        }

        // if the desired logger does not exist, them create all the loggers
        // in between as well (if they don't already exist)
        //如果期望的logger不存在,則創建中間所有的loggers
        String childName;
        while (true) {
            int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
            if (h == -1) {
                childName = name;
            } else {
                childName = name.substring(0, h);
            }
            // move i left of the last point
            i = h + 1;
            synchronized (logger) {
                childLogger = logger.getChildByName(childName);
                if (childLogger == null) {
                    childLogger = logger.createChildByName(childName);
                    loggerCache.put(childName, childLogger);
                    incSize();
                }
            }
            logger = childLogger;
            if (h == -1) {
                return childLogger;
            }
        }
    }

 

上面代碼比較清晰,就是根據"."來解析name,然后創建Logger,每創建一個Logger,都放到loggerCache中,並且把size++。

創建Child Logger是有點講究的,除了創建Logger實例之外,還有維護父子關系,並且處理Level繼承的問題,這個是在Logger類的createChildByName(String childName)方法里實現的。我們來先來看看這些方法:

   /**
     * The default size of child list arrays. The JDK 1.5 default is 10. We use a
     * smaller value to save a little space.
     * 根據名稱創建子logger
     */

    Logger createChildByName(final String childName) {
        // 檢查childName的合法性 是不是該logger的child
        int i_index = LoggerNameUtil.getSeparatorIndexOf(childName, this.name.length() + 1);
        if (i_index != -1) {
            throw new IllegalArgumentException("For logger [" + this.name + "] child name [" + childName
                            + " passed as parameter, may not include '.' after index" + (this.name.length() + 1));
        }

        if (childrenList == null) {
            childrenList = new CopyOnWriteArrayList<Logger>();
        }
        Logger childLogger;
        childLogger = new Logger(childName, this, this.loggerContext);
        childrenList.add(childLogger);
        childLogger.effectiveLevelInt = this.effectiveLevelInt;
        return childLogger;
    }

 

 

    /**
     * 根據名稱查詢子logger
     * @param childName
     * @return
     */
    Logger getChildByName(final String childName) {
        if (childrenList == null) {
            return null;
        } else {
            int len = this.childrenList.size();
            for (int i = 0; i < len; i++) {
                final Logger childLogger_i = (Logger) childrenList.get(i);
                final String childName_i = childLogger_i.getName();

                if (childName.equals(childName_i)) {
                    return childLogger_i;
                }
            }
            // no child found
            return null;
        }
    }

 

 

/**
     * 為該logger設置等級level
     * @param newLevel
     */
    public synchronized void setLevel(Level newLevel) {
        //賦值level和當前level相等,因為level是固定的幾個常量所以用==比較即可,則什么也不做並結束
        if (level == newLevel) {
            // nothing to do;
            return;
        }
        //當該looger為root時,該level不能為null
        if (newLevel == null && isRootLogger()) {
            throw new IllegalArgumentException("The level of the root logger cannot be set to null");
        }

        //賦新值
        level = newLevel;
        if (newLevel == null) {//若newLevel為null,則繼承父logger的effectiveLevelInt
            effectiveLevelInt = parent.effectiveLevelInt;
            newLevel = parent.getEffectiveLevel();
        } else {//否則,effectiveLevelInt = newLevel.levelInt
            effectiveLevelInt = newLevel.levelInt;
        }

        if (childrenList != null) {//若子loggers不為空,則為每一個子logger處理他們的effectiveLevelInt的值
            int len = childrenList.size();
            for (int i = 0; i < len; i++) {
                Logger child = (Logger) childrenList.get(i);
                // tell child to handle parent levelInt change
                child.handleParentLevelChange(effectiveLevelInt);
            }
        }
        // inform listeners
        //通知
        loggerContext.fireOnLevelChange(this, newLevel);
    }

    /**
     * This method is invoked by parent logger to let this logger know that the
     * prent's levelInt changed.
     *
     * 遞歸處理父logger的level變化,該loggere的effectiveLevelInt值處理
     * @param newParentLevelInt
     */
    private synchronized void handleParentLevelChange(int newParentLevelInt) {
        // changes in the parent levelInt affect children only if their levelInt is
        // null
        //當該logger的level為空時,父logger的level變化才會影響子logger的effectiveLevelInt
        //因為當該logger的level不為空時,effectiveLevelInt=level.levelInt,否則effectiveLevelInt繼承父logger的effectiveLevelInt
        if (level == null) {
            effectiveLevelInt = newParentLevelInt;

            // propagate the parent levelInt change to this logger's children
            if (childrenList != null) {
                int len = childrenList.size();
                for (int i = 0; i < len; i++) {
                    Logger child = (Logger) childrenList.get(i);
                    child.handleParentLevelChange(newParentLevelInt);
                }
            }
        }

        /**
         * 我寫該遞歸函數的方式
         *  先寫該遞歸函數的終止條件
         *  if (level != null){
         *             return;
         *         }
         *  level為null時處理
         *          effectiveLevelInt = newParentLevelInt;
         *         if (childrenList != null) {
         *             int len = childrenList.size();
         *             for (int i = 0; i < len; i++) {
         *                 Logger child = (Logger) childrenList.get(i);
         *                 child.handleParentLevelChange(newParentLevelInt);
         *             }
         *         }
         */

    }

 

構造方法:this.parent = parent里,設置了父Logger

   Logger(String name, Logger parent, LoggerContext loggerContext) {
        this.name = name;
        this.parent = parent;
        this.loggerContext = loggerContext;
    }

 

 
總結一下創建Logger的完整流程:
1、如果請求ROOT logger,則直接返回root
2、如果請求的Logger已經存在,則直接返回
3、如果請求的Logger尚未創建,則從ROOT開始,級聯創建所有Logger
4、每創建一個Logger,都要設置父子關系,繼承生效級別
5、每創建一個Logger,都將其放入loggerCache,並將size++

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM