Upgrade to Micrometer 1.5 snapshots

pull/20874/head
Stephane Nicoll 5 years ago
commit 5a20403327

@ -101,6 +101,7 @@ dependencies {
optional("org.springframework.data:spring-data-redis")
optional("org.springframework.data:spring-data-solr")
optional("org.springframework.integration:spring-integration-core")
optional("org.springframework.kafka:spring-kafka")
optional("org.springframework.security:spring-security-config")
optional("org.springframework.security:spring-security-web")
optional("org.springframework.session:spring-session-core")

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,37 +18,37 @@ package org.springframework.boot.actuate.autoconfigure.metrics;
import java.util.Collections;
import javax.management.MBeanServer;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.kafka.KafkaConsumerMetrics;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import io.micrometer.core.instrument.binder.kafka.KafkaClientMetrics;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.core.ProducerFactory;
/**
* Auto-configuration for Kafka metrics.
*
* @author Andy Wilkinson
* @author Stephane Nicoll
* @since 2.1.0
*/
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter({ MetricsAutoConfiguration.class, JmxAutoConfiguration.class })
@ConditionalOnClass({ KafkaConsumerMetrics.class, KafkaConsumer.class })
@AutoConfigureAfter({ MetricsAutoConfiguration.class, KafkaAutoConfiguration.class })
@ConditionalOnClass({ KafkaClientMetrics.class, ProducerFactory.class })
@ConditionalOnBean(MeterRegistry.class)
public class KafkaMetricsAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(MBeanServer.class)
public KafkaConsumerMetrics kafkaConsumerMetrics(MBeanServer mbeanServer) {
return new KafkaConsumerMetrics(mbeanServer, Collections.emptyList());
@ConditionalOnSingleCandidate(ProducerFactory.class)
public KafkaClientMetrics kafkaClientMetrics(ProducerFactory<?, ?> producerFactory) {
return new KafkaClientMetrics(producerFactory.createProducer(), Collections.emptyList());
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -25,17 +25,18 @@ import org.springframework.boot.convert.DurationStyle;
/**
* A meter value that is used when configuring micrometer. Can be a String representation
* of either a {@link Long} (applicable to timers and distribution summaries) or a
* of either a {@link Double} (applicable to timers and distribution summaries) or a
* {@link Duration} (applicable to only timers).
*
* @author Phillip Webb
* @author Stephane Nicoll
* @since 2.2.0
*/
public final class MeterValue {
private final Object value;
MeterValue(long value) {
MeterValue(double value) {
this.value = value;
}
@ -48,26 +49,29 @@ public final class MeterValue {
* @param meterType the meter type
* @return the value or {@code null} if the value cannot be applied
*/
public Long getValue(Type meterType) {
public Double getValue(Type meterType) {
if (meterType == Type.DISTRIBUTION_SUMMARY) {
return getDistributionSummaryValue();
}
if (meterType == Type.TIMER) {
return getTimerValue();
Long timerValue = getTimerValue();
if (timerValue != null) {
return timerValue.doubleValue();
}
}
return null;
}
private Long getDistributionSummaryValue() {
if (this.value instanceof Long) {
return (Long) this.value;
private Double getDistributionSummaryValue() {
if (this.value instanceof Double) {
return (Double) this.value;
}
return null;
}
private Long getTimerValue() {
if (this.value instanceof Long) {
return TimeUnit.MILLISECONDS.toNanos((long) this.value);
if (this.value instanceof Double) {
return TimeUnit.MILLISECONDS.toNanos(((Double) this.value).longValue());
}
if (this.value instanceof Duration) {
return ((Duration) this.value).toNanos();
@ -82,8 +86,9 @@ public final class MeterValue {
* @return a {@link MeterValue} instance
*/
public static MeterValue valueOf(String value) {
if (isNumber(value)) {
return new MeterValue(Long.parseLong(value));
Double number = safeParseDouble(value);
if (number != null) {
return new MeterValue(number);
}
return new MeterValue(DurationStyle.detectAndParse(value));
}
@ -92,13 +97,29 @@ public final class MeterValue {
* Return a new {@link MeterValue} instance for the given long value.
* @param value the source value
* @return a {@link MeterValue} instance
* @deprecated as of 2.3.0 in favor of {@link #valueOf(double)}
*/
@Deprecated
public static MeterValue valueOf(long value) {
return new MeterValue(value);
}
private static boolean isNumber(String value) {
return value.chars().allMatch(Character::isDigit);
/**
* Return a new {@link MeterValue} instance for the given double value.
* @param value the source value
* @return a {@link MeterValue} instance
*/
public static MeterValue valueOf(double value) {
return new MeterValue(value);
}
private static Double safeParseDouble(String value) {
try {
return Double.parseDouble(value);
}
catch (NumberFormatException nfe) {
return null;
}
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -91,16 +91,16 @@ public class PropertiesMeterFilter implements MeterFilter {
.build().merge(config);
}
private long[] convertSla(Meter.Type meterType, ServiceLevelAgreementBoundary[] sla) {
private double[] convertSla(Meter.Type meterType, ServiceLevelAgreementBoundary[] sla) {
if (sla == null) {
return null;
}
long[] converted = Arrays.stream(sla).map((candidate) -> candidate.getValue(meterType)).filter(Objects::nonNull)
.mapToLong(Long::longValue).toArray();
double[] converted = Arrays.stream(sla).map((candidate) -> candidate.getValue(meterType))
.filter(Objects::nonNull).mapToDouble(Double::doubleValue).toArray();
return (converted.length != 0) ? converted : null;
}
private Long convertMeterValue(Meter.Type meterType, String value) {
private Double convertMeterValue(Meter.Type meterType, String value) {
return (value != null) ? MeterValue.valueOf(value).getValue(meterType) : null;
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,8 +22,8 @@ import io.micrometer.core.instrument.Meter;
/**
* A service level agreement boundary for use when configuring Micrometer. Can be
* specified as either a {@link Long} (applicable to timers and distribution summaries) or
* a {@link Duration} (applicable to only timers).
* specified as either a {@link Double} (applicable to timers and distribution summaries)
* or a {@link Duration} (applicable to only timers).
*
* @author Phillip Webb
* @since 2.0.0
@ -42,17 +42,17 @@ public final class ServiceLevelAgreementBoundary {
* @param meterType the meter type
* @return the value or {@code null} if the value cannot be applied
*/
public Long getValue(Meter.Type meterType) {
public Double getValue(Meter.Type meterType) {
return this.value.getValue(meterType);
}
/**
* Return a new {@link ServiceLevelAgreementBoundary} instance for the given long
* Return a new {@link ServiceLevelAgreementBoundary} instance for the given double
* value.
* @param value the source value
* @return a {@link ServiceLevelAgreementBoundary} instance
*/
public static ServiceLevelAgreementBoundary valueOf(long value) {
public static ServiceLevelAgreementBoundary valueOf(double value) {
return new ServiceLevelAgreementBoundary(MeterValue.valueOf(value));
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -46,6 +46,12 @@ public class AppOpticsProperties extends StepRegistryProperties {
*/
private String hostTag = "instance";
/**
* Whether to ship a floored time, useful when sending measurements from multiple
* hosts to align them on a given time boundary.
*/
private boolean floorTimes;
/**
* Number of measurements per request to use for this backend. If more measurements
* are found, then multiple requests will be made.
@ -81,6 +87,14 @@ public class AppOpticsProperties extends StepRegistryProperties {
this.hostTag = hostTag;
}
public boolean isFloorTimes() {
return this.floorTimes;
}
public void setFloorTimes(boolean floorTimes) {
this.floorTimes = floorTimes;
}
@Override
public Integer getBatchSize() {
return this.batchSize;

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -47,4 +47,9 @@ class AppOpticsPropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapt
return get(AppOpticsProperties::getHostTag, AppOpticsConfig.super::hostTag);
}
@Override
public boolean floorTimes() {
return get(AppOpticsProperties::isFloorTimes, AppOpticsConfig.super::floorTimes);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -56,7 +56,7 @@ public class DatadogProperties extends StepRegistryProperties {
* URI to ship metrics to. If you need to publish metrics to an internal proxy
* en-route to Datadog, you can define the location of the proxy with this.
*/
private String uri = "https://app.datadoghq.com";
private String uri = "https://api.datadoghq.com";
public String getApiKey() {
return this.apiKey;

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -40,11 +40,15 @@ public class ElasticProperties extends StepRegistryProperties {
private String index = "metrics";
/**
* Index date format used for rolling indices. Appended to the index name, preceded by
* a '-'.
* Index date format used for rolling indices. Appended to the index name.
*/
private String indexDateFormat = "yyyy-MM";
/**
* Prefix to separate the index name from the date format used for rolling indices.
*/
private String indexDateSeparator = "-";
/**
* Name of the timestamp field.
*/
@ -65,6 +69,11 @@ public class ElasticProperties extends StepRegistryProperties {
*/
private String password = "";
/**
* Ingest pipeline name. By default, events are not pre-processed.
*/
private String pipeline = "";
public String getHost() {
return this.host;
}
@ -89,6 +98,14 @@ public class ElasticProperties extends StepRegistryProperties {
this.indexDateFormat = indexDateFormat;
}
public String getIndexDateSeparator() {
return this.indexDateSeparator;
}
public void setIndexDateSeparator(String indexDateSeparator) {
this.indexDateSeparator = indexDateSeparator;
}
public String getTimestampFieldName() {
return this.timestampFieldName;
}
@ -121,4 +138,12 @@ public class ElasticProperties extends StepRegistryProperties {
this.password = password;
}
public String getPipeline() {
return this.pipeline;
}
public void setPipeline(String pipeline) {
this.pipeline = pipeline;
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -47,6 +47,11 @@ class ElasticPropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapter
return get(ElasticProperties::getIndexDateFormat, ElasticConfig.super::indexDateFormat);
}
@Override
public String indexDateSeparator() {
return get(ElasticProperties::getIndexDateSeparator, ElasticConfig.super::indexDateSeparator);
}
@Override
public String timestampFieldName() {
return get(ElasticProperties::getTimestampFieldName, ElasticConfig.super::timestampFieldName);
@ -67,4 +72,9 @@ class ElasticPropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapter
return get(ElasticProperties::getPassword, ElasticConfig.super::password);
}
@Override
public String pipeline() {
return get(ElasticProperties::getPipeline, ElasticConfig.super::pipeline);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -70,8 +70,14 @@ public class GraphiteProperties {
private GraphiteProtocol protocol = GraphiteProtocol.PICKLED;
/**
* For the default naming convention, turn the specified tag keys into part of the
* metric prefix.
* Whether Graphite tags should be used, as opposed to a hierarchical naming
* convention.
*/
private boolean graphiteTagsEnabled = true;
/**
* For the hierarchical naming convention, turn the specified tag keys into part of
* the metric prefix. Ignored if "graphiteTagsEnabled" is true.
*/
private String[] tagsAsPrefix = new String[0];
@ -131,6 +137,14 @@ public class GraphiteProperties {
this.protocol = protocol;
}
public boolean isGraphiteTagsEnabled() {
return this.graphiteTagsEnabled;
}
public void setGraphiteTagsEnabled(boolean graphiteTagsEnabled) {
this.graphiteTagsEnabled = graphiteTagsEnabled;
}
public String[] getTagsAsPrefix() {
return this.tagsAsPrefix;
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -76,6 +76,11 @@ class GraphitePropertiesConfigAdapter extends PropertiesConfigAdapter<GraphitePr
return get(GraphiteProperties::getProtocol, GraphiteConfig.super::protocol);
}
@Override
public boolean graphiteTagsEnabled() {
return get(GraphiteProperties::isGraphiteTagsEnabled, GraphiteConfig.super::graphiteTagsEnabled);
}
@Override
public String[] tagsAsPrefix() {
return get(GraphiteProperties::getTagsAsPrefix, GraphiteConfig.super::tagsAsPrefix);

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,6 +20,8 @@ import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import io.micrometer.prometheus.HistogramFlavor;
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager.ShutdownOperation;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ -46,6 +48,11 @@ public class PrometheusProperties {
*/
private final Pushgateway pushgateway = new Pushgateway();
/**
* Histogram type for backing DistributionSummary and Timer.
*/
private HistogramFlavor histogramFlavor = HistogramFlavor.Prometheus;
/**
* Step size (i.e. reporting frequency) to use.
*/
@ -59,6 +66,14 @@ public class PrometheusProperties {
this.descriptions = descriptions;
}
public HistogramFlavor getHistogramFlavor() {
return this.histogramFlavor;
}
public void setHistogramFlavor(HistogramFlavor histogramFlavor) {
this.histogramFlavor = histogramFlavor;
}
public Duration getStep() {
return this.step;
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,6 +18,7 @@ package org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus
import java.time.Duration;
import io.micrometer.prometheus.HistogramFlavor;
import io.micrometer.prometheus.PrometheusConfig;
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.PropertiesConfigAdapter;
@ -45,6 +46,11 @@ class PrometheusPropertiesConfigAdapter extends PropertiesConfigAdapter<Promethe
return get(PrometheusProperties::isDescriptions, PrometheusConfig.super::descriptions);
}
@Override
public HistogramFlavor histogramFlavor() {
return get(PrometheusProperties::getHistogramFlavor, PrometheusConfig.super::histogramFlavor);
}
@Override
public Duration step() {
return get(PrometheusProperties::getStep, PrometheusConfig.super::step);

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,7 +19,6 @@ package org.springframework.boot.actuate.autoconfigure.metrics.export.statsd;
import io.micrometer.core.instrument.Clock;
import io.micrometer.statsd.StatsdConfig;
import io.micrometer.statsd.StatsdMeterRegistry;
import io.micrometer.statsd.StatsdMetrics;
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
@ -63,9 +62,4 @@ public class StatsdMetricsExportAutoConfiguration {
return new StatsdMeterRegistry(statsdConfig, clock);
}
@Bean
public StatsdMetrics statsdMetrics() {
return new StatsdMetrics();
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,14 +16,19 @@
package org.springframework.boot.actuate.autoconfigure.metrics.export.wavefront;
import java.time.Duration;
import com.wavefront.sdk.common.WavefrontSender;
import com.wavefront.sdk.direct.ingestion.WavefrontDirectIngestionClient.Builder;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.ipc.http.HttpUrlConnectionSender;
import io.micrometer.core.instrument.config.MissingRequiredConfigurationException;
import io.micrometer.wavefront.WavefrontConfig;
import io.micrometer.wavefront.WavefrontMeterRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.wavefront.WavefrontProperties.Sender;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@ -32,21 +37,25 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import org.springframework.util.unit.DataSize;
/**
* {@link EnableAutoConfiguration Auto-configuration} for exporting metrics to Wavefront.
*
* @author Jon Schneider
* @author Artsiom Yudovin
* @author Stephane Nicoll
* @since 2.0.0
*/
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore({ CompositeMeterRegistryAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class })
@AutoConfigureAfter(MetricsAutoConfiguration.class)
@ConditionalOnBean(Clock.class)
@ConditionalOnClass(WavefrontMeterRegistry.class)
@ConditionalOnClass({ WavefrontMeterRegistry.class, WavefrontSender.class })
@ConditionalOnProperty(prefix = "management.metrics.export.wavefront", name = "enabled", havingValue = "true",
matchIfMissing = true)
@EnableConfigurationProperties(WavefrontProperties.class)
@ -66,10 +75,38 @@ public class WavefrontMetricsExportAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public WavefrontMeterRegistry wavefrontMeterRegistry(WavefrontConfig wavefrontConfig, Clock clock) {
return WavefrontMeterRegistry.builder(wavefrontConfig).clock(clock).httpClient(
new HttpUrlConnectionSender(this.properties.getConnectTimeout(), this.properties.getReadTimeout()))
.build();
public WavefrontSender wavefrontSender(WavefrontConfig wavefrontConfig) {
if (!StringUtils.hasText(wavefrontConfig.apiToken())) {
throw new MissingRequiredConfigurationException(
"apiToken must be set whenever publishing directly to the Wavefront API");
}
return createWavefrontSender(wavefrontConfig);
}
@Bean
@ConditionalOnMissingBean
public WavefrontMeterRegistry wavefrontMeterRegistry(WavefrontConfig wavefrontConfig, Clock clock,
WavefrontSender wavefrontSender) {
return WavefrontMeterRegistry.builder(wavefrontConfig).clock(clock).wavefrontSender(wavefrontSender).build();
}
private WavefrontSender createWavefrontSender(WavefrontConfig wavefrontConfig) {
Builder builder = new Builder(getWavefrontReportingUri(wavefrontConfig.uri()), wavefrontConfig.apiToken());
PropertyMapper mapper = PropertyMapper.get().alwaysApplyingWhenNonNull();
Sender sender = this.properties.getSender();
mapper.from(sender.getMaxQueueSize()).to(builder::maxQueueSize);
mapper.from(sender.getBatchSize()).to(builder::batchSize);
mapper.from(sender.getFlushInterval()).asInt(Duration::getSeconds).to(builder::flushIntervalSeconds);
mapper.from(sender.getMessageSize()).asInt(DataSize::toBytes).to(builder::messageSizeBytes);
return builder.build();
}
private String getWavefrontReportingUri(String uri) {
// proxy reporting is now http reporting on newer wavefront proxies.
if (uri.startsWith("proxy")) {
return "http" + uri.substring(5);
}
return uri;
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,22 +21,19 @@ import java.time.Duration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.PushRegistryProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.unit.DataSize;
/**
* {@link ConfigurationProperties @ConfigurationProperties} for configuring Wavefront
* metrics export.
*
* @author Jon Schneider
* @author Stephane Nicoll
* @since 2.0.0
*/
@ConfigurationProperties("management.metrics.export.wavefront")
public class WavefrontProperties extends PushRegistryProperties {
/**
* Step size (i.e. reporting frequency) to use.
*/
private Duration step = Duration.ofSeconds(10);
/**
* URI to ship metrics to.
*/
@ -60,6 +57,8 @@ public class WavefrontProperties extends PushRegistryProperties {
*/
private String globalPrefix;
private final Sender sender = new Sender();
public URI getUri() {
return this.uri;
}
@ -68,16 +67,6 @@ public class WavefrontProperties extends PushRegistryProperties {
this.uri = uri;
}
@Override
public Duration getStep() {
return this.step;
}
@Override
public void setStep(Duration step) {
this.step = step;
}
public String getSource() {
return this.source;
}
@ -102,4 +91,52 @@ public class WavefrontProperties extends PushRegistryProperties {
this.globalPrefix = globalPrefix;
}
public Sender getSender() {
return this.sender;
}
public static class Sender {
private int maxQueueSize = 50000;
private int batchSize = 10000;
private Duration flushInterval = Duration.ofSeconds(1);
private DataSize messageSize = DataSize.ofBytes(Integer.MAX_VALUE);
public int getMaxQueueSize() {
return this.maxQueueSize;
}
public void setMaxQueueSize(int maxQueueSize) {
this.maxQueueSize = maxQueueSize;
}
public int getBatchSize() {
return this.batchSize;
}
public void setBatchSize(int batchSize) {
this.batchSize = batchSize;
}
public Duration getFlushInterval() {
return this.flushInterval;
}
public void setFlushInterval(Duration flushInterval) {
this.flushInterval = flushInterval;
}
public DataSize getMessageSize() {
return this.messageSize;
}
public void setMessageSize(DataSize messageSize) {
this.messageSize = messageSize;
}
}
}

@ -328,6 +328,10 @@
"description": "Whether exporting of metrics to Prometheus is enabled.",
"defaultValue": true
},
{
"name": "management.metrics.export.prometheus.histogram-flavor",
"defaultValue": "prometheus"
},
{
"name": "management.metrics.export.signalfx.num-threads",
"type": "java.lang.Integer",
@ -376,6 +380,18 @@
"level": "error"
}
},
{
"name": "management.metrics.export.wavefront.connect-timeout",
"deprecation": {
"level": "error"
}
},
{
"name": "management.metrics.export.wavefront.read-timeout",
"deprecation": {
"level": "error"
}
},
{
"name": "management.metrics.web.client.request.autotime.enabled",
"description": "Whether to automatically time web client requests.",

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,54 +16,54 @@
package org.springframework.boot.actuate.autoconfigure.metrics;
import io.micrometer.core.instrument.binder.kafka.KafkaConsumerMetrics;
import io.micrometer.core.instrument.binder.kafka.KafkaClientMetrics;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link KafkaMetricsAutoConfiguration}.
*
* @author Andy Wilkinson
* @author Stephane Nicoll
*/
class KafkaMetricsAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().with(MetricsRun.simple())
.withPropertyValues("spring.jmx.enabled=true")
.withConfiguration(AutoConfigurations.of(KafkaMetricsAutoConfiguration.class));
@Test
void whenThereIsNoMBeanServerAutoConfigurationBacksOff() {
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(KafkaConsumerMetrics.class));
void whenThereIsNoProducerFactoryAutoConfigurationBacksOff() {
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(KafkaClientMetrics.class));
}
@Test
void whenThereIsAnMBeanServerKafkaConsumerMetricsIsConfigured() {
this.contextRunner.withConfiguration(AutoConfigurations.of(JmxAutoConfiguration.class))
.run((context) -> assertThat(context).hasSingleBean(KafkaConsumerMetrics.class));
void whenThereIsAnAProducerFactoryKafkaClientMetricsIsConfigured() {
this.contextRunner.withConfiguration(AutoConfigurations.of(KafkaAutoConfiguration.class))
.run((context) -> assertThat(context).hasSingleBean(KafkaClientMetrics.class));
}
@Test
void allowsCustomKafkaConsumerMetricsToBeUsed() {
this.contextRunner.withConfiguration(AutoConfigurations.of(JmxAutoConfiguration.class))
.withUserConfiguration(CustomKafkaConsumerMetricsConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(KafkaConsumerMetrics.class)
.hasBean("customKafkaConsumerMetrics"));
void allowsCustomKafkaClientMetricsToBeUsed() {
this.contextRunner.withConfiguration(AutoConfigurations.of(KafkaAutoConfiguration.class))
.withUserConfiguration(CustomKafkaClientMetricsConfiguration.class).run((context) -> assertThat(context)
.hasSingleBean(KafkaClientMetrics.class).hasBean("customKafkaClientMetrics"));
}
@Configuration(proxyBeanMethods = false)
static class CustomKafkaConsumerMetricsConfiguration {
static class CustomKafkaClientMetricsConfiguration {
@Bean
KafkaConsumerMetrics customKafkaConsumerMetrics() {
return new KafkaConsumerMetrics();
KafkaClientMetrics customKafkaClientMetrics() {
return mock(KafkaClientMetrics.class);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -30,17 +30,18 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link MeterValue}.
*
* @author Phillip Webb
* @author Stephane Nicoll
*/
class MeterValueTests {
@Test
void getValueForDistributionSummaryWhenFromLongShouldReturnLongValue() {
MeterValue meterValue = MeterValue.valueOf(123L);
assertThat(meterValue.getValue(Type.DISTRIBUTION_SUMMARY)).isEqualTo(123);
void getValueForDistributionSummaryWhenFromNumberShouldReturnDoubleValue() {
MeterValue meterValue = MeterValue.valueOf(123.42);
assertThat(meterValue.getValue(Type.DISTRIBUTION_SUMMARY)).isEqualTo(123.42);
}
@Test
void getValueForDistributionSummaryWhenFromNumberStringShouldReturnLongValue() {
void getValueForDistributionSummaryWhenFromNumberStringShouldReturnDoubleValue() {
MeterValue meterValue = MeterValue.valueOf("123");
assertThat(meterValue.getValue(Type.DISTRIBUTION_SUMMARY)).isEqualTo(123);
}
@ -52,8 +53,8 @@ class MeterValueTests {
}
@Test
void getValueForTimerWhenFromLongShouldReturnMsToNanosValue() {
MeterValue meterValue = MeterValue.valueOf(123L);
void getValueForTimerWhenFromNumberShouldReturnMsToNanosValue() {
MeterValue meterValue = MeterValue.valueOf(123d);
assertThat(meterValue.getValue(Type.TIMER)).isEqualTo(123000000);
}
@ -81,11 +82,11 @@ class MeterValueTests {
@Test
void valueOfShouldWorkInBinder() {
MockEnvironment environment = new MockEnvironment();
TestPropertyValues.of("duration=10ms", "long=20").applyTo(environment);
TestPropertyValues.of("duration=10ms", "number=20.42").applyTo(environment);
assertThat(Binder.get(environment).bind("duration", Bindable.of(MeterValue.class)).get().getValue(Type.TIMER))
.isEqualTo(10000000);
assertThat(Binder.get(environment).bind("long", Bindable.of(MeterValue.class)).get().getValue(Type.TIMER))
.isEqualTo(20000000);
assertThat(Binder.get(environment).bind("number", Bindable.of(MeterValue.class)).get()
.getValue(Type.DISTRIBUTION_SUMMARY)).isEqualTo(20.42);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -25,6 +25,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link ServiceLevelAgreementBoundary}.
*
* @author Phillip Webb
* @author Stephane Nicoll
*/
class ServiceLevelAgreementBoundaryTests {
@ -46,4 +47,22 @@ class ServiceLevelAgreementBoundaryTests {
assertThat(sla.getValue(Type.TIMER)).isEqualTo(123000000);
}
@Test
void getValueForDistributionSummaryWhenFromDoubleShouldReturnDoubleValue() {
ServiceLevelAgreementBoundary sla = ServiceLevelAgreementBoundary.valueOf(123.42);
assertThat(sla.getValue(Type.DISTRIBUTION_SUMMARY)).isEqualTo(123.42);
}
@Test
void getValueForDistributionSummaryWhenFromStringShouldReturnDoubleValue() {
ServiceLevelAgreementBoundary sla = ServiceLevelAgreementBoundary.valueOf("123.42");
assertThat(sla.getValue(Type.DISTRIBUTION_SUMMARY)).isEqualTo(123.42);
}
@Test
void getValueForDistributionSummaryWhenFromDurationShouldReturnNull() {
ServiceLevelAgreementBoundary sla = ServiceLevelAgreementBoundary.valueOf("123ms");
assertThat(sla.getValue(Type.DISTRIBUTION_SUMMARY)).isNull();
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -61,4 +61,11 @@ class AppOpticsPropertiesConfigAdapterTests
assertThat(createConfigAdapter(properties).hostTag()).isEqualTo("node");
}
@Test
void whenPropertiesFloorTimesIsSetAdapterHostTagReturnsIt() {
AppOpticsProperties properties = createProperties();
properties.setFloorTimes(true);
assertThat(createConfigAdapter(properties).floorTimes()).isTrue();
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -37,6 +37,7 @@ class AppOpticsPropertiesTests extends StepRegistryPropertiesTests {
assertStepRegistryDefaultValues(properties, config);
assertThat(properties.getUri()).isEqualToIgnoringWhitespace(config.uri());
assertThat(properties.getHostTag()).isEqualToIgnoringWhitespace(config.hostTag());
assertThat(properties.isFloorTimes()).isEqualTo(config.floorTimes());
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -48,6 +48,13 @@ class ElasticPropertiesConfigAdapterTests {
assertThat(new ElasticPropertiesConfigAdapter(properties).indexDateFormat()).isEqualTo("yyyy");
}
@Test
void whenPropertiesIndexDateSeparatorIsSetAdapterIndexDateFormatReturnsIt() {
ElasticProperties properties = new ElasticProperties();
properties.setIndexDateSeparator("*");
assertThat(new ElasticPropertiesConfigAdapter(properties).indexDateSeparator()).isEqualTo("*");
}
@Test
void whenPropertiesTimestampFieldNameIsSetAdapterTimestampFieldNameReturnsIt() {
ElasticProperties properties = new ElasticProperties();
@ -76,4 +83,11 @@ class ElasticPropertiesConfigAdapterTests {
assertThat(new ElasticPropertiesConfigAdapter(properties).password()).isEqualTo("secret");
}
@Test
void whenPropertiesPipelineIsSetAdapterPasswordReturnsIt() {
ElasticProperties properties = new ElasticProperties();
properties.setPipeline("testPipeline");
assertThat(new ElasticPropertiesConfigAdapter(properties).pipeline()).isEqualTo("testPipeline");
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -38,10 +38,12 @@ class ElasticPropertiesTests extends StepRegistryPropertiesTests {
assertThat(properties.getHost()).isEqualTo(config.host());
assertThat(properties.getIndex()).isEqualTo(config.index());
assertThat(properties.getIndexDateFormat()).isEqualTo(config.indexDateFormat());
assertThat(properties.getIndexDateSeparator()).isEqualTo(config.indexDateSeparator());
assertThat(properties.getPassword()).isEqualTo(config.password());
assertThat(properties.getTimestampFieldName()).isEqualTo(config.timestampFieldName());
assertThat(properties.getUserName()).isEqualTo(config.userName());
assertThat(properties.isAutoCreateIndex()).isEqualTo(config.autoCreateIndex());
assertThat(properties.getPipeline()).isEqualTo(config.pipeline());
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -46,10 +46,23 @@ class GraphiteMetricsExportAutoConfigurationTests {
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(GraphiteMeterRegistry.class));
}
@Test
void autoConfiguresUseTagsAsPrefixIsIgnoredByDefault() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.withPropertyValues("management.metrics.export.graphite.tags-as-prefix=ignored").run((context) -> {
assertThat(context).hasSingleBean(GraphiteMeterRegistry.class);
GraphiteMeterRegistry registry = context.getBean(GraphiteMeterRegistry.class);
registry.counter("test.count", Tags.of("app", "myapp"));
assertThat(registry.getDropwizardRegistry().getMeters()).containsOnlyKeys("test.count;app=myapp");
});
}
@Test
void autoConfiguresUseTagsAsPrefix() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.withPropertyValues("management.metrics.export.graphite.tags-as-prefix=app").run((context) -> {
.withPropertyValues("management.metrics.export.graphite.tags-as-prefix=app",
"management.metrics.export.graphite.graphite-tags-enabled=false")
.run((context) -> {
assertThat(context).hasSingleBean(GraphiteMeterRegistry.class);
GraphiteMeterRegistry registry = context.getBean(GraphiteMeterRegistry.class);
registry.counter("test.count", Tags.of("app", "myapp"));

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -39,6 +39,7 @@ class GraphitePropertiesTests {
assertThat(properties.getHost()).isEqualTo(config.host());
assertThat(properties.getPort()).isEqualTo(config.port());
assertThat(properties.getProtocol()).isEqualTo(config.protocol());
assertThat(properties.isGraphiteTagsEnabled()).isEqualTo(config.graphiteTagsEnabled());
assertThat(properties.getTagsAsPrefix()).isEqualTo(config.tagsAsPrefix());
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -33,6 +33,7 @@ class PrometheusPropertiesTests {
PrometheusProperties properties = new PrometheusProperties();
PrometheusConfig config = PrometheusConfig.DEFAULT;
assertThat(properties.isDescriptions()).isEqualTo(config.descriptions());
assertThat(properties.getHistogramFlavor()).isEqualTo(config.histogramFlavor());
assertThat(properties.getStep()).isEqualTo(config.step());
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,7 +19,6 @@ package org.springframework.boot.actuate.autoconfigure.metrics.export.statsd;
import io.micrometer.core.instrument.Clock;
import io.micrometer.statsd.StatsdConfig;
import io.micrometer.statsd.StatsdMeterRegistry;
import io.micrometer.statsd.StatsdMetrics;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
@ -47,9 +46,8 @@ class StatsdMetricsExportAutoConfigurationTests {
@Test
void autoConfiguresItsConfigMeterRegistryAndMetrics() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(StatsdMeterRegistry.class)
.hasSingleBean(StatsdConfig.class).hasSingleBean(StatsdMetrics.class));
this.contextRunner.withUserConfiguration(BaseConfiguration.class).run((context) -> assertThat(context)
.hasSingleBean(StatsdMeterRegistry.class).hasSingleBean(StatsdConfig.class));
}
@Test

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +16,7 @@
package org.springframework.boot.actuate.autoconfigure.metrics.export.wavefront;
import com.wavefront.sdk.common.WavefrontSender;
import io.micrometer.core.instrument.Clock;
import io.micrometer.wavefront.WavefrontConfig;
import io.micrometer.wavefront.WavefrontMeterRegistry;
@ -28,6 +29,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link WavefrontMetricsExportAutoConfiguration}.
@ -53,9 +55,10 @@ class WavefrontMetricsExportAutoConfigurationTests {
@Test
void autoConfigurationCanBeDisabled() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.withPropertyValues("management.metrics.export.wavefront.enabled=false")
.withPropertyValues("management.metrics.export.wavefront.api-token=abcde",
"management.metrics.export.wavefront.enabled=false")
.run((context) -> assertThat(context).doesNotHaveBean(WavefrontMeterRegistry.class)
.doesNotHaveBean(WavefrontConfig.class));
.doesNotHaveBean(WavefrontConfig.class).doesNotHaveBean(WavefrontSender.class));
}
@Test
@ -63,7 +66,30 @@ class WavefrontMetricsExportAutoConfigurationTests {
this.contextRunner.withUserConfiguration(CustomConfigConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(Clock.class)
.hasSingleBean(WavefrontMeterRegistry.class).hasSingleBean(WavefrontConfig.class)
.hasBean("customConfig"));
.hasSingleBean(WavefrontSender.class).hasBean("customConfig"));
}
@Test
void configureWavefrontSender() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.withPropertyValues("management.metrics.export.wavefront.api-token=abcde",
"management.metrics.export.wavefront.sender.max-queue-size=100",
"management.metrics.export.wavefront.sender.batch-size=200",
"management.metrics.export.wavefront.sender.message-size=1KB")
.run((context) -> {
WavefrontSender sender = context.getBean(WavefrontSender.class);
assertThat(sender).extracting("metricsBuffer").hasFieldOrPropertyWithValue("capacity", 100);
assertThat(sender).hasFieldOrPropertyWithValue("batchSize", 200);
assertThat(sender).hasFieldOrPropertyWithValue("messageSizeBytes", 1024);
});
}
@Test
void allowsWavefrontSenderToBeCustomized() {
this.contextRunner.withUserConfiguration(CustomSenderConfiguration.class)
.run((context) -> assertThat(context).hasSingleBean(Clock.class)
.hasSingleBean(WavefrontMeterRegistry.class).hasSingleBean(WavefrontConfig.class)
.hasSingleBean(WavefrontSender.class).hasBean("customSender"));
}
@Test
@ -109,13 +135,29 @@ class WavefrontMetricsExportAutoConfigurationTests {
@Override
public String uri() {
return WavefrontConfig.DEFAULT_PROXY.uri();
return WavefrontConfig.DEFAULT_DIRECT.uri();
}
@Override
public String apiToken() {
return "abc-def";
}
};
}
}
@Configuration(proxyBeanMethods = false)
@Import(BaseConfiguration.class)
static class CustomSenderConfiguration {
@Bean
WavefrontSender customSender() {
return mock(WavefrontSender.class);
}
}
@Configuration(proxyBeanMethods = false)
@Import(BaseConfiguration.class)
static class CustomRegistryConfiguration {

@ -1182,7 +1182,7 @@ bom {
]
}
}
library("Micrometer", "1.3.7") {
library("Micrometer", "1.5.0-SNAPSHOT") {
group("io.micrometer") {
modules = [
"micrometer-registry-stackdriver" {

@ -1791,7 +1791,7 @@ Spring Boot registers the following core metrics when applicable:
** Number of classes loaded/unloaded
* CPU metrics
* File descriptor metrics
* Kafka consumer metrics (<<production-ready-jmx,JMX support>> should be enabled)
* Kafka consumer metrics
* Log4j2 metrics: record the number of events logged to Log4j2 at each level
* Logback metrics: record the number of events logged to Logback at each level
* Uptime metrics: report a gauge for uptime and a fixed gauge representing the application's absolute start time

Loading…
Cancel
Save