logback OutputStreamAppender高效日志输出源码解析

 更新时间:2023年10月29日 17:05:21   作者:codecraft  
这篇文章主要介绍了为大家logback OutputStreamAppender日志输出效率提升示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

本文主要研究一下logback的OutputStreamAppender

OutputStreamAppender

logback-core/src/main/java/ch/qos/logback/core/OutputStreamAppender.java

public class OutputStreamAppender<E> extends UnsynchronizedAppenderBase<E> {
    /**
     * It is the encoder which is ultimately responsible for writing the event to an
     * {@link OutputStream}.
     */
    protected Encoder<E> encoder;
    /**
     * All synchronization in this class is done via the lock object.
     */
    protected final ReentrantLock streamWriteLock = new ReentrantLock(false);
    /**
     * This is the {@link OutputStream outputStream} where output will be written.
     */
    private OutputStream outputStream;
    boolean immediateFlush = true;
    //......
}
OutputStreamAppender继承了UnsynchronizedAppenderBase,它定义了encoder、streamWriteLock、outputStream、immediateFlush属性

start

/**
     * Checks that requires parameters are set and if everything is in order,
     * activates this appender.
     */
    public void start() {
        int errors = 0;
        if (this.encoder == null) {
            addStatus(new ErrorStatus("No encoder set for the appender named \"" + name + "\".", this));
            errors++;
        }
        if (this.outputStream == null) {
            addStatus(new ErrorStatus("No output stream set for the appender named \"" + name + "\".", this));
            errors++;
        }
        // only error free appenders should be activated
        if (errors == 0) {
            super.start();
        }
    }
其start方法主要是校验encoder、outputStream,然后执行super.start()

stop

/**
     * Stop this appender instance. The underlying stream or writer is also closed.
     * 
     * <p>
     * Stopped appenders cannot be reused.
     */
    public void stop() {
        if(!isStarted())
            return;
        streamWriteLock.lock();
        try {
            closeOutputStream();
            super.stop();
        } finally {
            streamWriteLock.unlock();
        }
    }
    protected void closeOutputStream() {
        if (this.outputStream != null) {
            try {
                // before closing we have to output out layout's footer
                encoderClose();
                this.outputStream.close();
                this.outputStream = null;
            } catch (IOException e) {
                addStatus(new ErrorStatus("Could not close output stream for OutputStreamAppender.", this, e));
            }
        }
    }
    void encoderClose() {
        if (encoder != null && this.outputStream != null) {
            try {
                byte[] footer = encoder.footerBytes();
                writeBytes(footer);
            } catch (IOException ioe) {
                this.started = false;
                addStatus(new ErrorStatus("Failed to write footer for appender named [" + name + "].", this, ioe));
            }
        }
    }
其stop方法主要是加锁然后closeOutputStream,再执行super.stop;closeOutputStream主要是先写入encoder.footerBytes(),再关闭outputStream

append

protected void append(E eventObject) {
        if (!isStarted()) {
            return;
        }
        subAppend(eventObject);
    }
    protected void subAppend(E event) {
        if (!isStarted()) {
            return;
        }
        try {
            // this step avoids LBCLASSIC-139
            if (event instanceof DeferredProcessingAware) {
                ((DeferredProcessingAware) event).prepareForDeferredProcessing();
            }
            writeOut(event);
        } 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 {
        byte[] byteArray = this.encoder.encode(event);
        writeBytes(byteArray);
    }
    private void writeBytes(byte[] byteArray) throws IOException {
        if (byteArray == null || byteArray.length == 0)
            return;
        streamWriteLock.lock();
        try {
            writeByteArrayToOutputStreamWithPossibleFlush(byteArray);
        } finally {
            streamWriteLock.unlock();
        }
    }
    /**
     * A simple method to write to an outputStream and flush the stream if immediateFlush is set to true.
     *
     * @since 1.3.9/1.4.9
     */
    protected final void writeByteArrayToOutputStreamWithPossibleFlush(byte[] byteArray) throws IOException {
        this.outputStream.write(byteArray);
        if (immediateFlush) {
            this.outputStream.flush();
        }
    }
append方法主要是执行writeOut操作,如果是DeferredProcessingAware类型,会先执行prepareForDeferredProcessing;writeOut先执行encoder的encode,然后加锁执行outputStream.write(byteArray),如果需要immediateFlush则会执行outputStream.flush()

setOutputStream

public void setOutputStream(OutputStream outputStream) {
        streamWriteLock.lock();
        try {
            // close any previously opened output stream
            closeOutputStream();
            this.outputStream = outputStream;
            if (encoder == null) {
                addWarn("Encoder has not been set. Cannot invoke its init method.");
                return;
            }
            encoderInit();
        } finally {
            streamWriteLock.unlock();
        }
    }
    protected void closeOutputStream() {
        if (this.outputStream != null) {
            try {
                // before closing we have to output out layout's footer
                encoderClose();
                this.outputStream.close();
                this.outputStream = null;
            } catch (IOException e) {
                addStatus(new ErrorStatus("Could not close output stream for OutputStreamAppender.", this, e));
            }
        }
    }    
    void encoderInit() {
        if (encoder != null && this.outputStream != null) {
            try {
                byte[] header = encoder.headerBytes();
                writeBytes(header);
            } catch (IOException ioe) {
                this.started = false;
                addStatus(
                        new ErrorStatus("Failed to initialize encoder for appender named [" + name + "].", this, ioe));
            }
        }
    }
setOutputStream方法会加锁执行closeOutputStream、给outputStream赋值,再执行encoderInit;closeOutputStream主要是为了清空前面遗留的数据,encoderInit则先写入encoder.headerBytes()

小结

logback的OutputStreamAppender主要是通过非公平的ReentrantLock来写入outputStream。

以上就是logback OutputStreamAppender日志输出效率提升源码解析的详细内容,更多关于logback OutputStreamAppender的资料请关注脚本之家其它相关文章!

相关文章

  • 最新springboot中必须要了解的自动装配原理

    最新springboot中必须要了解的自动装配原理

    本文给大家介绍springboot中必须要了解的自动装配原理,spring-boot-dependencies:核心依赖都在父工程中,这个里面主要是管理项目的资源过滤及插件,本文对springboot自动装配原理给大家介绍的非常详细,需要的朋友参考下吧
    2022-05-05
  • SpringBoot自定义全局异常处理器的问题总结

    SpringBoot自定义全局异常处理器的问题总结

    Springboot框架提供两个注解帮助我们十分方便实现全局异常处理器以及自定义异常,处理器会优先处理更具体的异常类型,如果没有找到匹配的处理器,那么它会寻找处理更一般异常类型的处理器,本文介绍SpringBoot自定义全局异常处理器的问题,一起看看吧
    2024-01-01
  • Java多线程之同步工具类Exchanger

    Java多线程之同步工具类Exchanger

    这篇文章主要介绍了Java多线程之同步工具类Exchanger,Exchanger 是一个用于线程间协作的工具类,Exchanger用于进行线程间的数据交换,它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据,下面来看看具体内容吧
    2021-10-10
  • MyBatis获取参数值的两种方式详解

    MyBatis获取参数值的两种方式详解

    本文主要介绍了MyBatis获取参数值的两种方式详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03
  • Java使用arthas修改日志级别详解

    Java使用arthas修改日志级别详解

    在我们线上环境中,一般不会开启debug级别的日志,为了提高性能 info和warning级别的日志也一般不会打印出来,那么如果遇到线上问题,除了使用arthas定位问题,想通过查询日志来实现问题定位,如何查看logger信息,更新logger level呢,下面我们来了解arthas修改日志级别
    2022-06-06
  • java判断用户输入的是否至少含有N位小数的实例

    java判断用户输入的是否至少含有N位小数的实例

    下面小编就为大家分享一篇java判断用户输入的是否至少含有N位小数的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2017-12-12
  • Java分布式锁由浅入深介绍

    Java分布式锁由浅入深介绍

    这篇文章主要介绍了Java分布式锁,数据库实现分布式锁方式比较多,如悲观锁(查询时增加for update)、乐观锁(通过version字段)、增加一个表记录锁信息等。因为依赖于数据库,比较好理解,但是也存在一些问题
    2023-03-03
  • PowerJob分布式任务调度源码流程解读

    PowerJob分布式任务调度源码流程解读

    这篇文章主要为大家介绍了PowerJob分布式任务调度源码流程解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-02-02
  • Springmvc @PathVariable的用法解析

    Springmvc @PathVariable的用法解析

    这篇文章主要介绍了Springmvc @PathVariable的用法解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • mybatis-plus批量更新updateBatchById问题

    mybatis-plus批量更新updateBatchById问题

    这篇文章主要介绍了mybatis-plus批量更新updateBatchById问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07

最新评论