Slf4j日志框架介绍

  • 日志的作用
    在日常开发和维护中
    1.需要知道程序在运行中的运行状态
    2.出问题后快速定位当前的问题
    3.调试分析代码
    4.对产生的日志进行处理和分析
  • 什么是slf4j
    slf4j(全称是Simple Loging Facade For Java)是一个为Java程序提供日志输出的统一接口,并不是一个具体的日志实现方案,就好像我们经常使用的JDBC一样,只是一种规则而已。因此单独的slf4j是不能工作的,它必须搭配其他具体的日志实现方案,比如apache的org.apache.log4j.Logger,jdk自带的java.util.logging.Logger等等。
    slf4j是门面模式的典型应用
    slf4j只做两件事:
    1.提供日志接口
    2.提供获取具体日志对象的方法
  • slf4j原理
//获取Logger接口
public static Logger getLogger(Class clazz) {  
  return getLogger(clazz.getName());  
} 
// ILoggerFactory接口,具体日志系统必须实现此接口和Logger接口,
public static Logger getLogger(String name) {  
  ILoggerFactory iLoggerFactory = getILoggerFactory();  
  return iLoggerFactory.getLogger(name);  //具体日志系统的实现 
}  
  
 //此方法会去绑定具体日志实现,根据绑定成功与否设置不同的标志state,再根据不同的标志state返回具体ILoggerFactory的实现类
public static ILoggerFactory getILoggerFactory() {
        if (INITIALIZATION_STATE == 0) { //进来的时候肯定是0,只有在绑定过程中才会改变INITIALIZATION_STATE的值
            INITIALIZATION_STATE = 1;
            performInitialization(); //初始化过程
        }
		//根据INITIALIZATION_STATE的值做不同的逻辑处理
        switch(INITIALIZATION_STATE) {
        case 1: //  即SubstituteLoggerFactory 空实现
            return TEMP_FACTORY;
        case 2: //初始化失败,报错
            throw new IllegalStateException("org.slf4j.LoggerFactory could not be successfully initialized. See also http://www.slf4j.org/codes.html#unsuccessfulInit");
        case 3: //初始化成功,返回具体实现类
            return StaticLoggerBinder.getSingleton().getLoggerFactory();  //StaticLoggerBinder这个类就是一个中间类,它用来将抽象的slf4j变成具体的日志系统类
        case 4: // NOPLoggerFactory 空实现,方法不做任何逻辑
            return NOP_FALLBACK_FACTORY;
        default: //抛出异常
            throw new IllegalStateException("Unreachable code");
        }
    } 
  
  //bind方法会进行绑定 如果绑定成功即state为3 还会进行版本号验证
 private static final void performInitialization() {
        bind();  //绑定过程
        if (INITIALIZATION_STATE == 3) {
            versionSanityCheck(); //版本号验证
        }

    } 
  //绑定方法具体逻辑
private static final void bind() {
        String msg;
        try {
            Set staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet(); //根据类加载器查询org/slf4j/impl/StaticLoggerBinder.class所在的path路径
            reportMultipleBindingAmbiguity(staticLoggerBinderPathSet); //检查是否引入多个日志系统实现,打印提示
            StaticLoggerBinder.getSingleton(); //初始化,如果失败 异常被捕获,设置state值
            INITIALIZATION_STATE = 3; //标志初始化成功
            reportActualBinding(staticLoggerBinderPathSet); //如果多个日志系统 打印真实绑定成功的日志系统信息提示
            emitSubstituteLoggerWarning();  //打印提示
        } 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.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -1) {
                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);
        }

    } 
  //classpath查找具体的实现类,这里可以看出,如果系统引入了多个日志系统,并不会报错,而是会把所有的日志系统实现放入一个Set集合中
   private static Set findPossibleStaticLoggerBinderPathSet() {
        LinkedHashSet staticLoggerBinderPathSet = new LinkedHashSet();
//STATIC_LOGGER_BINDER_PATH 为org/slf4j/impl/StaticLoggerBinder.class
        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);
            }

            while(paths.hasMoreElements()) {
                URL path = (URL)paths.nextElement();
                staticLoggerBinderPathSet.add(path);
            }
        } catch (IOException var4) {
            Util.report("Error getting resources from path", var4);
        }

        return staticLoggerBinderPathSet;
    }
 //获取 new StaticLoggerBinder()
public static StaticLoggerBinder getSingleton() {
        return SINGLETON;  // new StaticLoggerBinder();   获取内部LoggerContext,此类实现了ILoggerFactory
    }
  • 正确的打日志姿势
    1.基本格式
    正例:logger.warn(“user is exist: name {}” ,userName);
    反例:logger.warn("user is exist: name " + userName);
    原因:使用参数化信息的方式,不会产生多余的String,占用空间,同时对象过多,容易gc
    2.debug打印先做判断
//slf4j已经自行做了判断
if (logger.isDebugEnabled()) {
    logger.debug(”xxx”);
}

原因:debug会产生大量日志信息,因此需谨慎使用,生产环境一般设置级别为error或者info
3.对于不同程度的日志做不同级别的日志打印
4.不要吞并异常

try {
       //do something     
        }catch (Exception e){
            
        }

原因:捕获异常,如果不做任何业务处理,请加日志打印,做到异常可寻

  • 日志输出性能优化
    1.日志输出到控制台比输出到文件慢
    2.日志输出格式不一样也会对性能产生影响
    3.日志级别越低,输出的日志内容越多
    4.日志输出方式不同,日志输出方式可以选择异步方式或同步方式
    5.每次接收到日志event就进行输出比当日志内容达到一定大小才进行输出性能要低
    解决方法:
    1.采用正确的输出格式
    2.只记录必要的关键日志信息,减少不必要的日志输出
    3.采用正确的日志级别
    4.输出采用缓存方式,当缓存到一定大小才进行输出
    5.采用异步方式进行日志输出
  • 日志打印模型
    同步日志打印模型
    异步打印日志模型
    异步打印日志维护的一个堵塞队列,当队列中的消息数过多,超过阈值,则会进行TRACE, DEBUG or INFO的日志的丢弃,但是就算这样,如果error级别的消息过多,还是会堵塞,因为默认使用的put()方法,如果希望队列不堵塞,可以通过设置neverBlock=true,则会通过offer()方法而不是put()方法,由此可见,异步打印日志是会存在日志消息丢失的可能性,因此对队列容量大小需要有一个合理的设置,才会达到很好的效果。

参考文章:
异步打印日志的一点事
Java日志框架:slf4j

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值