Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输出地;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。那么是不是这样,我们就可以完全使用log4j,而不需要扩展定制了呢?

1、基本介绍Log4j

  Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输出地;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

  log4j的好处在于:

  1) 通过修改配置文件,就可以决定log信息的目的地——控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等

  2) 通过修改配置文件,可以定义每一条日志信息的级别,从而控制是否输出。在系统开发阶段可以打印详细的log信息以跟踪系统运行情况,而在系统稳定后可以关闭log输出,从而在能跟踪系统运行情况的同时,又减少了垃圾代码(System.out.println(......)等)。

  3) 使用log4j,需要整个系统有一个统一的log机制,有利于系统的规划。

  那么是不是这样,我们就可以完全使用log4j,而不需要扩展定制了呢?当然不是这样,因为每个项目的需求不一样,而且log4j本身也提供了灵活的扩展机制。下面我们说说log4j常用的扩展方式。

2、扩展点Log4j

2.1 自己的日志系统

  每一个项目都想有自己的一套日志系统,而不受其他项目、jar包的影响。所以日志系统需要独立存在,且有相应的自己单独的配置文件而不受影响。我们先来看看common-logging和log4j的默认工作流程:

  common-logging

  当我们用Log log = LogFactory.getLog(“loggerName”);取得log时,让我们看看他是如何工作的?
  1)、首先在classpath下寻找自己的配置文件commons-logging.properties,如果找到,则使用其中定义的Log实现类
  2)、如果找不到commons-logging.properties文件,则在查找是否已定义系统环境变量org.apache.commons.logging.Log,找到则使用其定义的Log实现类
  3)、查看classpath中是否有Log4j的包,如果发现,则自动使用Log4j作为日志实现类
  4)、使用JDK自身的日志实现类(JDK1.4以后才有日志实现类)
  5)、使用commons-logging自己提供的一个简单的日志实现类SimpleLog

  commons-logging总是能找到一个日志实现类,并且尽可能找到一个"最合适"的日志实现类.
  .可以不需要配置文件
    .自动判断有没有Log4j包,有则自动使用之
    .最悲观的情况下也总能保证提供一个日志实现(SimpleLog)

  log4j

  当我们用Logger.getLogger(loggerName);取得log时,让我们看看他是如何工作的?
  1)、查找是否已定义系统环境变量log4j.configuration,找到则使用其定义的Log配置文件;否则搜索log4j.xml,如果存在,则执行4)
  2)、如果不存在,搜索log4j.properties
  3)、如果不存在,则使用程序默认的日志仓库
  4)、如果存在,注册配置日志仓库
  5)、去日志仓库中查询,如果不存在,则new一个
 
  从上面我们可以看到无论是common-logging还是log4j,以及其他的日志开源系统都会提供一套默认的遍历规则,去搜索他的log记录实例。一般情况下,我们都会使用它的默认规则,但是这样的话,我们就会受制于它,比如如果有人在默认规则中改变了某一个条件。
如果我们不想使用它的默认规则,该怎么办?很简单,在log4j中是通过日志仓库来维护一套独立日志系统,只要我们直接使用它的日志仓库,写一个我们自己的日志工厂就可以了。具体如下:

Public final class CustomLogFactory {
 //日志仓库实例
 Static LoggerRepository h = new Hierarchy(new RootLogger(Level.DEBUG));

 Static {
  //配置文件定制日志仓库属性,这里面你可以做更多的文章
  //来定义自己的配置文件位置等等
  new PropertyConfigurator().configure(h, urlConfigFile);
 }

 public static Logger getLogger(String loggerName) {
  return h.getLogger(loggerName);
 }
}

  这样你的日志系统完全独立了。是不是很容易?

2.2 自己的日志记录器

  Log4j的logger本身提供八种级别的日志记录,如果你想让你的logger没有级别概念,或者让日志信息支持国际化,那该怎么办?

  Log4j用于第三方扩展的记录日志方法API

public boolean isEnabledFor(Priority level);
public void log(String callerFQCN, Priority level, Object message, Throwable t);

  扩展示例

  1)、没有级别概念

Public final class CustomLogger {
 //log4j日志记录器
 Logger log4j;
 
 //自己的FQCN
 Private static final String FQCN = CustomLogger.class.getName();
 
 //构造方法
 Private CustomLogger(String loggerName) {
  log4j = CustomLogFactory.getLogger(loggerName);
 }
 
 //工厂方法
 Public static CustomLogger getLogger(String loggerName) {
  Return new CustomLogger(loggerName);
 }
 
 Public Boolean isEntryEnabled() {
  Return log4j. isEnabledFor(Level.INFO);
 }

 //程序入口日志
 //你完全可以在message上做些手脚,加上一些自己的特殊的日志信息
 //当然你也可以后面介绍的Appender的时候加
 Public void entry(String message, Throwable t) {
  Log4j.log(FQCN , Level.INFO, message, t);
 }
 
 Public Boolean isExitEnabled() {
  Return log4j. isEnabledFor(Level.INFO);
 }

 //程序结束日志
 Public void exit(String message, Throwable t) {
  Log4j.log(FQCN , Level.INFO, message, t);
 }
}

  2)、支持国际化

  主要是在message取得上做点事情。就像上面那个Public void entry(String message, Throwable t);如果你把API改为Public void entry(String messageID, Throwable t);实现改为:

Public void entry(String messageID, Throwable t) {
 Log4j.log(FQCN , Level.INFO, getMessage(messageID), t);
}

//取得相应的国际化信息
Private String getMessage(String messageID) {
 Return InternalResource.getLogMessage(messageID, Locale.getDefault());
}

2.3 自己的日志输出地

  Log4j本身提供了大量的默认Appender实现,已经能很好的解决大部分应用,但是有时候我们还是有一些自己的需要,比如只有在输出到控制台Appender时加一个运行时才能确定的固定前缀。我们可以这么写:

public final class CustomConsoleAppender extends org.apache.log4j.ConsoleAppender {
 
 @Override
 protected void subAppend(LoggingEvent event) {
  StringBuffer msgBuf = new StringBuffer();
 
  //固定前缀
  msgBuf.append('[');
  msgBuf.append(Manager.current ().getName());
  msgBuf.append(']');
 
  //根据配置文件中的格式化信息格式化event
  msgBuf.append(this.layout.format(event));
  this.qw.write(msgBuf.toString());
  ……
 }
}

  也就是我们想定制某一种日志输出地,直接继承相应的log4j中的类似的Appender,然后复写相应的方法即可。最后在配置文件中使用自己定义的Appender类就行了。

2.4 自己的日志过滤器

  Log4j的日志输出控制级别比较中有一个缺陷就是只能大于某个级别时才能输出,没有小于某个级别时才输出的控制,当然一般不会用到,如果有类似这样的需求是不是就做不到了呢?不是的,这时候你可以使用日志过滤器来实现。比如有这样一个需求,输出到控制台的日志信息,当级别小于WARN时,用System.out;当大于等于WARN时,用System.err。

  过滤器定义如下:

public final class CustomWarnLevelFilter extends org.apache.log4j.spi.Filter {
 @Override
 public int decide(LoggingEvent event) {
  //大于等于WARN的日志不允许输出
  if(event.getLevel().toInt() >= Level. WARN.toInt()) {
   return DENY;
  } else {
   return ACCEPT;
  }
 }
}

 
  配置文件中可以这样配置:

<appender class="com.primeton.ext.common.log.EOSConsoleAppender" name="CONSOLE_OUT">
 <param name="Target" value="System.out"/>
 <layout class="org.apache.log4j.PatternLayout">
  <param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss,SSS}][%p][%C][Line:%L] %m%n"/>
 </layout>
 <filter class=" CustomWarnLevelFilter "/>
</appender>
 
<appender class="com.primeton.ext.common.log.EOSConsoleAppender" name="CONSOLE_ERR">
 <param name="Target" value="System.err"/>
 <param name="Threshold" value="WARN"/>
 <layout class="org.apache.log4j.PatternLayout">
  <param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss,SSS}][%p][%C][Line:%L] %m%n"/>
 </layout>
</appender>
 
<root>
 <level value="DEBUG"/>
 <appender-ref ref="CONSOLE_OUT"/>
 <appender-ref ref="CONSOLE_ERR"/>
</root>

   因为log4j设计的灵活性,所以当然也有其他的方式来达到这个目的。

  上面简单介绍了对log4j常用的扩展方式,你还可以扩展像layout、errorhandler等等,这里不再赘述。

3、性能影响因素Log4j

  Log4j一直被人所诟病的也就是它的性能问题了,所以在这里简要的说明一下使用log4j的注意事项:
  1)、如果你的日志实例的名称不是经常变化,请将它定义为static变量。
  2)、如果你的message构造很复杂,那么在构造之前,请先使用isXXXEnabled()判断。
  3)、配置文件中格式控制如果不需要,尽量不要输出类名和行号。
  4)、根据log4j的级别判断控制流程,在配置文件中尽可能配置的比较细致。
    Logger.info(message)的比较流程:
    a. 比较日志仓库的threashold
    b. 与自身的有效Level(没有则是父亲的)比较
    c. 与Appender的threashold比较
    d. 询问Appender的filter
    e. Layout格式化
    f. 记录日志
  5)、如果Appender是文件类型,请不要把文件大小设的太小。至少设为10MB(<param name="MaxFileSize" value="10MB"/>)。
  6)、如果不是即时调试程序,把你的级别设定为高级别,最好是threshold=INFO之上。
  7)、如果不是想即时看到日志信息,你也可以把Appender的ImmediateFlush 设为false(<param name="ImmediateFlush" value="false"/>)。
 
4、附录

4.1 Log4j基本概念

  Log4j体系结构图


  日志仓库(或者容器)loggerRepository:跟一个配置文件相对应,顾名思义,里面存放着日志实例。
  1)、属性:threashold(阈值)
  2)、默认实现:org.apache.log4j.Hierarchy
  3)、日志系统━日志仓库━配置文件

 

  日志记录器:根日志和子日志(继承的概念,用“.”来区分,日志名称的重要性)
  1)、的概念(用于第三方封装和继承)FQCN
  2)、属性:level, appender, additivity(是否继承父日志的appender)

  Level:日志级别

  Appender:日志输出端
  属性:threashold(阈值),filter,Layout,Filter,ErrorHandler

  Layout:布局(输出格式)


  Filter:过滤器

查看更多精彩图片

  ErrorHandler:错误处理器   
查看更多精彩图片
 

4.2   Log4j配置文件详细说明(*.properties和*.xml)

4.2.1   属性文件Properties
 

properties 属性文件
编号
配置项
配置项描述
示例
1
log4j.threshold
阈值项
log4j.threshold = error
2
log4j.rootLogger
根日志属性项
log4j.rootLogger = info,stdout1,stdout2
3
log4j.category.
子日志属性项(旧)
log4j.category.com.eos = NULL,stdout1
4
log4j.logger.
子日志属性项(新)
log4j.logger.com.eos.log = debug,stdout2
5
log4j.additivity.
appender是否继承设置
log4j.additivity.com.eos = false
6
log4j.appender.
输出目的地定义项
log4j.appender.stdout2 = org.apache.log4j.ConsoleAppender
7
log4j.appender.A.layout
输出格式定义项
log4j.appender.stdout2.layout = org.apache.log4j.PatternLayout
 
4.2.2   文件xml
 
xml 文件
编号
配置项
配置项描述
示例
1
threshold
阈值项
<log4j:configuration xmlns:log4j=" http://jakarta.apache.org/log4j/" debug="false" threshold="null">
2
root
根日志属性项
<root>
    <priority value="info"/>
     <appender-ref ref="CONSOLE"/>
   </root>
3
priority
级别项(旧)
<priority value="info"/>
4
level
级别项(新)
<level value="info"/>
5
category
子日志属性项(旧)
<category additivity="true" name="com.eos.log">
6
logger
子日志属性项(新)
<logger additivity="true" name="com.eos">
7
appender-ref
输出端控制项
<appender-ref ref="CONSOLE"/>
8
additivity
appender是否继承设置
<logger additivity="true" name="com.eos">
9
appender
输出目的地定义项
<appender class="org.apache.log4j.ConsoleAppender" name="CONSOLE">
10
layout
输出格式定义项
<layout class="org.apache.log4j.PatternLayout">

 

 
4.2.3   详细说明(只针对Log4j常用的,用户可以自定义)Appender

  Appender继承关系

  Appender基本种类

  org.apache.log4j.ConsoleAppender(控制台)
  org.apache.log4j.FileAppender(文件)
  org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
  org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
  org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)

  · ConsoleAppender选项

  Threshold=WARN:指定日志消息的输出最低层次。
  ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。
  Target=System.err:默认情况下是:System.out,指定输出控制台

  · FileAppender 选项

  Threshold=WARN:指定日志消息的输出最低层次。
  ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。
  File=mylog.txt:指定消息输出到mylog.txt文件。
  Append=false:默认值是true,即将消息增加到指定文件中,false指将消息覆盖指定的文件内容。

  · DailyRollingFileAppender 选项

  Threshold=WARN:指定日志消息的输出最低层次。
  ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。
  File=mylog.txt:指定消息输出到mylog.txt文件。
  Append=false:默认值是true,即将消息增加到指定文件中,false指将消息覆盖指定的文件内容。
  DatePattern='.'yyyy-ww:每周滚动一次文件,即每周产生一个新的文件。当然也可以指定按月、周、天、时和分。即对应的格式如下:
  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: 每分钟
  n 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:指定可以产生的滚动文件的最大数。

4.2.4   详细说明(只针对Log4j,用户可以自定义)Layout

  Log4j的Layout基本种类

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

  · HTMLLayout选项

  LocationInfo=true:默认值是false,输出java文件名称和行号
  Title=my app file: 默认值是 Log4J Log Messages.      
  n PatternLayout 选项
  log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n
  这里需要说明的就是日志信息格式中几个符号所代表的含义:
   %X: 信息输出时左对齐;
  %p: 输出日志信息优先级,即DEBUG,INFO,WARN,ERROR,FATAL,
  %d: 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
  %r: 输出自应用启动到输出该log信息耗费的毫秒数
  %c: 输出日志信息所属的类目,通常就是所在类的全名
  %t: 输出产生该日志事件的线程名
  %l: 输出日志事件的发生位置,相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)
  %x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。
  %%: 输出一个"%"字符
  %F: 输出日志消息产生时所在的文件名称
  %L: 输出代码中的行号
  %m: 输出代码中指定的消息,产生的日志具体信息
  %n: 输出一个回车换行符,Windows平台为"/r/n",Unix平台为"/n"输出日志信息换行,可以在%与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。如:
  1)%20c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,默认的情况下右对齐。
  2)%-20c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,"-"号指定左对齐。
  3)%.30c:指定输出category的名称,最大的宽度是30,如果category的名称大于30的话,就会将左边多出的字符截掉,但小于30的话也不会有空格。
  4)%20.30c:如果category的名称小于20就补空格,并且右对齐,如果其名称长于30字符, 就从左边交远销出的字符截掉。

  · XMLLayout 选项

  LocationInfo=true:默认值是false,输出java文件和行号

4.3   日志配置文件内容范例

4.3.1   log4j.properties

log4j.rootLogger=DEBUG, CONSOLE
#DEBUG, CONSOLE,FILE,ROLLING_FILE,MAIL,DATABASE

log4j.logger.org.apache=INFO, FILE
log4j.additivity.org.apache=false

###################
# Console Appender
###################
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.Threshold=DEBUG
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d [%p] - %m%n


#####################
# File Appender
#####################
log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=file.log
log4j.appender.FILE.Append=false
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d [%p] - %m%n


########################
# Rolling File
########################
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.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=%d [%p] - %m%n


####################
# Socket Appender
####################
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=%d [%p] - %m%n


########################
# Log Factor 5 Appender
########################
log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender
log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000


########################
# SMTP Appender
#######################
log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender
log4j.appender.MAIL.Threshold=FATAL
log4j.appender.MAIL.BufferSize=10
log4j.appender.MAIL.From=yourname@domain.com
log4j.appender.MAIL.SMTPHost=mail.primeton.com
log4j.appender.MAIL.Subject=Log4J Message
log4j.appender.MAIL.To=test@domain.com
log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout
log4j.appender.MAIL.layout.ConversionPattern=%d [%p] - %m%n


########################
# JDBC Appender
#######################
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 (%d [%p] - %m%n)
log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout
log4j.appender.DATABASE.layout.ConversionPattern=%d [%p] - %m%n


log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A1.File=SampleMessages.log4j
log4j.appender.A1.DatePattern=yyyyMMdd-HH'.log4j'
log4j.appender.A1.layout=org.apache.log4j.xml.XMLLayout

###################
#自定义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 = yourname@domain.com

log4j.appender.im.layout=org.apache.log4j.PatternLayout
log4j.appender.im.layout.ConversionPattern =%d [%p] - %m%n

  #注意:在属性配置文件中,属性值的第一个一定是级别,输出端可有可无,以逗号分割。(而xml文件格式没有这种限制)

4.3.2   log4j.xml

< xml version="1.0" encoding="UTF-8" >
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false" threshold="null">


<appender class="org.apache.log4j.ConsoleAppender" name="CONSOLE">
<param name="Target" value="System.out"/>
<param name="Threshold" value="INFO"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d [%p] - %m%n "/>
</layout>
<filter class="org.apache.log4j.varia.DenyAllFilter"/>
<errorHandler class="org.apache.log4j.varia. FallbackErrorHandler"/>
</appender>

<appender class="org.apache.log4j.FileAppender" name="FILE">
<param name="File" value="file.log"/>
<param name="Append" value="false"/>
<param name="Threshold" value="INFO"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d [%p] - %m%n "/>
</layout>
</appender>

<appender class="org.apache.log4j.RollingFileAppender" name="ROLLING_FILE">
<param name="Threshold" value="INFO"/>
<param name="File" value="rolling.log"/>
<param name="Append" value="false"/>
<param name="MaxFileSize" value="10KB"/>
<param name="MaxBackupIndex" value="1"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d [%p] - %m%n "/>
</layout>
</appender>

<logger additivity="false" name="com.eos">
<level value="info"/>
<appender-ref ref="CONSOLE"/>
</logger>

<category additivity="true" name="com.eos.log">
<priority value="warn"/>
</category>

<root>
<priority value="info"/>
<appender-ref ref="CONSOLE"/>
</root>
</log4j:configuration>

 

 

查看更多精彩图片

查看更多精彩图片

查看更多精彩图片
Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐