Auto-configure Micrometer's Elastic registry

Closes gh-14523
pull/14592/head
Andy Wilkinson 6 years ago
parent 95ecbc736b
commit 4c3e2d10d1

@ -107,6 +107,11 @@
<artifactId>micrometer-registry-dynatrace</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-elastic</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-ganglia</artifactId>

@ -0,0 +1,66 @@
/*
* Copyright 2012-2018 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.metrics.export.elastic;
import io.micrometer.core.instrument.Clock;
import io.micrometer.elastic.ElasticConfig;
import io.micrometer.elastic.ElasticMeterRegistry;
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.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
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.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link EnableAutoConfiguration Auto-configuration} for exporting metrics to Elastic.
*
* @author Andy Wilkinson
* @since 2.1.0
*/
@Configuration
@AutoConfigureBefore({ CompositeMeterRegistryAutoConfiguration.class,
SimpleMetricsExportAutoConfiguration.class })
@AutoConfigureAfter(MetricsAutoConfiguration.class)
@ConditionalOnBean(Clock.class)
@ConditionalOnClass(ElasticMeterRegistry.class)
@ConditionalOnProperty(prefix = "management.metrics.export.elastic", name = "enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(ElasticProperties.class)
public class ElasticMetricsExportAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public ElasticConfig elasticConfig(ElasticProperties elasticProperties) {
return new ElasticPropertiesConfigAdapter(elasticProperties);
}
@Bean
@ConditionalOnMissingBean
public ElasticMeterRegistry elasticMeterRegistry(ElasticConfig elasticConfig,
Clock clock) {
return new ElasticMeterRegistry(elasticConfig, clock);
}
}

@ -0,0 +1,123 @@
/*
* Copyright 2012-2018 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.metrics.export.elastic;
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* {@link ConfigurationProperties} for configuring Elastic metrics export.
*
* @author Andy Wilkinson
* @since 2.1.0
*/
@ConfigurationProperties(prefix = "management.metrics.export.elastic")
public class ElasticProperties extends StepRegistryProperties {
/**
* Hosts to export metrics to.
*/
private String[] hosts = new String[] { "http://localhost:9200" };
/**
* Index to export metrics to.
*/
private String index = "metrics";
/**
* Index date format used for rolling indices. Appended to the index name, preceded by
* a '-'.
*/
private String indexDateFormat = "yyyy-MM";
/**
* Name of the timestamp field.
*/
private String timestampFieldName = "@timestamp";
/**
* Whether to create the index automatically if it does not exist.
*/
private boolean autoCreateIndex = true;
/**
* Username for basic authentication.
*/
private String userName = "";
/**
* Password for basic authentication.
*/
private String password = "";
public String[] getHosts() {
return this.hosts;
}
public void setHosts(String[] hosts) {
this.hosts = hosts;
}
public String getIndex() {
return this.index;
}
public void setIndex(String index) {
this.index = index;
}
public String getIndexDateFormat() {
return this.indexDateFormat;
}
public void setIndexDateFormat(String indexDateFormat) {
this.indexDateFormat = indexDateFormat;
}
public String getTimestampFieldName() {
return this.timestampFieldName;
}
public void setTimestampFieldName(String timestampFieldName) {
this.timestampFieldName = timestampFieldName;
}
public boolean isAutoCreateIndex() {
return this.autoCreateIndex;
}
public void setAutoCreateIndex(boolean autoCreateIndex) {
this.autoCreateIndex = autoCreateIndex;
}
public String getUserName() {
return this.userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
}

@ -0,0 +1,73 @@
/*
* Copyright 2012-2018 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.metrics.export.elastic;
import io.micrometer.elastic.ElasticConfig;
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryPropertiesConfigAdapter;
/**
* Adapter to convert {@link ElasticProperties} to an {@link ElasticConfig}.
*
* @author Andy Wilkinson
*/
class ElasticPropertiesConfigAdapter extends
StepRegistryPropertiesConfigAdapter<ElasticProperties> implements ElasticConfig {
ElasticPropertiesConfigAdapter(ElasticProperties properties) {
super(properties);
}
@Override
public String[] hosts() {
return get(ElasticProperties::getHosts, ElasticConfig.super::hosts);
}
@Override
public String index() {
return get(ElasticProperties::getIndex, ElasticConfig.super::index);
}
@Override
public String indexDateFormat() {
return get(ElasticProperties::getIndexDateFormat,
ElasticConfig.super::indexDateFormat);
}
@Override
public String timestampFieldName() {
return get(ElasticProperties::getTimestampFieldName,
ElasticConfig.super::timestampFieldName);
}
@Override
public boolean autoCreateIndex() {
return get(ElasticProperties::isAutoCreateIndex,
ElasticConfig.super::autoCreateIndex);
}
@Override
public String userName() {
return get(ElasticProperties::getUserName, ElasticConfig.super::userName);
}
@Override
public String password() {
return get(ElasticProperties::getPassword, ElasticConfig.super::password);
}
}

@ -0,0 +1,20 @@
/*
* Copyright 2012-2018 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Support for exporting actuator metrics to Elastic.
*/
package org.springframework.boot.actuate.autoconfigure.metrics.export.elastic;

@ -46,6 +46,7 @@ org.springframework.boot.actuate.autoconfigure.metrics.cache.CacheMetricsAutoCon
org.springframework.boot.actuate.autoconfigure.metrics.export.atlas.AtlasMetricsExportAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.export.datadog.DatadogMetricsExportAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.export.dynatrace.DynatraceMetricsExportAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.export.elastic.ElasticMetricsExportAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.export.ganglia.GangliaMetricsExportAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.export.graphite.GraphiteMetricsExportAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.export.influx.InfluxMetricsExportAutoConfiguration,\

@ -0,0 +1,146 @@
/*
* Copyright 2012-2018 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.metrics.export.elastic;
import java.util.Map;
import io.micrometer.core.instrument.Clock;
import io.micrometer.elastic.ElasticConfig;
import io.micrometer.elastic.ElasticMeterRegistry;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link ElasticMetricsExportAutoConfiguration}.
*
* @author Andy Wilkinson
*/
public class ElasticMetricsExportAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(ElasticMetricsExportAutoConfiguration.class));
@Test
public void backsOffWithoutAClock() {
this.contextRunner.run((context) -> assertThat(context)
.doesNotHaveBean(ElasticMeterRegistry.class));
}
@Test
public void autoConfiguresConfigAndMeterRegistry() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.run((context) -> assertThat(context)
.hasSingleBean(ElasticMeterRegistry.class)
.hasSingleBean(ElasticConfig.class));
}
@Test
public void autoConfigurationCanBeDisabled() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.withPropertyValues("management.metrics.export.elastic.enabled=false")
.run((context) -> assertThat(context)
.doesNotHaveBean(ElasticMeterRegistry.class)
.doesNotHaveBean(ElasticConfig.class));
}
@Test
public void allowsCustomConfigToBeUsed() {
this.contextRunner.withUserConfiguration(CustomConfigConfiguration.class)
.run((context) -> assertThat(context)
.hasSingleBean(ElasticMeterRegistry.class)
.hasSingleBean(ElasticConfig.class).hasBean("customConfig"));
}
@Test
public void allowsCustomRegistryToBeUsed() {
this.contextRunner.withUserConfiguration(CustomRegistryConfiguration.class)
.run((context) -> assertThat(context)
.hasSingleBean(ElasticMeterRegistry.class)
.hasBean("customRegistry").hasSingleBean(ElasticConfig.class));
}
@Test
public void stopsMeterRegistryWhenContextIsClosed() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
.run((context) -> {
ElasticMeterRegistry registry = spyOnDisposableBean(
ElasticMeterRegistry.class, context);
context.close();
verify(registry).stop();
});
}
@SuppressWarnings("unchecked")
private <T> T spyOnDisposableBean(Class<T> type,
AssertableApplicationContext context) {
String[] names = context.getBeanNamesForType(type);
assertThat(names).hasSize(1);
String registryBeanName = names[0];
Map<String, Object> disposableBeans = (Map<String, Object>) ReflectionTestUtils
.getField(context.getAutowireCapableBeanFactory(), "disposableBeans");
Object registryAdapter = disposableBeans.get(registryBeanName);
T registry = (T) spy(ReflectionTestUtils.getField(registryAdapter, "bean"));
ReflectionTestUtils.setField(registryAdapter, "bean", registry);
return registry;
}
@Configuration
static class BaseConfiguration {
@Bean
public Clock clock() {
return Clock.SYSTEM;
}
}
@Configuration
@Import(BaseConfiguration.class)
static class CustomConfigConfiguration {
@Bean
public ElasticConfig customConfig() {
return (k) -> null;
}
}
@Configuration
@Import(BaseConfiguration.class)
static class CustomRegistryConfiguration {
@Bean
public ElasticMeterRegistry customRegistry(ElasticConfig config, Clock clock) {
return new ElasticMeterRegistry(config, clock);
}
}
}

@ -0,0 +1,86 @@
/*
* Copyright 2012-2018 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.metrics.export.elastic;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ElasticPropertiesConfigAdapter}.
*
* @author Andy Wilkiknson
*/
public class ElasticPropertiesConfigAdapterTests {
@Test
public void whenPropertiesHostsIsSetAdapterHostsReturnsIt() {
ElasticProperties properties = new ElasticProperties();
properties.setHosts(new String[] { "https://elastic.example.com" });
assertThat(new ElasticPropertiesConfigAdapter(properties).hosts())
.isEqualTo(new String[] { "https://elastic.example.com" });
}
@Test
public void whenPropertiesIndexIsSetAdapterIndexReturnsIt() {
ElasticProperties properties = new ElasticProperties();
properties.setIndex("test-metrics");
assertThat(new ElasticPropertiesConfigAdapter(properties).index())
.isEqualTo("test-metrics");
}
@Test
public void whenPropertiesIndexDateFormatIsSetAdapterIndexDateFormatReturnsIt() {
ElasticProperties properties = new ElasticProperties();
properties.setIndexDateFormat("yyyy");
assertThat(new ElasticPropertiesConfigAdapter(properties).indexDateFormat())
.isEqualTo("yyyy");
}
@Test
public void whenPropertiesTimestampFieldNameIsSetAdapterTimestampFieldNameReturnsIt() {
ElasticProperties properties = new ElasticProperties();
properties.setTimestampFieldName("@test");
assertThat(new ElasticPropertiesConfigAdapter(properties).timestampFieldName())
.isEqualTo("@test");
}
@Test
public void whenPropertiesAutoCreateIndexIsSetAdapterAutoCreateIndexReturnsIt() {
ElasticProperties properties = new ElasticProperties();
properties.setAutoCreateIndex(false);
assertThat(new ElasticPropertiesConfigAdapter(properties).autoCreateIndex())
.isFalse();
}
@Test
public void whenPropertiesUserNameIsSetAdapterUserNameReturnsIt() {
ElasticProperties properties = new ElasticProperties();
properties.setUserName("alice");
assertThat(new ElasticPropertiesConfigAdapter(properties).userName())
.isEqualTo("alice");
}
@Test
public void whenPropertiesPasswordIsSetAdapterPasswordReturnsIt() {
ElasticProperties properties = new ElasticProperties();
properties.setPassword("secret");
assertThat(new ElasticPropertiesConfigAdapter(properties).password())
.isEqualTo("secret");
}
}

@ -0,0 +1,47 @@
/*
* Copyright 2012-2018 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.metrics.export.elastic;
import io.micrometer.elastic.ElasticConfig;
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryPropertiesTests;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ElasticProperties}.
*
* @author Andy Wilkinson
*/
public class ElasticPropertiesTests extends StepRegistryPropertiesTests {
@Override
public void defaultValuesAreConsistent() {
ElasticProperties properties = new ElasticProperties();
ElasticConfig config = ElasticConfig.DEFAULT;
assertStepRegistryDefaultValues(properties, config);
assertThat(properties.getHosts()).isEqualTo(config.hosts());
assertThat(properties.getIndex()).isEqualTo(config.index());
assertThat(properties.getIndexDateFormat()).isEqualTo(config.indexDateFormat());
assertThat(properties.getPassword()).isEqualTo(config.password());
assertThat(properties.getTimestampFieldName())
.isEqualTo(config.timestampFieldName());
assertThat(properties.getUserName()).isEqualTo(config.userName());
assertThat(properties.isAutoCreateIndex()).isEqualTo(config.autoCreateIndex());
}
}

@ -909,6 +909,11 @@
<artifactId>micrometer-registry-dynatrace</artifactId>
<version>${micrometer.version}</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-elastic</artifactId>
<version>${micrometer.version}</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-influx</artifactId>

@ -216,6 +216,11 @@
<artifactId>micrometer-registry-dynatrace</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-elastic</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-ganglia</artifactId>

@ -1416,6 +1416,19 @@ content into your application. Rather, pick only the properties that you need.
management.metrics.export.dynatrace.step=1m # Step size (i.e. reporting frequency) to use.
management.metrics.export.dynatrace.technology-type=java # Technology type for exported metrics. Used to group metrics under a logical technology name in the Dynatrace UI.
management.metrics.export.dynatrace.uri= # URI to ship metrics to. Should be used for SaaS, self managed instances or to en-route through an internal proxy.
management.metrics.export.elastic.auto-create-index=true # Whether to create the index automatically if it does not exist.
management.metrics.export.elastic.batch-size=10000 # Number of measurements per request to use for this backend. If more measurements are found, then multiple requests will be made.
management.metrics.export.elastic.connect-timeout=1s # Connection timeout for requests to this backend.
management.metrics.export.elastic.enabled=true # Whether exporting of metrics to this backend is enabled.
management.metrics.export.elastic.hosts= # Hosts to export metrics to.
management.metrics.export.elastic.index= # Index to export metrics to.
management.metrics.export.elastic.index-date-format= # Index date format used for rolling indices. Appended to the index name, preceded by a '-'.
management.metrics.export.elastic.num-threads=2 # Number of threads to use with the metrics publishing scheduler.
management.metrics.export.elastic.password= # Password for basic authentication.
management.metrics.export.elastic.read-timeout=10s # Read timeout for requests to this backend.
management.metrics.export.elastic.step=1m # Step size (i.e. reporting frequency) to use.
management.metrics.export.elastic.timestamp-field-name=@timestamp # Name of the timestamp field.
management.metrics.export.elastic.user-name= # User name for basic authentication.
management.metrics.export.ganglia.addressing-mode=multicast # UDP addressing mode, either unicast or multicast.
management.metrics.export.ganglia.duration-units=milliseconds # Base time unit used to report durations.
management.metrics.export.ganglia.enabled=true # Whether exporting of metrics to Ganglia is enabled.

@ -1337,6 +1337,7 @@ monitoring systems, including:
- <<production-ready-metrics-export-atlas,Atlas>>
- <<production-ready-metrics-export-datadog,Datadog>>
- <<production-ready-metrics-export-dynatrace,Dynatrace>>
- <<production-ready-metrics-export-dynatrace,Elastic>>
- <<production-ready-metrics-export-ganglia,Ganglia>>
- <<production-ready-metrics-export-graphite,Graphite>>
- <<production-ready-metrics-export-influx,Influx>>
@ -1475,6 +1476,19 @@ You can also change the interval at which metrics are sent to Dynatrace:
[[production-ready-metrics-export-elastic]]
==== Elastic
By default, metrics are exported to {micrometer-registry-documentation}/elastic[Elastic]
running on your local machine. The location of the Elastic server to use can be provided
using the following property:
[source,properties,indent=0]
----
management.metrics.export.elastic.hosts=http://elastic.example.com:8086
----
[[production-ready-metrics-export-ganglia]]
==== Ganglia
By default, metrics are exported to {micrometer-registry-documentation}/ganglia[Ganglia]

Loading…
Cancel
Save