springboot2自定义statsd指标前缀

本文主要研究下springboot2自定义statsd指标前缀

背景

springboot2引入了micrometer,1.x版本的spring.metrics.export.statsd.prefix在2版本中已经被标记为废弃,但是2版本没有给出对应的配置项。

FlavorStatsdLineBuilder

micrometer-registry-statsd-1.0.1-sources.jar!/io/micrometer/statsd/internal/FlavorStatsdLineBuilder.java

/**
 * A Statsd serializer for a particular {@link Meter} that formats the line in different
 * ways depending on the prevailing {@link StatsdFlavor}.
 *
 * @author Jon Schneider
 */
public class FlavorStatsdLineBuilder implements StatsdLineBuilder {
    private final Meter.Id id;
    private final StatsdFlavor flavor;
    private final HierarchicalNameMapper nameMapper;
    private final MeterRegistry.Config config;

    private final Function<NamingConvention, String> datadogTagString;
    private final Function<NamingConvention, String> telegrafTagString;

    public FlavorStatsdLineBuilder(Meter.Id id, StatsdFlavor flavor, HierarchicalNameMapper nameMapper, MeterRegistry.Config config) {
        this.id = id;
        this.flavor = flavor;
        this.nameMapper = nameMapper;
        this.config = config;

        // service:payroll,region:us-west
        this.datadogTagString = memoize(convention ->
                id.getTags().iterator().hasNext() ?
                        id.getConventionTags(convention).stream()
                                .map(t -> t.getKey() + ":" + t.getValue())
                                .collect(Collectors.joining(","))
                        : null
        );

        // service=payroll,region=us-west
        this.telegrafTagString = memoize(convention ->
                id.getTags().iterator().hasNext() ?
                        id.getConventionTags(convention).stream()
                                .map(t -> t.getKey() + "=" + t.getValue())
                                .collect(Collectors.joining(","))
                        : null
        );
    }

    @Override
    public String count(long amount, Statistic stat) {
        return line(Long.toString(amount), stat, "c");
    }

    @Override
    public String gauge(double amount, Statistic stat) {
        return line(DoubleFormat.decimalOrNan(amount), stat, "g");
    }

    @Override
    public String histogram(double amount) {
        return line(DoubleFormat.decimalOrNan(amount), null, "h");
    }

    @Override
    public String timing(double timeMs) {
        return line(DoubleFormat.decimalOrNan(timeMs), null, "ms");
    }

    private String line(String amount, @Nullable Statistic stat, String type) {
        switch (flavor) {
            case ETSY:
                return metricName(stat) + ":" + amount + "|" + type;
            case DATADOG:
                return metricName(stat) + ":" + amount + "|" + type + tags(stat, datadogTagString.apply(config.namingConvention()),":", "|#");
            case TELEGRAF:
            default:
                return metricName(stat) + tags(stat, telegrafTagString.apply(config.namingConvention()),"=", ",") + ":" + amount + "|" + type;
        }
    }

    private String tags(@Nullable Statistic stat, String otherTags, String keyValueSeparator, String preamble) {
        String tags = of(stat == null ? null : "statistic" + keyValueSeparator + stat.getTagValueRepresentation(), otherTags)
                .filter(Objects::nonNull)
                .collect(Collectors.joining(","));

        if(!tags.isEmpty())
            tags = preamble + tags;
        return tags;
    }

    private String metricName(@Nullable Statistic stat) {
        switch (flavor) {
            case ETSY:
                return nameMapper.toHierarchicalName(stat != null ? id.withTag(stat) : id, config.namingConvention());
            case DATADOG:
            case TELEGRAF:
            default:
                return config.namingConvention().name(id.getName(), id.getType(), id.getBaseUnit());
        }
    }
}

可以看到count、gauge、histogram、timing方法内部都调用了line方法,而line方法调用metricName来构造指标名称,而metricName则是调用HierarchicalNameMapper的toHierarchicalName方法(flavor为ESTY)

HierarchicalNameMapper

micrometer-core-1.0.1-sources.jar!/io/micrometer/core/instrument/util/HierarchicalNameMapper.java

/**
 * Defines the mapping between a combination of name + dimensional tags and a hierarchical name.
 *
 * @author Jon Schneider
 */
public interface HierarchicalNameMapper {
    /**
     * Sort tags alphabetically by key and append tag key values to the name with '.', e.g.
     * {@code http_server_requests.response.200.method.GET}
     */
    HierarchicalNameMapper DEFAULT = (id, convention) -> {
        String tags = "";

        if (id.getTags().iterator().hasNext()) {
            tags = "." + id.getConventionTags(convention).stream()
                .map(t -> t.getKey() + "." + t.getValue())
                .map(nameSegment -> nameSegment.replace(" ", "_"))
                .collect(Collectors.joining("."));
        }

        return id.getConventionName(convention) + tags;
    };

    String toHierarchicalName(Meter.Id id, NamingConvention convention);
}

HierarchicalNameMapper接口定义了一个DEFAULT实现,而在StatsdMetricsExportAutoConfiguration则是默认使用这个DEFAULT实现

  • StatsdMetricsExportAutoConfiguration spring-boot-actuator-autoconfigure-2.0.0.RELEASE-sources.jar!/org/springframework/boot/actuate/autoconfigure/metrics/export/statsd/StatsdMetricsExportAutoConfiguration.java @Bean @ConditionalOnMissingBean public HierarchicalNameMapper hierarchicalNameMapper() { return HierarchicalNameMapper.DEFAULT; }

自定义

通过自定义个HierarchicalNameMapper,就可以自定义statsd指标的prefix,实例如下

    @Bean
    public HierarchicalNameMapper hierarchicalNameMapper() {
        return new HierarchicalNameMapper(){

            @Override
            public String toHierarchicalName(Meter.Id id, NamingConvention convention) {
                String tags = "";

                if (id.getTags().iterator().hasNext()) {
                    tags = "." + id.getConventionTags(convention).stream()
                            .map(t -> t.getKey() + "." + t.getValue())
                            .map(nameSegment -> nameSegment.replace(" ", "_"))
                            .collect(Collectors.joining("."));
                }

                return "demo." + id.getConventionName(convention) + tags;
            }
        };
    }

这里修改了DEFAULT方法,在return那里添加了一个demo作为prefix,这样就大功告成了。

小结

springboot2目前虽然没有通过配置文件直接支持指定statsd的prefix,但是可以通过少许代码自定义HierarchicalNameMapper来实现。

doc

  • management.metrics.export.tags-as-prefix property seems not to be honored in Spring Boot 2.0.0.RELEASE #12557

原文发布于微信公众号 - 码匠的流水账(geek_luandun)

原文发表时间:2018-03-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Hongten

python开发_calendar

如果你用过linux,你可能知道在linux下面的有一个强大的calendar功能,即日历

13520
来自专栏个人分享

XML封装与验证消息

22340
来自专栏码匠的流水账

聊聊sentinel的ModifyRulesCommandHandler

本文主要研究一下sentinel的ModifyRulesCommandHandler

13210
来自专栏小樱的经验随笔

Code forces 719A Vitya in the Countryside

A. Vitya in the Countryside time limit per test:1 second memory limit per test:2...

36060
来自专栏开发之途

Android Gson使用详解

33750
来自专栏java、Spring、技术分享

深入分析Spring MVC中RequestBody与ResponseBody

  在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分别完成请求报文到对象和对象到响应报文的转换。在Sprin...

31310
来自专栏个人分享

Socket与Http方式解析发送xml消息封装中间件jar包

  最近项目代码中太多重复的编写Document,不同的接口需要不同的模板,于是重写提取公共部分打成jar包,方便各个系统统一使用~

17130
来自专栏mukekeheart的iOS之旅

No.009 Palindrome Number

9. Palindrome Number Total Accepted: 136330 Total Submissions: 418995 Difficulty...

23270
来自专栏闻道于事

SSM框架整合项目 :投票系统

框架: Spring SpringMVC MyBatis 题目: 投票系统 ? 导包: 1, spring 2, MyBatis 3, mybatis-spri...

1.1K50
来自专栏Lambda

常用Lambda表达式实例

集合操作 从集合中过滤出某一个字段存入到新集合 // 从商品集合中过滤出商品类目id为一个新 List<Integer>集合 List<Integer> c...

21980

扫码关注云+社区

领取腾讯云代金券