springmvc controller自動打印出入引數以及列印其他有用資訊
阿新 • • 發佈:2020-12-22
使用說明
com.xxx包下
加了@RestController註解的controller
列印的日誌規格如下:
包含:ip地址、url、全限定類名+方法名、請求時間、請求引數(支援多個)、響應時間、響應引數、響應時間(毫秒)、關鍵字、序列號(用於和響應列印匹配)
# 請求列印 2020-12-22 16:15:08.473 INFO 9920 --- [nio-9600-exec-8] p.DefaltControllerPrintInputOutputAcpect : xxxx_cloud_aop_request: { "ipaddr":"127.0.0.1:9600", "url":"/testcfg4", "method":"com.xxx.biz.api.DictController.testcfg4","requestTime":"2020-12-22 16:15:08", "request":"[{\"a\":\"aa\",\"b\":\"bb\",\"c\":\"cc\"}]", "keyword":"zxp", "sn":"1608624908470_58" } # 響應列印 2020-12-22 16:15:08.474 INFO 9920 --- [nio-9600-exec-8] p.DefaltControllerPrintInputOutputAcpect : xxxx_cloud_aop_response: { "ipaddr":"127.0.0.1:9600", "url":"/testcfg4", "method":"com.xxx.biz.api.DictController.testcfg4","responseTime":"2020-12-22 16:15:08", "response":"{\"code\":200,\"msg\":\"\"}", "rt":4, "keyword":"zxp", "sn":"1608624908470_58" }
列印個性配置@PrintControllerLog
不列印請求報文
@RequestMapping(value = "/testdown", method = RequestMethod.GET) @PrintControllerLog(notPrintRequest = true) public Result downloadFile(HttpServletResponse response) {
不列印響應報文
@RequestMapping(value = "/testdown", method = RequestMethod.GET) @PrintControllerLog(notPrintResponse = true) public Result downloadFile(HttpServletResponse response) {
都不列印
@PrintControllerLog(notPrintResponse = true,notPrintRequest = true)
配置keyword
@RequestMapping(value = "/testdown", method = RequestMethod.GET) @PrintControllerLog(keyword = "zxp") public Result downloadFile(HttpServletResponse response) {
輸出
2020-12-22 16:15:08.474 INFO 9920 --- [nio-9600-exec-8] p.DefaltControllerPrintInputOutputAcpect : xxxx_cloud_aop_response: { "ipaddr":"127.0.0.1:9600", "url":"/testcfg4", "method":"com.xxxx.biz.api.DictController.testcfg4", "responseTime":"2020-12-22 16:15:08", "response":"{\"code\":200,\"msg\":\"\"}", "rt":4, "keyword":"zxp", "sn":"1608624908470_58" }
配置pretty
可以配置輸出是否格式化json,預設格式化
@PrintControllerLog(pretty = false)
2020-12-22 17:02:07.922 INFO 13516 --- [nio-9600-exec-1] p.DefaltControllerPrintInputOutputAcpect : xxxx_cloud_aop_response: { "ipaddr":"127.0.0.1:9600", "url":"/dict/batchcode", "method":"com.xxx.biz.api.DictController.getBatchCode", "authorization":"Bearer 02c28b9e-e554-453d-836d-0968f9c48e3c", "responseTime":"2020-12-22 17:02:07", "rt":287, "keyword":"", "sn":"1608627727565_70", "response":{ "code":200, "data":{ "opLogLevel":{ "1":"提示", "2":"警告", "3":"嚴重", "4":"致命" } }, "msg":"" } }
關鍵實現思路
- 切面切RestController,且可以限定包名
- 通過ThreadLocal實現rt計算以及sn,並在完成計算後removeThreadLocal
- 可以根據PrintControllerLog做一些更靈活的配置
註解PrintControllerLog
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface PrintControllerLog { //是否美化json輸出 public boolean pretty() default true; //是否列印請求 public boolean notPrintRequest() default false; //是否列印返回 public boolean notPrintResponse() default false; //斌哥提的需求,設定keyword方便統一查詢 public String keyword() default ""; }
切面類DefaltControllerPrintInputOutputAcpect實現
@Aspect @Component @Slf4j public class DefaltControllerPrintInputOutputAcpect { private ThreadLocal<PrintRunnerInfo> SN_CONTEXT = new ThreadLocal<>(); /** * XXX包下的切面 */ @Pointcut("within(com.XXX..*)") public void anController0() { } /** * 加了RestController註解的切面 */ @Pointcut("@within(org.springframework.web.bind.annotation.RestController)") public void anController99() { } @Before("anController99() && anController0()") public void before(JoinPoint joinPoint) { try{ printBaseAndRequest(joinPoint); }catch (Exception e){ } } @AfterReturning(returning = "ret",pointcut="anController99() && anController0()") public void after(JoinPoint joinPoint,Object ret){ try{ printResponse(ret,joinPoint); }catch (Exception e){ } } /** * 執行前列印 * @param joinPoint */ public void printBaseAndRequest(JoinPoint joinPoint) { PrintReqInfo printReqInfo = new PrintReqInfo(); //設定開始時間 printReqInfo.setRequestTime(genNow()); //獲取一個sn,並對TL中的執行情況物件做相應設定 printReqInfo.setSn(getAndSetupSn()); // 設定方法路徑 printReqInfo.setMethod(getMethod(joinPoint)); // 取配置 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); PrintControllerLog printControllerLog = methodSignature.getMethod().getAnnotation(PrintControllerLog.class); PrintCfgInfo printCfgInfo = getCfg(printControllerLog); printReqInfo.setKeyword(printCfgInfo.getKeyword()); //設定url printReqInfo.setUrl(getUrl()); //設定Authorization printReqInfo.setAuthorization(getAuthorization()); //設定IpAddr printReqInfo.setIpaddr(getIpAddr()); //設定請求引數 fillPrintReqInfo(joinPoint,printReqInfo,printCfgInfo.notPrintRequest); //列印請求引數 if (!printCfgInfo.isNotPrintRequest()) { if(printCfgInfo.isPretty()){ log.info("xxxx_cloud_aop_request: {}", JSON.toJSONString(printReqInfo,true)); }else{ log.info("xxxx_cloud_aop_request: {}", JSON.toJSONString(printReqInfo)); } } } /** * 獲取配置 * @param printControllerLog * @return */ private PrintCfgInfo getCfg(PrintControllerLog printControllerLog){ PrintCfgInfo printCfgInfo = new PrintCfgInfo(); if (printControllerLog != null) { printCfgInfo.setKeyword(printControllerLog.keyword()); printCfgInfo.setNotPrintRequest(printControllerLog.notPrintRequest()); printCfgInfo.setNotPrintResponse(printControllerLog.notPrintResponse()); printCfgInfo.setPretty(printControllerLog.pretty()); }else{ printCfgInfo.setKeyword(""); printCfgInfo.setNotPrintRequest(false); printCfgInfo.setNotPrintResponse(false); printCfgInfo.setPretty(true); } return printCfgInfo; } /** * 執行後列印 * @param joinPoint */ public void printResponse(Object ret,JoinPoint joinPoint) { PrintResInfo printResInfo = new PrintResInfo(); //設定開始時間 printResInfo.setResponseTime(genNow()); //獲取一個sn,並對TL中的執行情況物件做相應設定 printResInfo.setSn(getAndSetupSn()); //設定rt printResInfo.setRt(getRt()); //清理TL cleanTL(); // 設定方法路徑 printResInfo.setMethod(getMethod(joinPoint)); // 取配置 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); PrintControllerLog printControllerLog = methodSignature.getMethod().getAnnotation(PrintControllerLog.class); PrintCfgInfo printCfgInfo = getCfg(printControllerLog); printResInfo.setKeyword(printCfgInfo.getKeyword()); //設定url printResInfo.setUrl(getUrl()); //設定Authorization printResInfo.setAuthorization(getAuthorization()); //設定IpAddr printResInfo.setIpaddr(getIpAddr()); //設定返回引數 fillPrintResInfo(ret,printResInfo,printCfgInfo.notPrintResponse); //列印返回結果 if (!printCfgInfo.isNotPrintResponse()) { if(printCfgInfo.isPretty()) { log.info("xxxx_cloud_aop_response: {}", JSON.toJSONString(printResInfo, true)); }else{ log.info("xxxx_cloud_aop_response: {}", JSON.toJSONString(printResInfo)); } } } /** * 填充剩餘資訊 * @param joinPoint * @param printReqInfo * @param notPrintReq */ private void fillPrintReqInfo(JoinPoint joinPoint,PrintReqInfo printReqInfo,boolean notPrintReq){ Object[] args = joinPoint.getArgs(); if(args != null && args.length > 0 ) { List<Object> objects = Arrays.asList(args).stream().filter(s -> !isFile(s)).collect(Collectors.toList()); if (objects != null && objects.size() > 0 && !notPrintReq) { try { printReqInfo.setRequest(args); }catch(Exception e){} } } } /** * 填充剩餘資訊 * @param ret * @param printResInfo * @param notPrintRes */ private void fillPrintResInfo(Object ret,PrintResInfo printResInfo,boolean notPrintRes){ if (ret != null && !notPrintRes) { try { printResInfo.setResponse(ret); }catch(Exception e){} } } private boolean isFile(Object obj){ if(obj instanceof MultipartFile){ return true; } return false; } /** * 獲取一個sn,並對TL中的執行情況物件做相應設定 * 當第二次執行TL中已經有相應資訊 * 此sn不能保證唯一,為了對應列印日誌的請求和響應 * @return */ private String getAndSetupSn(){ if(SN_CONTEXT.get() != null && !StringUtils.isEmpty(SN_CONTEXT.get().getSn())){ SN_CONTEXT.get().setEnd(System.currentTimeMillis()); SN_CONTEXT.get().setRt(SN_CONTEXT.get().getEnd()-SN_CONTEXT.get().getStart()); return SN_CONTEXT.get().getSn(); }else{ String sn = System.currentTimeMillis()+"_"+new Random().nextInt(100); SN_CONTEXT.set(PrintRunnerInfo.builder().sn(sn).start(System.currentTimeMillis()).build()); return sn; } } /** * 獲取rt * @return */ private Long getRt(){ if(SN_CONTEXT.get() != null){ return SN_CONTEXT.get().getRt(); }else{ return 0L; } } /** * 清楚TL */ private void cleanTL(){ SN_CONTEXT.remove(); } private String genNow(){ return DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()); } /** * 獲取當前請求的url * @return */ private String getUrl(){ ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = requestAttributes.getRequest(); String requestURL = request.getRequestURI(); return requestURL; } /** * 獲取當前請求的Authorization * @return */ private String getAuthorization(){ ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = requestAttributes.getRequest(); return request.getHeader("Authorization"); } /** * 獲取IpAddr * @return */ private String getIpAddr(){ ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = requestAttributes.getRequest(); // 取得伺服器IP String ip = request.getLocalAddr(); // 取得伺服器埠 int port = request.getLocalPort(); return ip+":"+port; } /** * 獲得方法名稱 * @param joinPoint * @return */ private String getMethod(JoinPoint joinPoint){ String method = ""; try{ MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); String methodPackage = methodSignature.getDeclaringTypeName(); method = methodPackage; if(methodSignature.getMethod() != null){ method+="."+methodSignature.getMethod().getName(); } return method; }catch (Exception e){ return method; } } /** * 配置物件 from PrintControllerLog */ @Data private static class PrintCfgInfo{ boolean pretty = true; //不列印基礎資訊 boolean notPrintRequest = false; //不列印基礎資訊 boolean notPrintResponse = false; //日誌關鍵字 String keyword = ""; } /** * 執行資料 */ @Data @Builder private static class PrintRunnerInfo{ //此sn不能保證唯一,為了對應列印日誌的請求和響應 private String sn; private Long start; private Long end; private Long rt; } /** * 請求列印 */ @Data private static class PrintReqInfo{ //ipaddr @JSONField(ordinal = 1) String ipaddr = ""; //此sn不能保證唯一,為了對應列印日誌的請求和響應 @JSONField(ordinal = 8) String sn = ""; //url @JSONField(ordinal = 2) String url = ""; //日誌關鍵字 @JSONField(ordinal = 7) String keyword = ""; //方法名(含全限定類名) @JSONField(ordinal = 3) String method = ""; //請求引數 @JSONField(ordinal = 10) Object[] request; //請求時間 @JSONField(ordinal = 5) String requestTime = ""; //Authorization @JSONField(ordinal = 4) String authorization = ""; } /** * 響應列印 */ @Data private static class PrintResInfo{ //ipaddr @JSONField(ordinal = 1) String ipaddr = ""; //此sn不能保證唯一,為了對應列印日誌的請求和響應 @JSONField(ordinal = 9) String sn = ""; //url @JSONField(ordinal = 2) String url = ""; //日誌關鍵字 @JSONField(ordinal = 8) String keyword = ""; //方法名(含全限定類名) @JSONField(ordinal = 3) String method = ""; //返回引數 @JSONField(ordinal = 10) Object response = ""; //響應時間 @JSONField(ordinal = 5) String responseTime = ""; //RT ms @JSONField(ordinal = 7) Long rt = 0L; //Authorization @JSONField(ordinal = 4) String authorization = ""; } }RestController詳細X 沒有英漢互譯結果
請嘗試網頁搜尋