-
核心对象: 框架的强制对象和框架的使用。core Objects
-
支持对象: 框架和支持体核心对象,可选的对象执行另外重要的任务。Support Objects
支持对象
-
Level对象:级别对象定义的任何记录信息的粒度和优先级。有记录的七个级别在API中定义:OFF, DEBUG, INFO, ERROR, WARN, FATAL 和 ALL
-
Filter对象:过滤对象用于分析日志信息及是否应记录或不用这些信息做出进一步的决定。一个appender对象可以有与之关联的几个Filter对象。如果日志记录信息传递给特定Appender对象,都和特定Appender相关的Filter对象批准的日志信息,然后才能发布到所连接的目的地。
-
对象渲染器:ObjectRenderer对象是一个指定提供传递到日志框架的不同对象的字符串表示。这个对象所使用的布局对象来准备最后的日志信息。
-
日志管理:日志管理对象管理的日志框架。它负责从一个系统级的配置文件或配置类读取初始配置参数。
核心对象
Logger、Appender、Layout
Log4j中有三个主要组成部分
===============
Logger:日志对象,负责捕捉日志记录信息
Logger对象是用来取代System.out或者System.err的日志输出器,负责日志信息的输出;其中,log4j日志框架提供了info、error、debug等API供Developer使用;
与commons-logging相同,log4j也有日志等级的概念;每一个logger对象都会分配一个等级,未被分配等级的logger则继承根logger的级别,进行日志的输出;每个日志对象方法的请求也有一个等级,如果方法请求的等于大于当前logger对象的等级,则该请求会被处理输出,否则该请求被忽略;
log4j在Level类中定义了7个等级,以此递增
-
ALL:打开所有日志;
-
DEBUG:适用于代码调试期间;
-
INFO:适用于代码运行期间;
-
WARN:适用于代码会有潜在错误事件;
-
ERROR:适用于代码存在错误事件;
-
FATAL:适用于严重错误事件;
-
OFF:关闭所有日志;
Appender:日志输出目的地,负责把格式好的日志信息输出到指定地方,可以是控制台、磁盘文件等
每个日志对象,都有一个对应的appender,每个appender代表着一个日志输出目的地;其中,log4j有以下几种appender可供选择:
-
ConsoleAppender:控制台;
-
FileAppender:磁盘文件;
-
DailyRollingFileAppender:每天产生一个日志磁盘文件;
-
RollingFileAppender:日志磁盘文件大小达到指定尺寸时产生一个新的文件;
Layout:日志格式化器,负责发布不同风格的日志信息
每个appender和一个Layout相对应,appende负责把日志信息输出到指定的地点,而Layout则负责把日志信息按照格式化的要求展示出来;其中,log4j有以下几种Layout可供选择:
-
HTMLLayout:以html表格形式布局展示;
-
PatternLayout:自定义指定格式展示;
-
SimpleLayout:包含日志信息的级别和信息字符串;
-
TTCCLayout:包含日志产生的时间、线程、类别等等信息;
示例
–
创建maven项目,学习log4j。
pom.xml添加依赖
log4j
log4j
1.2.17
复制代码
测试类 Log4jTest
public class Log4jTest {
Logger logger = Logger.getLogger(Log4jTest.class);
@Test
public void test() {
logger.fatal(“Fatal Message!”);
logger.error(“Error Message!”);
logger.warn(“Warn Message!”);
logger.info(“Info Message!”);
logger.debug(“Debug Message!”);
logger.trace(“Trace Message!”);
}
}
复制代码
没有配置文件怎么样,看一下执行结果
log4j:WARN No appenders could be found for logger (cn.lingyiwin.logs.Log4jTest).
警告无法为记录器找到附加器
log4j:WARN Please initialize the log4j system properly.
警告:请正确初始化log4j系统
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
警告见http://logging.apache.org/log4j/1.2/faq.html#noconfig获取更多信息
Process finished with exit code 0
复制代码
配置log4j.properties或者log4j.xml文件到resources
log4j.properties
log4j.rootLogger = INFO, FILE, CONSOLE
log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=D:/logs/log.out
log4j.appender.FILE.ImmediateFlush=true
log4j.appender.FILE.Threshold = DEBUG
log4j.appender.FILE.Append=true
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.conversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.ImmediateFlush=true
log4j.appender.CONSOLE.Threshold = DEBUG
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.encoding=UTF-8
log4j.appender.CONSOLE.layout.conversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
复制代码
log4j.xml
<?xml version="1.0" encoding="UTF-8" ?>log4j:configuration
</log4j:configuration>
复制代码
==如果是非maven项目,参考www.cnblogs.com/fps2tao/p/1…
log4j配置文件详解
===========
Logger 日志对象,负责捕捉日志记录信息
配置根Logger,其语法为:
log4j.rootLogger = [ level ] , appenderName, appenderName, …
复制代码
-
level指的是根logger对象的日志等级,ALL < DEBUG < INFO < WARN < ERROR < FATAL < OFF
-
Log4j建议只使用4个级别,从高到低分别为ERROR > WARN > INFO > DEBUG;
-
appenderName指的是根logger对象的日志信息输出目的地,在此可以指定多个输出目的地;
日志输出级别INFO
输出目的地 FILE CONSOLE
log4j.rootLogger = INFO, FILE, CONSOLE
复制代码
Appender 日志输出目的地,负责把格式好的日志信息输出到指定地方,可以是控制台、磁盘文件等
配置日志信息输出目的地Appender,其语法为:
log4j.appender.appenderName = className
复制代码
-
appenderName指的是日志信息输出目的地的名称,可自定义,需要与根Logger中的appenderName一致;
-
className指的是日志输出目的地处理类,必须为全限定类名;
将日志信息输出到对应的磁盘文件中
log4j.appender.FILE = org.apache.log4j.FileAppender
指定日志输出的最低级别,默认为DEBUG;如果日志请求的级别低于此级别,则不会输出此请求日志信息
log4j.appender.FILE.Threshold = DEBUG
将日志输出到D盘的logs/log.out文件中
log4j.appender.FILE.File=D:/logs/log.out
设置日志输出的编码
log4j.appender.FILE.Encoding=UTF-8
将新增日志追加到文件中,默认为true为不覆盖,false为覆盖
log4j.appender.FILE.Append=false
请求的日志消息被立即输出,默认为true
log4j.appender.FILE.ImmediateFlush=true
请求的日志消息不会立即输出,存储到缓存当中,当缓存满了后才输出到磁盘文件中,默认为false,此时ImmediateFlush应当设置为false
log4j.appender.FILE.BufferedIO=true
缓存大小,默认为8k
log4j.appender.FILE.BufferSize= 8192
将日志信息输出到控制台中
log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender
将日志信息使用System.out.println输出到控制台
log4j.appender.CONSOLE.Target=System.out
请求的日志消息被立即输出,默认为true(同上)
log4j.appender.CONSOLE.ImmediateFlush=true
指定日志输出的最低级别,默认为DEBUG;如果日志请求的级别低于此级别,则不会输出此请求日志信息(同上)
log4j.appender.CONSOLE.Threshold = DEBUG
设置日志输出的编码(同上)
log4j.appender.CONSOLE.encoding=UTF-8
将输出的日志信息,每天产生一个日志文件,与上面FileAppender不同
log4j.appender.DRFILE = org.apache.log4j.DailyRollingFileAppender()
将日志输出到D盘的logs/log.out文件中(同上)
log4j.appender.DRFILE.File=D:/logs/log.out
请求的日志消息被立即输出,默认为true(同上)
log4j.appender.DRFILE.ImmediateFlush=true
指定日志输出的最低级别,默认为DEBUG;如果日志请求的级别低于此级别,则不会输出此请求日志信息(同上)
log4j.appender.DRFILE.Threshold = DEBUG
将新增日志追加到文件中,默认为true为不覆盖,false为覆盖 (同上)
log4j.appender.DRFILE.Append=true
标识每天产生一个新的日志文件,当然也可以指定按月、周、时、分
log4j.appender.DRFILE.DatePattern='.'yyyy-MM-dd-HH-mm
具体格式如下:
1)'.'yyyy-MM: 每月
2)'.'yyyy-ww: 每周
3)'.'yyyy-MM-dd: 每天
4)'.'yyyy-MM-dd-a: 每天两次
5)'.'yyyy-MM-dd-HH: 每小时
6)'.'yyyy-MM-dd-HH-mm: 每分钟
在日志文件达到指定的大小后,再产生新的文件继续记录日志
log4j.appender.RFILE = org.apache.log4j.RollingFileAppender
将日志输出到D盘的logs/log.out文件中(同上)
log4j.appender.RFILE.File=D:/logs/log.out
指定日志输出的最低级别,默认为DEBUG;如果日志请求的级别低于此级别,则不会输出此请求日志信息(同上)
log4j.appender.RFILE.Threshold = DEBUG
设置日志输出的编码(同上)
log4j.appender.RFILE.encoding=UTF-8
将新增日志追加到文件中,默认为true为不覆盖,false为覆盖 (同上)
log4j.appender.RFILE.Append=false
请求的日志消息被立即输出,默认为true(同上)
log4j.appender.RFILE.ImmediateFlush=true
指定日志文件切割大小,默认10MB,单位KB/MB/GB;当日志文件达到指定大小后,将当前日志文件内容剪切到新的日志文件中,新的文件默认以“原文件名+.1”、“原文件名+.2”的形式命名
log4j.appender.RFILE.MaxFileSize=100KB
#产生的切割文件最大数量,如果第二个文件超过了指定大小,那么第一个文件将会被删除
log4j.appender.RFILE.MaxBackupIndex=2
复制代码
Layout
配置日志信息的格式Layout,其语法为:
log4j.appender.appenderName.layout = className
复制代码
-
ppenderName就是上面所讲的Appender的名称,Appender必须与Layout相互绑定;
-
className则是处理日志格式的类,也必须是全限定类名;
以html表格形式布局
log4j.appender.FILE.layout = org.apache.log4j.HTMLLayout
输出java文件名称和行号,默认值false
log4j.appender.FILE.layout.LocationInfo = true
简单风格布局,只包含日志信息和级别
log4j.appender.FILE.layout = org.apache.log4j.SimpleLayout
#自定义风格布局,可以包含时间,日志级别,日志类
log4j.appender.FILE.layout = org.apache.log4j.PatternLayout
#指定怎样格式化的消息
log4j.appender.FILE.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %5p %c{1}:%L - %m%n
具体的格式化说明:
%p:输出日志信息的优先级,即DEBUG,INFO,WARN,ERROR,FATAL。
%d:输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,如:%d{yyyy/MM/dd HH:mm:ss,SSS}。
%r:输出自应用程序启动到输出该log信息耗费的毫秒数。
%t:输出产生该日志事件的线程名。
%l:输出日志事件的发生位置,相当于%c.%M(%F:%L)的组合,包括类全名、方法、文件名以及在代码中的行数。例如:test.Log4jTest.main(Log4jTest.java:10)。
%c:输出日志信息所属的日志对象,也就是getLogger()中的内容。
%C:输出日志信息所属的类目;
%logger:log4j中没有此格式;
%M:输出产生日志信息的方法名。
%F:输出日志消息产生时所在的文件名称。
%L::输出代码中的行号。
%m::输出代码中指定的具体日志信息。
%n:输出一个回车换行符,Windows平台为"rn",Unix平台为"n"。
%x:输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。
%%:输出一个"%"字符。
复制代码
性能优化
====
由于频繁的IO和磁盘的读写,应用的性能也随之降低。并且,java的IO是阻塞式,加锁后导致也同样降低性能。因此对于日志的调优,就成了必备功课。
首先,抛开频繁的IO和磁盘读写不谈,就单纯讨论log4j的性能而言,在高并发的情况下,log4j的锁也会导致应用的性能下降,究其原因,还是看以下的代码:
Category类的callAppenders方法:
//日志对象唤起日志输出目的地Appender:进行日志打印
public void callAppenders(LoggingEvent event) {
int writes = 0;
//遍历日志对象集合
for(Category c = this; c != null; c=c.parent) {
//Category是Logger的父类,此处的c就是请求的日志对象本身:
synchronized© {
//此处加锁了:
if(c.aai != null) {
writes += c.aai.appendLoopOnAppenders(event);
}
if(!c.additive) {
break;
}
}
}
if(writes == 0) {
repository.emitNoAppenderWarning(this);
}
}
复制代码
通过以上代码,我们可以发现,为了获得对应日志对象的Appender,会在每次获取之前都加上synchronized同步锁。无论多少个线程进行请求,到此处都需要进行获取锁的操作,才可以进行日志的打印。这也就是说,线程越多,并发越大,此处的锁的竞争越激烈,进而导致系统性能的降低。
其次,我们再回过头来看下IO和磁盘读写的问题。在实际的生产环境下,系统所产生的日志信息需要保存在磁盘文件中,以便日后进行系统分析,或者系统问题的查找。
之前,我们说过Java的IO是阻塞式的,下面就来看下实际的代码:JDK1.7中的sun.nio.cs.StreamEncoder类:
public void write(char cbuf[], int off, int len) throws IOException {
synchronized (lock) {
ensureOpen();
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
implWrite(cbuf, off, len);
}
}
复制代码
可以看到,在java-IO流最终输出阶段,也同样加了synchronized同步锁。这也就是我们所说的java阻塞式IO。
log4j性能测试
FileAppender类主要功能就是将日志信输出到磁盘文件中。其中,有ImmediateFlush、BufferedIO、BufferSize这三个属性尤为值得关注;
当ImmediateFlush=true时候,表示每一条打印日志请求都会被立即输出,也就是立刻同步到磁盘中去。在高并发下,系统性能受到很大的影响,IO和磁盘读写数大大提升。
当ImmediateFlush=false时候,与上面正好相反,表示每一条打印日志请求不会被立即输出,会使用java.io.OutputStreamWriter的缓存,缓存大小为1024字节。
当ImmediateFlush=false、BufferedIO=true、BufferSize=8192时候,表示使用java.io.BufferedWriter缓存,缓存大小为默认8192字节,每一条打印请求不会立即输出,当缓存达到8192字节后才会落盘操作。这样一来,大大减少了IO和磁盘读写操作,提升了系统的性能。
测试代码:
public class log4jDemo {
Logger log = Logger.getLogger(log4jDemo.class);
@Test
public void test() throws InterruptedException {
for(int x=0;x<20;x++) {
long start = System.currentTimeMillis();
for (int y = 0; y < 50; y++) {
log.info(“Info Message!”);
}
long time = System.currentTimeMillis() - start;
System.out.println(time);
}
}
}
复制代码
例子1:当ImmediateFlush=true时,测试结果(单位毫秒):