Springboot整合log4j日誌系統_aop監聽service執行時間

1.SpringBoot整合log4j日誌系統

大家都知道程序員敲代碼是非常爽的,把自己頭腦裏的想法行雲流水的變成一行行代碼敲出來,但是!!!每次找BUG確實非常痛苦,那麼在程序中記錄日誌就是很有必要的了,今天我就要教大家如何在SpringBoot整合log4j日誌系統。

1.首先把SpringBoot自帶的日誌系統剔除
<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>
2.添加日誌框架依賴
<dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.21</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.21</version>
        </dependency>
3.添加log4j.properties
#把指定級別以上的日誌信息輸出到指定的一個或者多個位置
log4j.rootLogger=DEBUG,stdout,file
#表示Logger會在父Logger的appender裏輸出,默認爲true
log4j.additivity.org.apache=true
#表示日誌在控制檯上輸出 (1)org.apache.log4j.ConsoleAppender(控制檯)
#(2)org.apache.log4j.FileAppender(文件)
#(3)org.apache.log4j.DailyRollingFileAppender(每天產生一個日誌文件)
#(4)org.apache.log4j.RollingFileAppender(文件大小到達指定尺寸的時候產生一個新的文件)
#(5)org.apache.log4j.WriterAppender(將日誌信息以流格式發送到任意指定的地方)
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
#日誌級別是INFO級別的,除了DEBUG以外都會輸出
log4j.appender.stdout.threshold=INFO
# 日誌的輸出格式:(1)org.apache.log4j.HTMLLayout(以HTML表格形式佈局)
#(2)org.apache.log4j.PatternLayout(可以靈活地指定佈局模式)
#(3)org.apache.log4j.SimpleLayout(包含日誌信息的級別和信息字符串)
#(4)org.apache.log4j.TTCCLayout(包含日誌產生的時間、線程、類別等等信息)
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 日誌的具體輸出格式:格式化符號說明:
#
#%p:輸出日誌信息的優先級,即DEBUG,INFO,WARN,ERROR,FATAL。
#%d:輸出日誌時間點的日期或時間,默認格式爲ISO8601,也可以在其後指定格式,如:%d{yyyy/MM/dd HH:mm:ss,SSS}。
#%r:輸出自應用程序啓動到輸出該log信息耗費的毫秒數。
#%t:輸出產生該日誌事件的線程名。
#%l:輸出日誌事件的發生位置,相當於%c.%M(%F:%L)的組合,包括類全名、方法、文件名以及在代碼中的行數。例如:test.TestLog4j.main(TestLog4j.java:10)。
#%c:輸出日誌信息所屬的類目,通常就是所在類的全名。
#%M:輸出產生日誌信息的方法名。
#%F:輸出日誌消息產生時所在的文件名稱。
#%L::輸出代碼中的行號。
#%m::輸出代碼中指定的具體日誌信息。
#%n:輸出一個回車換行符,Windows平臺爲"rn",Unix平臺爲"n"。
#%x:輸出和當前線程相關聯的NDC(嵌套診斷環境),尤其用到像java servlets這樣的多客戶多線程的應用中。
#%%:輸出一個"%"字符。
log4j.appender.stdout.layout.ConversionPattern=%-5p %c{1}:%L - %m%n

log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.DatePattern='.'yyyy-MM-dd-HH-mm
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
log4j.appender.file.Threshold=INFO
log4j.appender.file.append=true
log4j.appender.file.File=/workspaces/logs/foodie-api/imooc.log

2.通過aop實現通過日誌實現監聽service執行時間

1.aop是什麼

AOP是Spring框架面向切面的編程思想,AOP採用一種稱爲“橫切”的技術,將涉及多業務流程的通用功能抽取並單獨封裝,形成獨立的切面,在合適的時機將這些切面橫向切入到業務流程指定的位置中。
例如,在一個業務系統中,用戶登錄是基礎功能,凡是涉及到用戶的業務流程都要求用戶進行系統登錄。如果把用戶登錄功能代碼寫入到每個業務流程中,會造成代碼冗餘,維護也非常麻煩,當需要修改用戶登錄功能時,就需要修改每個業務流程的用戶登錄代碼,這種處理方式顯然是不可取的。比較好的做法是把用戶登錄功能抽取出來,形成獨立的模塊,當業務流程需要用戶登錄時,系統自動把登錄功能切入到業務流程中。

2.在項目中使用Aop,添加aop的依賴
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
3.創建切面類

要想把一個類變成切面類,需要兩步,
① 在類上使用 @Component 註解 把切面類加入到IOC容器中
② 在類上使用 @Aspect 註解 使之成爲切面類

4.AOP支持的通知

1、前置通知@Before:在某連接點之前執行的通知,除非拋出一個異常,否則這個通知不能阻止連接點之前的執行流程。
2、後置通知@AfterReturning:在某連接點之後執行的通知,通常在一個匹配的方法返回的時候執行(可以在後置通知中綁定返回值)
3、後置異常通知@AfterThrowing:在方法拋出異常退出時執行的通知。
4、後置最終通知@After:當某連接點退出時執行的通知(不論是正常返回還是異常退出)。
5、環繞通知@Around:包圍一個連接點的通知,如方法調用等。這是最強大的一種通知類型。環繞通知可以在方法調用前後完成自定義的行爲,它也會選擇是否繼續執行連接點或者直接返回它自己的返回值或拋出異常來結束執行。

5.切入點表達式

定義切入點的時候需要一個包含名字和任意參數的簽名,還有一個切入點表達式,如execution(public * com.example.aop…(…))
切入點表達式的格式:execution([可見性]返回類型[聲明類型].方法名(參數)[異常])
其中[]內的是可選的,其它的還支持通配符的使用:

  1. *:匹配所有字符
  2. …:一般用於匹配多個包,多個參數
  3. +:表示類及其子類
    4)運算符有:&&,||,!
6.通過aop實現通過日誌實現監聽service執行時間代碼
package com.test.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class ServiceLogAspect {
    final static Logger log = LoggerFactory.getLogger(ServiceLogAspect.class);

    /**
     * AOP通知:
     * 1. 前置通知:在方法調用之前執行
     * 2. 後置通知:在方法正常調用之後執行
     * 3. 環繞通知:在方法調用之前和之後,都分別可以執行的通知
     * 4. 異常通知:如果在方法調用過程中發生異常,則通知
     * 5. 最終通知:在方法調用之後執行
     */

    /**
     * 切面表達式:
     * execution 代表所要執行的表達式主體
     * 第一處 * 代表方法返回類型 *代表所有類型
     * 第二處 包名代表aop監控的類所在的包
     * 第三處 .. 代表該包以及其子包下的所有類方法
     * 第四處 * 代表類名,*代表所有類
     * 第五處 *(..) *代表類中的方法名,(..)表示方法中的任何參數
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */


    @Around("execution(* com.imooc.service.impl..*.*(..))")
    public Object recordTimeLog(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("====== 開始執行 {}.{} ======",
                joinPoint.getTarget().getClass(),
                joinPoint.getSignature().getName());

        // 記錄開始時間
        long begin = System.currentTimeMillis();

        // 執行目標 service
        Object result = joinPoint.proceed();

        // 記錄結束時間
        long end = System.currentTimeMillis();
        long takeTime = end - begin;

        if (takeTime > 3000) {
            log.error("====== 執行結束,耗時:{} 毫秒 ======", takeTime);
        } else if (takeTime > 2000) {
            log.warn("====== 執行結束,耗時:{} 毫秒 ======", takeTime);
        } else {
            log.info("====== 執行結束,耗時:{} 毫秒 ======", takeTime);
        }

        return result;
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章