SpringBoot(审计) 统计接口调用次数及成功率_Java_Zsigner的博客-CSDN博客

转载:https://www.cnblogs.com/yuwenhui/p/9809671.html

SpringBoot(审计) 统计接口调用次数及成功率

介绍:

  很多时候会需要提供一些统计记录的,比如某个服务一个月的被调用量、接口的调用次数、成功调用次数等等。

优点:

  使用AOP+Hendler对业务逻辑代码无侵入,完全解耦。通过spring boot自带的健康检查接口(/health)方便、安全。

注意:

  数据没有被持久化,只保存在内存中,重启后数据将被重置。可按需自己实现 

代码:

  AOP:在AOP中调用Handler

@Component@Aspectpublic 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);    }}

Health接口

@Componentpublic class RuntimeHealthIndicator extends AbstractHealthIndicator {    private static ILogger log = LoggerFactory.getLogger(ApplicationInstanceManager.class);    private static Map<String, RestApiInvokeStatus> restApiInvokeStatuses = new HashMap<>();    public static long failedRequestCount = 0;    public static long successRequestCount = 0;     public static Map<String, Object> historyRequestRecode;    private Map<String, Object> details;     public RuntimeHealthIndicator() {        this.details = new HashMap<>();        RuntimeHealthIndicator.historyRequestRecode = new HashMap<>();         this.details.put("startTime", new Date(ManagementFactory.getRuntimeMXBean().getStartTime()));        this.details.put("path", RuntimeHealthIndicator.class.getClassLoader().getResource("").getPath());        this.details.put("osName", System.getProperty("os.name"));        this.details.put("osVersion", System.getProperty("os.version"));        this.details.put("javaVersion", System.getProperty("java.version"));        try {            this.details.put("ip", ZGHelper.getIpV4());        } catch (SocketException e) {            log.error(e, "Failed to get Ipv4.");        }    }     @Override    protected void doHealthCheck(Health.Builder builder) throws Exception {         ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();        while (null != threadGroup.getParent()) {            threadGroup = threadGroup.getParent();        }        this.details.put("threadCount", threadGroup.activeCount());        OperatingSystemMXBean operatingSystemMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();        this.details.put("cpuUsageRate", operatingSystemMXBean.getSystemCpuLoad());        this.details.put(                "memoryUsageRate",                (float) (operatingSystemMXBean.getTotalPhysicalMemorySize() - operatingSystemMXBean.getFreePhysicalMemorySize()) / (float) operatingSystemMXBean.getTotalPhysicalMemorySize());        this.details.put("failedRequestCount", RuntimeHealthIndicator.failedRequestCount);        this.details.put("successRequestCount", RuntimeHealthIndicator.successRequestCount);        this.details.put("restApiInvokeStatuses", RuntimeHealthIndicator.restApiInvokeStatuses);        this.details.put("historyRequestRecode",RuntimeHealthIndicator.historyRequestRecode);         for (Map.Entry<String, Object> detail : this.details.entrySet()) {            builder.withDetail(detail.getKey(), detail.getValue());        }        builder.up();    }     public static void markRestApiInvoked(String name, int duration) {        if (StringUtils.isBlank(name)) {            return;        }         if (!RuntimeHealthIndicator.restApiInvokeStatuses.containsKey(name)) {            RuntimeHealthIndicator.restApiInvokeStatuses.put(name, new RestApiInvokeStatus(name));        }         RestApiInvokeStatus restApiInvokeStatus = RuntimeHealthIndicator.restApiInvokeStatuses.get(name);        restApiInvokeStatus.setDuration(duration);    }}
public class RestApiInvokeStatus {    private String name;    private Date startDate;    private Date latestDate;    private long times;    private float averageDuration;    private int minDuration;    private int maxDuration;    private int[] durations;     public String getName() {        return name;    }     public Date getStartDate() {        return startDate;    }     public Date getLatestDate() {        return latestDate;    }     public long getTimes() {        return times;    }     public int getMinDuration() {        return minDuration;    }      public int getMaxDuration() {        return maxDuration;    }     public RestApiInvokeStatus(String name) {        Assert.isNotBlank(name, "name");         this.name = name;        this.durations = new int[1000];        this.minDuration = Integer.MAX_VALUE;        this.maxDuration = Integer.MIN_VALUE;        Date now = new Date();        this.startDate = now;        this.latestDate = now;     }     public void setDuration(int duration) {        this.durations[(int) (this.times % this.durations.length)] = duration;        this.maxDuration = this.maxDuration > duration ? this.maxDuration : duration;        this.minDuration = this.minDuration < duration ? this.minDuration : duration;        this.latestDate = new Date();        this.times++;    }     public float getAverageDuration() {        long length = this.times < this.durations.length ? this.times : this.durations.length;         int count = 0;        for (int i = 0; i < length; i++) {            count += this.durations[i];        }        this.averageDuration = (float) count / (float) length;         return this.averageDuration;    }}

Original url: Access
Created at: 2020-05-12 15:55:04
Category: default
Tags: none

请先后发表评论
  • 最新评论
  • 总共0条评论