AopLog是基於Spring Aop 和ThreadLocal實現的一個專門對請求方法內容日志的攔截與處理的日志工具包。
場景 :
- 使用Spring Aop攔截參數日志目前大部分做法都基本上大同小異,不想日后每個項目工程都寫一份這樣的Aop攔截處理日志的代碼,甚至代碼侵入。
- 我想知道一些相對重要的請求方法的請求參數,響應參數,請求頭,以及內部耗時,方法是成功還是失敗等等信息。發生錯誤時我也不知道執行到哪一步發生了異常,是不是某個參數導致出的邏輯問題。
- 普通的log.info或warn信息沒有所屬請求的上下關系,並不方便查看和分析。
- 正式環境中,我並不想打印太多無意義的info日志(有些只是為了排查問題打印的日志,程序正常運行時其實毫無意義),只希望在發生異常時記錄日志或者只希望每次請求只記錄一條關鍵的請求信息。
- 日志的收集,我希望將這些請求的日志記錄下來,記錄的實現方式我自己決定,比如正常的日志打印,常見的日志寫入數據庫,日志寫入到文件,日志入隊列等等。
- 整個日志的記錄完全不干擾正常請求方法的流程,日志的收集處理異步化,完全不影響正常請求方法的性能與響應。
- 只需要通過
@AopLog
注解決定是否記錄。
快速開始
項目通過maven的pom.xml引入
<dependency>
<groupId>com.github.ealenxie</groupId>
<artifactId>aop-log</artifactId>
<version>2.4</version>
</dependency>
或者通過gradle引入
compile group: 'com.github.ealenxie', name: 'aop-log', version: '2.4'
@AopLog注解使用,進行日志記錄
直接在類(作用類的所有方法)或類方法(作用於方法)上加上注解@AopLog,進行日志記錄
例如 :
import com.github.AopLog;
import name.ealen.infra.base.resp.RespBody;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author EalenXie create on 2020/6/22 14:28
*/
@AopLog(type = "測試",stackTraceOnErr = true)
@RestController
public class AppController {
@GetMapping("/app/sayHello")
public RespBody<String> sayHello() {
return RespBody.ok("hello EalenXie");
}
}
自定義全局的日志收集器實現收集 LogCollector
例如只是簡單打印,或寫入到庫等等。
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.LogData;
import com.github.collector.LogCollector;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @author EalenXie create on 2020/9/15 13:46
* 此為樣例參考
* 配置一個簡單的日志收集器 這里只是做了一個log.info打印一下,可以在這里寫入到數據庫中或者寫入
*/
@Slf4j
@Component
public class AopLogCollector implements LogCollector {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public void collect(LogData logData) {
try {
log.info(objectMapper.writeValueAsString(logData));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
配置@Component的全局日志收集器只能配置一個。
接口調用 /say/hello
測試即可看看到控制台打印出結果 :
2020-09-16 16:01:04.782 INFO 2012 --- [AsyncExecutor-2] name.ealen.infra.advice.AopLogCollector : {"appName":"app-template","host":"127.0.0.1","port":8080,"clientIp":"192.168.110.1","reqUrl":"http://localhost:8080/app/sayHello","httpMethod":"GET","headers":{"User-Agent":"Apache-HttpClient/4.5.10 (Java/11.0.5)"},"type":"測試","content":"","method":"name.ealen.api.facade.AppController#sayHello","args":null,"respBody":{"code":"200","desc":"OK","message":"請求成功","dateTime":"2020-09-16 16:01:04","body":"hello EalenXie"},"logDate":1600243264780,"costTime":1,"threadName":"http-nio-8080-exec-3","threadId":33,"success":true}
記錄的日志對象LogData屬性說明
LogData 記錄的內容
字段 | 類型 | 注釋 |
---|---|---|
appName | String | 應用名稱 |
host | String | 主機 |
port | int | 端口號 |
clientIp | String | 請求客戶端的Ip |
reqUrl | String | 請求地址 |
headers | Object | 請求頭部信息(可選擇記錄) 默認記錄user-agent,content-type |
type | String | 操作類型,默認值undefined |
content | String | 方法步驟內容,默認是空,可使用LogData.step進行內容步驟記錄 |
method | String | 請求的本地java方法 |
args | Object | 方法請求參數 |
respBody | Object | 方法響應參數 |
costTime | long | 整個方法耗時 |
logDate | Date | Log產生時間,LogData對象初始化的時間 |
threadName | String | 線程名稱 |
threadId | long | 線程Id |
success | boolean | 執行狀態,成功(true)/異常(false) |
AopLog 注解選項說明
選項 | 類型 | 說明 | 默認 |
---|---|---|---|
logOnErr | boolean | 僅當發生異常時才記錄收集 | false |
type | String | 操作類型 | 默認值"undefined" |
headers | String[] | 記錄的header信息 ,選擇要記錄哪些header信息 | 默認"User-Agent","content-type" |
args | boolean | 是否記錄請求參數 | true |
respBody | boolean | 是否記錄響應參數 | true |
stackTraceOnErr | boolean | 當目標方法發生異常時,是否追加異常堆棧信息到LogData的content中 | false |
asyncMode | boolean | 異步方式收集 | true |
collector | Class<? extends LogCollector> | 指定日志收集器 | 默認不調整收集器,使用全局的日志收集器 |
LogData的step方法。
記錄步驟。(如果某些重要步驟希望被記錄下來)
例如 :
import com.github.AopLog;
import com.github.LogData;
import name.ealen.infra.base.resp.RespBody;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author EalenXie create on 2020/6/22 14:28
*/
@AopLog(type = "測試",stackTraceOnErr = true)
@RestController
public class AppController {
@GetMapping("/app/sayHello")
public RespBody<String> sayHello() {
LogData.step("1. 第一步執行完成");
//......
LogData.step("2. 第二步執行完成");
//.....
LogData.step("3. service的方法執行完成");
//.....
return RespBody.ok("hello EalenXie");
}
}
此時再次接口調用 /say/hello
測試即可看看到控制台打印出結果,重點觀察content字段 :
2020-09-16 17:26:20.285 INFO 3284 --- [AsyncExecutor-2] name.ealen.infra.advice.AopLogCollector : {"appName":"app-template","host":"127.0.0.1","port":8080,"clientIp":"192.168.110.1","reqUrl":"http://localhost:8080/app/sayHello","httpMethod":"GET","headers":{"User-Agent":"Apache-HttpClient/4.5.10 (Java/11.0.5)"},"type":"測試","content":"1. 第一步執行完成\n2. 第二步執行完成\n3. service的方法執行完成\n","method":"name.ealen.api.facade.AppController#sayHello","args":null,"respBody":{"code":"200","desc":"OK","message":"請求成功","dateTime":"2020-09-16 17:26:20","body":"hello EalenXie"},"logDate":1600248380283,"costTime":1,"threadName":"http-nio-8080-exec-2","threadId":32,"success":true}
關於
開源Github地址 : https://github.com/EalenXie/aop-log
感謝各位提出意見和支持。