源码阅读-logback的logger如何记录日志
上篇博客介绍了LoggerContext的设计以及如何创建Logger,Logger是logback的核心类,也是所有日志框架的核心类。这篇博客详细介绍一下Logger的各字段和方法,重点介绍Logger类是怎样记录日志的。
Logger类实现了slf4j框架定义的Logger接口,并且Logger实现了AppenderAttachable接口,它实现该接口的方式,是持有AppenderAttachableImpl类,然后委托该类来实现AppenderAttachable接口定义的方法,这里用到了代理模式,是一个比较精巧的设计。
这里扯点题外话,关于代理模式和适配器模式的区别。
代理模式:代理类实现被代理类的接口,并拥有一个被代理类的引用,通过委托被代理类的实现来完成这些功能。
适配器模式:适配器模式是为了适配一些已经存在的接口或类,为他们提供一个新的接口,从而使这些旧的类得以继续使用,适配器类通过实现新的接口,并持有旧的类的引用,从而为旧的类提供一个新的入口。
接下来我们继续看看Logger类的字段和方法:
/** * The fully qualified name of this class. Used in gathering caller * information. */ public static final String FQCN = ch.qos.logback.classic.Logger.class.getName(); /** * The name of this logger * logger名称 */ private String name; // The assigned levelInt of this logger. Can be null. //该logger的等级,可以为空 transient private Level level; // The effective levelInt is the assigned levelInt and if null, a levelInt is // inherited form a parent. //生效的等级数,当logger的level被设置,则effectiveLevelInt=level.levelInt //当level为null时,则继承父logger的effectiveLevelInt transient private int effectiveLevelInt; /** * 父logger */ transient private Logger parent; /** * 子loggers 延迟加载 */ transient private List<Logger> childrenList; /** * 可连接的输出终端 * Logger是委托这个类实现AppenderAttachable接口, *也是委托这个类来调用Appender组件来实际记录日志,所以这个字段是最关键的 */ transient private AppenderAttachableImpl<ILoggingEvent> aai; /** * Additivity is set to true by default, that is children inherit the * appenders of their ancestors by default. If this variable is set to * <code>false</code> then the appenders located in the ancestors of this * logger will not be used. However, the children of this logger will inherit * its appenders, unless the children have their additivity flag set to * <code>false</code> too. See the user manual for more details. */ transient private boolean additive = true; final transient LoggerContext loggerContext;
构造方法:
Logger(String name, Logger parent, LoggerContext loggerContext) { this.name = name; this.parent = parent; this.loggerContext = loggerContext; }
方法:
提供给LoggerContext创建logger
/** * 根据名称查询子logger * @param childName * @return */ Logger getChildByName(final String childName) { if (childrenList == null) { return null; } else { int len = this.childrenList.size(); for (int i = 0; i < len; i++) { final Logger childLogger_i = (Logger) childrenList.get(i); final String childName_i = childLogger_i.getName(); if (childName.equals(childName_i)) { return childLogger_i; } } // no child found return null; } } /** * The default size of child list arrays. The JDK 1.5 default is 10. We use a * smaller value to save a little space. * 根据名称创建子logger */ Logger createChildByName(final String childName) { // 检查childName的合法性 是不是该logger的child int i_index = LoggerNameUtil.getSeparatorIndexOf(childName, this.name.length() + 1); if (i_index != -1) { throw new IllegalArgumentException("For logger [" + this.name + "] child name [" + childName + " passed as parameter, may not include '.' after index" + (this.name.length() + 1)); } if (childrenList == null) { childrenList = new CopyOnWriteArrayList<Logger>(); } Logger childLogger; childLogger = new Logger(childName, this, this.loggerContext); childrenList.add(childLogger); childLogger.effectiveLevelInt = this.effectiveLevelInt; return childLogger; }
维护父子等级变化
/** * 为该logger设置等级level * @param newLevel */ public synchronized void setLevel(Level newLevel) { //赋值level和当前level相等,因为level是固定的几个常量所以用==比较即可,则什么也不做并结束 if (level == newLevel) { // nothing to do; return; } //当该looger为root时,该level不能为null if (newLevel == null && isRootLogger()) { throw new IllegalArgumentException("The level of the root logger cannot be set to null"); } //赋新值 level = newLevel; if (newLevel == null) {//若newLevel为null,则继承父logger的effectiveLevelInt effectiveLevelInt = parent.effectiveLevelInt; newLevel = parent.getEffectiveLevel(); } else {//否则,effectiveLevelInt = newLevel.levelInt effectiveLevelInt = newLevel.levelInt; } if (childrenList != null) {//若子loggers不为空,则为每一个子logger处理他们的effectiveLevelInt的值 int len = childrenList.size(); for (int i = 0; i < len; i++) { Logger child = (Logger) childrenList.get(i); // tell child to handle parent levelInt change child.handleParentLevelChange(effectiveLevelInt); } } // inform listeners //通知 loggerContext.fireOnLevelChange(this, newLevel); } /** * This method is invoked by parent logger to let this logger know that the * prent's levelInt changed. * * 递归处理父logger的level变化,该loggere的effectiveLevelInt值处理 * @param newParentLevelInt */ private synchronized void handleParentLevelChange(int newParentLevelInt) { // changes in the parent levelInt affect children only if their levelInt is // null //当该logger的level为空时,父logger的level变化才会影响子logger的effectiveLevelInt //因为当该logger的level不为空时,effectiveLevelInt=level.levelInt,否则effectiveLevelInt继承父logger的effectiveLevelInt if (level == null) { effectiveLevelInt = newParentLevelInt; // propagate the parent levelInt change to this logger's children if (childrenList != null) { int len = childrenList.size(); for (int i = 0; i < len; i++) { Logger child = (Logger) childrenList.get(i); child.handleParentLevelChange(newParentLevelInt); } } } /** * 我写该递归函数的方式 * 先写该递归函数的终止条件 * if (level != null){ * return; * } * level为null时处理 * effectiveLevelInt = newParentLevelInt; * if (childrenList != null) { * int len = childrenList.size(); * for (int i = 0; i < len; i++) { * Logger child = (Logger) childrenList.get(i); * child.handleParentLevelChange(newParentLevelInt); * } * } */ }
委托AppenderAttachableImpl实现AppenderAttachable接口,提供绑定可连接的输出终端的功能
/** * Remove all previously added appenders from this logger instance. * <p/> * This is useful when re-reading configuration information. */ public void detachAndStopAllAppenders() { if (aai != null) { aai.detachAndStopAllAppenders(); } } public boolean detachAppender(String name) { if (aai == null) { return false; } return aai.detachAppender(name); } // this method MUST be synchronized. See comments on 'aai' field for further // details. public synchronized void addAppender(Appender<ILoggingEvent> newAppender) { if (aai == null) { aai = new AppenderAttachableImpl<ILoggingEvent>(); } aai.addAppender(newAppender); } public boolean isAttached(Appender<ILoggingEvent> appender) { if (aai == null) { return false; } return aai.isAttached(appender); } @SuppressWarnings("unchecked") public Iterator<Appender<ILoggingEvent>> iteratorForAppenders() { if (aai == null) { return Collections.EMPTY_LIST.iterator(); } return aai.iteratorForAppenders(); } public Appender<ILoggingEvent> getAppender(String name) { if (aai == null) { return null; } return aai.getAppender(name); } /** * Invoke all the appenders of this logger.
该方法会调用此Logger关联的所有Appender,
而且还会调用所有父Logger关联的Appender,
直到遇到父Logger的additive属性设置为false为止,
这也是为什么如果子Logger和父Logger都关联了同样的Appender,则日志信息会重复记录的原因 * @param event * The event to log */ 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); } } private int appendLoopOnAppenders(ILoggingEvent event) { if (aai != null) { return aai.appendLoopOnAppenders(event); } else { return 0; } } /** * Remove the appender passed as parameter form the list of appenders. */ public boolean detachAppender(Appender<ILoggingEvent> appender) { if (aai == null) { return false; } return aai.detachAppender(appender); }
记录日志,LoggingEvent对象是承载了日志信息的类,最后输出的日志信息,就来源于这个事件对象
/** * 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. * 先用快速过滤器过滤一下 是否要记录日志 * 再构建日志事件并添加 * 筛选发生在LoggingEvent创建之前 这种设计也是为了提高性能 */ 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; } buildLoggingEventAndAppend(localFQCN, marker, level, msg, params, t); } private void filterAndLog_1(final String localFQCN, final Marker marker, final Level level, final String msg, final Object param, final Throwable t) { final FilterReply decision = loggerContext.getTurboFilterChainDecision_1(marker, this, level, msg, param, t); if (decision == FilterReply.NEUTRAL) { if (effectiveLevelInt > level.levelInt) { return; } } else if (decision == FilterReply.DENY) { return; } buildLoggingEventAndAppend(localFQCN, marker, level, msg, new Object[] { param }, t); } private void filterAndLog_2(final String localFQCN, final Marker marker, final Level level, final String msg, final Object param1, final Object param2, final Throwable t) { final FilterReply decision = loggerContext.getTurboFilterChainDecision_2(marker, this, level, msg, param1, param2, t); if (decision == FilterReply.NEUTRAL) { if (effectiveLevelInt > level.levelInt) { return; } } else if (decision == FilterReply.DENY) { return; } buildLoggingEventAndAppend(localFQCN, marker, level, msg, new Object[] { param1, param2 }, t); } 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); } public void trace(String msg) { filterAndLog_0_Or3Plus(FQCN, null, Level.TRACE, msg, null, null); } public void trace(String format, Object arg) { filterAndLog_1(FQCN, null, Level.TRACE, format, arg, null); } public void trace(String format, Object arg1, Object arg2) { filterAndLog_2(FQCN, null, Level.TRACE, format, arg1, arg2, null); } public void trace(String format, Object... argArray) { filterAndLog_0_Or3Plus(FQCN, null, Level.TRACE, format, argArray, null); } public void trace(String msg, Throwable t) { filterAndLog_0_Or3Plus(FQCN, null, Level.TRACE, msg, null, t); } public void trace(Marker marker, String msg) { filterAndLog_0_Or3Plus(FQCN, marker, Level.TRACE, msg, null, null); } public void trace(Marker marker, String format, Object arg) { filterAndLog_1(FQCN, marker, Level.TRACE, format, arg, null); } public void trace(Marker marker, String format, Object arg1, Object arg2) { filterAndLog_2(FQCN, marker, Level.TRACE, format, arg1, arg2, null); } public void trace(Marker marker, String format, Object... argArray) { filterAndLog_0_Or3Plus(FQCN, marker, Level.TRACE, format, argArray, null); } public void trace(Marker marker, String msg, Throwable t) { filterAndLog_0_Or3Plus(FQCN, marker, Level.TRACE, msg, null, t); } public boolean isDebugEnabled() { return isDebugEnabled(null); } public boolean isDebugEnabled(Marker marker) { final FilterReply decision = callTurboFilters(marker, Level.DEBUG); if (decision == FilterReply.NEUTRAL) { return effectiveLevelInt <= Level.DEBUG_INT; } else if (decision == FilterReply.DENY) { return false; } else if (decision == FilterReply.ACCEPT) { return true; } else { throw new IllegalStateException("Unknown FilterReply value: " + decision); } } public void debug(String msg) { filterAndLog_0_Or3Plus(FQCN, null, Level.DEBUG, msg, null, null); } public void debug(String format, Object arg) { filterAndLog_1(FQCN, null, Level.DEBUG, format, arg, null); } public void debug(String format, Object arg1, Object arg2) { filterAndLog_2(FQCN, null, Level.DEBUG, format, arg1, arg2, null); } public void debug(String format, Object... argArray) { filterAndLog_0_Or3Plus(FQCN, null, Level.DEBUG, format, argArray, null); } public void debug(String msg, Throwable t) { filterAndLog_0_Or3Plus(FQCN, null, Level.DEBUG, msg, null, t); } public void debug(Marker marker, String msg) { filterAndLog_0_Or3Plus(FQCN, marker, Level.DEBUG, msg, null, null); } public void debug(Marker marker, String format, Object arg) { filterAndLog_1(FQCN, marker, Level.DEBUG, format, arg, null); } public void debug(Marker marker, String format, Object arg1, Object arg2) { filterAndLog_2(FQCN, marker, Level.DEBUG, format, arg1, arg2, null); } public void debug(Marker marker, String format, Object... argArray) { filterAndLog_0_Or3Plus(FQCN, marker, Level.DEBUG, format, argArray, null); } public void debug(Marker marker, String msg, Throwable t) { filterAndLog_0_Or3Plus(FQCN, marker, Level.DEBUG, msg, null, t); } public void error(String msg) { filterAndLog_0_Or3Plus(FQCN, null, Level.ERROR, msg, null, null); } public void error(String format, Object arg) { filterAndLog_1(FQCN, null, Level.ERROR, format, arg, null); } public void error(String format, Object arg1, Object arg2) { filterAndLog_2(FQCN, null, Level.ERROR, format, arg1, arg2, null); } public void error(String format, Object... argArray) { filterAndLog_0_Or3Plus(FQCN, null, Level.ERROR, format, argArray, null); } public void error(String msg, Throwable t) { filterAndLog_0_Or3Plus(FQCN, null, Level.ERROR, msg, null, t); } public void error(Marker marker, String msg) { filterAndLog_0_Or3Plus(FQCN, marker, Level.ERROR, msg, null, null); } public void error(Marker marker, String format, Object arg) { filterAndLog_1(FQCN, marker, Level.ERROR, format, arg, null); } public void error(Marker marker, String format, Object arg1, Object arg2) { filterAndLog_2(FQCN, marker, Level.ERROR, format, arg1, arg2, null); } public void error(Marker marker, String format, Object... argArray) { filterAndLog_0_Or3Plus(FQCN, marker, Level.ERROR, format, argArray, null); } public void error(Marker marker, String msg, Throwable t) { filterAndLog_0_Or3Plus(FQCN, marker, Level.ERROR, msg, null, t); } public boolean isInfoEnabled() { return isInfoEnabled(null); } public boolean isInfoEnabled(Marker marker) { FilterReply decision = callTurboFilters(marker, Level.INFO); if (decision == FilterReply.NEUTRAL) { return effectiveLevelInt <= Level.INFO_INT; } else if (decision == FilterReply.DENY) { return false; } else if (decision == FilterReply.ACCEPT) { return true; } else { throw new IllegalStateException("Unknown FilterReply value: " + decision); } } public void info(String msg) { filterAndLog_0_Or3Plus(FQCN, null, Level.INFO, msg, null, null); } public void info(String format, Object arg) { filterAndLog_1(FQCN, null, Level.INFO, format, arg, null); } public void info(String format, Object arg1, Object arg2) { filterAndLog_2(FQCN, null, Level.INFO, format, arg1, arg2, null); } public void info(String format, Object... argArray) { filterAndLog_0_Or3Plus(FQCN, null, Level.INFO, format, argArray, null); } public void info(String msg, Throwable t) { filterAndLog_0_Or3Plus(FQCN, null, Level.INFO, msg, null, t); } public void info(Marker marker, String msg) { filterAndLog_0_Or3Plus(FQCN, marker, Level.INFO, msg, null, null); } public void info(Marker marker, String format, Object arg) { filterAndLog_1(FQCN, marker, Level.INFO, format, arg, null); } public void info(Marker marker, String format, Object arg1, Object arg2) { filterAndLog_2(FQCN, marker, Level.INFO, format, arg1, arg2, null); } public void info(Marker marker, String format, Object... argArray) { filterAndLog_0_Or3Plus(FQCN, marker, Level.INFO, format, argArray, null); } public void info(Marker marker, String msg, Throwable t) { filterAndLog_0_Or3Plus(FQCN, marker, Level.INFO, msg, null, t); } public boolean isTraceEnabled() { return isTraceEnabled(null); } public boolean isTraceEnabled(Marker marker) { final FilterReply decision = callTurboFilters(marker, Level.TRACE); if (decision == FilterReply.NEUTRAL) { return effectiveLevelInt <= Level.TRACE_INT; } else if (decision == FilterReply.DENY) { return false; } else if (decision == FilterReply.ACCEPT) { return true; } else { throw new IllegalStateException("Unknown FilterReply value: " + decision); } } public boolean isErrorEnabled() { return isErrorEnabled(null); } public boolean isErrorEnabled(Marker marker) { FilterReply decision = callTurboFilters(marker, Level.ERROR); if (decision == FilterReply.NEUTRAL) { return effectiveLevelInt <= Level.ERROR_INT; } else if (decision == FilterReply.DENY) { return false; } else if (decision == FilterReply.ACCEPT) { return true; } else { throw new IllegalStateException("Unknown FilterReply value: " + decision); } } public boolean isWarnEnabled() { return isWarnEnabled(null); } public boolean isWarnEnabled(Marker marker) { FilterReply decision = callTurboFilters(marker, Level.WARN); if (decision == FilterReply.NEUTRAL) { return effectiveLevelInt <= Level.WARN_INT; } else if (decision == FilterReply.DENY) { return false; } else if (decision == FilterReply.ACCEPT) { return true; } else { throw new IllegalStateException("Unknown FilterReply value: " + decision); } } public boolean isEnabledFor(Marker marker, Level level) { FilterReply decision = callTurboFilters(marker, level); if (decision == FilterReply.NEUTRAL) { return effectiveLevelInt <= level.levelInt; } else if (decision == FilterReply.DENY) { return false; } else if (decision == FilterReply.ACCEPT) { return true; } else { throw new IllegalStateException("Unknown FilterReply value: " + decision); } } public boolean isEnabledFor(Level level) { return isEnabledFor(null, level); } public void warn(String msg) { filterAndLog_0_Or3Plus(FQCN, null, Level.WARN, msg, null, null); } public void warn(String msg, Throwable t) { filterAndLog_0_Or3Plus(FQCN, null, Level.WARN, msg, null, t); } public void warn(String format, Object arg) { filterAndLog_1(FQCN, null, Level.WARN, format, arg, null); } public void warn(String format, Object arg1, Object arg2) { filterAndLog_2(FQCN, null, Level.WARN, format, arg1, arg2, null); } public void warn(String format, Object... argArray) { filterAndLog_0_Or3Plus(FQCN, null, Level.WARN, format, argArray, null); } public void warn(Marker marker, String msg) { filterAndLog_0_Or3Plus(FQCN, marker, Level.WARN, msg, null, null); } public void warn(Marker marker, String format, Object arg) { filterAndLog_1(FQCN, marker, Level.WARN, format, arg, null); } public void warn(Marker marker, String format, Object... argArray) { filterAndLog_0_Or3Plus(FQCN, marker, Level.WARN, format, argArray, null); } public void warn(Marker marker, String format, Object arg1, Object arg2) { filterAndLog_2(FQCN, marker, Level.WARN, format, arg1, arg2, null); } public void warn(Marker marker, String msg, Throwable t) { filterAndLog_0_Or3Plus(FQCN, marker, Level.WARN, msg, null, t); } public boolean isAdditive() { return additive; } public void setAdditive(boolean additive) { this.additive = additive; } public String toString() { return "Logger[" + name + "]"; }
我们也大致看一看AppenderAttachableImpl的源码:
/** * 可连接的输出终端接口实现类 * * 通过拥有一个成员变量appenderList 并提供addAppender() 和 appendLoopOnAppenders()可随意连接需要的appender并append * 这种写法值得我们借鉴!! * 在logback中还有很多地方用到了这种写法 例如FilterAttachable,很大程度的提高了代码的灵活性 * @author Ceki Gülcü */ public class AppenderAttachableImpl<E> implements AppenderAttachable<E> { @SuppressWarnings("unchecked") final private COWArrayList<Appender<E>> appenderList = new COWArrayList<Appender<E>>(new Appender[0]); /** * Attach an appender. If the appender is already in the list in won't be * added again. * 添加一个Appender */ public void addAppender(Appender<E> newAppender) { if (newAppender == null) { throw new IllegalArgumentException("Null argument disallowed"); } appenderList.addIfAbsent(newAppender); } /** * Call the <code>doAppend</code> method on all attached appenders. * 遍历所有的appenders并添加日志 */ 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++) { //委托appender去添加日志 appenderArray[i].doAppend(e); size++; } return size; } /** * Get all attached appenders as an Enumeration. If there are no attached * appenders <code>null</code> is returned. * * 返回一个遍历器 * @return Iterator An iterator of attached appenders. */ public Iterator<Appender<E>> iteratorForAppenders() { return appenderList.iterator(); } /** * Look for an attached appender named as <code>name</code>. * <p/> * * 根据名称返回一个appender * <p> Return the appender with that name if in the list. Return null * otherwise. */ public Appender<E> getAppender(String name) { if (name == null) { return null; } for (Appender<E> appender : appenderList) { if (name.equals(appender.getName())) { return appender; } } return null; } /** * Returns <code>true</code> if the specified appender is in the list of * attached appenders, <code>false</code> otherwise. * * 是否连接了该appender * @since 1.2 */ public boolean isAttached(Appender<E> appender) { if (appender == null) { return false; } for (Appender<E> a : appenderList) { if (a == appender) return true; } return false; } /** * Remove and processPriorToRemoval all previously attached appenders. * 断开并且停止所有的appender */ public void detachAndStopAllAppenders() { for (Appender<E> a : appenderList) { a.stop(); } appenderList.clear(); } static final long START = System.currentTimeMillis(); /** * Remove the appender passed as parameter form the list of attached * appenders. * 断开该appender */ public boolean detachAppender(Appender<E> appender) { if (appender == null) { return false; } boolean result; result = appenderList.remove(appender); return result; } /** * Remove the appender with the name passed as parameter form the list of * appenders. * 根据名称断开该appender */ public boolean detachAppender(String name) { if (name == null) { return false; } boolean removed = false; for (Appender<E> a : appenderList) { if (name.equals((a).getName())) { removed = appenderList.remove(a); break; } } return removed; } }
总结来说,Logger类中定义的字段和方法,是出于以下目的:
1、定义parent和childList,用于实现父子Logger的树形结构
2、定义createChildByName()、getChildByName()方法,是供LoggerContext创建Logger
3、定义level、effectiveLevelInt,是为了判定日志级别是否足够
4、最后,filterAndLog()、buildLoggingEventAndAppend()、callAppenders()、appendLoopOnAppenders()方法,是Logger类的核心方法,一步步地委托AppenderAttachableImpl类来实际记录日志。