logback使用笔记

概述

异步输出日志

之前的日志配置方式是基于同步的,每次日志输出到文件都会进行一次磁盘IO。采用异步写日志的方式而不让此次写日志发生磁盘IO,阻塞线程从而造成不必要的性能损耗。添加一个基于异步写日志的appender,并指向原先配置的appender即可:

<!-- 异步输出 -->
<appender name="ASYNC-INFO" class="ch.qos.logback.classic.AsyncAppender">
    <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
    <discardingThreshold>0</discardingThreshold>
    <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
    <queueSize>256</queueSize>
    <!-- 添加附加的appender,最多只能添加一个 -->
    <appender-ref ref="INFO-LOG"/>
</appender>
<appender name="ASYNC-ERROR" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>256</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="ERROR-LOG"/>
</appender>

原理浅析:
log.info开始,进入方法Logger.filterAndLog_0_Or3Plus(),进入方法Logger.buildLoggingEventAndAppend(),进入方法Logger.callAppenders(),进入方法Logger.appendLoopOnAppenders(),进入方法AppenderAttachableImpl.appendLoopOnAppenders(),进入方法UnsynchronizedAppenderBase.doAppend(),进入方法AsyncAppenderBase.append(),即ch.qos.logback.classic.AsyncAppenderBase.append方法:

protected void append(E eventObject) {
    if (!this.isQueueBelowDiscardingThreshold() || !this.isDiscardable(eventObject)) {
        this.preprocess(eventObject);
        this.put(eventObject);
    }
}

通过队列情况判断是否需要丢弃日志,不丢弃的话将它放到阻塞队列中,这个阻塞队列为ArrayBlockingQueueu,默认大小为256,可以通过配置文件进行修改。Logger.info(…)到append(…)就结束,只将日志塞入到阻塞队列的事,然后继续执行Logger.info(…)下面的语句。在AsyncAppenderBase类中定义一个Worker线程,run方法:

E e = parent.blockingQueue.take();
aai.appendLoopOnAppenders(e);

从阻塞队列中取出一个日志,并调用AppenderAttachableImpl类中的appendLoopOnAppenders方法维护一个Append列表。Worker线程中调用方法过程主要如下图:
在这里插入图片描述

最主要的两个方法就是encode和write,前一个法方会根据配置文件中encode指定的方式转化为字节码,后一个方法将转化成的字节码写入到文件中去。所以写文件是通过新起一个线程去完成的,主线程将日志扔到阻塞队列中,然后又去做其他事情。

配置错误日志发送到指定邮箱

    <appender name="EMAIL" class="com.johnny.logclient.logback.MailAppender">
        <evaluator class="com.johnny.logback.SMTPFrequencyEvaluator">
            <maxCount>1000</maxCount>
            <expiredTime>300</expiredTime>
        </evaluator>
        <cyclicBufferTracker class="ch.qos.logback.core.spi.CyclicBufferTracker">
            <bufferSize>1</bufferSize>
        </cyclicBufferTracker>
        <smtpHost>smtp.awesome.com</smtpHost>
        <smtpPort>25</smtpPort>
        <username>ccc@awesome.com</username>
        <password></password>
        <to>aaa@awesome.com</to>
        <from>bbbb@awesome.com</from>
        <subject>【报错邮件】%X{ip}: %logger{20} - %m 累积错误数:%X{error_count}
        </subject>
        <layout class="ch.qos.logback.classic.html.HTMLLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}%level%thread%logger{35}%message
            </pattern>
        </layout>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

SMTPAppender为logback里面提供的API,该API的MailAppender源码如下:

public class MailAppender extends SMTPAppender {
    public MailAppender() {
    }

    protected void append(ILoggingEvent leo) {
        VariableLoggingEventVO eventVO = VariableLoggingEventVO.build(leo);
        this.filter(eventVO);
        super.append(eventVO);
    }

    private void filter(VariableLoggingEventVO eventVO) {
        Map<String, String> newMdc = new HashMap(eventVO.getMdc());
        eventVO.setMdcPropertyMap(newMdc);
        eventVO.setFormattedMessage(Desensitiver.desensitive(eventVO.getFormattedMessage()));
    }
}
public class Desensitiver {
    public Desensitiver() {
    }

    public static String desensitive(String source) {
        return PhoneDesensitiver.filterPhone(IdCardDesensitiver.filterIdCard(source));
    }
}
posted @ 2021-12-27 22:58  johnny233  阅读(52)  评论(0编辑  收藏  举报  来源