Log4jjava |
全面详解数据库 |
香山風乘(wjiang1)apache 2012/9/12服务器 |
声 明app
本文是针对在工做中,学习过程当中遇到的问题和解决方案作的总结,以供有须要的同事和朋友提供参考,在本文中可能有阐述不全面和观点有误的地方,敬请读者谅解和支持。框架
如您对本文持有不一样的意见和看法请联系以下邮件:jsp
对于使用本文中的解决方法形成的损失做者概不负责。工具
Log4j是Apache的一个开放源代码项目,经过使用Log4j,咱们能够控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;咱们也能够控制每一条日志的输出格式;经过定义每一条日志信息的级别,咱们可以更加细致地控制日志的生成过程。这些能够经过一个配置文件来灵活地进行配置,而不须要修改应用的代码。
优势:首先,它能精确地提供运行时的上下文(context)。一旦在程序中加入了Log 代码,它就能自动的生成并输出logging信息而不须要人为的干预。另外,log信息的输出能够被保存到一个固定的地方,以备之后研究。除了在开发过程当中发挥它的做用外,一个性能丰富的日志记录软件包能看成一个审计工具(audit tool)使用。
缺点:Logging确实也有它的缺陷。它下降了程序运行的速度。它太冗长,查看时很容易错过。为了减小这些负面影响,log4j 被设计得可靠,高效和灵活。由于,记录日志不多是一个应用程序的主要焦点,log4j API 尽可能作到容易被理解和使用。
任何logging API 与简单的System.out.println输出调试信息方法比较,最主要的优势在于它可以关闭一些调试信息输出而不影响其余人的调试。这种能力的实现是假设这些logging空间,也就是全部的可能发生的日志说明空间,能够根据程序开发人员选择的标准进行分类。这一观察之前使得咱们选择了category做为这个软件包的中心概念。可是,在log4j 1.2版本之后,Logger类取代了Category类。对于那些熟悉早先版本的log4j的开发人员来讲,Logger类只不过是Category类的一个别名。
Logger实际一个类Logger是被命名的实体。Logger的名字大小写有区别(case-sensitive),而且它们遵照阶层式的命名规则: Named Hierarchy
若是一个logger 的名字后面跟着一个点号(dot),它就是点号(dot)后面的那个logger的前辈( ancestor),是这个晚辈(descendant) 的前缀。若是在它本身和这个晚辈之间没有其它的前辈,它和这个晚辈之间就是父子关系。
|
例如,叫作"com.foo"的logger是叫作 "com.foo.Bar"的logger的父辈 。一样地,"java"是"java.util" 的父辈,是"java.util.Vector"的前辈。大多数开发人员都熟悉这种命名方法。"com.foo" "com.foo.Bar" "java" "java.util" "java.util.Vector"
根(root)logger 位于logger 阶层的最上层。它在两个方面很特别:
它老是存在的
不能经过使用它的名字直接获得它
经过这个类的静态方法Logger.getRootLogger获得它(指RootLogger)。全部其余的loggers是经过静态方法Logger.getLogger来实例化并获取的。这个方法Logger.getLogger把所想要的logger的名字做为参数。
log4j实现的原理猜想是经过抛出有一个异常,获取执行的类名和包名,方法名,行号 例如: StackTraceElement st =new Throwable.getStackTrace();
Log4j由三个重要的组件构成:日志信息的优先级,日志信息的输出目的地,日志信息的输出格式。
从上倒下级别依次下降:
OFF(关闭)
FATAL(严重错误)
ERROR(错误)
WARN(警告)
INFO(通常信息)
DEBUG(调试)
TRACE(跟踪)
ALL(所有)
也能够经过Level类的子类去定义你本身的优先级别,对于一个给定的logger C,它继承的级别等于logger阶层里,从C开始往root logger上去的第一个non-null级别。要保证全部的loggers最终都继承一个优先级别,root logger老是有一个被指派的优先级。
下面是具备各类指派优先级别值的四个表格,以及根据上面的规则所得出的继承优先级别。
例1:
Logger |
Assigned |
Inherited |
root |
Proot |
Proot |
X |
none |
Proot |
X.Y |
none |
Proot |
X.Y.Z |
none |
Proot |
在上面的示例1中,只有root logger被指派了级别。这个级别的值,Proot,被其它的loggers X, X.Y 和 X.Y.Z继承了。
例2:
Logger |
Assigned |
Inherited |
root |
Proot |
Proot |
X |
Px |
Px |
X.Y |
Pxy |
Pxy |
X.Y.Z |
Pxyz |
Pxyz |
在上面的示例2中,全部的loggers都有一个指派的级别值。不须要级别继承。
例3:
Logger |
Assigned |
Inherited |
root |
Proot |
Proot |
X |
Px |
Px |
X.Y |
none |
Px |
X.Y.Z |
Pxyz |
Pxyz |
在示例3中,loggers root, X 和 X.Y.Z 分别被指派级别值Proot, Px 和Pxyz。Logger X.Y 从它的父辈X那里继承它的级别值。
例4:
Logger |
Assigned |
Inherited |
root |
Proot |
Proot |
X |
Px |
Px |
X.Y |
none |
Px |
X.Y.Z |
none |
Px |
Example 4 |
在示例4中,loggers root和X 分别被指派级别值Proot和Px。Logger X.Y和X.Y.Z继承它们最接近的父辈X的被指派的级别值。
org.apache.log4j.ConsoleAppender(控制台),org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(天天产生一个日志文件),
org.apache.log4j.RollingFileAppender(文件到达指定大小的时候产生新文件),
org.apache.log4j.WriterAppender(将日志信息以流格式发送)
org.apache.log4j.lf5.LF5Appender(日志查看工具)
org.apache.log4j.jdbc.JDBCAppender(日志数据库)
更多输出目地请参考API
1) ConsoleAppender 选项
Threshold=WARN:指定日志消息的输出最低层次。
ImmediateFlush=true:默认值是 true,意谓着全部的消息都会被立即输出。 Target=System.err:默认情况下是:
System.out,指定输出控制台
2) FileAppender 选项
Threshold=WARN:指定日志消息的输出最低层次。
ImmediateFlush=true:默认值是 true,意谓着全部的消息都会被立即输出。 File=mylog.txt:指定消息输出到 mylog.txt 文件。
Append=false:默认值是 true,即将消息增加到指定文件中,false 指将消息覆盖指定的文件内容。
3) DailyRollingFileAppender 选项
Threshold=WARN:指定日志消息的输出最低层次。
ImmediateFlush=true:默认值是 true,意谓着全部的消息都会被立即输出。File=mylog.txt:指定消息输出到 mylog.txt 文件。
Append=false:默认值是 true,即将消息增加到指定文件中,false 指将消息覆盖指定的文件内容。
DatePattern=''.''yyyy-ww:每周滚动一次文件,即每周产生一个新的文件。固然也可以指定按月、周、天、时和分。即对应的格式如下:
''.''yyyy-MM: 每个月
''.''yyyy-ww: 每周
''.''yyyy-MM-dd: 天天
''.''yyyy-MM-dd-a: 每天两次
''.''yyyy-MM-dd-HH: 每小时
''.''yyyy-MM-dd-HH-mm: 每分钟
4) RollingFileAppender 选项
Threshold=WARN:指定日志消息的输出最低层次。
ImmediateFlush=true:默认值是 true,意谓着全部的消息都会被立即输出。File=mylog.txt:指定消息输出到 mylog.txt 文件。
Append=false:默认值是 true,即将消息增加到指定文件中,false 指将消息覆盖指定的文件内容。
MaxFileSize=100KB: 后缀可以是 KB, MB 或者是 GB. 在日志文件到达该大小时,将会自动滚动,即将原来的内容移到 mylog.log.1 文件。
MaxBackupIndex=2:指定可以产生的滚动文件的最大数。
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(能够灵活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
1) HTMLLayout 选项
LocationInfo=true:默认值是 false,输出 java 文件名称和行号
Title=my app file: 默认值是 Log4J Log Messages.
2) PatternLayout 选项
ConversionPattern=%m%n:指定怎样格式化指定的消息。
下面是经常使用的格式符号:
%m 输出代码中指定的消息
%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},输出相似:2002年10月18日22:10:28,921
%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。
能够在%与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。如:
l %20c:指定输出category的名称,最小的宽度是20,若是category的名称小于20的话,默认的状况下右对齐。
l %-20c:指定输出category的名称,最小的宽度是20,若是category的名称小于20的话,”-”号指定左对齐。
l %.30c:指定输出category的名称,最大的宽度是30,若是category的名称大于30的话,就会将左边多出的字符截掉,但小于30的话也不会有空格。
l %20.30c:若是category的名称小于20就补空格,而且右对齐,若是其名称长于30字符,就从左边交远销出的字符截掉。
3) XMLLayout 选项
LocationInfo=true:默认值是 false,输出 java 文件和行号
Log4j支持两种配置文件格式,一种是XML格式的文件,一种是properties格式的文件。在没有配置文件的状况下在获取logger以前使用以下方法自动快速地使用缺省 Log4j 环境。
BasicConfigurator.configure ();
咱们常常会遇到修改log4j配置的状况,在这种状况下,频繁重启应用显然是不可接受的。幸亏log4j提供了自动从新加载配置文件的能力,在配置文件修改后,便会本身从新加载配置。在1.2及之前的版本中DOMConfigurator和PropertyConfigurator都提供了configureAndWatch方法,对指定的配置文件进行监控,而且能够设置检查的间隔时间。
若是想了解log4j的加载过程,能够设置系统属性"log4j.debug", "true",便是系统classpath属性,也能够代码实现将属性加载到系统属性中,以下:
System.setProperty("log4j.debug", "true");
配置文件位置:在项目的src目录下,在获取logger时会自动寻找到,自定义配置文件位置,在获取logger以前使用以下任意一个方法:
PropertyConfigurator.configure(java.net.URL configURL);
PropertyConfigurator.configure(InputStream inputStream);
PropertyConfigurator.configure(Properties properties);
PropertyConfigurator.configure(String configFilename);
示例:
log4j.rootLogger=error,javass.Console,javass.File
#设置根节点的输出级别,输出目的地
log4j.logger.testClient.TestLog4j=ERROR,CONSOLE
#设置子代的输出级别,输出目的地
log4j.appender.javass.Console=org.apache.log4j.ConsoleAppender
#目的地的处理类、Consloe 只是一个名称
log4j.appender.javass.Console.layout=org.apache.log4j.PatternLayout
#输出格式,选择不一样的layout有不一样的属性,参照前面的内容
log4j.appender.javass.Console.layout.ConversionPattern=%d{HH:mm:ss,SSS}%5p (%C{1}:%M) - %m%n
#输出格式的定义
#下面是一样的定义
log4j.appender.javass.File=org.apache.log4j.DailyRollingFileAppender
log4j.appender.javass.File.file=javass.log
log4j.appender.javass.File.DatePattern=.yyyy-MM-dd
log4j.appender.javass.File.layout=org.apache.log4j.PatternLayout
log4j.appender.javass.File.layout.ConversionPattern=%d{HH:mm:ss,SSS} %5p(%C{1}:%M) - %m%n
代码示例解释以下附件
配置文件位置:在项目的src目录下,在获取logger时会自动寻找到
自定义配置文件位置,在获取logger以前使用以下任意一个方法
DOMConfigurator.configure(URL rul);
DOMConfigurator.configure(String filename);
校验文件:org/apache/log4j/xml/log4j.dtd
在拥有xml和Properties时,会优先查找xml文件
xml declaration and dtd
|
log4j:configuration
|
+-- appender (name, class)
| |
| +-- param (name, value)
| +-- layout (class)
| |
| +-- param (name, value)
+-- logger (name, additivity)
| |
| +-- level (class, value)
| | |
| | +-- param (name, value)
| +-- appender-ref (ref)
+-- root
|
+-- param (name, class)
+-- level
| |
| +-- param (name, value)
+-- appender-ref (ref)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
<appender name="test" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%d{dd HH:mm:ss,SSS\} %-5p] [%t] %c{2\} - %m%n" />
</layout>
</appender>
<appender name="file" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="E:/test.log" />
<param name="DatePattern" value="'.'yyyy-MM-dd'.log'" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%d{MMdd HH:mm:ss SSS\} %-5p] [%t] %c{3\} - %m%n" />
</layout>
</appender>
<logger name="cn.javass" additivity="true"><!—true是添加appender-->
<level value="debug" /><!—设置子代looger的输出级别-->
<appender-ref ref="file" /><!—设置子代logger的输出目的地-->
</logger>
<root>
<level value="error"/>
<appender-ref ref="test" />
<appender-ref ref="file" />
</root>
</log4j:configuration>
在log4j.properties里,控制级别的时候,只能打印出大于指定级别的全部信
息;可是在log4j.xml中能够经过filter来完成过滤:典型的引用是只打印出某
种级别的信息。
<appender name="log.file"
class="org.apache.log4j.DailyRollingFileAppender">
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="levelMin" value="info" />
<param name="levelMax" value="info" />
<param name="AcceptOnMatch" value="true" />
</filter>
</appender>
一个常常提出的争议就是logging的运算开销。这种关注是有道理的,由于即使是一个中等大小的应用程序至少也会产生几千个log输出。许多工做都花费在测量和改进logging性能上。Log4j声明它是快速和灵活的:速度第一,灵活性第二。
咱们在看一些成熟框架的源代码中,常常看到以下代码:
if (logger.isDebugEnabled()){
logger.debug(“debug:“+name);
}
为何不是直接logger.debug(“debug:“+name);呢?在配置文件中虽然可使用控制级别为比debug级别更高的级别,而不输出debug信息;可是,这里的字符串链接操做仍然会影响运行效率;若是先判断当前logger的级别,若是级别不合适的话,连这句字符串链接均可以不作了。
NDC(Nested Diagnostic Context)和MDC(Mapped Diagnostic Context)是log4j种很是有用的两个类,它们用于存储应用程序的上下文信息(context infomation),从而便于在log中使用这些上下文信息。
NDC采用了一个相似栈的机制来push和pop上下文信息,每个线程都独立地储存上下文信息。好比说一个servlet就能够针对每个request建立对应的NDC,储存客户端地址等等信息。
当使用的时候,咱们要尽量确保在进入一个context的时候,把相关的信息使用NDC.push(message);在离开这个context的时候使用NDC.pop()将信息删除。另外因为设计上的一些问题,还须要保证在当前thread结束的时候使用NDC.remove()清除内存,不然会产生内存泄漏的问题。
存储了上下文信息以后,咱们就能够在log的时候将信息输出。在相应的PatternLayout中使用”%x”来输出存储的上下文信息,下面是一个PatternLayout的例子:
%r [%t] %-5p %c{2} %x - %m%n
使用NDC最重要的好处就是,当咱们想输出一些上下文的信息的时候,不须要让logger去寻找这些信息,而只须要在适当的位置进行存储,而后再配置文件中修改PatternLayout。在最新的log4j 1.3版本中增长了一个org.apache.log4j.filters.NDCMatchFilter,用来
根据NDC中存储的信息接受或拒绝一条log信息。
MDC和NDC很是类似,所不一样的是MDC内部使用了相似map的机制来存储信息,上下文信息也是每一个线程独立地储存,所不一样的是信息都是以它们的key值存储在”map”中。相对应的方法,MDC.put(key, value); MDC.remove(key); MDC.get(key); 在配置PatternLayout的时候使用:%x{key}来输出对应的value。一样地,MDC也有一个org.apache.log4j.filters.MDCMatchFilter。这里须要注意的一点,MDC是线程独立的,可是一个子线程会自动得到一个父线程MDC的copy。
至于选择NDC仍是MDC要看须要存储的上下文信息是堆栈式的仍是key/value形式的。
之前一直使用文本工具查看日志文件,如今才发现那是多么浪费时间的一件事情,将日志输出目的地设置为:
log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender
log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000
将日志输出到日志工具LogFactort5