论坛首页 Java企业应用论坛

log4j 动态日志文件名 的线程安全问题

浏览 14720 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-03-15  
hasi 写道
下面的代码能够做到 按clientId写日志文件,,在线程启动的时候 MDC.put("clientId",clientId);
import org.apache.log4j.RollingFileAppender;
import org.apache.log4j.spi.LoggingEvent;

public class MyRollingFileAppender extends RollingFileAppender{


	@Override
	public synchronized void doAppend(LoggingEvent event) {
		// TODO Auto-generated method stub
		String filename = (String)event.getMDC("clientId") ;
		setFile(filename);
		activateOptions();
		super.doAppend(event);
	}

}



测试了一下,效率略有影响,不知道有没有更好的办法。
效率下降的原因应该是调用activateOptions() 时log4j关闭其它clientId的log文件,打开当前线程对应的clientId的log文件。

不知道有没有更好的办法




打印了10万条日志 RollingFileAppender 4秒 ,MyRollingFileAppender 41秒。效率下降太厉害了
0 请登录后投票
   发表时间:2012-03-15  
你log.info的前面,还是在用activateOptions() ???
0 请登录后投票
   发表时间:2012-03-16  
LZ还是在用一个log一个appender,那自然慢了。

public class Test {

  public static void main(String[] args) throws Exception {
    final Logger log = Logger.getLogger("test");
    final AppenderFactory appenderFactory = new AppenderFactory();
    Thread[] threads = new Thread[10];
    long start = System.currentTimeMillis();
    for (int i = 0; i < threads.length; i++) {
      final String clientId = String.valueOf(i);
      threads[i] = new Thread() {

        public void run() {
          MDC.put("clientId", clientId);
          for (int i = 0; i < 10000; i++) {
            Appender appender = appenderFactory.getAppender(clientId);
            if (!log.isAttached(appender)) {
              log.addAppender(appender);
            }
            log.info("start: clientId = " + clientId);
            foo();
            log.info("  end: clientId = " + clientId);
          }
        }
        void foo() {}
      };
      threads[i].start();
    }
    for (int i = 0; i < threads.length; i++) {
      threads[i].join();
    }
    long end = System.currentTimeMillis();
    System.out.println(end - start);
  }

}

class AppenderFactory {

  Map<String, Appender> appenders = new HashMap<String, Appender>();

  public Appender getAppender(String clientId) {
    Appender appender = appenders.get(clientId);
    if (appender == null) {
      appender = new MyRollingFileAppender(clientId);
      appenders.put(clientId, appender);
    }
    return appender;
  }
}

class MyRollingFileAppender extends RollingFileAppender {

  private final String clientId;

  public MyRollingFileAppender(String clientId) {
    this.clientId = clientId;
    setFile(clientId);
    setLayout(new PatternLayout());
    activateOptions(); // 只开一次,而不是一个msg一次,那太夸张了
  }

  @Override // 为速度计,Override这个方法,而不是append, subAppend之类
  public synchronized void doAppend(LoggingEvent event) {
    String clientId = (String) MDC.get("clientId");
    if (this.clientId.equals(clientId)) {
      // 一个Appender对应一个Client,其他Client的event直接忽视
      super.doAppend(event);
    }
  }

}
0 请登录后投票
   发表时间:2012-03-16  
还是那句话,治本还是
Logger log = Logger.getLogger("client." + clientId);  


就好象以前经常和同事说S1的ActionForward一般是通过struts-config得来。
但是不等于说,ActionForward只能通过struts-config得来,你还可以在运行时自己动态生成以便设置不同的路径参数。

同理,Log4j(或其他很多Log框架)一般都是通过配置文件。
但是不等于说,Logger只能通过配置文件得来,你还可以在运行时自己动态生成以便设置不同的输出路径。
0 请登录后投票
   发表时间:2012-03-16   最后修改:2012-03-16
captmjc 写道
LZ还是在用一个log一个appender,那自然慢了。

public class Test {

  public static void main(String[] args) throws Exception {
    final Logger log = Logger.getLogger("test");
    final AppenderFactory appenderFactory = new AppenderFactory();
    Thread[] threads = new Thread[10];
    long start = System.currentTimeMillis();
    for (int i = 0; i < threads.length; i++) {
      final String clientId = String.valueOf(i);
      threads[i] = new Thread() {

        public void run() {
          MDC.put("clientId", clientId);
          for (int i = 0; i < 10000; i++) {
            Appender appender = appenderFactory.getAppender(clientId);
            if (!log.isAttached(appender)) {
              log.addAppender(appender);
            }
            log.info("start: clientId = " + clientId);
            foo();
            log.info("  end: clientId = " + clientId);
          }
        }
        void foo() {}
      };
      threads[i].start();
    }
    for (int i = 0; i < threads.length; i++) {
      threads[i].join();
    }
    long end = System.currentTimeMillis();
    System.out.println(end - start);
  }

}

class AppenderFactory {

  Map<String, Appender> appenders = new HashMap<String, Appender>();

  public Appender getAppender(String clientId) {
    Appender appender = appenders.get(clientId);
    if (appender == null) {
      appender = new MyRollingFileAppender(clientId);
      appenders.put(clientId, appender);
    }
    return appender;
  }
}

class MyRollingFileAppender extends RollingFileAppender {

  private final String clientId;

  public MyRollingFileAppender(String clientId) {
    this.clientId = clientId;
    setFile(clientId);
    setLayout(new PatternLayout());
    activateOptions(); // 只开一次,而不是一个msg一次,那太夸张了
  }

  @Override // 为速度计,Override这个方法,而不是append, subAppend之类
  public synchronized void doAppend(LoggingEvent event) {
    String clientId = (String) MDC.get("clientId");
    if (this.clientId.equals(clientId)) {
      // 一个Appender对应一个Client,其他Client的event直接忽视
      super.doAppend(event);
    }
  }

}

问题是实际当中Test 类里调用很多业务类,难道clientId 一直传下去吗?或者存到ThreadLocal里吗?
这样对所有业务类的代码都要修改,都要重新获取logger。业务类里没有任何代码是依赖log4j的,目前之有接收请求的类(就一个类)和这个扩展的appender依赖log4j。
0 请登录后投票
   发表时间:2012-03-16  
楼主可以用backlog里面那个sift,筛发配置,或者根据用户身份之类的mda。
0 请登录后投票
   发表时间:2012-03-16   最后修改:2012-03-16
hasi 写道
hasi 写道
下面的代码能够做到 按clientId写日志文件,,在线程启动的时候 MDC.put("clientId",clientId);
import org.apache.log4j.RollingFileAppender;
import org.apache.log4j.spi.LoggingEvent;

public class MyRollingFileAppender extends RollingFileAppender{


	@Override
	public synchronized void doAppend(LoggingEvent event) {
		// TODO Auto-generated method stub
		String filename = (String)event.getMDC("clientId") ;
		setFile(filename);
		activateOptions();
		super.doAppend(event);
	}

}



测试了一下,效率略有影响,不知道有没有更好的办法。
效率下降的原因应该是调用activateOptions() 时log4j关闭其它clientId的log文件,打开当前线程对应的clientId的log文件。


不知道有没有更好的办法




打印了10万条日志 RollingFileAppender 4秒 ,MyRollingFileAppender 41秒。效率下降太厉害了



---------------------------------------------------------

如果是反复开关log文件使得效率变低,那就开好以后别关掉了,不知道下面的方案可行不?
import org.apache.log4j.RollingFileAppender;  
import org.apache.log4j.spi.LoggingEvent;  

public class MyRollingFileAppender extend .... {  
	private Map<String,RollingFileAppender> appenderCache = new HashMap<String,RollingFileAppender>();
	

	@Override  
     public synchronized void doAppend(LoggingEvent event) {  
	    // TODO Auto-generated method stub  
	    String clientID = (String)event.getMDC("clientId") ; 
	    appender =  appenderCache.get(clientID);
	    if(appender == null) {
            	appender = createAppender4Client(clientID);
		appenderCache.put(clientID,appender);
            }
	    appender.doAppend(event); 
	}  

     private RollingFileAppender createAppender4Client(String clientID) {
		RollingFileAppender result = new RollingFileAppender();
                ......
                return  result;

        }

}  
0 请登录后投票
   发表时间:2012-03-16  
shenyu 写道
hasi 写道
hasi 写道
下面的代码能够做到 按clientId写日志文件,,在线程启动的时候 MDC.put("clientId",clientId);
import org.apache.log4j.RollingFileAppender;
import org.apache.log4j.spi.LoggingEvent;

public class MyRollingFileAppender extends RollingFileAppender{


	@Override
	public synchronized void doAppend(LoggingEvent event) {
		// TODO Auto-generated method stub
		String filename = (String)event.getMDC("clientId") ;
		setFile(filename);
		activateOptions();
		super.doAppend(event);
	}

}



测试了一下,效率略有影响,不知道有没有更好的办法。
效率下降的原因应该是调用activateOptions() 时log4j关闭其它clientId的log文件,打开当前线程对应的clientId的log文件。


不知道有没有更好的办法




打印了10万条日志 RollingFileAppender 4秒 ,MyRollingFileAppender 41秒。效率下降太厉害了



---------------------------------------------------------

如果是反复开关log文件使得效率变低,那就开好以后别关掉了,不知道下面的方案可行不?
import org.apache.log4j.RollingFileAppender;  
import org.apache.log4j.spi.LoggingEvent;  

public class MyRollingFileAppender extend .... {  
	private Map<String,RollingFileAppender> appenderCache = new HashMap<String,RollingFileAppender>();
	

	@Override  
     public synchronized void doAppend(LoggingEvent event) {  
	    // TODO Auto-generated method stub  
	    String clientID = (String)event.getMDC("clientId") ; 
	    appender =  appenderCache.get(clientID);
	    if(appender == null) {
            	appender = createAppender4Client(clientID);
		appenderCache.put(clientID,appender);
            }
	    appender.doAppend(event); 
	}  

     private RollingFileAppender createAppender4Client(String clientID) {
		RollingFileAppender result = new RollingFileAppender();
                ......
                return  result;

        }

}  

对啊,不行你就这样搞啊。我那个只是举个例子。说明,N个Appender速度并不会慢,慢是如@Shenyu所言,频繁开关
0 请登录后投票
   发表时间:2012-03-16  
非常 感谢 captmjc 朋友,终于解决了。
0 请登录后投票
论坛首页 Java企业应用版

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