logback源碼閱讀-Appender(四)


前面我們看到 最終logger輸出是委托給了appender 如果沒有配置appender是不會輸出的

示例配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="CHARSET" value="UTF-8"/>
    <!--為了防止進程退出時,內存中的數據丟失,請加上此選項-->
    <shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <Encoder>
            <pattern><pattern>|%p|%d{yyyy-MM-dd HH:mm:ss.SSS}|%t|%logger{10}:%line%n   %m%n%n</pattern></pattern>
            <charset>${CHARSET}</charset>
        </Encoder>
    </appender>
    <appender name="ASYNC_STDOUT" class="ch.qos.logback.classic.AsyncAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>info</level>
        </filter>
        <appender-ref ref="STDOUT"/><!--最終異步委托給consoleAppender-->
        <includeCallerData>true</includeCallerData>
    </appender>
    <!--給root關聯appender 默認我們所有logger都沒有appender 前面看到是遞歸網上找父類輸出 都是父類統一輸出-->
    <root level="info">
        <appender-ref ref="ASYNC_STDOUT"/>
    </root>
</configuration>

 

 

 

默認的appender實現

我們可以根據需求選擇以下默認的實現 如果沒有合適的需要擴展再參考下面的相關類擴展

 

 

下面我們舉例看其中 一個如果我們有定制化需求可以參考實現定制

AsyncAppender

類圖

 

 

 我們需要自定義appender只需要繼承UnsynchronizedAppenderBase就行了

start

ch.qos.logback.core.AsyncAppenderBase#start

調用時機在解析標簽處 分別調用start 和end 

   private AsyncAppenderBase<E>.Worker worker = new AsyncAppenderBase.Worker();
    /**
     * 啟動worker
     */
    public void start() {
        //防止重復啟用
        if (!this.isStarted()) {
            //AsyncAppenderBase.AppenderAttachableImpl的數量如果美而有保存
            if (this.appenderCount == 0) {
                this.addError("No attached appenders found.");
            } else if (this.queueSize < 1) {
                this.addError("Invalid queue size [" + this.queueSize + "]");
            } else {
                //初始化一個線程安全的數組阻塞隊列 ququeSize默認為256
                this.blockingQueue = new ArrayBlockingQueue(this.queueSize);
                if (this.discardingThreshold == -1) {
                    this.discardingThreshold = this.queueSize / 5;
                }

                this.addInfo("Setting discardingThreshold to " + this.discardingThreshold);
                this.worker.setDaemon(true);
                this.worker.setName("AsyncAppender-Worker-" + this.getName());
                //重寫了父類的start 所以保證不破壞父類邏輯 所以調用父類start
                super.start();
                //<1>啟動一個worker AsyncAppenderBase<E>.Worker worker = new AsyncAppenderBase.Worker();內部類 消費隊列數據 委托給當前類的 AppenderAttachableImpl<E> aai = new AppenderAttachableImpl();
                this.worker.start();//這里是調用線程的start
            }
        }
    }

start調用點在解析完append end標簽之后 后面會講

ch.qos.logback.core.joran.action.AppenderAction#end

public void end(InterpretationContext ec, String name) {
        if (!this.inError) {
            //如果實現了LifeCycle 接口則調用start方法
            if (this.appender instanceof LifeCycle) {
                this.appender.start();
            }

            Object o = ec.peekObject();
            if (o != this.appender) {
                this.addWarn("The object at the of the stack is not the appender named [" + this.appender.getName() + "] pushed earlier.");
            } else {
                ec.popObject();
            }

        }
    }

<1>

class Worker extends Thread {
        Worker() {
        }

        public void run() {
            AsyncAppenderBase<E> parent = AsyncAppenderBase.this;
            AppenderAttachableImpl aai = parent.aai;

            //判斷主類對象是否started
            while (parent.isStarted()) {
                try {
                    //循環消費
                    E e = parent.blockingQueue.take();
                    //<3>這里是委托給前面 <appender-ref ref="STDOUT"/> 配置最終還是Console 這里我們可以配置的appender 上面用例配置的STDOUT
                    aai.appendLoopOnAppenders(e);
                } catch (InterruptedException var5) {
                    break;
                }
            }
            AsyncAppenderBase.this.addInfo("Worker thread will flush remaining events before exiting. ");
            //以下是當started關閉 不接受消息 但是還是要消費完
            Iterator i$ = parent.blockingQueue.iterator();

            while (i$.hasNext()) {
                E ex = i$.next();
                aai.appendLoopOnAppenders(ex);
                parent.blockingQueue.remove(ex);
            }
            //相關委托的appender started也標識為false
            aai.detachAndStopAllAppenders();
        }
    }

可以看到aynycAppender最終沒有做實際的事情最終還是委托給了 AppenderAttachableImpl 對應上面配置內部封裝的就是ConsoleAppender

AppenderAttachableImpl

<3>appendLoopOnAppenders

ch.qos.logback.core.spi.AppenderAttachableImpl#appendLoopOnAppenders

 public int appendLoopOnAppenders(E e) {
        int size = 0;
        //獲得容器內的所有appender
        Appender<E>[] appenderArray = (Appender[])this.appenderList.asTypedArray();
        int len = appenderArray.length;

        for(int i = 0; i < len; ++i) {
            //<4>逐個調用doAppend
            appenderArray[i].doAppend(e);
            ++size;
        }

        return size;
    }

UnsynchronizedAppenderBase

<4>doAppend

ch.qos.logback.core.spi.AppenderAttachableImpl#appendLoopOnAppenders

->

ch.qos.logback.core.UnsynchronizedAppenderBase#doAppend

    private ThreadLocal<Boolean> guard = new ThreadLocal();
    public void doAppend(E eventObject) {
        //防止一個線程同時進入多個吧。。。
        if (!Boolean.TRUE.equals(this.guard.get())) {
            try {
                this.guard.set(Boolean.TRUE);
                //必須為started狀態
                if (this.started) {
                    //appender的過濾器 參考ThresholdFilter 實現 可以對消息搜集做過濾
                    if (this.getFilterChainDecision(eventObject) == FilterReply.DENY) {
                        return;
                    }
                    //<5>模板模式化 由子類實現
                    this.append(eventObject);
                    return;
                }

                if (this.statusRepeatCount++ < 3) {
                    this.addStatus(new WarnStatus("Attempted to append to non started appender [" + this.name + "].", this));
                }
            } catch (Exception var6) {
                if (this.exceptionCount++ < 3) {
                    this.addError("Appender [" + this.name + "] failed to append.", var6);
                }

                return;
            } finally {
                this.guard.set(Boolean.FALSE);
            }

        }
    }

    protected abstract void append(E var1);

OutputStreamAppender

<5>append

ch.qos.logback.core.spi.AppenderAttachableImpl#appendLoopOnAppenders

->

ch.qos.logback.core.UnsynchronizedAppenderBase#doAppend

->

ch.qos.logback.core.OutputStreamAppender#append

 protected void append(E eventObject) {
        if (this.isStarted()) {
            this.subAppend(eventObject);
        }
    }

ch.qos.logback.core.OutputStreamAppender#subAppend

 protected void subAppend(E event) {
        if (this.isStarted()) {
            try {
                if (event instanceof DeferredProcessingAware) {
                    ((DeferredProcessingAware)event).prepareForDeferredProcessing();
                }
//encoder具體查看https://www.cnblogs.com/LQBlog/p/12164918.html#autoid-2-0-0
byte[] byteArray = this.encoder.encode(event); this.writeBytes(byteArray); } catch (IOException var3) { this.started = false; this.addStatus(new ErrorStatus("IO failure in appender", this, var3)); } } }
 private void writeBytes(byte[] byteArray) throws IOException {
        if (byteArray != null && byteArray.length != 0) {
            this.lock.lock();

            try {
                //System.out進行輸出
                this.outputStream.write(byteArray);
                if (this.immediateFlush) {
                    this.outputStream.flush();
                }
            } finally {
                this.lock.unlock();
            }

        }
    }

總結

1.Logger必須和appender關聯才可以產生作用

2.我們可以為Appender配置Filter做定制的過濾


免責聲明!

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



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