论坛首页 Java企业应用论坛

log4j与logback的性能探讨(基于源码)

浏览 19247 次
该帖已经被评为良好帖
作者 正文
   发表时间:2011-11-28   最后修改:2011-11-29
今天下午闲来无事,于是研究了一下log4j和logback的源码,本人能力有限,现在在家上网,就不付源代码了。
log4j性能比logback的性能差很多。
首先说明的是log4j的代码太臃肿,有很多代码都是不需要的,logback的代码很简洁而且性能比log4j快很多,唯一不解额是为什么logback的代码还有synchronized,而没有用cas呢,如果用cas重写一遍我感觉性能应该比现在的快把。



“某些关键操作,比如判定是否记录一条日志语句的操作,其性能得到了显著的提高。这个操作在LogBack中需要3纳秒,而在Log4J中则需要30纳秒LogBack创建记录器(logger)的速度也更快:13毫秒,而在Log4J中需要23毫秒。更重要的是,它获取已存在的记录器只需94纳秒,而Log4J需要2234纳秒,时间减少到了1/23。跟JUL相比的性能提高也是显著的”



1.首先判断是否记录一条日志语句,在logback中使用一下语句,采用facade的模式,用messageformtter进行统一字符串格式话,这样可以节省内存,把真正的运行时字符串开销进行移到执行时。而log4j则把所有的都由让开发者做,导致开发者不得运行时把字符串拼好,这样还是很耗内存的。[color=red][/color]

  final public void debug(String format, Object arg1, Object arg2) {
    filterAndLog_2(FQCN, null, Level.DEBUG, format, arg1, arg2, null);
  }


public String getFormattedMessage() {
    if (formattedMessage != null) {
      return formattedMessage;
    }
    if (argumentArray != null) {
      formattedMessage = MessageFormatter.arrayFormat(message, argumentArray)
          .getMessage();
    } else {
      formattedMessage = message;
    }

    return formattedMessage;
  }



2.  LogBack创建记录器(logger)的速度也更快:13毫秒,而在Log4J中需要23毫秒[color=red][/color]

1)log4j与logback都是把每个类的log,进行放到hashtable中的。例如:com.company.manager的类包,这会解析成为
com,com.company,com.company.manager为key放到hashtable里,value为logger对象。为了提高性能不让hashtable的类太多,这建议进行封装,或者采用继承的方式重用log对象。以防止log把内存撑满。
2)log4j比较蛋疼的问题就是把logger对象放到hashtable里,每个logger对象又区分是logger还是ProvisionNode对象,所以当logger对象新增的时候,会更新hashtable的值,这个有性能问题。而logback则是把所有的logger不分状态统统都放到hashtable里,则这样比较简单,用的时候拿就行了,不需要分什么状态。

logback的代码
 public final Logger getLogger(final String name) {

   if (name == null) {
      throw new IllegalArgumentException("name argument cannot be null");
    }
    // if we are asking for the root logger, then let us return it without
    // wasting time
    if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
      return root;
    }

    int i = 0;
    Logger logger = root;

    // check if the desired logger exists, if it does, return it
    // without further ado.
    Logger childLogger = (Logger) loggerCache.get(name);
    // if we have the child, then let us return it without wasting time
    if (childLogger != null) {
     return childLogger;
    }

    // if the desired logger does not exist, them create all the loggers
    // in between as well (if they don't already exist)
   String childName;
    while (true) {
      int h = Logger.getSeparatorIndexOf(name, i);
      if (h == -1) {
        childName = name;
      } else {
        childName = name.substring(0, h);
      }
      // move i left of the last point
      i = h + 1;
      synchronized (logger) {
        childLogger = logger.getChildByName(childName);
        if (childLogger == null) {
          childLogger = logger.createChildByName(childName);
          loggerCache.put(childName, childLogger);
          incSize();
        }
     }
      logger = childLogger;
      if (h == -1) {
        return childLogger;
      }
    }
  }



log4j的代码为:


public
  Logger getLogger(String name, LoggerFactory factory) {
    //System.out.println("getInstance("+name+") called.");
    CategoryKey key = new CategoryKey(name);
    // Synchronize to prevent write conflicts. Read conflicts (in
    // getChainedLevel method) are possible only if variable
    // assignments are non-atomic.
    Logger logger;

    synchronized(ht) {
      Object o = ht.get(key);
      if(o == null) {
	logger = factory.makeNewLoggerInstance(name);
	logger.setHierarchy(this);	ht.put(key, logger);
	updateParents(logger);
	return logger;
      } else if(o instanceof Logger) {
	return (Logger) o;
      } else if (o instanceof ProvisionNode) {
	//System.out.println("("+name+") ht.get(this) returned ProvisionNode");
	logger = factory.makeNewLoggerInstance(name);
	logger.setHierarchy(this);
	ht.put(key, logger);
	updateChildren((ProvisionNode) o, logger);
	updateParents(logger);
	return logger;
      }
      else {
	// It should be impossible to arrive here
	return null;  // but let's keep the compiler happy.
      }
    }
  }





3.它获取已存在的记录器只需94纳秒,而Log4J需要2234纳秒,时间减少到了1/23

从以上代码可以看出,log4j是以ht为同步对象的这个粒度很大,而logback是以rootlogger为粒度的,这样可以提高并发量。


已经更新代码。
   发表时间:2011-11-28  
logback确实是好东东
0 请登录后投票
   发表时间:2011-11-28  
自己回复一下,希望第二天可以冲顶啊,呵呵。这段时间很久没写博客了
0 请登录后投票
   发表时间:2011-11-29  
欢迎大家讨论啊,我只是抛砖引玉。
0 请登录后投票
   发表时间:2011-11-29  
目前讨论logback的文章,很多都是说性能很好,但是又有谁能够静下心来研究,为什么性能好呢,其实开源的东西代码质量未必很好,里边很多需要改进的,至少我感觉log4j的代码,真的不敢恭维啊。
0 请登录后投票
   发表时间:2011-11-29  
我现在都是用logback了。
0 请登录后投票
   发表时间:2011-11-29  
只会log4j ……
0 请登录后投票
   发表时间:2011-11-29  
没看出来lz哪里的数据显示logback性能好的
而且引用logback的官方发言来说明其性能好是完全没说服力的
0 请登录后投票
   发表时间:2011-11-29   最后修改:2011-11-29
richard_2010 写道
没看出来lz哪里的数据显示logback性能好的
而且引用logback的官方发言来说明其性能好是完全没说服力的

其实我上边引用的官方的数据,跟下边说明的几条基本上对应的。

已经可以更新帖子了,这个是我在家写的,家里没开发工具,只有上grepcode上把代码贴下了,还请指正呵呵。



0 请登录后投票
   发表时间:2011-11-29  
目前我们公司正在更新中ing
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics