锁定老帖子 主题:log4j 动态日志文件名 的线程安全问题
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2012-03-14
最后修改:2012-03-14
有很多客户端频繁调用这个webservice服务,webservice方法的参数中有一项为clientId 日志要求是,希望在webservice服务程序对每一个clientId能够单独文件记录日志(clientId 数量不算太多,但是数量和名称不固定) 我目前的实现方式如下: Appender appender = LogManager.getLoggerRepository().getRootLogger().getAppender("R"); ((FileAppender)appender).setFile(DIR+File.seperator+clientId+".txt"); ((FileAppender)appender).activateOptions(); log.info("client :" + clientId +" in-------<"); //业务代码 省略 log.info("client :" +clientId+" out------->"); 测试了一下多个client并发的情况,日志文件没有按要求记录。问题应该是appender是非线程安全的。 我目前想到的办法是 log.info 里从threadLocal读出filename 再设置日志文件名称 ,再设置文件名,不知道这样可不可行。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2012-03-14
自定义一个SelfFileAppender,重写activateOptions方法,设置自己的文件命名逻辑,
指定使用此appender log4j.appender.R=SelfFileAppender 不管考虑线程的事,log4j会管理 |
|
返回顶楼 | |
发表时间:2012-03-14
tianzizhi 写道 自定义一个SelfFileAppender,重写activateOptions方法,设置自己的文件命名逻辑,
指定使用此appender log4j.appender.R=SelfFileAppender 不管考虑线程的事,log4j会管理 activateOptions不会被自动调用的 |
|
返回顶楼 | |
发表时间:2012-03-14
你Appender是同一个,你替换Appender全世界当然都变了。重点在Appender的doAppend(LoggingEvent),输出目的地不同,应该在这里动手
方法一:只是理论,没实践过 1 Logger每次名字不一样,比如都叫"client." + clientId,点很重要。 2 自定义一个Appender,实现文件的输出。但是每次输出流不是一开始就打开,而是根据doAppend(LoggingEvent)的参数名称来(即1中的client.后部分),Writer/OutputStream可以考虑存入map,以加快速度 方法二:最简便 1 Logger还是每次名字不一样,附带clientId信息 考虑用JDBCAppender,表名或者某个字段,基于clientId。可以参考JDBCAppender,不过这个有点简陋。稍微复杂一点的需要覆盖getLogStatement(LoggingEvent)或者自己写一个 |
|
返回顶楼 | |
发表时间:2012-03-14
前面之所以说logger的name中,用“.”是因为:
1 配置,你只要设置client就可以覆盖所有n个logger 2 patternLayout也能%c{1}直接得到clientId |
|
返回顶楼 | |
发表时间:2012-03-15
captmjc 写道 你Appender是同一个,你替换Appender全世界当然都变了。重点在Appender的doAppend(LoggingEvent),输出目的地不同,应该在这里动手
方法一:只是理论,没实践过 1 Logger每次名字不一样,比如都叫"client." + clientId,点很重要。 2 自定义一个Appender,实现文件的输出。但是每次输出流不是一开始就打开,而是根据doAppend(LoggingEvent)的参数名称来(即1中的client.后部分),Writer/OutputStream可以考虑存入map,以加快速度 方法二:最简便 1 Logger还是每次名字不一样,附带clientId信息 考虑用JDBCAppender,表名或者某个字段,基于clientId。可以参考JDBCAppender,不过这个有点简陋。稍微复杂一点的需要覆盖getLogStatement(LoggingEvent)或者自己写一个 方法一对原有写日志的程序需要改动所以我觉得不是很合适 方法二试过效率实在太低了,影响业务程序 |
|
返回顶楼 | |
发表时间:2012-03-15
> 方法一对原有写日志的程序需要改动所以我觉得不是很合适
反正你原来的代码,: 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保留原有实例。 |
|
返回顶楼 | |
发表时间:2012-03-15
打字漏了:反正你原来的代码,IMHO,和下面代码无异:
System.setOut(....); System.out.println(.....); 根本不是相关代码线程安全不安全的问题,而是你错误使用,所以,补充前面第一条的回复,改动日志输出的方式,才是治本。 |
|
返回顶楼 | |
发表时间:2012-03-15
前段时间,我也做了一个多线程并发的程序,也要写日志,翻新log4j打出来的日志,也无法保证日志的连贯性,多个线程的日志会互相交叉。
后来想了一个办法。 日志代码,我们自己在每个线程自己的程序开头,用StringBuffer拼接好。到运行结束的地方,把日志用log4j记录下来,这样就能保证 整块日志的是同一个功能的日志了。 这样讲,明白了吗? |
|
返回顶楼 | |
发表时间:2012-03-15
youjianbo_han_87 写道 前段时间,我也做了一个多线程并发的程序,也要写日志,翻新log4j打出来的日志,也无法保证日志的连贯性,多个线程的日志会互相交叉。 后来想了一个办法。 日志代码,我们自己在每个线程自己的程序开头,用StringBuffer拼接好。到运行结束的地方,把日志用log4j记录下来,这样就能保证 整块日志的是同一个功能的日志了。 这样讲,明白了吗? 不要意思,没有看到是文件名的问题。回答的牛头不对马嘴。 |
|
返回顶楼 | |