2023年11月29日发(作者:)

SpringBoot(审计)统计接⼝调⽤次数及成功率

介绍:

很多时候会需要提供⼀些统计记录的,⽐如某个服务⼀个⽉的被调⽤量、接⼝的调⽤次数、成功调⽤次数等等。

优点:

使⽤AOP+Hendler对业务逻辑代码⽆侵⼊,完全解耦。通过spring boot⾃带的健康检查接⼝(/health)⽅便、安全。

注意:

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

代码:

AOP:AOP中调⽤Handler

@Component

@Aspect

public class ControllerAdvice {

private static ILogger log = ger(ControllerAdvice.class);

@Around("execution(public * *..*controller.*.*(..))")

public Object handle(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

Object result;

try {

Function build = ld();

if (null == build) {

erBuildFunction(DefaultControllerHandler::new);

}

build = ld();

AbstractControllerHandler controllerHandler = (proceedingJoinPoint);

if (null == controllerHandler) {

(("The method(%s) do not be handle by controller handler.", nature().getName()));

result = d();

} else {

result = ();

}

} catch (Throwable throwable) {

RequestCount++;

(new Exception(throwable), "Unknown exception- -!");

throw throwable;

}

return result;

}

}

Handler:执⾏记录的逻辑

抽象类:AbstractControllerHandler

public abstract class AbstractControllerHandler {

private static ILogger log = ger(AbstractControllerHandler.class);

private static Function build;

public static Function getBuild() {

return build;

}

public static void registerBuildFunction(Function build) {

ull(build, "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) {

ull(proceedingJoinPoint, "proceedingJoinPoint");

this.proceedingJoinPoint = proceedingJoinPoint;

Signature signature = this.nature();

this.httpServletRequest = this.getHttpServletRequest(this.s());

this.methodName = e();

this.uri = null == this.httpServletRequest ? null : this.uestURI();

this.requestBody = this.formatParameters(this.s());

this.ip = null == this.httpServletRequest ? "" : (this.httpServletRequest);

this.inDataMasking = false;

this.outDataMasking = false;

if (signature instanceof MethodSignature) {

MethodSignature methodSignature = (MethodSignature) signature;

try {

this.method = get().getClass().getMethod(this.methodName, ameterTypes());

if (null != this.method) {

LogDataMasking dataMasking = this.laredAnnotation(LogDataMasking.class);

if (null != dataMasking) {

this.inDataMasking = ();

this.outDataMasking = ();

}

}

} catch (NoSuchMethodException e) {

tackTrace();

}

}

}

public abstract Object handle() throws Throwable;

protected void logIn() {

String requestBody = this.requestBody;

if (this.inDataMasking) {

requestBody = "Data Masking";

}

(("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";

}

(

(

"Success(%s)-[%s][%s][%s][response body: %s]",

elapsedMilliseconds,

this.ip,

this.uri,

this.methodName,

responseBody));

} else {

(

(

"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) uestAttributes()).getRequest();

} catch (Exception e) {

(e);

return null;

}

}

protected String formatParameters(Object[] parameters) {

if (null == parameters) {

return null;

} else {

StringBuilder stringBuilder = new StringBuilder();

for (int i = 0; i < ; i++) {

Object parameter = parameters[i];

if (parameter instanceof HttpServletRequest || parameter instanceof HttpServletResponse) {

continue;

}

(("[%s]: %s.", i, String(parameter)));

}

return ng();

}

}

实现类:

this.logIn();

ResponseDto responseDto;

boolean success = false;

try {

Object result = d();

if (result instanceof ResponseDto) {

responseDto = (ResponseDto) result;

} else {

responseDto = s(result);

}

success = true;

sRequestCount++;

} catch (BusinessException e) {

// RequestCount++;

if (this.isDebugLogLevel()) {

(e);

}

responseDto = new ResponseDto<>(e(), sage(), null);

} catch (Exception e) {

RequestCount++;

if (this.isDebugLogLevel()) {

(e);

}

responseDto = (Error, sage(), null);

} finally {

Calendar cale = tance();

if (currentMonth != (() + 1)) {

String recodeKey = ("%d%d",

(), () + 1);

String recodeValue = "successCount:" + sRequestCount +

" failedCount:" + RequestCount;

(recodeKey, recodeValue);

sRequestCount = 0;

RequestCount = 0;

currentMonth = ();

}

}

long duration = tTimeMillis() - timestamp;

stApiInvoked(this.methodName, (int) duration);

this.logOut(duration, success, String(responseDto));

return responseDto;

}

public boolean isDebugLogLevel() {

return led();

}

}

View Code

Health接⼝

@Component

public class RuntimeHealthIndicator extends AbstractHealthIndicator {

private static ILogger log = ger(ApplicationInstanceManager.class);

private static Map restApiInvokeStatuses = new HashMap<>();

public static long failedRequestCount = 0;

public static long successRequestCount = 0;

public static Map historyRequestRecode;

private Map details;

public RuntimeHealthIndicator() {

this.details = new HashMap<>();

yRequestRecode = new HashMap<>();

this.("startTime", new Date(timeMXBean().getStartTime()));

this.("path", RuntimeHealthIndicator.class.getClassLoader().getResource("").getPath());

this.("osName", perty(""));

this.("osVersion", perty("n"));

this.("javaVersion", perty("n"));

try {

this.("ip", 4());

} catch (SocketException e) {

(e, "Failed to get Ipv4.");

}

this.("restApiInvokeStatuses", iInvokeStatuses);

this.("historyRequestRecode",yRequestRecode);

for ( detail : this.et()) {

tail((), ue());

}

();

}

public static void markRestApiInvoked(String name, int duration) {

if (k(name)) {

return;

}

if (!nsKey(name)) {

(name, new RestApiInvokeStatus(name));

}

RestApiInvokeStatus restApiInvokeStatus = (name);

ation(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) {

lank(name, "name");

this.name = name;

this.durations = new int[1000];

this.minDuration = _VALUE;

this.maxDuration = _VALUE;

Date now = new Date();

this.startDate = now;

this.latestDate = now;

}

public void setDuration(int duration) {

this.durations[(int) (this.times % this.)] = 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. ? this.times : this.;

int count = 0;