logback源碼分析-4.日誌打印

源碼基於logback 1.1.7app

本文將分析logger打印日誌功能的源碼,好比logger.info("hi"):oop

//ch.qos.logback.classic.Logger
public void info(String msg) {
        filterAndLog_0_Or3Plus(FQCN, null, Level.INFO, msg, null, null);
    }
    
/**
 * The next methods are not merged into one because of the time we gain by not
 * creating a new Object[] with the params. This reduces the cost of not
 * logging by about 20 nanoseconds.
 */
private void filterAndLog_0_Or3Plus(final String localFQCN, final Marker marker, final Level level, final String msg, final Object[] params,final Throwable t) {

        final FilterReply decision = loggerContext.getTurboFilterChainDecision_0_3OrMore(marker, this, level, msg, params, t);

        if (decision == FilterReply.NEUTRAL) {
            if (effectiveLevelInt > level.levelInt) {
                return;
            }
        } else if (decision == FilterReply.DENY) {
            return;
        }
        //構建loggingEvent並進行日誌輸出處理
        buildLoggingEventAndAppend(localFQCN, marker, level, msg, params, t);
    }    

private void buildLoggingEventAndAppend(final String localFQCN, final Marker marker, final Level level, final String msg, final Object[] params,final Throwable t) {
        //構建loggingEvent對象,主要設置日誌級別以及日誌內容。
        LoggingEvent le = new LoggingEvent(localFQCN, this, level, msg, t, params);
        le.setMarker(marker);
        //執行logger下全部的appender
        callAppenders(le);
    }    

//首先獲取logger自己的appender list進行處理,處理完後判斷logger的additive屬性是否是true,false的當即中止,true的話
//獲得他的父級logger,好比com.logback.test.LoggerTest對應的logger的父級logger是com.logback.test對應的logger。
//而後使用他的父級logger的appender list進行處理,處理完後並重復以前的邏輯,直到root或則additive爲false。
public void callAppenders(ILoggingEvent event) {
        int writes = 0;
        for (Logger l = this; l != null; l = l.parent) {
            writes += l.appendLoopOnAppenders(event);
            if (!l.additive) {
                break;
            }
        }
        // No appenders in hierarchy
        if (writes == 0) {
            loggerContext.noAppenderDefinedWarning(this);
        }
    }

接下來分析Logger的appendLoopOnAppenders方法:ui

private int appendLoopOnAppenders(ILoggingEvent event) {
        if (aai != null) {
            return aai.appendLoopOnAppenders(event);
        } else {
            return 0;
        }
    }
    
//AppenderAttachableImpl
public int appendLoopOnAppenders(E e) {
        int size = 0;
        //appenderList是該logger的全部的appender,而後循環調用appender的doAppend方法
        for (Appender<E> appender : appenderList) {
            appender.doAppend(e);
            size++;
        }
        return size;
    }

下面咱們用RollingFileAppender的doAppend方法來分析下:this

public void doAppend(E eventObject) {
        // WARNING: The guard check MUST be the first statement in the
        // doAppend() method.

        // prevent re-entry.
        if (Boolean.TRUE.equals(guard.get())) {
            return;
        }

        try {
            guard.set(Boolean.TRUE);

            if (!this.started) {
                if (statusRepeatCount++ < ALLOWED_REPEATS) {
                    addStatus(new WarnStatus("Attempted to append to non started appender [" + name + "].", this));
                }
                return;
            }

            if (getFilterChainDecision(eventObject) == FilterReply.DENY) {
                return;
            }

            // ok, we now invoke derived class' implementation of append
            //核心,下面分析
            this.append(eventObject);

        } catch (Exception e) {
            if (exceptionCount++ < ALLOWED_REPEATS) {
                addError("Appender [" + name + "] failed to append.", e);
            }
        } finally {
            guard.set(Boolean.FALSE);
        }
    }
    
//OutputStreamAppender
protected void append(E eventObject) {
        if (!isStarted()) {
            return;
        }

        subAppend(eventObject);
    }

//RollingFileAppender
protected void subAppend(E event) {
        // The roll-over check must precede actual writing. This is the
        // only correct behavior for time driven triggers.

        // We need to synchronize on triggeringPolicy so that only one rollover
        // occurs at a time
        synchronized (triggeringPolicy) {
            //判斷是否觸發trigger policy,好比基於時間策略來歸檔日誌的,會判斷當前時間是否是大於下次檢測的時間戳,
            //若是大於則會設置下次檢測時間戳進行日誌歸檔。            
            if (triggeringPolicy.isTriggeringEvent(currentlyActiveFile, event)) {
                //假如輸出日誌文件未info.log,這裏便會關閉info.log輸出流,而後對他進行歸檔,好比info.2017.06.08.log,
                //最後從新建立info.log,打開輸出流,等待後續日誌寫入
                rollover();
            }
        }
        //調用OutputStreamAppender的subAppend方法
        super.subAppend(event);
    }
    
//OutputStreamAppender
protected void subAppend(E event) {
        if (!isStarted()) {
            return;
        }
        try {
            // this step avoids LBCLASSIC-139
            if (event instanceof DeferredProcessingAware) {
                ((DeferredProcessingAware) event).prepareForDeferredProcessing();
            }
            // the synchronization prevents the OutputStream from being closed while we
            // are writing. It also prevents multiple threads from entering the same
            // converter. Converters assume that they are in a synchronized block.
            //正如上面所說,這裏加鎖是爲了保護當咱們正在寫的時候輸出流被關閉,還有就是防止多個線程進入操做
            lock.lock();
            try {
                //輸出日誌
                writeOut(event);
            } finally {
                lock.unlock();
            }
        } catch (IOException ioe) {
            // as soon as an exception occurs, move to non-started state
            // and add a single ErrorStatus to the SM.
            this.started = false;
            addStatus(new ErrorStatus("IO failure in appender", this, ioe));
        }
    }
    
protected void writeOut(E event) throws IOException {
        this.encoder.doEncode(event);
    }
    
//LayoutWrappingEncoder  
public void doEncode(E event) throws IOException {
        //根據encoder partten轉換msg爲真正的輸出內容
        String txt = layout.doLayout(event);
        //轉化成byte而後輸出到日誌文件中
        outputStream.write(convertToBytes(txt));
        if (immediateFlush)
            outputStream.flush();
    }
相關文章
相關標籤/搜索