一、 apache commons-logging与log4j和JDK logger的关系:
> commons-logging的目的是为”所有的Java日志实现“提供统一的接口,而log4j和JDK Logger可以看做是其实现
> 由于log4j功能非常强大、效率高,所以经常将commons-logging和log4j结合使用
> commons-logging通过检测系统属性和类路径来选择合适的日志框架
> commons-logging查找日志框架,顺序如下:
>> 查看系统属性"org.apache.commons.logging.Log"是否被设置,若设置,使用该配置的日志框架
eg. #
System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.Log.impl.Log4JLogger");
>> 检测classpath是否存在log4j,若存在,则使用Log4JLogger实例
>> 检测classpath是否存在JDK1.4日志框架,若存在则使用JDK14Logger
>> 以上都不成立,则使用默认的SimpleLog
二、使用Log4J:
> 使用Commons-logging和log4j结合的方法:(注意:将log4j.xml文件放在classpath下)
package org.rsljdkt.research.log4j; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class TestLog { private static Log log = LogFactory.getLog(TestLog.class); public static void main(String[] args){ TestLog testLog = new TestLog(); testLog.test(); } public void test(){ log.debug("debug_msg"); log.warn("warn_msg"); log.info("info_msg"); log.error("error_msg"); log.fatal("fatal_msg"); } }
> 单独使用Log4J:(也将log4j.xml文件放在classpath下)
package org.rsljdkt.research.log4j; import org.apache.log4j.Logger; import org.apache.log4j.xml.DOMConfigurator; public class TestLog4J { static Logger logger = Logger.getLogger(TestLog4J.class); public static void main(String[] args) { DOMConfigurator.configure("src\\log4j.xml"); logger.debug("debug_msg"); logger.warn("warn_msg"); logger.info("info_msg"); logger.error("error_msg"); logger.fatal("fatal_msg"); } }
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"> <appender name="terminal" class="org.apache.log4j.ConsoleAppender"> <param name="Target" value="System.out"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n"/> </layout> </appender> <root> <level value="ALL"/> <appender-ref ref="terminal"/> </root> </log4j:configuration>
三、Log4J关键概念:
> 记录器(Logger):
>> 获取记录器:
# Logger logger = Logger.getLogger(JavaLoggingExample.class.getName());
>> 记录器的层次结构:
在每个类中,使用Log4J的Logger.getLogger()方法都会创建一个Logger实例,为了方便Logger的配置,
Log4J采用了一种树状的集成层次解决此问题
static Logger root = Logger.getRootLogger();
static Logger log1 = Logger.getLogger("org");
static Logger log2 = Logger.getLogger("org.rsljdkt");
以上,root是树根,log1是root的孩子,log2是log1的孩子,因此常在log4j的配置文件中配置rootLog即可。
> 级别(Level):
日志级别,用于控制日志的是否输出。Log4J中规定了5种日志级别:
DEBUG < INFO < WARN < ERROR < FATAL
> 布局(Layout):
指定了日志信息的格式化输出方式,使用类似C语言的printf方式。
> 输出源(Appenders):
>> 指定记录器按照指定的布局将日志信息写于一个或者多个输出源。
>> 输出源可以是控制台、文本文件、XML文件、Socket、数据库等,甚至可以将日志信息通过邮件发送
>> 输出源需要相应的类来处理,eg. ConsoleAppender, FileAppender, SocketAppender, NtEventLogAppender, JMSAppender。
四、Log4J的配置:
> 属性(Property)文件方式
>> 配置根Logger,格式为:
log4j.rootLogger = [ level ], appenderName1, appenderName2, ...
其中level为日志优先级,分别为OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL或者你定义的级别。
Log4J建议只使用四个级别,从高到底依次是:ERROR, WARN, INFO, DEBUG。
>> 配置日志信息输出目的地Appender,格式为:
log4j.appender.appenderName = fully.qualified.name.of.appender.class log4j.appender.appenderName.option1 = value1 ... log4j.appender.appenderName.optionN = valueN
其中,Log4J提供的appender有以下几种:
org.apahce.log4j.ConsoleAppender(控制台)
org.apache.log4j.FileAppender(文件)
org.apahce.log4j.DailyRollingFileAppender(每天产生一个日志文件)
org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸时产生一个新的文件)
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的方式)
>> 配置日志信息的格式(Layout),格式为:
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class log4j.appender.appenderName.layout.option1 = value1 ... log4j.appender.appenderName.layout.optionN = valueN
其中,Log4J提供的layout有以下几种:
org.apahce.log4j.HTMLLayout(以HTML表格形式布局)
org.apache.log4j.PatternLayout(可以灵活的指定布局)
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串)
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
Log4J采用类似C语言的printf函数控制打印日志信息的格式化,打印参数如下:
>> %m 输出代码中指出的消息
>> %p 输出优先级,即DEBUG, INFO, WARN, ERROR, FATAL
>> %r 输出子应用启动到输出log信息所耗费的毫秒数
>> %c 输出所属的类目,通常就是所在类的全名
>> %t 输出产生该日志事件的线程名
>> %n 输出一个换行回车符,Windows平台对应”\r\n“,Unix平台对应”\n“
>> %d 输出日志时间点的日期或者时间,默认为ISO8601格式,也可以在其后指定格式,eg.
%d{yyyy MM dd HH:mm:ss,SSS},输出的格式类似:2010 12 21 22:22:28 987
>> %l 输出日志事件的发生位置,包括类目、发生的进程以及代码中的行数
eg.
## 使用两个输出源,一个是控制台,一个是文件
log4j.rootLogger=debug, stdout, R # 设置特定包的级别和输出源 log4j.logger.your.package.name=WARN, stdout ## 第一个输出源 log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout # 输出调用者的文件名和行号 log4j.appender.stdout.layout.ConversionPattern=%5p[%t](%F:%L)-%m%n ## 第二个输出源 log4j.appender.R=org.apache.log4j.RoolingFileAppender log4j.appender.R.File=example.log # 日志文件的最大大小 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
另外参见:http://rsljdkt.iteye.com/blog/848805
> XML文件方式:
参见:http://rsljdkt.iteye.com/blog/848786
五、Log4J在Web中的应用:
> Log4J必须在应用的其他代码执行前进行初始化。
> 在Web应用职工,一般专门配置一个Servlet来完成Log4J的配置,并保证在web.xml中,这个Servlet位于其他Servlet之前
eg.
java code:
package org.rsljdkt.research.log4j; import java.io.*; import javax.servlet.*; import org.apache.log4j.*; public class Log4JInit extends HttpServlet{ public void init()throws ServletException{ String prefix = getServletContext().getRealPath("/"); String file = getServletConext().getInitParameter("log4j-config-file"); if(file != null){//从Servlet参数中读取log4j配置文件 PropertyConfigurator.configure(prefix+file); } } public void doGet(HttpServletRequest request, HttpServletResponse respons) throws IOException, ServletException{ } public void doPost(HttpServletRequest request, HttpServletResponse response) thows IOExcepion, ServletException{ } }
web.xml(部分):
<servlet> <servlet-name>log4jinit</servlet-name> <servlet-class>org.javaresearch.log4j.Log4JInit</servlet-class> <init-param> <param-name>log4j-config-file</param-name> <param-value>/properties/log4j.properties</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
六、高级话题:
> 性能:
>> Log4J的首要设计目的就是高效
>> 根据Log4J小组的报告,在AMD Duron 800MHz + JDK1.3.1环境下,Log4J判断一条语句是否需要输出仅需5ns。
实际的日志执行语句非常快速,共使用SimpleLayout的21ms(几乎和System.out.println一样快)到TTCCLayout的37ms不等
> 嵌套诊断环境NDC
>> Log4J采用NDC机制来解决多线程记录日志的情况下区分日志信息是由那个线程输出的问题
>> Log4J为同一类别的线程生成一个Logger,多个线程共享使用
>> Log4J的实现如下:
1. 在进入一个环境时,调用NDC.push(String ),然后创建一个NDC
2. 所做的日志操作输出包含NDC的信息
3. 离开该环境时,调用NDC.pop()方法
4. 当从一个进程退出时,调用NDC.remove()方法,以释放资源
七、补充:Log4J的使用步骤:
> 1. 得到记录器:
public staitc Logger getLogger(String name); public static Logger getLogger(Class clazz);
> 2. 读取配置文件:
BasicConfigurator.configure();//快速使用Log4J缺省的环境 PropertyConfigurator.configure(String configFileName);//读取Java属性文件编写的配置文件 DOMConfigurator.configure(String fileName);//读取XML格式的配置文件
> 3. 插入记录信息(格式化日志信息):
Logger.debug(Object message); Logger.info(Object message); Logger.warn(Object message); Logger.error(Object message);