转载

SLF4j:Log facade abstract

内 容:

  • 应用中使用slf4j的工作流程
  • 简单示例
  • ILoggerFactory实例化过程
  • 由ILoggerFactory创建Logger实例
  • slf4j 适配器实现
  • 自定义适配器

现如今,日志框架层出不穷,JDKLogger、Log4j、Logback等这些是最常用的了。然而现在越来越多的框架中,都会在使用日志框架的同时,还会使用到一个门面(slf4j-api.jar),使用这个门面的的最方便的地方大抵是它提供格式化字符串的功能。

slf4j 与其他日志框架的关系

在应用程序中,直接使用slf4j-api.jar就可以完成日志的记录,而不用在代码里直接使用某一种日志框架了(虽然最终记录日志还是有日志框架来完成的)。

SLF4j:Log facade abstract

下面是使用了slf4j时,应用程序、slf4j、slf4j-adapter.jar、日志框架之间的调用关系:

下面是一个简单的示例:

 package com.fjn.frame.slf4j;  import org.slf4j.Logger; import org.slf4j.LoggerFactory;  public class HelloWorld {     public static void main(String[] args) {       Logger logger = LoggerFactory.getLogger(HelloWorld.class);       logger.info("Hello World");    } } 

LoggerFactory.getLogger(xxx)要分为两个过程:

1、实例化ILoggerFactory, 这个步骤只是在第一次进行。

2、根据ILoggerFactory实例创建Logger实例。

下面就分别来说说这两个过程:

ILoggerFactory 实例化的过程

在使用slf4j时,并不需要指定具体使用哪种日志框架,只需要给定相关的适配器包就可以了。那么如何拿到真正的ILoggerFactory实现,并实例化的呢?

1、在LogFactory的classloader的搜索路径下查找” org/slf4j/impl/StaticLoggerBinder.class ” 类

 private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";    private static void singleImplementationSanityCheck() {     try {       ClassLoader loggerFactoryClassLoader = LoggerFactory.class           .getClassLoader();       Enumeration paths;       if (loggerFactoryClassLoader == null) {         paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);       } else {         paths = loggerFactoryClassLoader             .getResources(STATIC_LOGGER_BINDER_PATH);       }       // use Set instead of list in order to deal with  bug #138       // LinkedHashSet appropriate here because it preserves insertion order during iteration       Set implementationSet = new LinkedHashSet();       while (paths.hasMoreElements()) {         URL path = (URL) paths.nextElement();         implementationSet.add(path);       }       if (implementationSet.size() > 1) {         Util.report("Class path contains multiple SLF4J bindings.");         Iterator iterator = implementationSet.iterator();         while(iterator.hasNext()) {           URL path = (URL) iterator.next();           Util.report("Found binding in [" + path + "]");         }         Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");       }     } catch (IOException ioe) {       Util.report("Error getting resources from path", ioe);     }   } 

如果有多个就会打印:

SLF4j:Log facade abstract

2、将slf4j与指定的实现进行绑定,这一步的操作,通常是:

1)单实例化StaticLoggerBinder。

2)检查StaticLoggerBinder的版本,其实就是需要有一个静态的非final的变量(这个变量不是必须得有的),

StaticLoggerBinder. REQUESTED_API_VERSION

3、获取到ILoggerFactory的实例

一般来说,是返回一个static ILoggerFactory impl=new   XXXLoggerFactory();

由ILoggerFactory来创建Logger实例

LoggerFactory创建Logger实例,其实由日志框架本身的LogManager创建一个Logger,然后包装成LoggerAdapter。例如Log4j的适配包中的Log4jLoggerFactory#getLogger(String name)的实现如下:

 public Logger getLogger(String name) {     Logger slf4jLogger = loggerMap.get(name);     if (slf4jLogger != null) {       return slf4jLogger;     } else {       org.apache.log4j.Logger log4jLogger;       if(name.equalsIgnoreCase(Logger.ROOT_LOGGER_NAME))         log4jLogger = LogManager.getRootLogger();       else         log4jLogger = LogManager.getLogger(name);        Logger newInstance = new Log4jLoggerAdapter(log4jLogger);       Logger oldInstance = loggerMap.putIfAbsent(name, newInstance);       return oldInstance == null ? newInstance : oldInstance;     }   } 

下面用是一张简易的关系图,显示了适配器的实现:

SLF4j:Log facade abstract

自定义日志框架适配器

在一些公司,肯定还有自己的Logger框架,如果也希望通过slf4j来做日志,就需要写相关的适配器包了。通过上述的两个过程的了解,很容易就能知道如何自定义适配器了。

自定义适配器中,必须包括3个组件:

· StaticLoggerBinder

这个类需要遵守下列规约:

1)  类名必须是org.slf4j.impl.StaticLoggerBinder

2)  这个类必须是单例的,必须有getSingleton()方法

3)  尽可能的有 public static String REQUESTED_API_VERSION 字段,并且不能是final的。

4)  要实现org.slf4j.spi.LoggerFactoryBinder接口。

· LoggerFactoryImpl

这个类要实现org.slf4j.ILoggerFactory接口

· LoggerAdapter

这个类要实现org.slf4j.Logger接口。

正文到此结束
Loading...