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中是通过日志仓库来维护一套独立日志系统,只要我们直接使用它的日志仓库,写一个我们自己的日志工厂就可以了。具体如下:
1.Public final class CustomLogFactory { 2.//日志仓库实例 3.Static LoggerRepository h = new Hierarchy(new RootLogger(Level.DEBUG)); 4. 5.Static { 6.//配置文件定制日志仓库属性,这里面你可以做更多的文章 7.//来定义自己的配置文件位置等等 8.new PropertyConfigurator().configure(h, urlConfigFile); 9.} 10. 11.public static Logger getLogger(String loggerName) { 12.return h.getLogger(loggerName); 13.} 14.}
这样你的日志系统完全独立了。是不是很容易?
2.2 自己的日志记录器
Log4j的logger本身提供八种级别的日志记录,如果你想让你的logger没有级别概念,或者让日志信息支持国际化,那该怎么办?
Log4j用于第三方扩展的记录日志方法API
public boolean isEnabledFor(Priority level);
public void log(String callerFQCN, Priority level, Object message, Throwable t);
扩展示例
1) 没有级别概念
1.Public final class CustomLogger { 2.//log4j日志记录器 3.Logger log4j; 4. 5.//自己的FQCN 6.Private static final String FQCN = CustomLogger.class.getName(); 7. 8.//构造方法 9.Private CustomLogger(String loggerName) { 10.log4j = CustomLogFactory.getLogger(loggerName); 11.} 12. 13.//工厂方法 14.Public static CustomLogger getLogger(String loggerName) { 15.Return new CustomLogger(loggerName); 16.} 17. 18.Public Boolean isEntryEnabled() { 19.Return log4j. isEnabledFor(Level.INFO); 20.} 21.//程序入口日志 22.//你完全可以在message上做些手脚,加上一些自己的特殊的日志信息 23.//当然你也可以后面介绍的Appender的时候加 24.Public void entry(String message, Throwable t) { 25.Log4j.log(FQCN , Level.INFO, message, t); 26.} 27. 28.Public Boolean isExitEnabled() { 29.Return log4j. isEnabledFor(Level.INFO); 30.} 31.//程序结束日志 32.Public void exit(String message, Throwable t) { 33.Log4j.log(FQCN , Level.INFO, message, t); 34.} 35.}
2) 支持国际化
主要是在message取得上做点事情。就像上面那个Public void entry(String message, Throwable t);如果你把API改为Public void entry(String messageID, Throwable t);实现改为:
1.Public void entry(String messageID, Throwable t) { 2.Log4j.log(FQCN , Level.INFO, getMessage(messageID), t); 3.} 4.//取得相应的国际化信息 5.Private String getMessage(String messageID) { 6.Return InternalResource.getLogMessage(messageID, Locale.getDefault()); 7.}
2.3 自己的日志输出地
Log4j本身提供了大量的默认Appender实现,已经能很好的解决大部分应用,但是有时候我们还是有一些自己的需要,比如只有在输出到控制台Appender时加一个运行时才能确定的固定前缀。我们可以这么写:
1.public final class CustomConsoleAppender extends org.apache.log4j.ConsoleAppender { 2. 3.@Override 4.protected void subAppend(LoggingEvent event) { 5.StringBuffer msgBuf = new StringBuffer(); 6. 7.//固定前缀 8.msgBuf.append('['); 9.msgBuf.append(Manager.current ().getName()); 10.msgBuf.append(']'); 11. 12.//根据配置文件中的格式化信息格式化event 13.msgBuf.append(this.layout.format(event)); 14.this.qw.write(msgBuf.toString()); 15.…… 16.} 17.}
也就是我们想定制某一种日志输出地,直接继承相应的log4j中的类似的Appender,然后复写相应的方法即可。最后在配置文件中使用自己定义的Appender类就行了。
2.4 自己的日志过滤器
Log4j的日志输出控制级别比较中有一个缺陷就是只能大于某个级别时才能输出,没有小于某个级别时才输出的控制,当然一般不会用到,如果有类似这样的需求是不是就做不到了呢?不是的,这时候你可以使用日志过滤器来实现。比如有这样一个需求,输出到控制台的日志信息,当级别小于WARN时,用System.out;当大于等于WARN时,用System.err。
过滤器定义如下:
1.public final class CustomWarnLevelFilter extends org.apache.log4j.spi.Filter { 2.@Override 3.public int decide(LoggingEvent event) { 4.//大于等于WARN的日志不允许输出 5.if(event.getLevel().toInt() >= Level. WARN.toInt()) { 6.return DENY; 7.} else { 8.return ACCEPT; 9.} 10.} 11.}
配置文件中可以这样配置:
1.<appender class="com.primeton.ext.common.log.EOSConsoleAppender" name="CONSOLE_OUT"> 2.<param name="Target" value="System.out"/> 3.<layout class="org.apache.log4j.PatternLayout"> 4.<param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss,SSS}][%p][%C][Line:%L] %m%n"/> 5.</layout> 6.<filter class=" CustomWarnLevelFilter "/> 7.</appender> 8. 9.<appender class="com.primeton.ext.common.log.EOSConsoleAppender" name="CONSOLE_ERR"> 10.<param name="Target" value="System.err"/> 11.<param name="Threshold" value="WARN"/> 12.<layout class="org.apache.log4j.PatternLayout"> 13.<param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss,SSS}][%p][%C][Line:%L] %m%n"/> 14.</layout> 15.</appender> 16. 17.<root> 18.<level value="DEBUG"/> 19.<appender-ref ref="CONSOLE_OUT"/> 20.<appender-ref ref="CONSOLE_ERR"/> 21.</root>
因为log4j设计的灵活性,所以当然也有其他的方式来达到这个目的。
上面简单介绍了对log4j常用的扩展方式,你还可以扩展像layout、errorhandler等等,这里不再赘述。
3 性能影响因素Log4j
Log4j一直被人所诟病的也就是它的性能问题了,所以在这里简要的说明一下使用log4j的注意事项:
1) 如果你的日志实例的名称不是经常变化,请将它定义为static变量。
2) 如果你的message构造很复杂,那么在构造之前,请先使用isXXXEnabled()判断。
3) 配置文件中格式控制如果不需要,尽量不要输出类名和行号。
4) 根据log4j的级别判断控制流程,在配置文件中尽可能配置的比较细致。
Logger.info(message)的比较流程:
i. 比较日志仓库的threashold
ii. 与自身的有效Level(没有则是父亲的)比较
iii. 与Appender的threashold比较
iv. 询问Appender的filter
v. Layout格式化
vi. 记录日志
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
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
|
- 大小: 35.6 KB
- 大小: 70.4 KB
- 大小: 25.7 KB
- 大小: 30.8 KB
- 大小: 33.5 KB
分享到:
相关推荐
Object.fromEntries()接受一个可迭代对象作为参数,该对象的元素必须是长度为二的数组,其中第一个元素为键,第二个元素为值。例如: ```javascript const obj = {one:1, two:2, three:3}; const entries = Object....
而`call()`则将作用域作为第一个参数,后续参数逐个列举。 ```javascript function sum(num1, num2) { return num1 + num2; } function callSum1(num1, num2) { return sum.apply(this, arguments); } function ...
这个函数接受一个函数作为参数,返回一个新函数,该新函数可以将它的第一个参数作为原函数的上下文来调用原函数,其余参数传递给原函数作为参数。 ```javascript var uncurrying = function(fn) { return function...
exp username/password file=export.dmp log=export.log tables=(table1,table2) ``` 导入数据的命令格式大致为: ```bash imp username/password file=export.dmp log=import.log full=y ``` 这里,`full=y`表示...
`arguments`对象的索引从0开始,`arguments[0]`对应第一个参数,以此类推。 举例来说,以下是一个使用`arguments`对象的例子: ```javascript function printArgs() { for (var i = 0; i ; i++) { console.log...
在Node.js中,异步编程是其核心特性之一,它使得程序能够非阻塞地执行,从而提高了系统性能。本文将深入探讨Node.js中的异步编程机制,包括事件发布/订阅模式、Promise/Deferred模式以及流程控制库的使用,以及如何...
在标题和描述中提到的“浅谈类似于(function(){}).call()的js语句”主要是讨论如何使用这个模式来执行函数,并改变函数内部`this`的指向。 首先,我们来看一下`function(){}.call()`结构的组成部分: 1. `function...
第1章 计算机,游戏,我 1 1.1 计算机 2 1.2 计算机游戏 3 1.3 计算机与我 7 1.3.1 启蒙 7 1.3.2 编程 9 第2章 算法,程序的灵魂 13 2.1 程序=算法+数据结构 14 2.1.1 算法 15 2.1.2 数据结构 17 2.2 搜索算法 23 ...
第1章 计算机,游戏,我 1 1.1 计算机 2 1.2 计算机游戏 3 1.3 计算机与我 7 1.3.1 启蒙 7 1.3.2 编程 9 第2章 算法,程序的灵魂 13 2.1 程序=算法+数据结构 14 2.1.1 算法 15 2.1.2 数据结构 17 2.2 搜索算法 23 ...
1. **使用`indexOf()`方法**:`indexOf()`返回给定元素在数组中第一次出现的位置,如果不存在则返回-1。我们可以遍历数组,当元素不在新数组中(即`indexOf()`返回-1)时,将其添加到新数组。这种方法简单但效率较低...