logback源码分析-4.日志打印

源码基于logback 1.1.7

本文将分析logger打印日志功能的源码,比如logger.info("hi"):

//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方法:

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方法来分析下:

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();
    }    

转载于:https://my.oschina.net/u/913896/blog/917288

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值