锁定老帖子 主题:log4j 动态日志文件名 的线程安全问题
精华帖 (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秒。效率下降太厉害了 |
|
返回顶楼 | |
发表时间:2012-03-15
你log.info的前面,还是在用activateOptions() ???
|
|
返回顶楼 | |
发表时间: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); } } } |
|
返回顶楼 | |
发表时间:2012-03-16
还是那句话,治本还是
Logger log = Logger.getLogger("client." + clientId); 就好象以前经常和同事说S1的ActionForward一般是通过struts-config得来。 但是不等于说,ActionForward只能通过struts-config得来,你还可以在运行时自己动态生成以便设置不同的路径参数。 同理,Log4j(或其他很多Log框架)一般都是通过配置文件。 但是不等于说,Logger只能通过配置文件得来,你还可以在运行时自己动态生成以便设置不同的输出路径。 |
|
返回顶楼 | |
发表时间: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。 |
|
返回顶楼 | |
发表时间:2012-03-16
楼主可以用backlog里面那个sift,筛发配置,或者根据用户身份之类的mda。
|
|
返回顶楼 | |
发表时间: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; } } |
|
返回顶楼 | |
发表时间: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所言,频繁开关 |
|
返回顶楼 | |
发表时间:2012-03-16
非常 感谢 captmjc 朋友,终于解决了。
|
|
返回顶楼 | |