log4j解惑

[size=medium]1、指定要打印文件的文件
log4j的日志中,可以配置一个全局的打印日志文件。
log4j.rootLogger = 【日志级别】,【appender1】,【appender2】...
这个是所有日志打印文件的根目录。
同时,也可以设置一个具体的打印文件路径。
如:log4j.logger.【A.B.C】 = 【日志级别】,【appender1】,【appender2】...
其中的【A.B.C】可以是包名,也可以是类名[/size]

[size=medium]当指定了多个要打印的日志文件配置时,如果这些文件配置存在交集,log4j默认是认为交集的文件是同时生效的。
比如:
log4j.rootLogger = debug,console,file
log4j.logger.A.B.C = info,console

在默认情况下,A.B.C包下的类请求日志输出时(info级别以上的),会输出到三个appender(两次console,一次file,父级路径的日志输出是不受日志级别限制的)
A.B.C包下的类请求debug级别的日志输出时,则一条日志都不会输出。[/size]

[size=medium]LoggerFactory.getLogger(A.class)时,log4j实际上维护了一个当前路径的父子路径关系(log4j.rootLogger 是这个关系中的祖先)[/size]
代码如下:
public
Logger getLogger(String name, LoggerFactory factory) {
CategoryKey key = new CategoryKey(name);
Logger logger;
synchronized(ht) {
Object o = ht.get(key);
if(o == null) {
logger = factory.makeNewLoggerInstance(name);
logger.setHierarchy(this);
ht.put(key, logger);
updateParents(logger);
return logger;
} else if(o instanceof Logger) {
return (Logger) o;
} else if (o instanceof ProvisionNode) {
//System.out.println("("+name+") ht.get(this) returned ProvisionNode");
logger = factory.makeNewLoggerInstance(name);
logger.setHierarchy(this);
ht.put(key, logger);
updateChildren((ProvisionNode) o, logger);
updateParents(logger);
return logger;
}
else {
// It should be impossible to arrive here
return null; // but let's keep the compiler happy.
}
}
}

[size=medium]维护父子路径的意义在于,有序的管理指定输出文件存在交集的路径,并且以父子关系来存储。这样当一个类请求日志输出时,默认地把当前类的全路径的日志appender,打印,无论找到或没找到,依次向上查找,直到最后的root。
所以如果项目中只配置了log4j.rootLogger,那么可以使用LoggerFactory.getLogger("ROOT")来获取直接获取ROOT日志对象,这样的好处是少了维护父子路径。

如果只想输出子路径上的日志,对于父级的日志不予输出,
log4j.additivity.【具体的类名】= 【true或false】,设置为false时(默认为true),将限制上级路径中日志的输出。
[/size]

[size=medium]2、指定日志的输出
日志输出的格式为:
logger.appender.【appender名】= log4j提供的appender实现

比较常用的有
ConsoleAppender 控制台输出日志
可以指定target(log4j.appender.[appender名称].Target=System.err或System.out)
DailyRollingFileAppender 按天打印日志
RollingFileAppender 按文件大小打印日志,默认日志划分大小为10M,即超出10M创建新的日志文件
可以通过maxFileSize设定文件划分大小,默认单位是KB,也可以指定MB,GB
可以通过maxBackupIndex设定日志文件的缓存备份,(日志文件的命名,file.maxBackupIndex-1)

在上述的appender中有一个属性为immediateFlush,这个属性默认为true,表示不使用buffer,一段日志请求后立即刷新输出到控制台或是文件。在file相关的appender中,使用另一个属性bufferedIO来封装了immediateFlush,这个值为true时,表示使用Buffer,同时还有一个bufferSize,默认大小为8K.(使用buffer的影响,由于是IO操作,会占用大量的时间,在大量日志请求的情况下会造成cpu等待IO)
有关immediateFlush字段文档中的叙述:[/size]
[size=medium][quote]Avoiding the flush operation at the end of each append results in a performance gain of 10 to 20 percent. However, there is safety tradeoff involved in skipping flushing. Indeed, when flushing is skipped, then it is likely that the last few log events will not be recorded on disk when the application exits. This is a high price to pay even for a 20% performance gain. [/quote][/size]
[size=medium]该字段为true时,会影响性能(cpu在等IO),设置为false后会提高20%的性能,但是应用退出时,可能会有日志打印不出来(因为还在buffer缓存中)

log4j中还有一个异步文件日志输出:AsyncAppender,这个appender建议在jdbc或stmp时使用。
具体性能可以参考:使用AsyncAppender是否可以提高性能[url] http://littcai.iteye.com/blog/316605[/url]
具体原理可以参考:AsyncAppender的实现原理[url] http://blackgu.blogbus.com/logs/163664173.html[/url]

3、指定打印日志的格式
日志的打印格式为:
log4j.appender.【appender】.layout = log4j提供的layout实现

使用的最多的就是PatternLayout
PatternLayout使用%做为格式头信息,可以使用的格式如下:
1、c为Category,即打印出当前日志是属于哪个日志request请求的。(如果使用LoggerFactory.getLogger("ROOT")则会打印出root,如果使用LoggerFactory.getLogger(A.class)则会打印出A的全路径)
2、C,大c是打印出ClassName(全路径),【文档中不建议使用】,并且是只打印到Class级别,没有l有效
3、d,打印日期,对应的日期,log4j提供了四种日期格式解析方案。
一是:默认方案,默认其实是%d{ISO8601},使用ISO8601DateFormat,时间格式为2013-12-10 14:32:14,315(月日时分秒不足2位时会用0补充)
二是:%d{ABSOLUTE},使用AbsoluteTimeDateFormat解析,时间格式为14:32:14,315
三是:%d{DATE},使用DateTimeDateFormat解析,时间格式为10 十二月 2013 14:37:11,331
四是自定义格式,如%d{yyyy-MM-dd},使用SimpleDateFormat解析,若解析失败,则采用ISO8601DateFormat
4、F 对应英文file,打印日志输出文件file的名称 【文档不建议使用】
5、l 对应英文location 打印日志的详细信息,具体到 类全路径。方法。行
6、L 只会打印出具体的行数
7、m 对应英文mesage,具体的日志信息
8、M 对应英文Method,打印日志所请求方法名称
9、p,打印日志级别
10、r,对应英文relative(相对) 打印相对时间,计算方式为event.timeStamp - LoggingEvent.getStartTime(),即本次日志请求的时间和日志初始化的时间差
11、t,对应英文thread,打印线程名称
12、x,打印NDC(嵌套诊断环境) 为每个线程分配了一个环境,使用stack栈来存储,在方法的开始位置NDC.push("A"),离开方法时采用NDC.clear();(一般在多线程的环境下使用NDC,NDC需要一个标识,标识出多线程的情况下该日志到底是哪个用户的日志)
[quote]NDC(Nested Diagnostic Context)是 Neil Harrison 在名为《Patterns for Logging Diagnostic Messages》的书中提出的嵌套诊断环境的机制。这种机制的提出,主要为了减少多线程的系统为每个客户单独记录日志的系统开销。在过去,区分两个客户 的日志输出的常用方法是单独为每个客户机实例化新类别,但该方法会增加类别数量,并增加日志记录的管理开销。Neil Harrison 提出的方法就是把用户的上下文信息放到嵌套式诊断环境 (NDC) 中。 [/quote]
13、X,大x,打印MDC(Map诊断环境),类似于NDC,也是对应不同的线程,只不过是使用的Map,打印日志的时候需要加上key,如%X{ip}
14、n,换行

其它的Layout还包括:
一、SimpleLayout
打印格式为:日志等级-日志信息
二、XMLLayout
打印成:xml文件
三、HTMLLayout
打印成html文件
四、TTCCLayout(time,thread,category and nested diagnostic context information)
五、EnhancedPatternLayout 需要extra包

日志中打印的请求日志的类名、方法名、行号,使用的是LocationInfo,
LocationInfo的原理是使用是异常链,从堆栈中的执行点找出日志的请求类信息。

也就是说,一次日志的location的打印,实际上是抛出了一个异常,并进行了一次栈查找,所以文档中有提醒:
[quote]The location information can be very useful. However, its generation is extremely slow and should be avoided unless execution speed is not an issue.[/quote]
“位置信息可以是非常有用的。然而,它的生成是极其缓慢的,应该避免,除非执行速度是不是一个问题。”[/size]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值