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
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
public static Function
return build;
}
public static void registerBuildFunction(Function
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
public static long failedRequestCount = 0;
public static long successRequestCount = 0;
public static Map
private Map
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 (
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;


发布评论