> 文章列表 > logback 随笔 写入日志

logback 随笔 写入日志

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();// loggerNameprivate 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、throwablefilterAndLog_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-opif (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是nullle.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) {// 将当前事件追加到 aaiwrites += l.appendLoopOnAppenders(event);// 如果当前logger不可追加,退出循环if (!l.additive) {break;}}// 抛出1个警告:没有appender绑定到当前的logger// No appenders in hierarchyif (writes == 0) {loggerContext.noAppenderDefinedWarning(this);}}// 这个impl指的是真正干事的、配置文件中能看到的 Appender// 比如,我们熟悉的 ConsoleAppender、FileAppender// 源码中出现的 AsyncAppender 等Appender作用只是委托给内部维护的aaitransient 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 appendthis.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;@Overrideprotected 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-139if (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;@Overrideprotected 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 startedwhile (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)