自定义 Spring 通用日志注解
1. 注解@Metrics
| @Retention(RetentionPolicy.RUNTIME) |
| @Target({ElementType.METHOD, ElementType.TYPE}) |
| public @interface Metrics { |
| |
| |
| |
| boolean recordSuccessMetrics() default true; |
| |
| |
| |
| |
| boolean recordFailMetrics() default true; |
| |
| |
| |
| |
| boolean logParameters() default true; |
| |
| |
| |
| |
| boolean logReturn() default true; |
| |
| |
| |
| |
| boolean logException() default true; |
| |
| |
| |
| |
| boolean ignoreException() default false; |
2. 切面MetricsAspect
| @Aspect |
| @Slf4j |
| @Order(Ordered.HIGHEST_PRECEDENCE) |
| public class MetricsAspect { |
| |
| |
| |
| @Resource |
| private ObjectMapper objectMapper; |
| |
| |
| |
| |
| |
| private static final Map<Class<?>, Object> DEFAULT_VALUES = Stream |
| .of(boolean.class, byte.class, char.class, double.class, float.class, int.class, long.class, short.class) |
| .collect(toMap(clazz -> clazz, clazz -> Array.get(Array.newInstance(clazz, 1), 0))); |
| |
| public static <T> T getDefaultValue(Class<T> clazz) { |
| |
| return (T) DEFAULT_VALUES.get(clazz); |
| } |
| |
| |
| |
| |
| @Pointcut("@annotation(com.common.config.metrics.annotation.Metrics)") |
| public void withMetricsAnnotationMethod() { |
| } |
| |
| |
| |
| |
| |
| @Pointcut("within(@org.springframework.web.bind.annotation.RestController *)") |
| public void controllerBean() { |
| } |
| |
| @Pointcut("@within(com.common.config.metrics.annotation.Metrics)") |
| public void withMetricsAnnotationClass() { |
| } |
| |
| @Around("controllerBean() || withMetricsAnnotationMethod() || withMetricsAnnotationClass()") |
| public Object metrics(ProceedingJoinPoint pjp) throws Throwable { |
| |
| MethodSignature signature = (MethodSignature) pjp.getSignature(); |
| Metrics metrics = signature.getMethod().getAnnotation(Metrics.class); |
| |
| String name = String.format("【%s】【%s】", signature.getDeclaringType().toString(), signature.toLongString()); |
| |
| if (metrics == null) { |
| @Metrics |
| final class InnerClass { |
| } |
| metrics = InnerClass.class.getAnnotation(Metrics.class); |
| } |
| |
| RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); |
| if (requestAttributes != null) { |
| HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest(); |
| name += String.format("【%s】", request.getRequestURL().toString()); |
| } |
| |
| if (metrics.logParameters()) { |
| log.info(String.format("【入参日志】调用 %s 的参数是:【%s】", name, objectMapper.writeValueAsString(pjp.getArgs()))); |
| } |
| |
| Object returnValue; |
| Instant start = Instant.now(); |
| try { |
| returnValue = pjp.proceed(); |
| if (metrics.recordSuccessMetrics()) { |
| |
| log.info(String.format("【成功打点】调用 %s 成功,耗时:%d ms", name, Duration.between(start, Instant.now()).toMillis())); |
| } |
| } catch (Exception ex) { |
| if (metrics.recordFailMetrics()) { |
| log.info(String.format("【失败打点】调用 %s 失败,耗时:%d ms", name, Duration.between(start, Instant.now()).toMillis())); |
| } |
| if (metrics.logException()) { |
| log.error(String.format("【异常日志】调用 %s 出现异常!", name), ex); |
| } |
| if (metrics.ignoreException()) { |
| returnValue = getDefaultValue(signature.getReturnType()); |
| } else { |
| throw ex; |
| } |
| } |
| |
| if (metrics.logReturn()) { |
| log.info(String.format("【出参日志】调用 %s 的返回是:【%s】", name, returnValue)); |
| } |
| return returnValue; |
| } |
3. 自动注入AutoConfiguration
| @AutoConfiguration |
| @Slf4j |
| @EnableConfigurationProperties(MetricsProperties.class) |
| @ConditionalOnProperty(prefix = "common.metrics", name = {"keep-alive"}, havingValue = "true", matchIfMissing = true) |
| public class AspectAutoConfiguration { |
| |
| public AspectAutoConfiguration() { |
| log.info("AspectAutoConfiguration initialize."); |
| } |
| |
| @Bean |
| public MetricsAspect metricsAspect() { |
| return new MetricsAspect(); |
| } |
| } |
4. 配置文件MetricsProperties
| @ConfigurationProperties(prefix = "common.metrics") |
| public class MetricsProperties { |
| public Boolean getKeepAlive() { |
| return keepAlive; |
| } |
| |
| public void setKeepAlive(Boolean keepAlive) { |
| this.keepAlive = keepAlive; |
| } |
| |
| private Boolean keepAlive = true; |
| |
| } |
5. 其它配置
配置自动注入
配置resource.META-INF.spring.org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,增加RunsOnProfilesAutoConfiguration
类路径。
配置文件提示
| { |
| "groups": [], |
| "properties": [ |
| { |
| "name": "common.metrics.keepAlive", |
| "type": "java.lang.Boolean", |
| "sourceType": "com.common.config.metrics.properties.MetricsProperties" |
| } |
| ] |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现