包括 Spring Boot 端点路径变量作为度量维度

问题描述 投票:0回答:5

我有API端点:/user/{tenant}/create

我正在使用带有千分尺的 Spring Boot 2 进行度量。

默认情况下,Spring Boot 2端点的@Timer注释包括以下标签:异常,方法,uri,状态

我想添加 api 参数“tenant”的传递值作为端点的额外标记

如何使用 Spring Boot 2 和千分尺做到这一点

java spring-boot micrometer spring-micrometer
5个回答
14
投票
@Bean
public WebMvcTagsProvider webMvcTagsProvider() {
    return new CustomWebMvcTagsProvider();
}

    public class CustomWebMvcTagsProvider extends DefaultWebMvcTagsProvider {
      public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response, Object handler, Throwable exception) {
        return Tags.of(super.getTags(request, response, handler, exception)).and(getTenantTag(request));
      }

      private Tag getTenantTag(HttpServletRequest request) {
        String tenant = ((Map<String, String>)request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE)).get("tenant");
        if(tenant == null){
            tenant = "na";
        }
        return Tag.of("tenant", tenant);
      }
    }


9
投票

使用自定义

WebMvcTagsProvider
,例如:

@Bean
public WebMvcTagsProvider webMvcTagsProvider() {
    return new WebMvcTagsProvider() {
        @Override
        public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response, Object handler, Throwable exception) {
            return ((Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE))
                    .entrySet()
                    .stream()
                    .map(entry -> new ImmutableTag(entry.getKey(), entry.getValue()))
                    .collect(Collectors.toList());
        }

        @Override
        public Iterable<Tag> getLongRequestTags(HttpServletRequest request, Object handler) {
            return new ArrayList<>();
        }
    };
}

8
投票

从 Spring Boot 2.3.0 开始,如果您想在某些请求的默认标签上添加额外的标签,更好的方法是添加 WebMvcTagsContributor 类的 @Bean。 这样您的代码就不必担心放入默认标签。

实现于https://github.com/spring-projects/spring-boot/issues/20175

代码如下所示:

@Bean
public WebMvcTagsContributor webMvcTagsContributor() {
    return new WebMvcTagsContributor() {
        @Override
        public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response, Object handler, Throwable exception) {
            Tags tags = Tags.empty();
            tags = tags.and(Tag.of("my_tag", "somevalue"));
            return tags;
        }

        @Override
        public Iterable<Tag> getLongRequestTags(HttpServletRequest request, Object handler) {
            return null;
        }
    };
}

2
投票

将路径变量添加到默认标签的方法:

import io.micrometer.core.instrument.Tag;
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcTagsContributor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.stream.Collectors;

@Configuration
public class WebMvcTagsProviderConfig {

    @Bean
    public WebMvcTagsContributor webMvcTagsContributor() {
        return new WebMvcTagsContributor() {
            @Override
            public Iterable<Tag> getTags(
                    HttpServletRequest request, HttpServletResponse response, Object handler, Throwable exception
            ) {
                Map<String, String> pathVariables = ((Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE));
                return pathVariables == null
                        ? null
                        : pathVariables
                                .entrySet()
                                .stream()
                                .map(entry -> Tag.of(entry.getKey(), entry.getValue()))
                                .collect(Collectors.toList());
            }

            @Override
            public Iterable<Tag> getLongRequestTags(HttpServletRequest request, Object handler) {
                return null;
            }
        };
    }

}

0
投票

由于我在处理 Spring Boot 3.x 项目时偶然发现了这个问题,即使它特别询问了 Spring Boot 2,我想我会提供一个如何使用 Spring Boot 3 执行此操作的答案。此功能将添加您的指标的

tenant
标签可以是
tenant
URI 变量的值,如果不存在则为空字符串。

创建一个扩展

DefaultServerRequestObservationConvention
并覆盖
getLowCardinalityKeyValues
的类以添加您有兴趣添加的标签:

import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
import org.springframework.http.server.observation.ServerRequestObservationContext;
import org.springframework.web.servlet.HandlerMapping;

import java.util.Map;

public class ExtendedServerRequestObservationConvention extends DefaultServerRequestObservationConvention {

    @Override
    public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) {

        return super.getLowCardinalityKeyValues(context)
            .and(KeyValue.of("tenant", getPathVariable(context.getCarrier(), "tenant")));
    }

    private String getPathVariable(HttpServletRequest request, String variableName) {

        Object uriVariables = request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);

        if (uriVariables instanceof Map) {
            @SuppressWarnings("unchecked")
            Map<String, String> uriVariablesMap = (Map<String, String>)uriVariables;
            return uriVariablesMap.getOrDefault(variableName, "");
        }
        return "";
    }
}

我包含了导入语句,因为我不小心导入了

org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention;
(注意reactive),从编译/运行时的角度来看,它工作得很好,但导致该功能对我的应用程序没有影响。

定义该类后,您可以将 bean 添加到您的配置中,它应该可以工作:

@Configuration
public class AppConfig {
    ...
    @Bean
    ExtendedServerRequestObservationConvention extendedServerRequestObservationConvention() {

        return new ExtendedServerRequestObservationConvention();
    }
    ...
}

更多信息可以在这里找到:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-Metrics-3.0#migration-tag-providers-and-contributors

替代方案:请注意,您还可以使用 ObservationFilter 来完成相同的操作,或应用于其他情况。在您的

@Configuration
类中看起来类似于以下内容:

@Bean
ObservationFilter tagAdderFilter() {

    return context -> {
        if (context instanceof ServerRequestObservationContext serverContext) {
            context.addLowCardinalityKeyValue(KeyValue.of("tenant", [the metric value you parsed from the context]));
        }
        return context;
    };
}
© www.soinside.com 2019 - 2024. All rights reserved.