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() 的判断,建议先获取好,然后再各个判断处使用,这样只要获取一次。

 

 

posted on   每当变幻时  阅读(807)  评论(0编辑  收藏  举报

编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述
历史上的今天:
2014-04-17 服务调用方案(Spring Http Invoker) - 我们到底能走多远系列(40)

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示