Log4J 自己用手册

来源:互联网 发布:mysql图形化工具 编辑:程序博客网 时间:2024/04/28 13:39

1. Introduction
 在开发的过程当中,我们经常会花很多时间去重现一个系统错误,来定位问题的根源。但往往是由于没有足够的信息,导致我们要花上很多时间去定位问题。所以,日志记录就显得异常的重要的,它记录在程序运行当中,对用户有用的信息,以方便问题的跟踪和修复。
 最简单的日志就是System.out.println(),相信大家一定写过System.out.println(“Hello World”),  几乎学习所有编程语言的第一个程序就是输出“Hello World”。但这种写法也问题多多,首先这种一般只输出在控制台,第二是维护这种语句的代价太沉重了,最后是组装输出的信息很麻烦。
 于是Log4J也就应运而生,它实现一整套记录日志的API,还可以在配置文件定义日志的级别,如何记录等等。

2. Log4J Architecture
 Log4J 的三驾马车分别是Logger, Appender, Layout. 这三驾马车套在一起,组成了整个日志记录框架。
Logger 是日志记录框架的core member, 它负责获取Logger 实例,根据用户定义记录日志的级别、日志的内容。
Appender将Logger收集到的信息输出到相应的输出源,这些输出源包括有控制台、文件、邮件、Socket等等。
Layout则负责将Appender输出的一堆信息,根据用户预先的全局定义,进行格式化操作,让杂乱的信息变得有序。
从网上找到下面这个图,介绍了Log4J的类关系。

 

3. Logger
 相比System.out.println,  Log4J最大的优点是:在某些日志语句被禁止之后,其它日志语句仍然能正常工作,这给用户带来了极大的灵活性。比如在开发阶段,为了方便调试,我们希望所有的日志信息都打印出来;但在production environment,  我们不希望也不应该将所有的日志信息都打印出来,这样服务器空间成本太大了,性能也不好,所有我们让Log4J 只记录警告或者异常的信息。

3.1 Logger 层次结构
 Logger是一个实体类, 它的名字是大小写敏感的。Logger的命名是按照继承规则来的:父类跟子类之间加一个 “.”。 比如类com.company 是类com.company.development的父辈。
 在Log4J有一个根类rootLogger, 之前是rootCategary。这是一个Final类,它的level是DEBUG,在程序加载Log4J的时候,rootLogger就已经被实例化,并且跟程序“共存亡“,所以无论在程序的何时何地,可以通过Logger.getRootLogger 取到这个对象。但不能给该对象重置Level。
 除了rootLogger之外,其它的Logger对象都可以通过Logger.getLogger 方法取到,如果该对象不存在,Logger会自动实例化一个;如果该对象存在,则直接调用。

3.2 Level
 前面说到拿到Logger的对象,接着就将信息记录起来,Logger继承了Category类,所以也就提供了debug(), info(), warn(), error(), fatal() 和log() 方法。除了log(),每个方法都对应一个输出级别,他们分别是 DEBUG, INFO, WARN, ERROR, FATAL, 其实log() 也可以说有对应的级别ALL。这些级别的排序是ALL<DEBUG<INFO<WARN<ERROR<FATAL<OFF. 

3.2.1 定义消息级别
那如何给消息定义级别呢?当程序中需要记录日志时,我们调用Logger中对应级别的方法记录日志,这时也就定义了相应的级别。比如:Logger.getLogger(“com.hello”).warn(“something wrong here”), 这时我们就给类com.hello的信息 ”something wrong here” 定义了WARN的级别。

3.2.2 Logger-Level Filter
定义这些级别有啥子用叻? Log4J 有一个叫 Logger-Level filter(日志级别过滤)。系统中每一个日志请求都会传给它来进行过滤,当它发现该请求的级别高于或等于系统定义的日志级别,该请求将被接受;如果该请求的级别低于系统定义的级别,则该请求会被丢掉。比如前面的日志请求,如何系统的日志级别是ALL\DEBUG\INFO\WARN,那么这个请求将会被写进log;如果系统的日志级别是ERROR\FATAL\OFF, 则该请求不会写进log。
前面说到Logger是名字继承的,有父类子类之分,他们之间的级别也是有继承的,继承的规则是:如果logger本身有被分配到级别,那就不存在也不需要继承;如果logger没有被主动分配到级别,则logger会从它第一个拥有非空级别的父辈那里继承级别,一直追溯到rootLogger。
 
4. Appender
 除了支持级别过滤,Log4J还允许一个日志请求打印到多个输出源,每一个输出源叫做一个Appender。Log4J 实现的输出源有console(控制台)、files(文件)、Swing components(图形界面)、 remote socket servers (远程套接字服务), JMS(Java 消息系统), NT Event Loggers, remote UNIX Syslog daemons( 远程UNIX后台服务进程)。
 前面说到Level是有继承的,Appender同样也是有继承的。Level是可以继承的,但一个Logger只能有一个Level,而一个Logger可以有多个Appender。当然,Logger也可以设置不从父类继承Appender,只使用自己定义的Appender,这就是Appender Additively。当一个Logger L的Additively=false, 它的子类Logger C将只能继承到L的Appender,而不能再追溯到往上的父类的Appender。
 Log4J提供的Appender有
 org.apache.log4j.ConsoleAppender ( 将日志打印到控制台)
 org.apache.log4j.FileAppender (将日志写到指定的文件)
 org.apache.log4j.DailyRollingFileAppender (每天产生一个日志文件)
 org.apache.log4j.RollingFileAppender(文件达到一定大小自动产生新的文件
 org.apache.log4j.WriterAppender( 将日志以流格式发送到任何指定的地方)

5. Layout
 一般情况下,我们除了定义自己的输出源,还需要定义输出的格式。Layout为了满足developer的愿望,实现了Layout。每一个Appender都可以设置自己的Layout。最常用的Layout是PatternLayout, 这个有点像C语言的printf 函数。比如,我们给com.hello Logger 配置了这个一个PatternLayout “% r [%t] %-5p %c - %m %n“, 则输出可能是这样
 168 [main] WARN com.hello – something wrong here
1) 168 表示从程序开始到现在的毫秒数
2)[main] :发出log请求的线程名
3)WARN:日志级别
4)com.hello: 日志名
5)something wrong here: 发送到logger的信息

 Log4J 提供的Logout有一下几种:
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

各种打印参数的意义如下:
%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
%r 输出自应用启动到输出该log信息耗费的毫秒数
%c 输出所属的类目,通常就是所在类的全名
%t 输出产生该日志事件的线程名
%n 输出一个回车换行符,Windows平台为"\r\n",Unix平台为"\n"
%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS}
%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。


6. Log4J 一瞥
 前面介绍了Log4J 的三驾马车,接着看看当调用这句Logger.getLogger(“com.hello”).warn(“something wrong here”), Log4J究竟做了什么事。
1)日志记录级别检查
     Logger首先看看“com.hello“是否存在,如不存在,则创建一个名为”com.hello”的Logger实例。然后Logger检查LoggerRepository是否已经开启了WARN级别,如果没有,则丢掉这个请求。( 在warn()方法中进行)

2)进行日志级别过滤
     检查WARN级别是否高于或等于该Logger ”com.hello” 的级别,如果WARN比Logger低,则不记录。( 在warn()方法中进行)

3)创建LoggingEvent 对象
如果前面的验证均通过,则将传递Message等信息去创建一个LoggingEvent.
4) 调用Appender
在创建完LoggingEvent对象之后,Log4J 开始调用Appender。首先遍历该Logger的AppenderList,逐个调用doAppend(). 实际上这里的Appender就是AppenderSkeleton,由它来决定调用哪个Appender进行输出。

5) 格式化LoggingEvent
Appender 将调用Layout的format方法对LoggingEvent进行格式化,返回字符串。
6) 送出LoggingEvent
把Layout返回的字符串送到相应的Appender进行输出。


7. Configuration
 Logger的配置有两种方式,第一在程序中调用:如果只是想在控制台看到消息,那就调用BasicConfigurator.configure即可完成简单的配置,如果想调用外部的配置文件,则可以调用PropertyConfigurator.configure( file),把外部的配置文件读进来; 第二种是直接使用外部的配置文件,Log4J支持XML和Properties格式。这里介绍Properties的配置。

#设置rootLogger的级别为DEBUG,输出源有两个stdout和R
log4j.rootLogger=debug, stdout, R

#设置Appender stdout的行为由ConsoleAppender来完成
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
#设置stdout的Layout是PatternLayout
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 设置输出文件名和行数
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) -
%m%n

#设置Appender R是当文件达到一定大小之后,产生一个新的文件
log4j.appender.R=org.apache.log4j.RollingFileAppender
#设置R的输出文件名
log4j.appender.R.File=example.log
#设置当日志文件达到100KB之后,产生一个新的文件
log4j.appender.R.MaxFileSize=100KB
# 设置最多只能有一个备份文件
log4j.appender.R.MaxBackupIndex=1
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n

#重置日志名com的级别为INFO
log4j.logger.com = INFO
#重置日志名com.hello的级别为WARN
log4j.logger.com.hello = WARN

 不管配置文件是XML或者Properties,该文件一定要跟log4j.jar 放在同一个目录底下。如果是Web应用的话,最简单的做法就是将两者一起放在WEB-INF/classes底下,这样当Container启动的时候,就会直接加载log4j,这时就需要在当前目录底下读取配置文件。

 

8. Log4J默认初始化过程
 前面说到当Application启动的时候会将log4j classes加载进内存,通过源代码我们可以看到有一个叫LogManager 的类,里面有一个static的初始化块。也就是说当Application启动的时候,在任何logger调用之前,log4j已经进行了相应的初始化操作。

8.1 默认的初始化流程
1)如果系统属性log4j.defaultInitOverride被设置为false,则直接跳过初始化过程
2)系统属性log4j.configuration定义了配置资源,这个值可以是一个URL,也可以是一个文件
3)  如果log4j.configuration没有被定义,则系统会根据如下的算法搜索log4j.xml。首先系统会在线程的上下文搜索该文件,如果搜不到,则尝试用加载log4j.jar包的加载器去加载该文件,如果还是失败的话,则尝试用ClassLoader.getSystemResource去加载文件。
4)如果加载不到log4j.xml文件,则系统会尝试用相同的算法去加载log4j.properties文件。如果这个文件也加载不到的话,则初始化动作到此结束。

8.2 初始化设置
如果配置文件加载成功,则开始如下的初始化设置
1) 首先读取log4j.debug的值,并设置debugenable.
2) 读取并设置以log4j.threshold开头的值
3) 读取以log4j.rootLogger开头的值,如果找不到,则读取以log4j.rootCategory的值(这里把读到的值命名为”rootLogger”), 然后解析rootLogger的值, 把第一个逗号前面的值设置为 Level,  后面的作为Appender。以前面的配置为例,把debug作为Level,后面是两个Appender: stdout 和 R。
4) 比如解析Appender R。 首先加载该Appender对应的类,创建并设置Appender的Layout,再把相应的Properties设置到Appender里面。
5)接下来读取并初始化以log4j.logger开头的值,并将对应的属性设置到相应的Logger。

9. 配置文件范例(来自网络)
 下面这个配置文件是来自网络的,里面设置实现了输出到控制台、文件、回滚文件、发送日志邮件、输出到数据库日志表、自定义标签等


og4j.rootLogger=DEBUG,CONSOLE,A1,im
log4j.addivity.org.apache=true
# 应用于控制台
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.Threshold=DEBUG
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.Encoding=GBK
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
#log4j.appender.CONSOLE.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD] n%c[CATEGORY]%n%m[MESSAGE]%n%n
#应用于文件
log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=file.log
log4j.appender.FILE.Append=false
log4j.appender.FILE.Encoding=GBK
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
# Use this layout for LogFactor 5 analysis
# 应用于文件回滚
log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender
log4j.appender.ROLLING_FILE.Threshold=ERROR
log4j.appender.ROLLING_FILE.File=rolling.log
log4j.appender.ROLLING_FILE.Append=true
log4j.appender.CONSOLE_FILE.Encoding=GBK
log4j.appender.ROLLING_FILE.MaxFileSize=10KB
log4j.appender.ROLLING_FILE.MaxBackupIndex=1
log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
#应用于socket
log4j.appender.SOCKET=org.apache.log4j.RollingFileAppender
log4j.appender.SOCKET.RemoteHost=localhost
log4j.appender.SOCKET.Port=5001
log4j.appender.SOCKET.LocationInfo=true
# Set up for Log Facter 5
log4j.appender.SOCKET.layout=org.apache.log4j.PatternLayout
log4j.appender.SOCET.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]%n%c[CATEGORY]%n%m[MESSAGE]%n%n
# Log Factor 5 Appender
log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender
log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000
# 发送日志给邮件
log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender
log4j.appender.MAIL.Threshold=FATAL
log4j.appender.MAIL.BufferSize=10
log4j.appender.MAIL.From=web@www.wuset.com
log4j.appender.MAIL.SMTPHost=www.wusetu.com
log4j.appender.MAIL.Subject=Log4J Message
log4j.appender.MAIL.To=web@www.wusetu.com
log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout
log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
# 用于数据库
log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/test
log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver
log4j.appender.DATABASE.user=root
log4j.appender.DATABASE.password=
log4j.appender.DATABASE.sql=INSERT INTO LOG4J (Message) VALUES ('[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n')
log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout
log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
# 每天新建日志
log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A1.File=log
log4j.appender.A1.Encoding=GBK
log4j.appender.A1.DatePattern='.'yyyy-MM-dd
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L : %m%n
#自定义Appender
log4j.appender.im = net.cybercorlin.util.logger.appender.IMAppender
log4j.appender.im.host = mail.cybercorlin.net
log4j.appender.im.username = username
log4j.appender.im.password = password
log4j.appender.im.recipient = corlin@cybercorlin.net
log4j.appender.im.layout=org.apache.log4j.PatternLayout
log4j.appender.im.layout.ConversionPattern =[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
原创粉丝点击