比较详细的源码走读
异步日志接入kafka
异步日志配置
日志写入的话,就从Logger开始吧
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;
}
}
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.
*
* Most subclasses of WriterAppender
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();
}
}
}
工作中遇到过接入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> {
// 没啥可考的东东,掠过...
}
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)