介绍:
很多时候会需要提供一些统计记录的,比如某个服务一个月的被调用量、接口的调用次数、成功调用次数等等。
优点:
使用AOP+Hendler对业务逻辑代码无侵入,完全解耦。通过spring boot自带的健康检查接口(/health)方便、安全。
注意:
数据没有被持久化,只保存在内存中,重启后数据将被重置。可按需自己实现
代码:
AOP:在AOP中调用Handler
@Component @Aspect public class ControllerAdvice { private static ILogger log = LoggerFactory.getLogger(ControllerAdvice.class); @Around("execution(public * *..*controller.*.*(..))") public Object handle(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { Object result; try { Function<ProceedingJoinPoint, AbstractControllerHandler> build = AbstractControllerHandler.getBuild(); if (null == build) { AbstractControllerHandler.registerBuildFunction(DefaultControllerHandler::new); } build = AbstractControllerHandler.getBuild(); AbstractControllerHandler controllerHandler = build.apply(proceedingJoinPoint); if (null == controllerHandler) { log.warn(String.format("The method(%s) do not be handle by controller handler.", proceedingJoinPoint.getSignature().getName())); result = proceedingJoinPoint.proceed(); } else { result = controllerHandler.handle(); } } catch (Throwable throwable) { RuntimeHealthIndicator.failedRequestCount++; log.error(new Exception(throwable), "Unknown exception- -!"); throw throwable; } return result; } }
Handler:执行记录的逻辑
抽象类:AbstractControllerHandler
public abstract class AbstractControllerHandler { private static ILogger log = LoggerFactory.getLogger(AbstractControllerHandler.class); private static Function<ProceedingJoinPoint, AbstractControllerHandler> build; public static Function<ProceedingJoinPoint, AbstractControllerHandler> getBuild() { return build; } public static void registerBuildFunction(Function<ProceedingJoinPoint, AbstractControllerHandler> build) { Assert.isNotNull(build, "build"); AbstractControllerHandler.build = build; } protected ProceedingJoinPoint proceedingJoinPoint; protected HttpServletRequest httpServletRequest; protected String methodName; protected String uri; protected String requestBody; protected String ip; protected Method method; protected boolean inDataMasking; protected boolean outDataMasking; public AbstractControllerHandler(ProceedingJoinPoint proceedingJoinPoint) { Assert.isNotNull(proceedingJoinPoint, "proceedingJoinPoint"); this.proceedingJoinPoint = proceedingJoinPoint; Signature signature = this.proceedingJoinPoint.getSignature(); this.httpServletRequest = this.getHttpServletRequest(this.proceedingJoinPoint.getArgs()); this.methodName = signature.getName(); this.uri = null == this.httpServletRequest ? null : this.httpServletRequest.getRequestURI(); this.requestBody = this.formatParameters(this.proceedingJoinPoint.getArgs()); this.ip = null == this.httpServletRequest ? "" : CommonHelper.getIp(this.httpServletRequest); this.inDataMasking = false; this.outDataMasking = false; if (signature instanceof MethodSignature) { MethodSignature methodSignature = (MethodSignature) signature; try { this.method = proceedingJoinPoint.getTarget().getClass().getMethod(this.methodName, methodSignature.getParameterTypes()); if (null != this.method) { LogDataMasking dataMasking = this.method.getDeclaredAnnotation(LogDataMasking.class); if (null != dataMasking) { this.inDataMasking = dataMasking.in(); this.outDataMasking = dataMasking.out(); } } } catch (NoSuchMethodException e) { e.printStackTrace(); } } } public abstract Object handle() throws Throwable; protected void logIn() { String requestBody = this.requestBody; if (this.inDataMasking) { requestBody = "Data Masking"; } log.info(String.format("Start-[%s][%s][%s][body: %s]", this.ip, this.uri, this.methodName, requestBody)); } protected void logOut(long elapsedMilliseconds, boolean success, String responseBody) { if (success) { if (this.outDataMasking) { responseBody = "Data Masking"; } log.info( String.format( "Success(%s)-[%s][%s][%s][response body: %s]", elapsedMilliseconds, this.ip, this.uri, this.methodName, responseBody)); } else { log.warn( String.format( "Failed(%s)-[%s][%s][%s][request body: %s][response body: %s]", elapsedMilliseconds, this.ip, this.uri, this.methodName, this.requestBody, responseBody)); } } protected HttpServletRequest getHttpServletRequest(Object[] parameters) { try { if (null != parameters) { for (Object parameter : parameters) { if (parameter instanceof HttpServletRequest) { return (HttpServletRequest) parameter; } } } return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); } catch (Exception e) { log.error(e); return null; } } protected String formatParameters(Object[] parameters) { if (null == parameters) { return null; } else { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < parameters.length; i++) { Object parameter = parameters[i]; if (parameter instanceof HttpServletRequest || parameter instanceof HttpServletResponse) { continue; } stringBuilder.append(String.format("[%s]: %s.", i, JSON.toJSONString(parameter))); } return stringBuilder.toString(); } }
实现类:
public class DefaultControllerHandler extends AbstractControllerHandler { private static ILogger log = LoggerFactory.getLogger(DefaultControllerHandler.class); private static int currentMonth = Calendar.getInstance().get(Calendar.MONTH) + 1; public DefaultControllerHandler(ProceedingJoinPoint proceedingJoinPoint) { super(proceedingJoinPoint); } @Override public Object handle() throws Throwable { long timestamp = System.currentTimeMillis(); this.logIn(); ResponseDto responseDto; boolean success = false; try { Object result = proceedingJoinPoint.proceed(); if (result instanceof ResponseDto) { responseDto = (ResponseDto) result; } else { responseDto = ResponseDto.success(result); } success = true; RuntimeHealthIndicator.successRequestCount++; } catch (BusinessException e) { // RuntimeHealthIndicator.failedRequestCount++; if (this.isDebugLogLevel()) { log.error(e); } responseDto = new ResponseDto<>(e.getCode(), e.getMessage(), null); } catch (Exception e) { RuntimeHealthIndicator.failedRequestCount++; if (this.isDebugLogLevel()) { log.error(e); } responseDto = ResponseDto.failed(ExceptionDefinitions.ServerError, e.getMessage(), null); } finally { Calendar cale = Calendar.getInstance(); if (currentMonth != (cale.get(Calendar.MONTH) + 1)) { String recodeKey = String.format("%d年%d月", cale.get(Calendar.YEAR), cale.get(Calendar.MONTH) + 1); String recodeValue = "successCount:" + RuntimeHealthIndicator.successRequestCount + " failedCount:" + RuntimeHealthIndicator.failedRequestCount; RuntimeHealthIndicator.historyRequestRecode.put(recodeKey, recodeValue); RuntimeHealthIndicator.successRequestCount = 0; RuntimeHealthIndicator.failedRequestCount = 0; currentMonth = cale.get(Calendar.MONTH); } } long duration = System.currentTimeMillis() - timestamp; RuntimeHealthIndicator.markRestApiInvoked(this.methodName, (int) duration); this.logOut(duration, success, JSON.toJSONString(responseDto)); return responseDto; } public boolean isDebugLogLevel() { return log.isEnabled(LogLevel.DEBUG); } }