最近在工作中,发现一个线上人员同步的定时任务总是同步失败,能收到同步失败发送的短信但是去日志里搜不到对应的信息,最后发现是在log.error()方法中参数给错了,导致日志里找到的错误只有错误信息,没有堆栈信息,一直没有办法定位错误发生的具体位置。所以这里做一个小小的总结,学习一下java web项目中使用log4j和commons- logging来完成错误捕获和日志输出。
common-logging
1.简介
Jakarta Commons Logging (JCL)提供的是一个日志(Log)接口(interface),同时兼顾轻量级和不依赖于具体的日志实现工具。它提供给中间件/日志工具开发者一个简单的日志操作抽象,允许程序开发人员使用不同的具体日志实现工具。用户被假定已熟悉某种日志实现工具的更高级别的细节。JCL提供的接口,对其它一些日志工具,包括Log4J, Avalon LogKit, and JDK 1.4等,进行了简单的包装,此接口更接近于Log4J和LogKit的实现。
2.基本信息
JCL有两个基本的抽象类:Log(基本记录器)和LogFactory(负责创建Log实例)。当commons-logging.jar被加入到 CLASSPATH之后,它会合理地猜测你想用的日志工具,然后进行自我设置,用户根本不需要做任何设置。默认的LogFactory是按照下列的步骤去发现并决定那个日志工具将被使用的(按照顺序,寻找过程会在找到第一个工具时中止):
1. 寻找当前factory中名叫org.apache.commons.logging.Log配置属性的值
2. 寻找系统中属性中名叫org.apache.commons.logging.Log的值
3. 如果应用程序的classpath中有log4j,则使用相关的包装(wrapper)类(Log4JLogger)
4. 如果应用程序运行在jdk1.4的系统中,使用相关的包装类(Jdk14Logger)
5. 使用简易日志包装类(SimpleLog)
这里贴上commons-logging中LogSource.java的源代码,不难看出实现顺序
package log.sample;
public class ca {
static {
// Is Log4J Available?用户Log4J是否可用
try {
/**
* 通过Class.forName("org.apache.log4j.Logger"))来查找Log4J,
* 只有将log4j.jar添加到classpath以后才能找到,
* 这也是为什么默认情况下只要将log4j.jar文件放在lib文件夹中
* 而不需要在common-logging.properties配置文件中进行配置就能自动使用log4j的原因
*/
if (null != Class.forName("org.apache.log4j.Logger")) {
log4jIsAvailable = true;
} else {
log4jIsAvailable = false;
}
} catch (Throwable t) {
log4jIsAvailable = false;
}
// Is JDK 1.4 Logging Available?原来同上面的Log4J
try {
if ((null != Class.forName("java.util.logging.Logger")) &&
(null != Class.forName("org.apache.commons.logging.impl.Jdk14Logger"))) {
jdk14IsAvailable = true;
} else {
jdk14IsAvailable = false;
}
} catch (Throwable t) {
jdk14IsAvailable = false;
}
// Set the default Log implementation,通过common-logging.properties配置文件来决定日志实现方式
String name = null;
try {
name = System.getProperty("org.apache.commons.logging.log");
if (name == null) {
name = System.getProperty("org.apache.commons.logging.Log");
}
} catch (Throwable t) {
}
if (name != null) {
try {
setLogImplementation(name);
} catch (Throwable t) {
try {
setLogImplementation
("org.apache.commons.logging.impl.NoOpLog");
} catch (Throwable u) {
;
}
}
} else {
try {
if (log4jIsAvailable) {//如果log4j可用,默认优先使用Log4JLogger
setLogImplementation
("org.apache.commons.logging.impl.Log4JLogger");
} else if (jdk14IsAvailable) {//第二优先使用Jdk14Logger
setLogImplementation
("org.apache.commons.logging.impl.Jdk14Logger");
} else {//最后使用commoms-logging中的自带的实现,但它不进行任何操作
setLogImplementation
("org.apache.commons.logging.impl.NoOpLog");
}
} catch (Throwable t) {
try {
setLogImplementation
("org.apache.commons.logging.impl.NoOpLog");
} catch (Throwable u) {
;
}
}
}
}
}
org.apache.commons.logging.Log的具体实现有如下:
-org.apache.commons.logging.impl.Jdk14Logger 使用JDK1.4。
-org.apache.commons.logging.impl.Log4JLogger 使用Log4J。
-org.apache.commons.logging.impl.LogKitLogger 使用 avalon-Logkit。
-org.apache.commons.logging.impl.SimpleLog common-logging自带日志实现类。它实现了Log接口,把日志消息都输出到系统错误流System.err 中。
-org.apache.commons.logging.impl.NoOpLog common-logging自带日志实现类。它实现了Log接口。 其输出日志的方法中不进行任何操作。
3.使用
因为项目上使用的就是log4j和commons- logging结合,所以这里只介绍这种方式。
(1)下载log4j和commons- logging的jar包,这里推荐用http://mvnrepository.com/去找jar包
(2)如果需要指定日志器,可以在属性配置文件common-logging.properties中设置,这里以log4j为例,只需要这一句即可。在目前工作的项目,是没有这个配置文件的,因为LogFactory会自动检测到应用程序的classpath。
org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JCategoryLog
(3)org.apache.commons.logging.Log接口中定义的方法,按严重性由高到低的顺序:
log.fatal(Object message);
log.fatal(Object message, Throwable t);
log.error(Object message);
log.error(Object message, Throwable t);
log.warn(Object message);
log.warn(Object message, Throwable t);
log.info(Object message);
log.info(Object message, Throwable t);
log.debug(Object message);
log.debug(Object message, Throwable t);
log.trace(Object message);
log.trace(Object message, Throwable t);
一般当我们在捕获error级别及以上是,通常使用log.error(Object message, Throwable t)方法,因为log.error(Object message)只能输出错误信息,无法输出堆栈信息,对我们分析日志是非常不利的,下面是我在实际项目中使用commons- logging的格式:
package TEST;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class Test {
private static final Log log = LogFactory.getLog(Test.class);
public void test( ) throws Exception {
log.info("-----------------START-----------------");
try {
} catch (Exception e) {
log.error("代码执行错误", e);
}
}
Log4j
1.简介
Log4j是Apache的一个开源项目,它允许开发者以任意间隔输出日志信息。Log4j主要由三大类组件构成:
1)Logger-负责输出日志信息,并能够对日志信息进行分类筛选,即决定哪些日志信息应该被输出,哪些该被忽略。Loggers组件输出日志信息时分为5个级别:DEBUG、INFO、WARN、ERROR、FATAL。这五个级别的顺序是:DEBUG<INFO<WARN<ERROR<FATAL。如果设置某个Logger组件的级别是P,则只有级别比P高的日志信息才能输出。Logger是有继承关系的,最上层是rootLogger,定义的其他Logger都会继承rootLogger。
2)Appender-定义了日志输出目的地,指定日志信息应该被输出到什么地方。输出的目的地可以是控制台、文件或网络设备。
3)Layout-通过在Appender的后面附加Layout来实现格式化输出。
一个Logger可以有多个Appender,每个Appender对应一个Layout。
2.详细配置
log4j.properties配置如下:
### set log levels ###
log4j.rootLogger = debug , stdout , D , E
### 输出到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
## 输出INFO级别以上的日志
log4j.appender.stdout.Threshold = INFO
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{1}:%L - %m%n
### 输出到日志文件 ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = D:/logs/log.log
log4j.appender.D.Append = true
## 输出DEBUG级别以上的日志
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 保存异常信息到单独文件 ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
## 异常日志文件名
log4j.appender.E.File = D:/logs/error.log
log4j.appender.E.Append = true
## 只输出ERROR级别以上的日志!!!
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n