首页 > 技术文章 > slf4j日志的一些理解

mufeng07 2020-04-07 14:40 原文

slf4j的一些理解

1.log4j

/**
* @description: 定制静态日志基础类
* @author: mufeng
* @create: 2020/4/7 10:26
*/
public class BaseLogger {
   static {
       InputStream in = BaseLogger.class.getClassLoader().getResourceAsStream("log4j.properties");
       Properties properties=new Properties();
       try {
           properties.load(in);
           PropertyConfigurator.configure(properties);
           in.close();
           logger = Logger.getLogger("Logger");
      } catch (IOException e) {
           e.printStackTrace();
      }
  }
   private static Logger logger;
   static final String FQCN = BaseLogger.class.getName();
   public static void info(String msg, Object... args){
       if(logger.isInfoEnabled()){
           FormattingTuple ft = MessageFormatter.arrayFormat(msg, args);
           logger.log(FQCN, Level.INFO,ft.getMessage(),ft.getThrowable());
      }
  }
}

 

Logger logger= LoggerFactory.getLogger(Test1.class);
   @Test
   public void test1(){
       BaseLogger.info("12{}","13");
  }
   @Test
   public void test2(){
       logger.info("12{}","13");
  }

日志输出:

test1:

11:05:59,145 INFO Test1:21 - 1213

test2:

11:05:59,145 INFO Test1:21 - 1213

可以看出日志输出是一样的。

实现原理:

通过Logger logger= LoggerFactory.getLogger(Test1.class)追溯源代码

LoggerFactory中 getILoggerFactory执行performInitialization方法,会调用bind方法

public static ILoggerFactory getILoggerFactory() {
       if (INITIALIZATION_STATE == 0) {
           Class var0 = LoggerFactory.class;
           synchronized(LoggerFactory.class) {
               if (INITIALIZATION_STATE == 0) {
                   INITIALIZATION_STATE = 1;
                   performInitialization();
              }
          }
      }

       switch(INITIALIZATION_STATE) {
       case 1:
           return SUBST_FACTORY;
       case 2:
           throw new IllegalStateException("org.slf4j.LoggerFactory in failed state. Original exception was thrown EARLIER. See also http://www.slf4j.org/codes.html#unsuccessfulInit");
       case 3:
           return StaticLoggerBinder.getSingleton().getLoggerFactory();
       case 4:
           return NOP_FALLBACK_FACTORY;
       default:
           throw new IllegalStateException("Unreachable code");
      }
  }

findPossibleStaticLoggerBinderPathSet找出类路径所有路径集合,StaticLoggerBinder.getSingleton();存在多个会选择一个。(http://www.slf4j.org/codes.html#multiple_bindings解释:by the JVM and for all practical purposes should be considered random)

日志输出可以看出:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/D:/persist/maven_Repositories/org/slf4j/slf4j-log4j12/1.7.26/slf4j-log4j12-1.7.26.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/D:/persist/maven_Repositories/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]

 

private static final void bind() {
  String msg;
  try {
      Set<URL> staticLoggerBinderPathSet = null;
      if (!isAndroid()) {
          staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
          reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
      }

      StaticLoggerBinder.getSingleton();
      INITIALIZATION_STATE = 3;
      reportActualBinding(staticLoggerBinderPathSet);
      fixSubstituteLoggers();
      replayEvents();
      SUBST_FACTORY.clear();
  } catch (NoClassDefFoundError var2) {
      msg = var2.getMessage();
      if (!messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
          failedBinding(var2);
          throw var2;
      }

      INITIALIZATION_STATE = 4;
      Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
      Util.report("Defaulting to no-operation (NOP) logger implementation");
      Util.report("See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.");
  } catch (NoSuchMethodError var3) {
      msg = var3.getMessage();
      if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
          INITIALIZATION_STATE = 2;
          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 var3;
  } catch (Exception var4) {
      failedBinding(var4);
      throw new IllegalStateException("Unexpected initialization failure", var4);
  }

}

得出:logger为Log4jLoggerFactory类getLogger方法得到的实例(Log4jLoggerAdapter)

public Logger getLogger(String name) {
       Logger slf4jLogger = (Logger)this.loggerMap.get(name);
       if (slf4jLogger != null) {
           return slf4jLogger;
      } else {
           org.apache.log4j.Logger log4jLogger;
           if (name.equalsIgnoreCase("ROOT")) {
               log4jLogger = LogManager.getRootLogger();
          } else {
               log4jLogger = LogManager.getLogger(name);
          }

           Logger newInstance = new Log4jLoggerAdapter(log4jLogger);
           Logger oldInstance = (Logger)this.loggerMap.putIfAbsent(name, newInstance);
           return (Logger)(oldInstance == null ? newInstance : oldInstance);
      }
  }

logger.info()实际调用Log4jLoggerAdapter类中的info方法

log4j怎么找到log4j.properties配置文件?

LogManager类存在static静态块,获取了配置信息,LogManager.getLogger(name)获取调起

(PropertyConfigurator.doConfigure方法解析log4j.properties)
 static {
   // By default we use a DefaultRepositorySelector which always returns 'h'.
   Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG));
   repositorySelector = new DefaultRepositorySelector(h);

   /** Search for the properties file log4j.properties in the CLASSPATH. */
   String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,
      null);

   // if there is no default init override, then get the resource
   // specified by the user or the default config file.
   if(override == null || "false".equalsIgnoreCase(override)) {

     String configurationOptionStr = OptionConverter.getSystemProperty(
 DEFAULT_CONFIGURATION_KEY,
 null);

     String configuratorClassName = OptionConverter.getSystemProperty(
                                                  CONFIGURATOR_CLASS_KEY,
  null);

     URL url = null;

     // if the user has not specified the log4j.configuration
     // property, we search first for the file "log4j.xml" and then
     // "log4j.properties"
     if(configurationOptionStr == null) {
url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
if(url == null) {
 url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
}
    } else {
try {
 url = new URL(configurationOptionStr);
} catch (MalformedURLException ex) {
 // so, resource is not a URL:
 // attempt to get the resource from the class path
 url = Loader.getResource(configurationOptionStr);
}
    }
     
     // If we have a non-null url, then delegate the rest of the
     // configuration to the OptionConverter.selectAndConfigure
     // method.
     if(url != null) {
   LogLog.debug("Using URL ["+url+"] for automatic log4j configuration.");
       try {
           OptionConverter.selectAndConfigure(url, configuratorClassName,
  LogManager.getLoggerRepository());
      } catch (NoClassDefFoundError e) {
           LogLog.warn("Error during default initialization", e);
      }
    } else {
   LogLog.debug("Could not find resource: ["+configurationOptionStr+"].");
    }
  } else {
       LogLog.debug("Default initialization of overridden by " +
           DEFAULT_INIT_OVERRIDE_KEY + "property.");
  }  
}

 

推荐阅读