一。背景分析
log4j 即便配置到炉火纯青,也无法回避大家面临的日志问题:
1.单个文件无法装下足够的log。曾经在IDC繁忙时,一个10M的log文件只可以支撑不到2分钟, 最大滚10个的话,那么20分钟以前出现的bug就找不到日志了。
2.都往一个地方记,不同模块的人记录内容不均,有的人一个操作打一屏log,有的人异常了都不记日志。。不好管理。
3. 如此快速的日志滚动,通过tail -f 去观察简直就是一个悲剧。
二。具体实现
这里提到的分文件记log,不是在log4j.properties里面一个个类的去指定输出到哪个文件。。这样每次修改、新增模块都需要改配置,而且不能动态的把一系列操作的日志轨迹串联起来。
1.下面分析了一下log4j几个核心类的关系,注意我们调用得最多的Logger对象(Category这个类已经被子类Logger代替,并且不推荐使用)
它从根类继承了这么一个犀利的方法:addAppender , 我们可以在Logger实例化的时候,顺带给它加上自己定制的appender
/**
Add <code>newAppender</code> to the list of appenders of this
Category instance.
<p>If <code>newAppender</code> is already in the list of
appenders, then it won't be added again.
*/
synchronized
public
void addAppender(Appender newAppender) {
if(aai == null) {
aai = new AppenderAttachableImpl();
}
aai.addAppender(newAppender);
repository.fireAddAppenderEvent(this, newAppender);
}
2.那么appender有什么好玩意呢?看起来真的很一般,但是它的子类FileAppender就 NB了,注意,有带fileName参数的构造器!!想把日志写到哪个文件,就看你是否会犀利的构造FileAppender了。
public class FileAppender extends WriterAppender {
protected boolean bufferedIO = false;
protected int bufferSize = 8*1024;
FileAppender() {
}
public
FileAppender(Layout layout, String filename, boolean append, boolean bufferedIO,
int bufferSize) throws IOException {
this.layout = layout;
this.setFile(filename, append, bufferedIO, bufferSize);
}
public
FileAppender(Layout layout, String filename, boolean append)
throws IOException {
this.layout = layout;
this.setFile(filename, append, false, bufferSize);
}
public
FileAppender(Layout layout, String filename) throws IOException {
this(layout, filename, true);
}
3.总结一下:
看起来是这样:在logger第一次初始化的时候,添加我自己的fileappender,
public void logXXX(String busiModule, Object message) {
String logName = "runtime."+busiModule; //log的name
org.apache.log4j.Logger logger = Log4jFactory.getInstance(logName);
if (logger.getAppender(logName) == null) {
initMyAppender(logName, busiModule+".log", logger);
}
* 初始化方法里面,使用了带指定filename参数的Fileappender,赋予appender单独的文件。
* @param appenderName
* @param logFileName
* @param log
*/
private void initMyAppender(String appenderName, String logFileName, org.apache.log4j.Logger log) {
try {
//1.创建log文件目录
File fileDir = new File(LogDir);
if (!fileDir.exists()) {
fileDir.mkdirs();
}
//2.创建appender
RollingFileAppender fileAppender;
PatternLayout patternLayout = new PatternLayout();
patternLayout.setConversionPattern("%d{yyyy-MM-dd HH:mm:ss} %m%n");
fileAppender = new RollingFileAppender(patternLayout, LogDir+logFileName);
fileAppender.setMaxFileSize(maxFileSize);
fileAppender.setMaxBackupIndex(maxBackupIndex);
fileAppender.setName(appenderName);
log.addAppender(fileAppender);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
那么在使用的时候,除了记录的消息对象,还需要额外提供一个String字段的model,相同的model导致相同的logger对象,会记录在一个文件;不同的model参数会使得日志分开记录到各自文件。关于这个参数,通常有两种提供方案:
1.选择包名的某个单词,比如"item","deal"传入,这样就是分业务模块记录。
2.使用ThreadLoacl,将用户访问的session做model,这样就可以在一个文件中完整记录用户访问的所有轨迹。
以上两种方案的model则,还以进一步通过封装到接口中,使得调用者无需关心具体model值。
FAQ:
1.addApender,每个log那不是有多个appender了么?用文件记录岂不是会写多份,耗IO的。。
~~请大家关心一下log4j的这个属性additivity:(#log4j.additivity.runtime=false)
more???