• 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做定制的过滤

  • 相关阅读:
    Python学习笔记:pandas.read_csv分块读取大文件(chunksize、iterator=True)
    Python学习笔记:os.stat().st_size、os.path.getsize()获取文件大小
    7-1 打印沙漏
    7-1 币值转换
    7-1 抓老鼠啊~亏了还是赚了?
    第四周编程总结哦也
    2018秋寒假作业6—PTA编程总结3
    PTA编程总结3
    PTA编程总结1
    秋季学期学习总结
  • 原文地址:https://www.cnblogs.com/LQBlog/p/12162879.html
Copyright © 2020-2023  润新知