SpringBoot(审计)统计接⼝调⽤次数及成功率介绍:
很多时候会需要提供⼀些统计记录的,⽐如某个服务⼀个⽉的被调⽤量、接⼝的调⽤次数、成功调⽤次数等等。
优点:
使⽤AOP+Hendler对业务逻辑代码⽆侵⼊,完全解耦。通过spring boot⾃带的健康检查接⼝(/health)⽅便、安全。
注意:
数据没有被持久化,只保存在内存中,重启后数据将被重置。可按需⾃⼰实现
代码:
AOP:在AOP中调⽤Handler
@Component
@Aspect
public class ControllerAdvice {
private static ILogger log = Logger(ControllerAdvice.class);
@Around("execution(public * *..*controller.*.*(..))")
public Object handle(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object result;
try {
Function<ProceedingJoinPoint, AbstractControllerHandler> build = Build();
if (null == build) {
}
build = Build();
AbstractControllerHandler controllerHandler = build.apply(proceedingJoinPoint);
if (null == controllerHandler) {
log.warn(String.format("The method(%s) do not be handle by controller handler.", Signature().getName()));
result = proceedingJoinPoint.proceed();
} else {
result = controllerHandler.handle();
}
} catch (Throwable throwable) {
RuntimeHealthIndicator.failedRequestCount++;
<(new Exception(throwable), "Unknown exception- -!");
throw throwable;
}
return result;
}
}
Handler:执⾏记录的逻辑
抽象类:AbstractControllerHandler
public abstract class AbstractControllerHandler {
private static ILogger log = Logger(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 = Signature();
this.httpServletRequest = HttpServletRequest(Args());
this.uri = null == this.httpServletRequest ? null : RequestURI();
this.ip = null == this.httpServletRequest ? "" : Ip(this.httpServletRequest);
this.inDataMasking = false;
this.outDataMasking = false;
if (signature instanceof MethodSignature) {
MethodSignature methodSignature = (MethodSignature) signature;
try {
if (null != hod) {
LogDataMasking dataMasking = DeclaredAnnotation(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 = questBody;
if (this.inDataMasking) {
requestBody = "Data Masking";
}
log.info(String.format("Start-[%s][%s][%s][body: %s]", this.ip, this.uri, hodName, 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,
responseBody));
} else {
log.warn(
String.format(
"Failed(%s)-[%s][%s][%s][request body: %s][response body: %s]",
elapsedMilliseconds,
this.ip,
this.uri,
responseBody));
}
}
protected HttpServletRequest getHttpServletRequest(Object[] parameters) {
try {
if (null != parameters) {
for (Object parameter : parameters) {
if (parameter instanceof HttpServletRequest) {
return (HttpServletRequest) parameter;
}
}
}
return ((ServletRequestAttributes) RequestAttributes()).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 < parameters.length; i++) {
Object parameter = parameters[i];
if (parameter instanceof HttpServletRequest || parameter instanceof HttpServletResponse) {
continue;
}
stringBuilder.append(String.format("[%s]: %s.", i, JSONString(parameter)));
}
String();
}
}
实现类:
public class DefaultControllerHandler extends AbstractControllerHandler {
private static ILogger log = Logger(DefaultControllerHandler.class);
private static int currentMonth = Instance().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()) {
<(e);
}
responseDto = new ResponseDto<>(e.getCode(), e.getMessage(), null);
} catch (Exception e) {
RuntimeHealthIndicator.failedRequestCount++;
if (this.isDebugLogLevel()) {
<(e);
}
responseDto = ResponseDto.failed(ExceptionDefinitions.ServerError, e.getMessage(), null);
} finally {
Calendar cale = Instance();
if (currentMonth != ((Calendar.MONTH) + 1)) {
String recodeKey = String.format("%d年%d⽉",
<(Calendar.YEAR), (Calendar.MONTH) + 1);
String recodeValue = "successCount:" + RuntimeHealthIndicator.successRequestCount +
" failedCount:" + RuntimeHealthIndicator.failedRequestCount;
RuntimeHealthIndicator.historyRequestRecode.put(recodeKey, recodeValue);
RuntimeHealthIndicator.successRequestCount = 0;
RuntimeHealthIndicator.failedRequestCount = 0;
currentMonth = (Calendar.MONTH);
}
}
long duration = System.currentTimeMillis() - timestamp;
RuntimeHealthIndicator.hodName, (int) duration);
this.logOut(duration, success, JSONString(responseDto));
return responseDto;
}
springboot aoppublic boolean isDebugLogLevel() {
return log.isEnabled(LogLevel.DEBUG);
}
}
View Code
Health接⼝
@Component
public class RuntimeHealthIndicator extends AbstractHealthIndicator {
private static ILogger log = Logger(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 RuntimeMXBean().getStartTime()));
this.details.put("path", ClassLoader().getResource("").getPath());
this.details.put("osName", Property("os.name"));
this.details.put("osVersion", Property("os.version"));
this.details.put("javaVersion", Property("java.version"));
try {
this.details.put("ip", IpV4());
} catch (SocketException e) {
<(e, "Failed to get Ipv4.");
}
}
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
while (null != Parent()) {
threadGroup = Parent();
}
this.details.put("threadCount", threadGroup.activeCount());
OperatingSystemMXBean operatingSystemMXBean = (OperatingSystemMXBean) OperatingSystemMXBean();
this.details.put("cpuUsageRate", SystemCpuLoad());
this.details.put(
"memoryUsageRate",
(float) (TotalPhysicalMemorySize() - FreePhysicalMemorySize()) / (float) TotalPhysicalMemorySize());
this.details.put("failedRequestCount", RuntimeHealthIndicator.failedRequestCount);
this.details.put("successRequestCount", RuntimeHealthIndicator.successRequestCount);
this.details.put("restApiInvokeStatuses", stApiInvokeStatuses);
this.details.put("historyRequestRecode",RuntimeHealthIndicator.historyRequestRecode);
for (Map.Entry<String, Object> detail : Set()) {
builder.Key(), Value());
}
builder.up();
}
public static void markRestApiInvoked(String name, int duration) {
if (StringUtils.isBlank(name)) {
return;
}
if (!ainsKey(name)) {
}
RestApiInvokeStatus restApiInvokeStatus = (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;
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论