1. 程式人生 > >SpringBoot學習1.6-配置log4j2及進行靜態方法封裝

SpringBoot學習1.6-配置log4j2及進行靜態方法封裝

環境:

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) -->
			<ThresholdFilter level="info" onMatch="ACCEPT"
				onMismatch="DENY" />
			<ThresholdFilter level="warn" onMatch="DENY"
				onMismatch="NEUTRAL" />
			<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">
			<ThresholdFilter level="warn" onMatch="ACCEPT"
				onMismatch="DENY" />
			<ThresholdFilter level="error" onMatch="DENY"
				onMismatch="NEUTRAL" />
			<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/:?]

5.demo地址