log4j是Apache的一个开源项目,陪伴了我们多年,但是现在已经不更新了。官网原文如下:
Log4j 1.x has been widely adopted and used in many applications. However, through the years development on it has slowed down. It has become more difficult to maintain due to its need to be compliant with very old versions of Java and became End of Life in August 2015. Its alternative, SLF4J/Logback made many needed improvements to the framework. So why bother with Log4j 2? Here are a few of the reasons.
一、log4j简介
1、log4j支持.properties或xml这两种文件类型的配置文件。
2、log4j只需要引入一个jar包即可:
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
3、.properties配置文件属性
### 设置根logger###
log4j.rootLogger = debug,stdout,D,E ### 输出信息到控制台###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n ### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E://logs/log.log
log4j.appender.D.Append = true
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 ### 输出ERROR 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log
log4j.appender.E.Append = true
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
根Logger:log4j.rootLogger = [ level ] , appenderName, appenderName2
level:日志级别,如果级别设置为INFO,则优先级大于等于INFO级别的日志信息将被输出,小于该级别的不会被输出。
appenderName:日志信息输出目的地,可以是控制台,可以是文件。
配置log输出目的地
org.apache.log4j.ConsoleAppender:输出到控制台
org.apache.log4j.FileAppender:文件
org.apache.log4j.DailyRollingFileAppender:每天产生一个文件
org.apache.log4j.RollingFileAppender:文件大小到达指定尺寸时产生一个新文件
选项
Append = false:默认值是true,将日志追加到文件的最后,false是指将新的日志消息覆盖文件原有的内容
Threshold = INFO:指定日志输出的最低层次,这里INFO及以上层次的日志都将被输出
filter.F=org.apache.log4j.varia.LevelRangeFilter:这个是对Threshold的一个补充,如果Threshold配置成DEBUG,则debug.log会记录debug、info、error等所有级别大于debug的日志
这样在通过日志定位问题的时候有点杂乱,我们想debug、info、error的日志分别记录到对应的文件中该怎么办?
filter.F.LevelMin=DEBUG
filter.F.LevelMax=DEBUG
这样就能通过该文件里记录的日志级别(最大和最小的都是debug,则只能记录debug)
layout = org.apache.log4j.PatternLayout:日志信息的输出格式,PatternLayout指可以灵活地指定布局模式
layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}:配置具体的日志格式
%d:输出日志时间点的日期或时间,也可以指定格式,如:%d{yyyy-MM-dd HH:mm:ss}
%c:输出日志信息所属的类目,通常就是所在类的全名
%t:输出产生该日志事件的线程名
%L:输出代码中的行号
%n:输出一个回车换行符
二、 普通的JAVA工程
1、pom.xml
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
log4j只需要引入这一个jar
2、log4j.properties的配置
### 设置根logger###
log4j.rootLogger = debug,stdout,D,E ### 输出信息到控制台###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%d{yyyyMMdd HH:mm:ssSSS\}%-5p]{%m} %l%n ### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E://logs/debug.log
log4j.appender.D.Append = true
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = [%d{yyyyMMdd HH:mm:ssSSS\}%-5p]{%m} %l%n
log4j.appender.D.filter.F=org.apache.log4j.varia.LevelRangeFilter
log4j.appender.D.filter.F.LevelMin=DEBUG
log4j.appender.D.filter.F.LevelMax=DEBUG ### 输出ERROR 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = [%d{yyyyMMdd HH:mm:ssSSS\}%-5p]{%m} %l%n
log4j.appender.E.filter.F=org.apache.log4j.varia.LevelRangeFilter
log4j.appender.E.filter.F.LevelMin=ERROR
log4j.appender.E.filter.F.LevelMax=ERROR
该配置文件我有意放在src目录下,项目启动的时候能直接查找到。后面会专门介绍log4j配置文件的加载
3、测试类:
package cd.com.log4j; import org.apache.log4j.Logger; public class App
{
private static Logger logger = Logger.getLogger(App.class); public static void main( String[] args )
{
//日志记录
logger.debug("debug: hello log4j");
logger.info("info: hello log4j");
logger.warn("warn: hello log4j");
logger.error("error: hello log4j");
}
}
4、控制台输出:
[20170524 10:38:02401DEBUG]{debug: hello log4j} cd.com.log4j.App.main(App.java:12)
[20170524 10:38:02401INFO ]{info: hello log4j} cd.com.log4j.App.main(App.java:13)
[20170524 10:38:02401WARN ]{warn: hello log4j} cd.com.log4j.App.main(App.java:14)
[20170524 10:38:02401ERROR]{error: hello log4j} cd.com.log4j.App.main(App.java:15)
5、输出的日志文件:
6.日志内容:
通过前面的配置,我们可以将不同级别的日志输出到对应的文件中
三、WEB工程
对于不同的应用服务器(或者web服务器)来说,classloader的层次不尽相同。这里以最简单的tomcat来说,如果你的应用是部署到 tomcat下的,使用log4j配置文件的顺序就是$TOMCAT_HOME/lib/log4j.xml或者log4j.properties;你自己web应用/WEB-INF/classes(或者lib)/log4j.xml
log4j的jar默认就是到src目录下去找log4j.properties 这个配置文件的。但是如果你没有将这个文件放在src下的。这个情况下就需要自己去加载了
我这里的工程是基于SpringMVC模式的
1、pom.xml:导入的还是跟上面一样的jar包
2.需要在web.xml中配置log4j.properties,这样在容器启动的时候可以加载到log4j的配置文件
3、测试类:
package com.cd.mvc.controller; import javax.servlet.ServletRequest; import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import com.cd.mvc.bean.Product; @Controller
public class DemoController implements EnvironmentAware
{
private Environment environment = null;
@Autowired
private Product product;
static Logger logger = Logger.getLogger(DemoController.class); @RequestMapping(value = "index/{id}", method = RequestMethod.GET,params="age=14")
public String index(@PathVariable int id,ServletRequest request,Model model)
{
logger.info("info id =" + id);
logger.warn("warn id =" + id);
logger.error("error id =" + id);
// String age = (String)request.getParameter("age");
// System.out.println(age);
// this.product.sayHello();
// model.addAttribute("title","hello hangzhou");
return "index";
} @Override
public void setEnvironment(Environment environment)
{
this.environment = environment;
}
}
4、使用postman进行访问:
5、控制台打印信息:
这样我们介绍了普通的JAVA工程和web工程两种方式集成log4j,后面一种应该是比较常见的场景:可以自己配置log4j.properties的存放位置,只需要在web.xml中正确引用即可。
四、spring + log4j的Junit测试
由于Junit测试非常方便,也是开发过程中必不可少的一个环节。但是这种整合起来比较复杂,由于spring+log4j的整合,是在web.xml中配置的,如上一个示例所示。在tomcat启动时,spring会主动去加载log4j的配置文件。而Junit测试是不需要启动tomcat的,所以会造成文件找到的报错。
这里提供两种方法:
一、被动查找
1、将配置文件放在src下面,log4j的jar包会默认从src目录下查找:
这里配置文件是放在src/main/resources下的,log4j的jar包会自动到该目录下寻找。
2、Junit测试类:
package com.cd.mvc.controller; import java.io.IOException; import org.apache.log4j.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:META-INF/spring/springContext.xml")
public class TestLog4j
{
private final static Logger logger = Logger.getLogger(TestLog4j.class); @Test
public void test() throws IOException
{
logger.info("info layout");
logger.warn("warn layout");
logger.error("error layout");
}
}
该测试类中的两个注解都是跟Junit相关的,与log4j无关。这里我们没有手动去加载log4j的配置文件。
3、测试结果:
[20170524 15:57:04030INFO ]{Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]} org.springframework.test.context.support.AbstractTestContextBootstrapper.getDefaultTestExecutionListenerClassNames(AbstractTestContextBootstrapper.java:256)
[20170524 15:57:04038INFO ]{Could not instantiate TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttributeSource]} org.springframework.test.context.support.AbstractTestContextBootstrapper.instantiateListeners(AbstractTestContextBootstrapper.java:204)
[20170524 15:57:04039INFO ]{Could not instantiate TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttribute]} org.springframework.test.context.support.AbstractTestContextBootstrapper.instantiateListeners(AbstractTestContextBootstrapper.java:204)
[20170524 15:57:04040INFO ]{Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@79560ca4, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@582138, org.springframework.test.context.support.DirtiesContextTestExecutionListener@19ece3b5]} org.springframework.test.context.support.AbstractTestContextBootstrapper.getTestExecutionListeners(AbstractTestContextBootstrapper.java:182)
[20170524 15:57:04130INFO ]{Loading XML bean definitions from class path resource [META-INF/spring/springContext.xml]} org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:317)
[20170524 15:57:04236INFO ]{Refreshing org.springframework.context.support.GenericApplicationContext@602f958: startup date [Wed May 24 15:57:04 CST 2017]; root of context hierarchy} org.springframework.context.support.AbstractApplicationContext.prepareRefresh(AbstractApplicationContext.java:582)
[20170524 15:57:04297INFO ]{Loading properties file from file [D:\workspace\SpringMVC\target\classes\META-INF\log4j.properties]} org.springframework.core.io.support.PropertiesLoaderSupport.loadProperties(PropertiesLoaderSupport.java:172)
[20170524 15:57:04335INFO ]{info layout} com.cd.mvc.controller.TestLog4j.test(TestLog4j.java:23)
[20170524 15:57:04336WARN ]{warn layout} com.cd.mvc.controller.TestLog4j.test(TestLog4j.java:24)
[20170524 15:57:04336ERROR]{error layout} com.cd.mvc.controller.TestLog4j.test(TestLog4j.java:25)
[20170524 15:57:04339INFO ]{Closing org.springframework.context.support.GenericApplicationContext@602f958: startup date [Wed May 24 15:57:04 CST 2017]; root of context hierarchy} org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:987)
控制台上打印了info以上级别日志,有spring启动过程中的日志,有业务中输出的日志。
二、主动查找
1、Junit测试类:
package com.cd.mvc.controller; import java.io.IOException; import org.apache.log4j.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.Log4jConfigurer; @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:META-INF/spring/springContext.xml")
public class TestLog4j
{
private final static Logger logger = Logger.getLogger(TestLog4j.class); @Test
public void test() throws IOException
{
Log4jConfigurer.initLogging("classpath:META-INF/log4j.properties");
logger.info("info layout");
logger.warn("warn layout");
logger.error("error layout");
}
}
2、这里比示例1多了一行用红色标记的代码,作用就是主动去查找log4j.properties,该配置文件不在默认的src目录下,log4j的jar包无法找到。
3、测试结果:
由于这种方式中配置文件灵活可配置,建议采用这种方式进行单元测试。