论坛首页 Java企业应用论坛

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

浏览 14719 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-03-15  
captmjc 写道
> 方法一对原有写日志的程序需要改动所以我觉得不是很合适
反正你原来的代码,:
System.setOut(....);
System.out.println(.....);
我觉得,使用同一个Logger,然后根据不同的msg,输出到不特定的有点麻烦,改动log4j的地方太多,IMHO反而更不合适。

> 方法二试过效率实在太低了,影响业务程序
不知道你并发量,JDBCAppender确实很弱,甚至还有bug。但是合理tuning或者自己重写一个DB类的Appender,理论上还是可行的。

还有一种方法,扩展一个File/WriterAppender,路径通过注入clientId自动获取。Override其中的doAppend方法,判断LoggingEvent中的message是否固定格式,比如你这里的"client: ${clientId} "开头,如果是,则super.doAppend(event)否则return.

然后写个AppenderFactory,根据clientId获得不同的实例。
log动作开始前,
Appender appender = factory.getAppender(clientId);
if (!log.isAttached(appender)) {
  log.addAppender(appender);
}
然后再写日志。

由于org.apache.log4j.helpers.AppenderAttachableImpl.isAttached(Appender)中间使用的是==比较,因此factory.getAppender(clientId)需要用map保留原有实例。


扩展一个File/WriterAppender,路径通过注入clientId自动获取 这个方法应该可以。

也可以参考下面这种方式
http://topic.csdn.net/u/20080102/17/7db9e11c-46ab-4bdc-bdcb-9c19e13c9ef9.html

楼主的client不是太多可以考虑这么做。
如果client会大量增加是不是考虑做日志分析。
0 请登录后投票
   发表时间:2012-03-15  
配置多个logger就没有并发问题,每个logger name为clientid,
LogManager.getLoggerRepository().getLogger(clientid)来获取独立的logger
0 请登录后投票
   发表时间:2012-03-15  
感觉这个问题本身用override Appender的subAppend和rollover有点杀鸡用牛刀。

最简单的就是log的同事加上当期thread的id或者name来实现in和out的匹配。
0 请登录后投票
   发表时间:2012-03-15  
kimmking 写道
感觉这个问题本身用override Appender的subAppend和rollover有点杀鸡用牛刀。

最简单的就是log的同事加上当期thread的id或者name来实现in和out的匹配。

请问  in 和out匹配  怎么理解?
0 请登录后投票
   发表时间:2012-03-15  
下面的代码能够做到 按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文件。

不知道有没有更好的办法



0 请登录后投票
   发表时间:2012-03-15   最后修改:2012-03-15
clientid是固定且是知道的吧,如果是,则可以在log4j配置里配置每个clientid对应logger,logger的名字按照C+clientid的规则,比如C1:
log4j.logger.CLIENTID1=INFO,C1
log4j.appender.C1=org.apache.log4j.DailyRollingFileAppender
log4j.appender.C1.file=../../logs/c1.log
log4j.appender.C1.file.datepattern=...
log4j.appender.C1.layout=org.apache.log4j.PatternLayout
log4j.appender.C1.layout.ConversionPattern=%d [%-5p][-t]

然后在程序中建立clientid对应的C+clientid的Map<clientid,Logger>缓存,当新的请求过来时,根据第一个参数clientid从Map中获取对应的Logger,将该Logger放入ThreadLocal中,供后续使用。
0 请登录后投票
   发表时间:2012-03-15  
railway 写道
clientid是固定且是知道的吧,如果是,则可以在log4j配置里配置每个clientid对应logger(名字按照C+clientid的规则,比如C1、C2...),然后在程序中建立clientid对应的C+clientid的Map<clientid,Logger>缓存,当新的请求过来时,根据第一个参数clientid从Map中获取对应的Logger,将该Logger放入ThreadLocal中,供后续使用。


也可行,但是对原来代码的改动太大了。原来的代码都是下面的模式打的日志
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class Hello{
private static Log log = LogFactory.getLog(Hello.class);
public void XX(){
log.info("client :" + clientId +" in-------<");
}




0 请登录后投票
   发表时间:2012-03-15   最后修改:2012-03-15
hasi 写道
railway 写道
clientid是固定且是知道的吧,如果是,则可以在log4j配置里配置每个clientid对应logger(名字按照C+clientid的规则,比如C1、C2...),然后在程序中建立clientid对应的C+clientid的Map<clientid,Logger>缓存,当新的请求过来时,根据第一个参数clientid从Map中获取对应的Logger,将该Logger放入ThreadLocal中,供后续使用。


也可行,但是对原来代码的改动太大了。原来的代码都是下面的模式打的日志
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class Hello{
private static Log log = LogFactory.getLog(Hello.class);
public void XX(){
log.info("client :" + clientId +" in-------<");
}







我觉得你要改动的就是LogFactory.getLog(Hello.class)这部分,将其封装成一个工厂,根据clientid返回Logger。

 

0 请登录后投票
   发表时间:2012-03-15  
railway 写道
hasi 写道
railway 写道
clientid是固定且是知道的吧,如果是,则可以在log4j配置里配置每个clientid对应logger(名字按照C+clientid的规则,比如C1、C2...),然后在程序中建立clientid对应的C+clientid的Map<clientid,Logger>缓存,当新的请求过来时,根据第一个参数clientid从Map中获取对应的Logger,将该Logger放入ThreadLocal中,供后续使用。


也可行,但是对原来代码的改动太大了。原来的代码都是下面的模式打的日志
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class Hello{
private static Log log = LogFactory.getLog(Hello.class);
public void XX(){
log.info("client :" + clientId +" in-------<");
}







我觉得你要改动的就是LogFactory.getLog(Hello.class)这部分,将其封装成一个工厂,根据clientid返回Logger。

 

业务类多的很,真的该不过来,再说如果改了就和log4j绑死了。

0 请登录后投票
   发表时间:2012-03-15  
railway 写道
hasi 写道
railway 写道
clientid是固定且是知道的吧,如果是,则可以在log4j配置里配置每个clientid对应logger(名字按照C+clientid的规则,比如C1、C2...),然后在程序中建立clientid对应的C+clientid的Map<clientid,Logger>缓存,当新的请求过来时,根据第一个参数clientid从Map中获取对应的Logger,将该Logger放入ThreadLocal中,供后续使用。


也可行,但是对原来代码的改动太大了。原来的代码都是下面的模式打的日志
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class Hello{
private static Log log = LogFactory.getLog(Hello.class);
public void XX(){
log.info("client :" + clientId +" in-------<");
}







我觉得你要改动的就是LogFactory.getLog(Hello.class)这部分,将其封装成一个工厂,根据clientid返回Logger。

 

再说了  假设以后不根据clientId分日志文件,,要根据别的特性拆分日志文件的时候也太不灵活了吧

0 请登录后投票
论坛首页 Java企业应用版

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