序
本文主要研究下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来实现。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。