`
cuker919
  • 浏览: 88384 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Log4J diy手册

 
阅读更多

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

构成

记录器(Loggers):用来记录日志,相当于日志记录对象,这个在配置文件中可能配置多个,用来对不同的目录进行分别记录

根记录器:

Java代码 复制代码
  1. log4j.rootLogger=[level],appenderName,appenderName,…

在早期的Log4j版本中,org.apache.Category实现了记录器的功能,为了提高向后兼容 性,Logger扩展了Category,因此rootCategory和rootLogger是可以互换的,但最后Category将从类库中删除,因 此请使用Logger类。 level级别: 分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定义的级别。Log4j建议只使用四个级别:ERROR、WARN、INFO、DEBUG:

自定义记录器:

这个东西一般在一个较大的项目中会存在多个,以目录为区分.语法为:

Java代码 复制代码
  1. log4j.logger.loggerName=[level],appenderName,appenderName,…

例如:

Java代码 复制代码
  1. log4j.logger.com.projectx.conf=info,stdout,logfile
  2. log4j.logger.com.projectx.platform=info,stdout,logfile
  3. log4j.logger.com.projectx.modules=warn,stdout,logfile
  4. log4j.loggerojectx.collie=debug,stdout,logfile
  5. log4j.logger.com.projectx.mq=debug,stdout,logfile
  6. log4j.logger.com.projectx.jmx=warn,stdout,logfile

输出源(Appenders):这个用来存放日志信息,可以是控制台,也可以是文件,有如下几种:

Java代码 复制代码
  1. org.apache.log4j.ConsoleAppender(控制台)
  2. org.apache.log4j.FileAppender(文件)
  3. org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
  4. org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
  5. org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
  6. org.apache.log4j.SocketAppender(Socket)
  7. org.apache.log4j.NtEventLogAppender(NT的EventLog)
  8. org.apache.log4j.JMSAppender(电子邮件)

输出源可以进行参数配置:(具体需要哪些参数可以参见源代码)

Java代码 复制代码
  1. log4j.appender.appenderName=fully.qualified.name.of.appender.classs
  2. log4j.appender.appenderName.option1=value1
  3. log4j.appender.appenderName.option=valueN

布局(Layouts):用来定义输入日志格式,有以下几种:

Java代码 复制代码
  1. org.apache.log4j.HTMLLayout(以HTML表格形式布局)
  2. org.apache.log4j.PatternLayout(可以灵活地指定布局模式)
  3. org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串)
  4. org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息

PatternLayout

Java代码 复制代码
  1. 这个比较长特殊,各种参数定义如下:
  2. %m输出代码中指定的消息
  3. %p输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
  4. %r输出自应用启动到输出该log信息耗费的毫秒数
  5. %c输出所属的类目,通常就是所在类的全名
  6. %t输出产生该日志事件的线程名
  7. %n输出一个回车换行符,Windows平台为“/r/n”,Unix平台为“/n”
  8. %d输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyMMMddHH:mm:ss,SSS},输出类似:20021018221028921
  9. %l输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)

配置文件详解

//根记录器

Log4j.properties代码 复制代码
  1. log4j.rootCategory=debug//全局记录器,注:这里没要配置输入源,因为下面每一个记录器都配置了输出源,记录器与记录器之间是继承关系,子类会覆盖掉父类的输出源(如果子类指定了,父类就没办法统一控制了),但输出的level级别是可以统一控制的,子类的级别如果高于父类则用子类,否则使用父类级别,例如:root级别为info,则子类为debug的时候日志也只输出info级别的,但子类如果是warn级别,则info不输出
  2. //输出源1:控制台
  3. log4j.appender.stdout=org.apache.log4j.ConsoleAppender//输出到控制台
  4. log4j.appender.stdout.layout=org.apache.log4j.PatternLayout//自定义布局
  5. log4j.appender.stdout.layout.ConversionPattern=[%d]-%p-[%F:%L]-->>%m%n
  6. //输出源2:日志文件
  7. log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
  8. log4j.appender.logfile.File=../logs/log4j.out//这里使用相对路径,便于不同平台部署
  9. log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
  10. log4j.appender.logfile.layout.ConversionPattern=[%d]-%p-[%F:%L]-->>%m%n
  11. //自定义记录器:
  12. log4j.logger.com.projectx.conf=info,stdout,logfile//这里都指定了输出源,如果不指定,则走root配置,如果root也没指定的话,这个我也不知道,没实验过,猜想应该没有日志输出吧
  13. log4j.logger.com.projectx.platform=info,stdout,logfile
  14. log4j.logger.com.projectx.modules=warn,stdout,logfile
  15. log4j.logger.com.projectx.collie=debug,stdout,logfile
  16. log4j.logger.com.projectx.mq=debug,stdout,logfile
  17. log4j.logger.com.projectx.jmx=warn,stdout,logfile
  18. //自定义框架记录器
  19. log4j.logger.org.hibernate=warn,stdout
  20. log4j.logger.org.directwebremoting=warn,stdout
  21. log4j.logger.org.springframework=WARN,stdout
  22. log4j.logger.uk.ltd.getahead.dwr=WARN,stdout
  23. log4j.logger.net.sf.ehcache=WARN,stdout
  24. log4j.logger.org.apache.commons=WARN,stdout
  25. log4j.logger.org.apache.struts=WARN,stdout
  26. log4j.logger.com.opensymphony.xwork2=WARN,stdout
  27. log4j.logger.org.apache.struts2=WARN,stdout
  28. log4j.logger.org.acegisecurity=WARN,stdout
  29. log4j.logger.freemarker=WARN,stdout
  30. log4j.logger.org.apache.activemq=WARN,stdout
  31. log4j.logger.org.quartz=WARN,stdout

代码中使用

代码中使用很简单,如下:

Java代码 复制代码
  1. privatestaticLoggerlog=Logger.getLogger(XXXXXXXXX.class)
  2. log.info("");
  3. log.debug("");

总结

总的来说,log4j大体上就包含这么多东西。这里我只是做个总结来给自己备忘,一般项目开发过程中尽量不要使用system.out.priintln(),因为这种打印出来的日志不可控。一直都不能静下心来好好学点东西,现在总算是学了这么一点点小东西。整个log4j配置起来不复杂,但它这种实现思想很值得借鉴

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics