slf4j框架源码分析

个人总结
slf4j 把staticLoggerBinder类的实现留给了日志子系统去实现。并且命名的方式需要是"org/slf4j/impl/staticLoggerBinder.class"
这个路径才可以。
并且对应的日志子类工厂需要实现ILoggerFactory。

模式说白了封装的是变化 当你的需求有变化并且变化比较大比较频繁的话使用模式是一种不错的选择 封装“改变”是设计模式的原则

slf4j框架源码分析(一)


1. 入口方法 getLogger(Class<?> clazz)
(1) 首先返回一个工厂
工厂采用单例模式返回,采用饿汉加载模式。
private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
public static StaticLoggerBinder getSingleton() {
    return SINGLETON;
}
得到logback实现的工厂
ch.qos.logback.classic.LoggerContext[default]


(2) 根据名称返回logger日志对象
获得根据名称的logger对象,以及logger的上下文,包括了父logger对象 是谁
比如 org.yinxueframework.dal.YinxueDataSource 的父节点是 org.yinxueframework.dal,按"."递归


2. slf4j只是一个日志系统门面,也是面向接口的设计,这个就好像servlet规范一样
看下它的包含内容:
(1) 两个包 helpers 和 spi包,分别是啥?
spi是接口包
helpers是实现包
父包下的核心接口和类:
a. ILoggerFactory
名字就知道了
生产logger对象的实际接口
只有一个方法,根据给定的名称生产Logger对象
getLogger(String name)


b. IMarkerFactory
这个方法比较多
getMarker(String name)获得Marker对象
exists(String name) 存在这样名称的Marker对象
boolean detachMarker(String name) 这个名称的Marker是否已经分派了
getDetacherMarker(String name)根据名称获得分派Marker对象




c. Marker
是被命名的对象,用来丰富log语句,确保日志系统实现类确定哪些信息被输出。
Marker可以包含别的Marker引用
继承了序列化接口
两个常量 "*"表示任意Marker "+"表示非空Marker
方法:
getName()标志对象名称
add(Marker reference)增加其他标志对象的引用
remove(Marker reference)移除对象
hasChildren() 是否有子引用
hasReferences() 上面的方法被这个取代
iterator() 返回迭代器
contains(Marker other)是否包含引用
contains(String name)是否包含了叫name的marker
equals(Object o)  比较marker.name
hashCode() 重写的hashCode


核心类:
LoggerFactory 门面类
final类,没有实现任何接口
包含了:
(1)一些错误String信息:没有静态绑定的URL,多个绑定URL,空URL,版本不匹配,可替换的URL
logger对象名称不匹配URL,URL初始化不成功,URL初始化不成功的信息
(2) 标志状态 int
0, 未初始化
1,正在初始化
2,初始化失败
3,成功初始化
4,初始化空对象(采用空对象模式,NOP)
 初始化状态为0 默认
 
缓存工厂,用一个ConcurrentHashMap缓存了已经有的logger对象
SubstituteLoggerFactory ,实现了ILoggerFactory这个接口
加入缓存里没有命中,用putIfAbsent这个方法加入new出来的new SubstituteLogger(name)对象
key为name,如果put成功,就返回这个logger,


有额外的几个方法
getLoggerNames()  getLoggers() 分别调用了map的keyset和values返回
clear() 方法调用了map的clear方法


这里是亨元模式的经典实现。这个缓存 loggers采用的是final修饰符
没有加静态修饰符,注意! 这样就不需要在全部的类中共享这个变量了。


SubstituteLogger这个类是工厂模式生产的类。它继承了logger接口
放置了两个变量 final修饰String name 和 volatile修饰的 Logger _delegate(委托类)
这个委托类如果不为空就返回本身,如果为空,就返回NOP对象,避免空指针。NOPLogger.NOP_LOGGER
 public static final NOPLogger NOP_LOGGER = new NOPLogger(); 这个是静态修饰的。
 
这个空对象的工厂很简单,实现工厂接口,然后返回该空对象的单例。
public class NOPLoggerFactory implements ILoggerFactory {
  
  public NOPLoggerFactory() {
    // nothing to do
  }
  
  public Logger getLogger(String name) {
    return NOPLogger.NOP_LOGGER;
  }


}
这个空对象单例,在NOPLogger中,但是它经过了多个继承才到logger接口,其中
增加了marker的一些方法。
继承了MarkerIgnoringBase 
这个类本身是为了透过NameLogerBase来适配接口Logger
NameLoggerBase提供了一个在序列化时使用的方法
  protected Object readResolve() throws ObjectStreamException {
    // using getName() instead of this.name works even for
    // NOPLogger
    return LoggerFactory.getLogger(getName());
  }
  
  一个常量,检测到logger NAME不匹配时的属性 slf4j.detectLoggerNameMismatch
  boolean对象 调用 Boolean.getBoolean(slf4j.detectLoggerNameMismatch)在系统参数里面找
  
兼容版本数组  new String[] { "1.6", "1.7" } 1.6都兼容。


  private LoggerFactory() {
  }私有构造
  
  静态块
  static void reset() {
    INITIALIZATION_STATE = UNINITIALIZED;
    TEMP_FACTORY = new SubstituteLoggerFactory();
  }
  
  
  这个factory所有的class入参 或者 name入参,都会到这个方法
    public static Logger getLogger(String name) {
// 先得到工厂
    ILoggerFactory iLoggerFactory = getILoggerFactory();
    // 然后工厂得到logger对象
return iLoggerFactory.getLogger(name);
  }
  
  这里的工厂就是生产logger对象的关键。
  不同的日志系统对它有不同的实现。看它本身的路由规则是什么?\
  // 状态为未初始化
  if (INITIALIZATION_STATE == UNINITIALIZED) {
  // 修改为正在初始化
      INITIALIZATION_STATE = ONGOING_INITIALIZATION;
  // 开始初始化
      performInitialization();
    }
// 根据初始化状态,路由到不同的工厂
    switch (INITIALIZATION_STATE) {
// 成功初始化采用这个静态绑定的工厂,这个类在logback中有。
// 其他日志系统可能也有。
    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
  // 绑定方法
  bind();
  // 成功了,版本检验下
    if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
      versionSanityCheck(); // 检验不通打印下错误信息,异常被吃了。可有可无。
    }
bind()中关键方法 ,找URL地址
Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
  
  
  找这个类下的资源
  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);
      }
  
  这些资源里面 自动配置的是 logback.xml 其他为不同配置的。
  所以logback需要配置下该文件
final public static String GROOVY_AUTOCONFIG_FILE = "logback.groovy";
final public static String AUTOCONFIG_FILE = "logback.xml";
final public static String TEST_AUTOCONFIG_FILE = "logback-test.xml";
final public static String CONFIG_FILE_PROPERTY = "logback.configurationFile";
final public static String STATUS_LISTENER_CLASS = "logback.statusListenerClass";
final public static String SYSOUT = "SYSOUT";


得到这个URL的方法
  private URL getResource(String filename, ClassLoader myClassLoader, boolean updateStatus) {
    URL url = Loader.getResource(filename, myClassLoader);
    if (updateStatus) {
      statusOnResourceSearch(filename, myClassLoader, url);
    }
    return url;
  }
  
  配置到这个上下文中
  else if (url.toString().endsWith("xml")) {
      JoranConfigurator configurator = new JoranConfigurator();
      configurator.setContext(loggerContext);
      configurator.doConfigure(url);
    } else {
      throw new LogbackException("Unexpected filename extension of file [" + url.toString() + "]. Should be either .groovy or .xml");
    }

以上就OK了
slf4j 把这个类的实现留给了日志子系统去实现。并且命名的方式需要是这个路径
才可以。如果有多个这个,那么不就冲突了!!!所以哈哈。
import org.slf4j.impl.StaticLoggerBinder;

MarkerFactory 门面类
 static IMarkerFactory markerFactory;


  private MarkerFactory() {
  }


  static {
    try {
      markerFactory = StaticMarkerBinder.SINGLETON.getMarkerFactory();
    } catch (NoClassDefFoundError e) {
      markerFactory = new BasicMarkerFactory();
      
    } catch (Exception e) {
      // we should never get here
      Util.report("Unexpected failure while binding MarkerFactory", e);
    }
  }
  这个鸡肋就不分析了,就把方法委托给实现类来做。如果没有日志子系统,那就是
  自己的基础实现来实现这些方法,委托使用的方法有。
  // 静态方法获得对象
  public static Marker getMarker(String name) {
    return markerFactory.getMarker(name);
  }
  
  // 静态方法获得已分派的对象
  public static Marker getDetachedMarker(String name) {
    return markerFactory.getDetachedMarker(name);
  }
  
  // 静态方法获得工厂
  public static IMarkerFactory getIMarkerFactory() {
    return markerFactory;
  }
  
 


d. Logger 定义了平常用来记录相关日志的抽象方法,常量有一个 "ROOT"
getName得到logger对象名称
分输出等级:trace, debug,info,warn,error 重要程度依次递增
输出的方法包括:两种参数 String 和Marker(标记的含义)
error(String msg) 直接打印信息
error(String format,Object arg) 前面是格式化语句,后面为参数
error(String format,Object arg1,Object arg2) 前面为格式化语句,后面为两个参数
error(String format,Object ...args) 前面为格式化语句,后面为加长数组
error(String msg,Throwable t) 打印信息和异常对象。sonar当中就要求打印e,否则报error
isErrorEnabled() 返回是否为这个等级
isErrorEnabled(Marker marker) 根据marker返回是否为这个等级

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值