slf4j

後續要作個日誌相關的東西,先筆記一下。java

slf4j是日誌框架的一個門面端,背後實現者有log4j,logback等等。api

如何實現這個門面的呢?安全

通常咱們使用的代碼以下:併發

private static final Logger logger = LoggerFactory.getLogger(JSPCashierController.class);

slf4j 的LoggerFactory具體實現了門面模式中對接各類實現的事情。app

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的版本上的代碼以下:是沒有作線程安全的的事情的。ui

不過我也不知道啥緣由,不深究了 哈哈。this

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方法:spa

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() 的判斷,建議先獲取好,而後再各個判斷處使用,這樣只要獲取一次。

相關文章
相關標籤/搜索