slf4j
后续要做个日志相关的东西,先笔记一下。
slf4j是日志框架的一个门面端,背后实现者有log4j,logback等等。
如何实现这个门面的呢?
一般我们使用的代码如下:
private static final Logger logger = LoggerFactory.getLogger(JSPCashierController.class);
slf4j 的LoggerFactory具体实现了门面模式中对接各种实现的事情。
public static Logger getLogger(Class<?> clazz) { Logger logger = getLogger(clazz.getName()); if (DETECT_LOGGER_NAME_MISMATCH) { Class<?> autoComputedCallingClass = Util.getCallingClass(); if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) { Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(), autoComputedCallingClass.getName())); Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation"); } } return logger; } public static Logger getLogger(String name) { ILoggerFactory iLoggerFactory = getILoggerFactory(); return iLoggerFactory.getLogger(name); }
getILoggerFactory方法:
public static ILoggerFactory getILoggerFactory() { if (INITIALIZATION_STATE == UNINITIALIZED) { // 防线程并发 synchronized (LoggerFactory.class) { if (INITIALIZATION_STATE == UNINITIALIZED) { INITIALIZATION_STATE = ONGOING_INITIALIZATION; performInitialization(); } } } switch (INITIALIZATION_STATE) { case SUCCESSFUL_INITIALIZATION: return StaticLoggerBinder.getSingleton().getLoggerFactory(); case NOP_FALLBACK_INITIALIZATION: return NOP_FALLBACK_FACTORY; case FAILED_INITIALIZATION: throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG); case ONGOING_INITIALIZATION: // support re-entrant behavior. // See also http://jira.qos.ch/browse/SLF4J-97 return SUBST_FACTORY; } throw new IllegalStateException("Unreachable code"); }
上面,注意到一个细节,就是在getILoggerFactory是做了线程安全操作的,我看的版本是1.7.21。而在1.7.7的版本上的代码如下:是没有做线程安全的的事情的。
不过我也不知道啥原因,不深究了 哈哈。
public static ILoggerFactory getILoggerFactory() { if (INITIALIZATION_STATE == UNINITIALIZED) { INITIALIZATION_STATE = ONGOING_INITIALIZATION; performInitialization(); } switch (INITIALIZATION_STATE) { case SUCCESSFUL_INITIALIZATION: return StaticLoggerBinder.getSingleton().getLoggerFactory(); case NOP_FALLBACK_INITIALIZATION: return NOP_FALLBACK_FACTORY; case FAILED_INITIALIZATION: throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG); case ONGOING_INITIALIZATION: // support re-entrant behavior. // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106 return TEMP_FACTORY; } throw new IllegalStateException("Unreachable code"); }
结下来看performInitialization方法:
private final static void performInitialization() { bind(); if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) { versionSanityCheck(); } }
到了bind方法了,具体绑定哪一个实现应该就在这里决定了:
private final static void bind() { try { Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet(); reportMultipleBindingAmbiguity(staticLoggerBinderPathSet); // the next line does the binding // 这里绑定的实例抽象,我们获取或设置所有logger相关信息都是通过它实现 StaticLoggerBinder.getSingleton(); INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION; reportActualBinding(staticLoggerBinderPathSet); fixSubstitutedLoggers(); } catch (NoClassDefFoundError ncde) { String msg = ncde.getMessage(); if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) { INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION; Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\"."); Util.report("Defaulting to no-operation (NOP) logger implementation"); Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details."); } else { failedBinding(ncde); throw ncde; } } catch (java.lang.NoSuchMethodError nsme) { String msg = nsme.getMessage(); if (msg != null && msg.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -1) { INITIALIZATION_STATE = FAILED_INITIALIZATION; Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding."); Util.report("Your binding is version 1.5.5 or earlier."); Util.report("Upgrade your binding to version 1.6.x."); } throw nsme; } catch (Exception e) { failedBinding(e); throw new IllegalStateException("Unexpected initialization failure", e); } }
findPossibleStaticLoggerBinderPathSet方法:
// 就是在classpath里找这个class,所以那些实现者比如log4j,logback,包里必然有这个路径下的class文件。 private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class"; private static Set<URL> findPossibleStaticLoggerBinderPathSet() { // use Set instead of list in order to deal with bug #138 // LinkedHashSet appropriate here because it preserves insertion order during iteration Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>(); try { ClassLoader loggerFactoryClassLoader = LoggerFactory.class .getClassLoader(); Enumeration<URL> paths; if (loggerFactoryClassLoader == null) { paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH); } else { paths = loggerFactoryClassLoader .getResources(STATIC_LOGGER_BINDER_PATH); } while (paths.hasMoreElements()) { URL path = (URL) paths.nextElement(); staticLoggerBinderPathSet.add(path); } } catch (IOException ioe) { Util.report("Error getting resources from path", ioe); } return staticLoggerBinderPathSet; }
关于打日志(来自官方文档):
如果日志打印级别是info,字符串拼接就成了不必要的消耗。
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
推荐下面写法,如果日志打印级别是debug,那么就会比上面的写法多一个判断的消耗。但是相对于拼接字符串打印日志来说可以忽略不计。
if(logger.isDebugEnabled()) { logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i])); }
而在一个方法中有多处 logger.isDebugEnabled() 的判断,建议先获取好,然后再各个判断处使用,这样只要获取一次。