logback 随笔 写入日志

0. 整个宇宙 浩瀚无边的尽头 ~

比较详细的源码走读
异步日志接入kafka
异步日志配置


日志写入的话,就从Logger开始吧

1. Logger 调用 Appender 插入日志

package ch.qos.logback.classic;

public final class Logger implements org.slf4j.Logger, LocationAwareLogger, AppenderAttachable<ILoggingEvent>, Serializable {
    public static final String FQCN = ch.qos.logback.classic.Logger.class.getName();

	// loggerName
    private String name;
	// 父、子层级的Logger相互持有对方的引用
    transient private Logger parent;
    transient private List<Logger> childrenList;
    final transient LoggerContext loggerContext;
	// 构造
    Logger(String name, Logger parent, LoggerContext loggerContext) {
        this.name = name;
        this.parent = parent;
        this.loggerContext = loggerContext;
    }

    transient private Level level;
	// 从父层级继承下来的层级
    transient private int effectiveLevelInt;

    public void info(String format, Object... argArray) {
		// 俩null分别是 marker、throwable
        filterAndLog_0_Or3Plus(FQCN, null, Level.INFO, format, argArray, null);
    }
	
	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) {
			// 如果 当前记录的层级 小于 日志记录生效的层级
			// no-op
            if (effectiveLevelInt > level.levelInt) {
                return;
            }
		// 	如果建议被否定,也一样 no-op
        } else if (decision == FilterReply.DENY) {
            return;
        }

		// step into ...
		// 那逻辑只能在这里了
        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) {
		// step into ...
		// 看看封装而成的 log事件 的构造
        LoggingEvent le = new LoggingEvent(localFQCN, this, level, msg, t, params);
		// 入参的marker是null
        le.setMarker(marker);
		// 这么看log.info() 就是 调用appenders(event)
        callAppenders(le);
    }
	
	public void callAppenders(ILoggingEvent event) {
        int writes = 0;
		// 向上递归以传播事件
        for (Logger l = this; l != null; l = l.parent) {
			// 将当前事件追加到 aai
            writes += l.appendLoopOnAppenders(event);
			// 如果当前logger不可追加,退出循环
            if (!l.additive) {
                break;
            }
        }
		// 抛出1个警告:没有appender绑定到当前的logger
        // No appenders in hierarchy
        if (writes == 0) {
            loggerContext.noAppenderDefinedWarning(this);
        }
    }
	
	// 这个impl指的是真正干事的、配置文件中能看到的 Appender
	// 比如,我们熟悉的 ConsoleAppender、FileAppender
	// 源码中出现的 AsyncAppender 等Appender作用只是委托给内部维护的aai
	transient private AppenderAttachableImpl<ILoggingEvent> aai;
    private int appendLoopOnAppenders(ILoggingEvent event) {
        if (aai != null) {
			// step into ...
			// AppenderAttachableImpl.appendLoopOnAppenders()
            return aai.appendLoopOnAppenders(event);
        } else {
            return 0;
        }
    }
}

-------------

package ch.qos.logback.core.spi;

public class AppenderAttachableImpl<E> implements AppenderAttachable<E> {

    public int appendLoopOnAppenders(E e) {
        int size = 0;
        final Appender<E>[] appenderArray = appenderList.asTypedArray();
        final int len = appenderArray.length;
        for (int i = 0; i < len; i++) {
			// step into ...
			// Appender.doAppend()
            appenderArray[i].doAppend(e);
            size++;
        }
        return size;
    }
	
}

2. 找个简单的Appender速通一下先

package ch.qos.logback.core;

abstract public class UnsynchronizedAppenderBase<E> extends ContextAwareBase implements Appender<E> {

    private ThreadLocal<Boolean> guard = new ThreadLocal<Boolean>();
    public void doAppend(E eventObject) {
        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;
            }

			// step into ...
			// 实现类的append()
            // 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);
        }
    }

}

---------------

package ch.qos.logback.core;

public class OutputStreamAppender<E> extends UnsynchronizedAppenderBase<E> {

    protected Encoder<E> encoder;
    protected final ReentrantLock lock = new ReentrantLock(false);
    private OutputStream outputStream;

    @Override
    protected void append(E eventObject) {
        if (!isStarted()) {
            return;
        }

        subAppend(eventObject);
    }

    /**
     * Actual writing occurs here.
     * <p>
     * Most subclasses of <code>WriterAppender</code> will need to override this
     * method.
     * 
     * @since 0.9.0
     */
    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();

            byte[] byteArray = this.encoder.encode(event);
            writeBytes(byteArray);

        } 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));
        }
    }
	
	private void writeBytes(byte[] byteArray) throws IOException {
        if(byteArray == null || byteArray.length == 0)
            return;
        
        lock.lock();
        try {
        	// 到这就不谈噢
            this.outputStream.write(byteArray);
            if (immediateFlush) {
                this.outputStream.flush();
            }
        } finally {
            lock.unlock();
        }
    }

}

3. 既然支持异步,那么看看线程模型

  • 工作中遇到过接入kafka的场景,于是引起了注意(异步日志也是log4j2性能真正优秀的地方)

  • 如果要接入外部异步队列,需要依赖logback-appender-kafka支持,具体参考顶部链接

package ch.qos.logback.core;

public class AsyncAppenderBase<E> extends UnsynchronizedAppenderBase<E> implements AppenderAttachable<E> {

    BlockingQueue<E> blockingQueue;

    @Override
    protected void append(E eventObject) {
        if (isQueueBelowDiscardingThreshold() && isDiscardable(eventObject)) {
            return;
        }
        preprocess(eventObject);
        put(eventObject);
    }

    private void put(E eventObject) {
        if (neverBlock) {
            blockingQueue.offer(eventObject);
        } else {
            putUninterruptibly(eventObject);
        }
    }

    private void putUninterruptibly(E eventObject) {
        boolean interrupted = false;
        try {
            while (true) {
                try {
                    blockingQueue.put(eventObject);
                    break;
                } catch (InterruptedException e) {
                    interrupted = true;
                }
            }
        } finally {
            if (interrupted) {
                Thread.currentThread().interrupt();
            }
        }
    }

	// 消费者
    class Worker extends Thread {

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

            // loop while the parent is started
            while (parent.isStarted()) {
                try {
                    E e = parent.blockingQueue.take();
                    aai.appendLoopOnAppenders(e);
                } catch (InterruptedException ie) {
                    break;
                }
            }

            addInfo("Worker thread will flush remaining events before exiting. ");

            for (E e : parent.blockingQueue) {
                aai.appendLoopOnAppenders(e);
                parent.blockingQueue.remove(e);
            }

            aai.detachAndStopAllAppenders();
        }
    }
}

------

package ch.qos.logback.classic;

public class AsyncAppender extends AsyncAppenderBase<ILoggingEvent> {
// 没啥可考的东东,掠过...
}

4. Appender 的生命周期初始化方法(start()方法)调用位置

  • 从调用栈输出的结果,可以看出——是在 静态绑定 LoggerFactory 时,解析配置文件过程中调用的
  • 像 FileAppender 创建文件、父级目录都是在start()中创建的,因此可以认为程序运行过程如果没有文件,后续的日志将丢失,需要重启程序
start:76, ConsoleAppender (ch.qos.logback.core)
end:90, AppenderAction (ch.qos.logback.core.joran.action)
callEndAction:309, Interpreter (ch.qos.logback.core.joran.spi)
endElement:193, Interpreter (ch.qos.logback.core.joran.spi)
endElement:179, Interpreter (ch.qos.logback.core.joran.spi)
play:62, EventPlayer (ch.qos.logback.core.joran.spi)
doConfigure:165, GenericConfigurator (ch.qos.logback.core.joran)
doConfigure:152, GenericConfigurator (ch.qos.logback.core.joran)
doConfigure:110, GenericConfigurator (ch.qos.logback.core.joran)
doConfigure:53, GenericConfigurator (ch.qos.logback.core.joran)
configureByResource:75, ContextInitializer (ch.qos.logback.classic.util)
autoConfig:150, ContextInitializer (ch.qos.logback.classic.util)
init:84, StaticLoggerBinder (org.slf4j.impl)
<clinit>:55, StaticLoggerBinder (org.slf4j.impl)
bind:150, LoggerFactory (org.slf4j)
performInitialization:124, LoggerFactory (org.slf4j)
getILoggerFactory:417, LoggerFactory (org.slf4j)
getLogger:362, LoggerFactory (org.slf4j)
createLocationAwareLog:130, LogAdapter$Slf4jAdapter (org.apache.commons.logging)
createLog:91, LogAdapter (org.apache.commons.logging)
getLog:67, LogFactory (org.apache.commons.logging)
getLog:59, LogFactory (org.apache.commons.logging)
<clinit>:196, SpringApplication (org.springframework.boot)
main:10, AngelMicroServiceSampleApplication (cn.angel.project.angelmicroservicesample)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肯尼思布赖恩埃德蒙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值