Logback源码分析 (4)

如果TurboFilter过滤器存在就会执行相关操作,并返回FilterReply。如果结果是FilterReply.DENY本条日志消息直接丢弃;如果是FilterReply.NEUTRAL会继续判断日志级别是否在该方法级别之上;如果是FilterReply.ACCEPT直接跳到下一步。

第3步:ch.qos.logback.classic.Logger#buildLoggingEventAndAppend

private void buildLoggingEventAndAppend(final String localFQCN, final Marker marker, final Level level, final String msg, final Object[] params, final Throwable t) { LoggingEvent le = new LoggingEvent(localFQCN, this, level, msg, t, params); le.setMarker(marker); callAppenders(le); }

创建了LoggingEvent对象,该对象包含日志请求所有相关的参数,请求的 logger,日志请求的级别,日志信息,与日志一同传递的异常信息,当前时间,当前线程,以及当前类的各种信息和 MDC。其实打印日志就是一个事件,所以这个对象是相关重要,下面全部是在操作该对象。

第4步:ch.qos.logback.classic.Logger#callAppenders

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

第5步:ch.qos.logback.classic.Logger#appendLoopOnAppenders

private int appendLoopOnAppenders(ILoggingEvent event) { if (aai != null) { return aai.appendLoopOnAppenders(event); } else { return 0; } }

从当前Logger到父节点遍历,直到AppenderAttachableImpl不为空(有appender-ref 标签)。

第6步:ch.qos.logback.core.spi.AppenderAttachableImpl#appendLoopOnAppenders

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++) { appenderArray[i].doAppend(e); size++; } return size; }

如果设置了多个日志输出目的地,这里就是循环调用对应的Appender进行输出。

第7步:ch.qos.logback.core.UnsynchronizedAppenderBase#doAppend

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; } this.append(eventObject); } catch (Exception e) { if (exceptionCount++ < ALLOWED_REPEATS) { addError("Appender [" + name + "] failed to append.", e); } } finally { guard.set(Boolean.FALSE); } }

通过ThreadLocal控制递归导致的重复提交

第8步:ch.qos.logback.core.OutputStreamAppender#append

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

第9步:ch.qos.logback.core.OutputStreamAppender#subAppend

protected void subAppend(E event) { if (!isStarted()) { return; } try { if (event instanceof DeferredProcessingAware) { // 拼接日志信息(填充占位符),设置当前线程以及MDC等信息 ((DeferredProcessingAware) event).prepareForDeferredProcessing(); } byte[] byteArray = this.encoder.encode(event); writeBytes(byteArray); } catch (IOException ioe) { this.started = false; addStatus(new ErrorStatus("IO failure in appender", this, ioe)); } }

Encoder在这里惨淡登场,返回byte数组。

第10步:ch.qos.logback.core.encoder.LayoutWrappingEncoder#encode

public byte[] encode(E event) { String txt = layout.doLayout(event); return convertToBytes(txt); }

Encoder先把LoggerEvent交给Layout,Layout组装日志信息,在每条信息后加上换行符。

第11步:ch.qos.logback.core.OutputStreamAppender#writeBytes

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

使用AQS锁控制并发问题。这也是Logback性能不如 Log4j2的原因。后面有时间分析一下Log4j2。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyyxxx.html