`
sw1982
  • 浏览: 511384 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

【bugfix】纠正博文'扩展log4j系列[二]动态、分文件记log'

阅读更多

原文链接见:http://sw1982.iteye.com/admin/blogs/611545

 

在上文中提到扩展appener方法的实现,结果本周同事在一段代码里面出现了log重复的情况。

 

情况描述是:root log文件输出一次,被我扩展过的分模块appender却根据Executor线程数重复输出n次。经过一番debug(多线程debug真的比较恶心...经验是配合一些sysout查看临界,否则线程被刮起很难模拟并发)

 

test代码如下:(这个testcase可以借鉴的地方是,在线程里面起executor,可以比较方便的mock多线程并发)

 

 

package test;

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

import com.paipai.core.logging.Logger;
import com.paipai.logicframework.biz.service.BaseService;
import com.paipai.logicframework.common.logging.LoggerUtil;

/**
 * created on 2010-5-6下午02:22:42
 * 
 * @author weisong
 */
public class MutilThreadLog {
	public static void main(String[] args) {

	Work work = new Work();
	work.doA();
       }
}

class Work {
	private ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors
			.newFixedThreadPool(3);
	private static Logger logger = LoggerUtil.getLogger();

	public void doA() {
		String[] sss = { "aa", "bb", "cc", "dd","ee","ff","gg","hh"
				,"ii","jj","kk","ll","mm","nn","oo","pp","qq","xx","yy","zz"};
		for (final String s : sss) {
			Thread thread = new Thread() {
				@Override
				public void run() {
					logger.log("descnotfound", s);
				}
			};
			executor.execute(thread);
		}
	}
}	
 

 

问题分析:(同步块过小)

 

 

		org.apache.log4j.Logger logger = Log4jFactory.getInstance(logName);
		if (logger.getAppender(logName) == null) {
			initBusiLogAppender(logName, busiModule+".log",	logger);
		}

 这段代码,将logger对象的获取变成工厂模式,确保了单例。 getAppender函数通过源码可以看到是synchronized。

  but,但是,initBusiLogAppender这个函数却是线程不安全的!(具体代码请参看本文开头的博文链接)

 

 出现线程不安全的步骤为:

1.当前class实例获取到 logger对象, 这个时候没有"logName"这个appender

2.当前class实例持有一个Executor,启动了多个线程, 每个线程都会去持有同一个logger对象。(多线程访问本地变量,应该是final,或者voliate的。当然这里的logger对象本身就会是final)

3.线程并发,每个线程都会通过if判断条件,进入initBusiLogAppender方法。(虽然getAppender()是一个同步方法,

可是锁的范围并未涉及到if条件内部)

4. initBusiLogAppender重复初始化n次。。然后就悲剧了。

 

 

解决方案:

 

	private void initBusiLogAppender(String appenderName, String logFileName,	org.apache.log4j.Logger  log) {
		synchronized(log) {
			if(log.getAppender(appenderName)!=null) return;
		try {

 

因为走到initBusiLogAppender函数的机会并不多,所以在这个对象上直接做同步。

 


其他的废话:

 

初步看起来log4j也是有点小问题的,比如定义了如下方法:

 

  /**
     Look for the appender named as <code>name</code>.

     <p>Return the appender with that name if in the list. Return
     <code>null</code> otherwise.  */
  synchronized
  public
  Appender getAppender(String name) {
     if(aai == null || name == null)
      return null;

     return aai.getAppender(name);
  }

 按说这么定义之后,一个log对象应该是不允许有name相同的appender。

但是本文所fix的那个bug,确实是因为初始化了多个相同appender导致!

 

跟进aai.getAppender(name)方法:这下好了,居然是遍历list取第一个name相同的返回,而不管是否存在相同name的appender。

  public
  Appender getAppender(String name) {
     if(appenderList == null || name == null)
      return null;

     int size = appenderList.size();
     Appender appender;
     for(int i = 0; i < size; i++) {
       appender = (Appender) appenderList.elementAt(i);
       if(name.equals(appender.getName()))
	  return appender;
     }
     return null;    
  }
 

 

那么继续跟进addAppender方法,看这里有无保证appender的名字不重复。

  /**
     Attach an appender. If the appender is already in the list in
     won't be added again.
  */
  public
  void addAppender(Appender newAppender) {
    // Null values for newAppender parameter are strictly forbidden.
    if(newAppender == null)
      return;
    
    if(appenderList == null) {
      appenderList = new Vector(1);
    }
    if(!appenderList.contains(newAppender))
      appenderList.addElement(newAppender);
  }
 

  这里使用appenderList.contains(newAppender),vecotr的对象比较来判断appender是否重复,但是不幸的是,

大多数Appender接口实现没有重载Ojbect的hashcode()方法,也就是name相同的appender在这里是可以通过不相等判断。。

分享到:
评论

相关推荐

    log4j-1.2.11jar和log4j.properties配置文件

    `log4j-1.2.11.jar`是Log4j 1.2.11版本的库文件,包含了Log4j的所有类和方法。将这个JAR文件添加到项目的类路径中,即可使用Log4j进行日志记录。这个版本相对于早期版本可能包含了一些性能优化和bug修复。 **三、...

    log4j jar包

    Log4j-1.2.17是Log4j 1.x系列的一个稳定版本,尽管后续发布了Log4j 2.x,但1.2版仍被许多遗留系统和项目广泛使用。这个版本修复了一些已知的bug,提升了性能和稳定性,同时也兼容了当时的Java环境。 总结,Log4j...

    log4j各个版本

    1. Log4j 1.2.6:这是Log4j的一个较早版本,提供了基本的日志记录功能,包括定义不同级别的日志(如DEBUG、INFO、WARN、ERROR和FATAL),以及通过配置文件自定义日志输出格式和目的地(如控制台、文件、SMTP等)。...

    log4j各版本和配置文件

    Log4j 1.2系列是其较为成熟的版本,包含了丰富的日志级别(如DEBUG、INFO、WARN、ERROR、FATAL)和多种日志输出方式(如控制台、文件、网络、数据库等)。 以`log4j.properties`为例,配置文件的基本结构包括以下几...

    log4j各版本jar包

    在配置文件(通常是log4j.properties或log4j.xml)中,我们可以设置日志级别(DEBUG、INFO、WARN、ERROR等)、输出目的地(如ConsoleAppender、FileAppender等)、以及自定义的布局格式(如PatternLayout)等。...

    log4j 下载

    然后配置log4j的属性文件(通常命名为`log4j.properties`或`log4j.xml`),在其中指定Logger、Appender和Layout的详细设置。最后,在代码中通过`org.apache.log4j.Logger`类创建并使用日志器。 **4. 示例配置** ``...

    如何在Java工程中使用Log4j

    在src源程序包中添加一个log4j.properties文件,用于配置Log4j的日志记录规则。以下是一个简单的log4j.properties文件示例: ```properties log4j.rootLogger=DEBUG, fileLogger,BORROW log4j.appender.fileLogger=...

    log4j实例,log4j-1.2.9.jar

    Log4j的配置文件通常是`log4j.properties`或`log4j.xml`,它定义了Logger、Appender和Layout的具体设置。例如,你可以设置某个类或整个包的日志级别,指定日志输出的位置和格式。 **4. 使用示例** 在描述中提到的...

    log4j-1.2.16

    在Log4j中,通常通过配置文件(如log4j.properties或log4j.xml)来设定日志行为。配置文件中可以定义Loggers、Appenders、Levels和Layouts。例如: ```properties # 配置控制台输出 log4j.appender.stdout=org....

    log4j-2.3.jar

    3. `log4j-slf4j-impl-2.8.2.jar` 和 `log4j-slf4j-impl-2.3.jar`:这是SLF4J(Simple Logging Facade for Java)到Log4j的适配器,允许使用SLF4J API的同时,底层日志系统使用Log4j。 4. `log4j-jcl-2.3.jar` 和 `...

    slf4j-api-1.6.1和slf4j-log4j12-1.6.1和log4j-1.2.16包

    SLF4J、slf4j-log4j12和Log4j三者组合,为Java项目提供了灵活且可扩展的日志解决方案。SLF4J作为接口层,允许代码独立于具体日志框架,slf4j-log4j12作为桥接器将SLF4J调用转换为Log4j操作,而Log4j则负责实际的日志...

    Java 日志工具 Log4j 示例源代码

    Log4j的配置通常通过XML或properties文件完成,例如`log4j.properties`或`log4j.xml`。下面是一个简单的配置示例: ```properties # 设置全局日志级别为INFO log4j.rootLogger=INFO, Console, File # 配置控制台...

    apache-log4j-2.0

    Apache Log4j 2.0 发布第 4 个 Beta 版本,包括的新特性有: o Added Log4j 2 to SLF4J adapter. o LOG4J2-131: Add SMTPAppender. Thanks to Scott Severtson. o Added hostName and contextName to property ...

    log4j_jar包

    在您提供的压缩包文件“log4j_jar包”中,包含了两个不同版本的Log4j JAR文件:`log4j-1.2.8.jar`和`log4j-1.2.16.jar`。这两个版本之间的差异主要在于修复的bug、性能优化以及对新特性的支持。 **1. log4j-1.2.8....

    log4j需要jar

    Log4j的配置通常通过一个名为log4j.properties或log4j.xml的文件进行。这个文件定义了Loggers、Appenders和Layouts的具体设置。以下是一个简单的配置示例: ```properties # log4j.properties log4j.rootLogger=...

    log4net配置,日志纪录log4j的.NET版

    Log4net是Apache软件基金会开发的一个强大的日志框架,它是log4j(Java版本)在.NET平台上的移植,提供了一套全面的日志记录解决方案。本文将深入探讨log4net的配置及其在.NET应用中的使用。 **1. log4net简介** ...

    android-logging-log4j-1.0.3

    通常,我们需要创建一个名为`log4j.properties`或`log4j.xml`的配置文件,定义日志输出的级别(如DEBUG, INFO, WARN, ERROR等)、输出目的地(控制台、文件、网络等)以及布局格式。例如,一个简单的`log4j....

    slf4j-log4j12-1.5.5.jar、slf4j-log4j12-1.5.6.jar、slf4j-api-1.5.6.jar

    SLF4J-log4j12桥接库则是连接SLF4J接口与Log4j实现的桥梁,使得开发者可以使用SLF4J的API,同时利用Log4j进行日志记录。 SLF4J-api-1.5.6.jar是SLF4J API的实现,它包含了一系列的日志记录接口,如`Logger`, `Level...

    log4j支持jar包

    描述中提到的`PropertyConfigurator.configure`是Log4j中的一个关键类,它的作用是读取并解析`log4j.properties`或`log4j.xml`配置文件。这个配置文件用于定义日志输出的行为,包括日志级别(如DEBUG, INFO, WARN, ...

Global site tag (gtag.js) - Google Analytics