环境:
jdk1.8;spring boot2.0.2;Maven3.3
摘要说明:
spring boot2默认使用的是Logback;且spring boot2开始不支持log4j只支持log4j2;Logback作为新开发的框架既然被spring boot当成默认日志框架性能肯定是非常好;但网上使用推广度及配置相对比较少,这里就修改配置成log4j2;
步骤:
1.依赖
引入log4j2及去除默认日志框架:
<!-- 引入log4j2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
2.日志配置文件
spring boot默认识别的日志配置文件名称有:
- Logback:logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy
- Log4j:log4j-spring.properties, log4j-spring.xml, log4j.properties, log4j.xml
- Log4j2:log4j2-spring.xml, log4j2.xml
- JDK (Java Util Logging):logging.properties
官网推荐使用的是带有-spring
的文件名作为你的日志配置如log4j2-spring.xml,并且放在 src/main/resources
下面即可;
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出 -->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数 -->
<configuration status="WARN" monitorInterval="30">
<!--先定义所有的appender -->
<appenders>
<!--这个输出控制台的配置 -->
<console name="Console" target="SYSTEM_OUT">
<!--输出日志的格式 -->
<PatternLayout
pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n" />
</console>
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用 -->
<File name="log" fileName="log/test.log" append="false">
<PatternLayout
pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n" />
</File>
<!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 -->
<RollingFile name="RollingFileInfo"
fileName="d:///logs/info.log"
filePattern="d:///logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
<Filters>
<ThresholdFilter level="info" onMatch="ACCEPT"
onMismatch="DENY" />
<ThresholdFilter level="warn" onMatch="DENY"
onMismatch="NEUTRAL" />
</Filters>
<PatternLayout
pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n" />
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="100 MB" />
</Policies>
</RollingFile>
<RollingFile name="RollingFileWarn"
fileName="d:///logs/warn.log"
filePattern="d:///logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
<Filters>
<ThresholdFilter level="warn" onMatch="ACCEPT"
onMismatch="DENY" />
<ThresholdFilter level="error" onMatch="DENY"
onMismatch="NEUTRAL" />
</Filters>
<PatternLayout
pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n" />
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="100 MB" />
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 -->
<DefaultRolloverStrategy max="20" />
</RollingFile>
<RollingFile name="RollingFileError"
fileName="d:///logs/error.log"
filePattern="d:///logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="error" onMatch="ACCEPT"
onMismatch="DENY" />
<PatternLayout
pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n" />
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="100 MB" />
</Policies>
</RollingFile>
</appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效 -->
<loggers>
<!--过滤掉spring和mybatis的一些无用的DEBUG信息 -->
<logger name="org.springframework" level="INFO"></logger>
<logger name="org.mybatis" level="INFO"></logger>
<!-- 可以使用此配置统一定义包下面需要打印的日志级别 -->
<!-- <logger name="com.example" level="INFO"></logger> -->
<root level="all">
<appender-ref ref="Console" />
<appender-ref ref="RollingFileInfo" />
<appender-ref ref="RollingFileWarn" />
<appender-ref ref="RollingFileError" />
</root>
</loggers>
</configuration>
3.使用
每个类使用LogManager.getLogger进行注入;
这里面使用接口和junit配合测试下:
@Component
public class TestLogComponentImpl implements TestLogComponent {
static final Logger logger = LogManager.getLogger(TestLogComponentImpl.class.getName());
@Override
public void test() {
// TODO Auto-generated method stub
logger.info("String类型");
try {
System.out.println(1 / 0);
} catch (Exception e) {
logger.error("Error类型", e);
}
}
}
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.test.context.junit4.SpringRunner;
import com.example.demo.test1.component.TestLogComponent;
@RunWith(SpringRunner.class)
// 引入SpringBootTest并生成随机接口
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class LogTest {
// 注入随机接口
@LocalServerPort
private int port;
@Autowired
private TestLogComponent testLogComponent;
@Test
public void testLog() {
testLogComponent.test();
//testLogComponent.test1();
}
}
测试结果如下:
[09:51:28:539] [INFO] - com.example.demo.test1.component.impl.TestLogComponentImpl.test(TestLogComponentImpl.java:17) - String类型
[09:51:28:540] [ERROR] - com.example.demo.test1.component.impl.TestLogComponentImpl.test(TestLogComponentImpl.java:21) - Error类型
java.lang.ArithmeticException: / by zero
at com.example.demo.test1.component.impl.TestLogComponentImpl.test(TestLogComponentImpl.java:19) [classes/:?]
at com.example.demo.LogTest.testLog(LogTest.java:27) [test-classes/:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_171]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_171]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_171]
at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_171]
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) [junit-4.12.jar:4.12]
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12]
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) [junit-4.12.jar:4.12]
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) [junit-4.12.jar:4.12]
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) [junit-4.12.jar:4.12]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) [.cp/:?]
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:?]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538) [.cp/:?]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760) [.cp/:?]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460) [.cp/:?]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206) [.cp/:?]
4.封装静态方法
从上面我们可以看到打印日志需要在每个类中进行注入;这样的话就比较繁琐,封装静态方法后面临到的问题是日志头部打印的一直是封装的静态方法;
故这里进行初步修改,从栈堆中找出调用方法加到日志头部;
package com.example.demo.taf.log;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* @模块名:demo
* @包名:com.example.demo.taf.log
* @类名称: TAFLog
* @类描述:【类描述】 log4j2封装工具类
* @版本:1.0
* @创建人:cc
* @创建时间:2018年9月29日下午2:48:43
*/
public class TAFLog {
public static String top = "";
private static Logger log = LogManager.getLogger();;
public static void debug(String message) {
if (log.isDebugEnabled()) {
log.debug(top + "-" + message);
}
}
public static void debug(String message, Exception e) {
if (log.isDebugEnabled()) {
getTop();
log.debug(top + "-" + message, e);
}
}
public static void info(String message) {
if (log.isInfoEnabled()) {
getTop();
log.info(top + "-" + message);
}
}
public static void info(String message, Exception e) {
if (log.isInfoEnabled()) {
getTop();
log.info(top + "-" + message, e);
}
}
public static void warn(String message) {
getTop();
log.warn(top + "-" + message);
}
public static void warn(String message, Exception e) {
getTop();
log.warn(top + "-" + message, e);
}
public static void error(String message) {
getTop();
log.error(top + "-" + message);
}
public static void error(String message, Exception e) {
getTop();
log.error(top + "-" + message, e);
}
public static void getTop() {
// 获取堆栈信息
StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
if (null == callStack) {
top = "";
}
else {
// 最原始被调用的堆栈信息
StackTraceElement caller = null;
// 日志类名称
String logClassName = TAFLog.class.getName();
// 循环遍历到日志类标识
boolean isEachLogClass = false;
// 遍历堆栈信息,获取出最原始被调用的方法信息
for (StackTraceElement s : callStack) {
// 遍历到日志类
if (logClassName.equals(s.getClassName())) {
isEachLogClass = true;
}
// 下一个非日志类的堆栈,就是最原始被调用的方法
if (isEachLogClass) {
if (!logClassName.equals(s.getClassName())) {
isEachLogClass = false;
caller = s;
break;
}
}
}
top = caller.toString();
}
}
public static void main(String[] args) throws ClassNotFoundException {
TAFLog.error("自定义LOG");
}
}
再测试接口中添加测试方法进行测试可查看效果:
@Override
public void test1() {
TAFLog.info("String类型");
try {
System.out.println(1 / 0);
} catch (Exception e) {
TAFLog.error("Error类型", e);
}
}
结果如下:
[10:00:29:674] [INFO] - com.example.demo.taf.log.TAFLog.info(TAFLog.java:47) - com.example.demo.test1.component.impl.TestLogComponentImpl.test1(TestLogComponentImpl.java:28)-String类型
[10:00:29:674] [ERROR] - com.example.demo.taf.log.TAFLog.error(TAFLog.java:75) - com.example.demo.test1.component.impl.TestLogComponentImpl.test1(TestLogComponentImpl.java:32)-Error类型
java.lang.ArithmeticException: / by zero
at com.example.demo.test1.component.impl.TestLogComponentImpl.test1(TestLogComponentImpl.java:30) [classes/:?]
at com.example.demo.LogTest.testLog(LogTest.java:28) [test-classes/:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_171]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_171]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_171]
at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_171]
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) [junit-4.12.jar:4.12]
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12]
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) [junit-4.12.jar:4.12]
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) [junit-4.12.jar:4.12]
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) [junit-4.12.jar:4.12]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) [spring-test-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) [.cp/:?]
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:?]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538) [.cp/:?]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760) [.cp/:?]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460) [.cp/:?]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206) [.cp/:?]