Convert BinderMetricsEmitter to an Exporter

Re-organize and re-name a bit
Rename config package
Use spring.metrics.export.* to configure the includes and excludes
This commit is contained in:
Dave Syer
2017-03-29 15:47:37 +01:00
committed by Vinicius Carvalho
parent c812fa0374
commit 07d0328d68
13 changed files with 382 additions and 353 deletions

View File

@@ -29,6 +29,11 @@
<artifactId>spring-cloud-stream-test-support</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@@ -1,110 +0,0 @@
/*
* Copyright 2017 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.cloud.stream.config.metrics;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.StringUtils;
/**
* @author Vinicius Carvalho
*/
@ConfigurationProperties(prefix = "spring.cloud.stream.metrics")
public class StreamMetricsProperties {
private String prefix;
@Value("${spring.application.name:${vcap.application.name:${spring.config.name:application}}}")
private String key;
private String metricName;
private String[] includes = new String[] { "integration**" };
private String[] excludes;
private String[] properties;
private Long delayMillis;
public String[] getIncludes() {
return includes;
}
public void setIncludes(String[] includes) {
this.includes = includes;
}
public String[] getExcludes() {
return excludes;
}
public void setExcludes(String[] excludes) {
this.excludes = excludes;
}
public Long getDelayMillis() {
return delayMillis;
}
public void setDelayMillis(Long delayMillis) {
this.delayMillis = delayMillis;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String[] getProperties() {
return properties;
}
public void setProperties(String[] properties) {
this.properties = properties;
}
public String getMetricName() {
if (this.metricName == null) {
this.metricName = resolveMetricName();
}
return metricName;
}
private String resolveMetricName() {
StringBuffer name = new StringBuffer(this.key);
if (!StringUtils.isEmpty(this.prefix)) {
String prefix = this.prefix;
if (prefix.lastIndexOf(".") == -1) {
prefix += ".";
}
name.insert(0, prefix);
}
return name.toString();
}
}

View File

@@ -38,13 +38,14 @@ public class ApplicationMetrics {
private int instanceIndex;
private Collection<Metric> metrics;
private Collection<Metric<?>> metrics;
private Map<String, Object> properties;
@JsonCreator
public ApplicationMetrics(@JsonProperty("name") String name, @JsonProperty("instanceIndex") int instanceIndex,
@JsonProperty("metrics") Collection<Metric> metrics) {
public ApplicationMetrics(@JsonProperty("name") String name,
@JsonProperty("instanceIndex") int instanceIndex,
@JsonProperty("metrics") Collection<Metric<?>> metrics) {
this.name = name;
this.instanceIndex = instanceIndex;
this.metrics = metrics;
@@ -67,11 +68,11 @@ public class ApplicationMetrics {
this.instanceIndex = instanceIndex;
}
public Collection<Metric> getMetrics() {
public Collection<Metric<?>> getMetrics() {
return metrics;
}
public void setMetrics(Collection<Metric> metrics) {
public void setMetrics(Collection<Metric<?>> metrics) {
this.metrics = metrics;
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2017 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.cloud.stream.metrics;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.actuate.endpoint.MetricsEndpointMetricReader;
import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.actuate.metrics.export.Exporter;
import org.springframework.boot.actuate.metrics.export.MetricCopyExporter;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.MessageChannel;
import org.springframework.util.ObjectUtils;
import org.springframework.util.PatternMatchUtils;
/**
*
* Component that sends {@link ApplicationMetrics} from
* {@link MetricsEndpointMetricReader} downstream via the configured metrics channel.
*
* It uses the Spring Boot support for {@link Exporter} to periodically emit messages
* polled from the endpoint.
*
* @author Vinicius Carvalho
*/
public class ApplicationMetricsExporter implements Exporter {
private MessageChannel source;
private ApplicationMetricsProperties properties;
private MetricsEndpointMetricReader metricsReader;
public ApplicationMetricsExporter(MetricsEndpoint endpoint, MessageChannel source,
ApplicationMetricsProperties properties) {
this.source = source;
this.properties = properties;
this.metricsReader = new MetricsEndpointMetricReader(endpoint);
}
@Override
public void export() {
ApplicationMetrics appMetrics = new ApplicationMetrics(
this.properties.getMetricName(), this.properties.getInstanceIndex(),
filter());
appMetrics.setProperties(this.properties.getExportProperties());
source.send(MessageBuilder.withPayload(appMetrics).build());
}
/**
* Copy of similarly named method in {@link MetricCopyExporter}.
*/
protected Collection<Metric<?>> filter() {
Collection<Metric<?>> result = new ArrayList<>();
Iterable<Metric<?>> metrics = metricsReader.findAll();
for (Metric<?> metric : metrics) {
if (isMatch(metric.getName(), this.properties.getTrigger().getIncludes(),
this.properties.getTrigger().getExcludes())) {
result.add(metric);
}
}
return result;
}
/**
* Copy of similarly named method in {@link MetricCopyExporter}.
*/
private boolean isMatch(String name, String[] includes, String[] excludes) {
if (ObjectUtils.isEmpty(includes)
|| PatternMatchUtils.simpleMatch(includes, name)) {
return !PatternMatchUtils.simpleMatch(excludes, name);
}
return false;
}
}

View File

@@ -0,0 +1,158 @@
/*
* Copyright 2017 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.cloud.stream.metrics;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.metrics.export.MetricExportProperties;
import org.springframework.boot.actuate.metrics.export.TriggerProperties;
import org.springframework.boot.bind.RelaxedNames;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.util.ObjectUtils;
import org.springframework.util.PatternMatchUtils;
/**
* @author Vinicius Carvalho
*/
@ConfigurationProperties(prefix = "spring.cloud.stream.metrics")
public class ApplicationMetricsProperties
implements ApplicationListener<ContextRefreshedEvent> {
private String prefix = "";
@Value("${spring.application.name:${vcap.application.name:${spring.config.name:application}}}")
private String key;
@Value("${INSTANCE_INDEX:${CF_INSTANCE_INDEX:0}}")
private int instanceIndex;
private String metricName;
private String[] properties;
private MetricExportProperties export;
public TriggerProperties getTrigger() {
return export.findTrigger("application");
}
public ApplicationMetricsProperties(MetricExportProperties export) {
this.export = export;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
if (!prefix.endsWith(".")) {
prefix += ".";
}
this.prefix = prefix;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public int getInstanceIndex() {
return instanceIndex;
}
public void setInstanceIndex(int instanceIndex) {
this.instanceIndex = instanceIndex;
}
public String[] getProperties() {
return properties;
}
public void setProperties(String[] properties) {
this.properties = properties;
}
/**
* List of properties that are going to be appended to each message. This gets
* populate by onApplicationEvent, once the context refreshes to avoid overhead of
* doing per message basis.
*/
private Map<String, Object> exportProperties = new HashMap<>();
public Map<String, Object> getExportProperties() {
return exportProperties;
}
public String getMetricName() {
if (this.metricName == null) {
this.metricName = resolveMetricName();
}
return metricName;
}
private String resolveMetricName() {
return this.prefix + this.key;
}
/**
* Iterates over all property sources from this application context and copies the
* ones listed in {@link ApplicationMetricsProperties} includes.
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) event
.getSource();
if (!ObjectUtils.isEmpty(this.properties)) {
for (PropertySource<?> source : ctx.getEnvironment().getPropertySources()) {
if (source instanceof EnumerablePropertySource) {
EnumerablePropertySource<?> e = (EnumerablePropertySource<?>) source;
for (String propertyName : e.getPropertyNames()) {
RelaxedNames relaxedNames = new RelaxedNames(propertyName);
relaxedLoop: for (String relaxedPropertyName : relaxedNames) {
if (isMatch(relaxedPropertyName, this.properties, null)) {
this.exportProperties.put(
RelaxedPropertiesUtils
.findCanonicalFormat(relaxedNames),
source.getProperty(propertyName));
break relaxedLoop;
}
}
}
}
}
}
}
private boolean isMatch(String name, String[] includes, String[] excludes) {
if (ObjectUtils.isEmpty(includes)
|| PatternMatchUtils.simpleMatch(includes, name)) {
return !PatternMatchUtils.simpleMatch(excludes, name);
}
return false;
}
}

View File

@@ -1,144 +0,0 @@
/*
* Copyright 2017 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.cloud.stream.metrics;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.actuate.endpoint.MetricsEndpointMetricReader;
import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.actuate.metrics.export.MetricCopyExporter;
import org.springframework.boot.bind.RelaxedNames;
import org.springframework.cloud.stream.config.BindingServiceProperties;
import org.springframework.cloud.stream.config.metrics.StreamMetricsProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.util.ObjectUtils;
import org.springframework.util.PatternMatchUtils;
/**
*
* Component that sends metrics from {@link MetricsEndpointMetricReader} downstream via
* the configured metrics channel.
*
* It uses {@link Scheduled} support to periodially emit messages polled from the
* endpoint.
*
* @author Vinicius Carvalho
*/
public class BinderMetricsEmitter implements ApplicationListener<ContextRefreshedEvent>, ApplicationContextAware {
@Autowired
private Emitter source;
@Autowired
private StreamMetricsProperties properties;
@Autowired
private BindingServiceProperties bindingServiceProperties;
private MetricsEndpointMetricReader metricsReader;
private ApplicationContext applicationContext;
/**
* List of properties that are going to be appended to each message. This gets
* populate by onApplicationEvent, once the context refreshes to avoid overhead of
* doing per message basis.
*/
private Map<String, Object> whitelistedProperties;
public BinderMetricsEmitter(MetricsEndpoint endpoint) {
this.metricsReader = new MetricsEndpointMetricReader(endpoint);
this.whitelistedProperties = new HashMap<>();
}
@Scheduled(fixedRateString = "${spring.cloud.stream.metrics.delay-millis:5000}")
public void sendMetrics() {
ApplicationMetrics appMetrics = new ApplicationMetrics(this.properties.getMetricName(),
this.bindingServiceProperties.getInstanceIndex(), filter());
appMetrics.setProperties(whitelistedProperties);
source.metrics().send(MessageBuilder.withPayload(appMetrics).build());
}
/**
* Copy of similarly named method in {@link MetricCopyExporter}.
*/
protected Collection<Metric> filter() {
Collection<Metric> result = new ArrayList<>();
Iterable<Metric<?>> metrics = metricsReader.findAll();
for (Metric metric : metrics) {
if (isMatch(metric.getName(), this.properties.getIncludes(), this.properties.getExcludes())) {
result.add(metric);
}
}
return result;
}
/**
* Copy of similarly named method in {@link MetricCopyExporter}.
*/
private boolean isMatch(String name, String[] includes, String[] excludes) {
if (ObjectUtils.isEmpty(includes) || PatternMatchUtils.simpleMatch(includes, name)) {
return !PatternMatchUtils.simpleMatch(excludes, name);
}
return false;
}
/**
* Iterates over all property sources from this application context and copies the
* ones listed in {@link StreamMetricsProperties} includes.
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) event.getSource();
if (!ObjectUtils.isEmpty(this.properties.getProperties())) {
for (PropertySource source : ctx.getEnvironment().getPropertySources()) {
if (source instanceof EnumerablePropertySource) {
EnumerablePropertySource e = (EnumerablePropertySource) source;
for (String propertyName : e.getPropertyNames()) {
RelaxedNames relaxedNames = new RelaxedNames(propertyName);
relaxedLoop: for (String relaxedPropertyName : relaxedNames) {
if (isMatch(relaxedPropertyName, this.properties.getProperties(), null)) {
whitelistedProperties.put(RelaxedPropertiesUtils.findCanonicalFormat(relaxedNames),
source.getProperty(propertyName));
break relaxedLoop;
}
}
}
}
}
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

View File

@@ -27,19 +27,19 @@ import org.springframework.util.StringUtils;
*
* @author Vinicius Carvalho
*/
public class RelaxedPropertiesUtils {
class RelaxedPropertiesUtils {
private static final Pattern HYPHEN_LOWER = Pattern.compile("-_|_-|__|\\.-|\\._");
private static final Pattern SEPARATED_TO_CAMEL_CASE_PATTERN = Pattern.compile("[_\\-.]");
private static final Pattern SEPARATED_TO_CAMEL_CASE_PATTERN = Pattern
.compile("[_\\-.]");
private static final char[] SUFFIXES = new char[] { '_', '-', '.' };
/**
* Searches relaxed names and tries to find a best match for a canonical form using
* dot notation.
* For example, if a new list was built with JAVA_HOME as property, the return of this
* method would be {@code java.home}.
* dot notation. For example, if a new list was built with JAVA_HOME as property, the
* return of this method would be {@code java.home}.
*
* Relaxed names generate a long list of variations of a property, it can be tricky
* trying to infer the correct format, which sometimes may not even exist in dot

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.stream.config.metrics;
package org.springframework.cloud.stream.metrics.config;
import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -22,32 +22,31 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.binder.Binder;
import org.springframework.cloud.stream.metrics.BinderMetricsEmitter;
import org.springframework.cloud.stream.metrics.BootMetricJsonSerializer;
import org.springframework.cloud.stream.metrics.Emitter;
import org.springframework.cloud.stream.metrics.ApplicationMetricsExporter;
import org.springframework.cloud.stream.metrics.ApplicationMetricsProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* @author Vinicius Carvalho
*/
@Configuration
@ConditionalOnClass(Binder.class)
@EnableScheduling
@EnableBinding(Emitter.class)
@EnableConfigurationProperties(StreamMetricsProperties.class)
@ConditionalOnProperty("spring.cloud.stream.bindings." + Emitter.METRICS_CHANNEL_NAME + ".destination")
@EnableConfigurationProperties(ApplicationMetricsProperties.class)
@ConditionalOnProperty("spring.cloud.stream.bindings." + Emitter.METRICS_CHANNEL_NAME
+ ".destination")
public class BinderMetricsAutoConfiguration {
@Bean
public BinderMetricsEmitter binderMetricsExporter(MetricsEndpoint endpoint) {
return new BinderMetricsEmitter(endpoint);
public ApplicationMetricsExporter aggregateMetricsExporter(MetricsEndpoint endpoint,
Emitter emitter, ApplicationMetricsProperties properties) {
return new ApplicationMetricsExporter(endpoint, emitter.metrics(), properties);
}
@Bean
public BootMetricJsonSerializer metricJsonSerializer() {
return new BootMetricJsonSerializer();
public MetricJsonSerializer metricJsonSerializer() {
return new MetricJsonSerializer();
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.stream.config.metrics;
package org.springframework.cloud.stream.metrics.config;
import java.util.HashMap;
import java.util.Map;
@@ -29,10 +29,15 @@ import org.springframework.core.env.MapPropertySource;
*/
public class BinderMetricsEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
Map<String, Object> propertiesToAdd = new HashMap<>();
propertiesToAdd.put("spring.cloud.stream.bindings.streamMetrics.contentType", "application/json");
environment.getPropertySources()
.addLast(new MapPropertySource("binderMetricsDefaultProperties", propertiesToAdd));
propertiesToAdd.put("spring.cloud.stream.bindings.streamMetrics.contentType",
"application/json");
propertiesToAdd.put("spring.cloud.stream.metrics.instanceIndex",
"${spring.cloud.stream.instanceIndex:${INSTANCE_INDEX:${CF_INSTANCE_INDEX:0}}}");
propertiesToAdd.put("spring.metrics.export.includes", "integration**");
environment.getPropertySources().addLast(
new MapPropertySource("binderMetricsDefaultProperties", propertiesToAdd));
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.stream.metrics;
package org.springframework.cloud.stream.metrics.config;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.stream.metrics;
package org.springframework.cloud.stream.metrics.config;
import java.io.IOException;
import java.text.DateFormat;
@@ -38,15 +38,15 @@ import org.springframework.boot.jackson.JsonComponent;
* @author Vinicius Carvalho
*/
@JsonComponent
public class BootMetricJsonSerializer {
public class MetricJsonSerializer {
final static DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
public static class Serializer extends JsonSerializer<Metric> {
public static class Serializer extends JsonSerializer<Metric<?>> {
@Override
public void serialize(Metric metric, JsonGenerator json, SerializerProvider serializerProvider)
throws IOException {
public void serialize(Metric<?> metric, JsonGenerator json,
SerializerProvider serializerProvider) throws IOException {
json.writeStartObject();
json.writeStringField("name", metric.getName());
json.writeNumberField("value", metric.getValue().doubleValue());
@@ -55,10 +55,10 @@ public class BootMetricJsonSerializer {
}
}
public static class Deserializer extends JsonDeserializer<Metric> {
public static class Deserializer extends JsonDeserializer<Metric<?>> {
@Override
public Metric deserialize(JsonParser p, DeserializationContext ctxt)
public Metric<?> deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = p.getCodec().readTree(p);
String name = node.get("name").asText();
@@ -70,7 +70,7 @@ public class BootMetricJsonSerializer {
}
catch (ParseException e) {
}
Metric<Number> metric = new Metric(name, value, timestamp);
Metric<Number> metric = new Metric<Number>(name, value, timestamp);
return metric;
}

View File

@@ -1,4 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.stream.config.metrics.BinderMetricsAutoConfiguration
org.springframework.cloud.stream.metrics.config.BinderMetricsAutoConfiguration
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.stream.config.metrics.BinderMetricsEnvironmentPostProcessor
org.springframework.cloud.stream.metrics.config.BinderMetricsEnvironmentPostProcessor

View File

@@ -20,6 +20,7 @@ import java.util.Collection;
import java.util.concurrent.TimeUnit;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
@@ -29,6 +30,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.stream.metrics.config.Emitter;
import org.springframework.cloud.stream.test.binder.MessageCollector;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.messaging.Message;
@@ -37,7 +39,7 @@ import org.springframework.util.CollectionUtils;
/**
* @author Vinicius Carvalho
*/
public class BinderMetricsEmitterTests {
public class ApplicationMetricsExporterTests {
@BeforeClass
public static void setSystemProps() {
@@ -51,8 +53,9 @@ public class BinderMetricsEmitterTests {
@Test(expected = NoSuchBeanDefinitionException.class)
public void checkDisabledConfiguration() throws Exception {
ConfigurableApplicationContext applicationContext = SpringApplication.run(BinderExporterApplication.class,
"--server.port=0", "--spring.jmx.enabled=false", "--spring.cloud.stream.metrics.delay-millis=500");
ConfigurableApplicationContext applicationContext = SpringApplication.run(
BinderExporterApplication.class, "--server.port=0",
"--spring.jmx.enabled=false");
try {
applicationContext.getBean(Emitter.class);
}
@@ -67,17 +70,20 @@ public class BinderMetricsEmitterTests {
@Test
public void defaultIncludes() throws Exception {
ConfigurableApplicationContext applicationContext = SpringApplication.run(BinderExporterApplication.class,
"--server.port=0", "--spring.jmx.enabled=false", "--spring.cloud.stream.metrics.delay-millis=500",
ConfigurableApplicationContext applicationContext = SpringApplication.run(
BinderExporterApplication.class, "--server.port=0",
"--spring.jmx.enabled=false", "--spring.metrics.export.delay-millis=500",
"--spring.cloud.stream.bindings.streamMetrics.destination=foo");
Emitter emitterSource = applicationContext.getBean(Emitter.class);
MessageCollector collector = applicationContext.getBean(MessageCollector.class);
Message message = collector.forChannel(emitterSource.metrics()).poll(1000, TimeUnit.MILLISECONDS);
Message<?> message = collector.forChannel(emitterSource.metrics()).poll(1000,
TimeUnit.MILLISECONDS);
Assert.assertNotNull(message);
ObjectMapper mapper = applicationContext.getBean(ObjectMapper.class);
ApplicationMetrics applicationMetrics = mapper.readValue((String) message.getPayload(),
ApplicationMetrics.class);
Assert.assertTrue(contains("integration.channel.errorChannel.errorRate.mean", applicationMetrics.getMetrics()));
ApplicationMetrics applicationMetrics = mapper
.readValue((String) message.getPayload(), ApplicationMetrics.class);
Assert.assertTrue(contains("integration.channel.errorChannel.errorRate.mean",
applicationMetrics.getMetrics()));
Assert.assertFalse(contains("mem", applicationMetrics.getMetrics()));
Assert.assertEquals("application", applicationMetrics.getName());
Assert.assertEquals(0, applicationMetrics.getInstanceIndex());
@@ -87,18 +93,21 @@ public class BinderMetricsEmitterTests {
@Test
public void customAppNameAndIndex() throws Exception {
ConfigurableApplicationContext applicationContext = SpringApplication.run(BinderExporterApplication.class,
"--server.port=0", "--spring.jmx.enabled=false", "--spring.cloud.stream.metrics.delay-millis=500",
ConfigurableApplicationContext applicationContext = SpringApplication.run(
BinderExporterApplication.class, "--server.port=0",
"--spring.jmx.enabled=false", "--spring.metrics.export.delay-millis=500",
"--spring.application.name=foo", "--spring.cloud.stream.instanceIndex=1",
"--spring.cloud.stream.bindings.streamMetrics.destination=foo");
Emitter emitterSource = applicationContext.getBean(Emitter.class);
MessageCollector collector = applicationContext.getBean(MessageCollector.class);
Message message = collector.forChannel(emitterSource.metrics()).poll(1000, TimeUnit.MILLISECONDS);
Message<?> message = collector.forChannel(emitterSource.metrics()).poll(1000,
TimeUnit.MILLISECONDS);
Assert.assertNotNull(message);
ObjectMapper mapper = applicationContext.getBean(ObjectMapper.class);
ApplicationMetrics applicationMetrics = mapper.readValue((String) message.getPayload(),
ApplicationMetrics.class);
Assert.assertTrue(contains("integration.channel.errorChannel.errorRate.mean", applicationMetrics.getMetrics()));
ApplicationMetrics applicationMetrics = mapper
.readValue((String) message.getPayload(), ApplicationMetrics.class);
Assert.assertTrue(contains("integration.channel.errorChannel.errorRate.mean",
applicationMetrics.getMetrics()));
Assert.assertFalse(contains("mem", applicationMetrics.getMetrics()));
Assert.assertEquals("foo", applicationMetrics.getName());
Assert.assertEquals(1, applicationMetrics.getInstanceIndex());
@@ -108,18 +117,22 @@ public class BinderMetricsEmitterTests {
@Test
public void usingPrefix() throws Exception {
ConfigurableApplicationContext applicationContext = SpringApplication.run(BinderExporterApplication.class,
"--server.port=0", "--spring.jmx.enabled=false", "--spring.cloud.stream.metrics.delay-millis=500",
"--spring.cloud.stream.metrics.prefix=foo", "--spring.cloud.stream.instanceIndex=1",
ConfigurableApplicationContext applicationContext = SpringApplication.run(
BinderExporterApplication.class, "--server.port=0",
"--spring.jmx.enabled=false", "--spring.metrics.export.delay-millis=500",
"--spring.cloud.stream.metrics.prefix=foo",
"--spring.cloud.stream.instanceIndex=1",
"--spring.cloud.stream.bindings.streamMetrics.destination=foo");
Emitter emitterSource = applicationContext.getBean(Emitter.class);
MessageCollector collector = applicationContext.getBean(MessageCollector.class);
Message message = collector.forChannel(emitterSource.metrics()).poll(1000, TimeUnit.MILLISECONDS);
Message<?> message = collector.forChannel(emitterSource.metrics()).poll(1000,
TimeUnit.MILLISECONDS);
Assert.assertNotNull(message);
ObjectMapper mapper = applicationContext.getBean(ObjectMapper.class);
ApplicationMetrics applicationMetrics = mapper.readValue((String) message.getPayload(),
ApplicationMetrics.class);
Assert.assertTrue(contains("integration.channel.errorChannel.errorRate.mean", applicationMetrics.getMetrics()));
ApplicationMetrics applicationMetrics = mapper
.readValue((String) message.getPayload(), ApplicationMetrics.class);
Assert.assertTrue(contains("integration.channel.errorChannel.errorRate.mean",
applicationMetrics.getMetrics()));
Assert.assertFalse(contains("mem", applicationMetrics.getMetrics()));
Assert.assertEquals("foo.application", applicationMetrics.getName());
Assert.assertEquals(1, applicationMetrics.getInstanceIndex());
@@ -129,19 +142,22 @@ public class BinderMetricsEmitterTests {
@Test
public void includesExcludes() throws Exception {
ConfigurableApplicationContext applicationContext = SpringApplication.run(BinderExporterApplication.class,
"--server.port=0", "--spring.jmx.enabled=false", "--spring.cloud.stream.metrics.delay-millis=500",
ConfigurableApplicationContext applicationContext = SpringApplication.run(
BinderExporterApplication.class, "--server.port=0",
"--spring.jmx.enabled=false", "--spring.metrics.export.delay-millis=500",
"--spring.cloud.stream.bindings.streamMetrics.destination=foo",
"--spring.cloud.stream.metrics.includes=mem**", "--spring.cloud.stream.metrics.excludes=integration**");
"--spring.metrics.export.includes=mem**",
"--spring.metrics.export.excludes=integration**");
Emitter emitterSource = applicationContext.getBean(Emitter.class);
MessageCollector collector = applicationContext.getBean(MessageCollector.class);
Message message = collector.forChannel(emitterSource.metrics()).poll(1000, TimeUnit.MILLISECONDS);
Message<?> message = collector.forChannel(emitterSource.metrics()).poll(1000,
TimeUnit.MILLISECONDS);
Assert.assertNotNull(message);
ObjectMapper mapper = applicationContext.getBean(ObjectMapper.class);
ApplicationMetrics applicationMetrics = mapper.readValue((String) message.getPayload(),
ApplicationMetrics.class);
Assert.assertFalse(
contains("integration.channel.errorChannel.errorRate.mean", applicationMetrics.getMetrics()));
ApplicationMetrics applicationMetrics = mapper
.readValue((String) message.getPayload(), ApplicationMetrics.class);
Assert.assertFalse(contains("integration.channel.errorChannel.errorRate.mean",
applicationMetrics.getMetrics()));
Assert.assertTrue(contains("mem", applicationMetrics.getMetrics()));
Assert.assertTrue(CollectionUtils.isEmpty(applicationMetrics.getProperties()));
applicationContext.close();
@@ -149,50 +165,57 @@ public class BinderMetricsEmitterTests {
@Test
public void includesExcludesWithProperties() throws Exception {
ConfigurableApplicationContext applicationContext = SpringApplication.run(BinderExporterApplication.class,
"--server.port=0", "--spring.jmx.enabled=false", "--spring.cloud.stream.metrics.delay-millis=500",
ConfigurableApplicationContext applicationContext = SpringApplication.run(
BinderExporterApplication.class, "--server.port=0",
"--spring.jmx.enabled=false", "--spring.metrics.export.delay-millis=500",
"--spring.cloud.stream.bindings.streamMetrics.destination=foo",
"--spring.cloud.stream.metrics.includes=integration**",
"--spring.metrics.export.includes=integration**",
"--spring.cloud.stream.metrics.properties=java**,spring.test.env**");
Emitter emitterSource = applicationContext.getBean(Emitter.class);
MessageCollector collector = applicationContext.getBean(MessageCollector.class);
Message message = collector.forChannel(emitterSource.metrics()).poll(1000, TimeUnit.MILLISECONDS);
Message<?> message = collector.forChannel(emitterSource.metrics()).poll(1000,
TimeUnit.MILLISECONDS);
Assert.assertNotNull(message);
ObjectMapper mapper = applicationContext.getBean(ObjectMapper.class);
ApplicationMetrics applicationMetrics = mapper.readValue((String) message.getPayload(),
ApplicationMetrics.class);
ApplicationMetrics applicationMetrics = mapper
.readValue((String) message.getPayload(), ApplicationMetrics.class);
Assert.assertFalse(contains("mem", applicationMetrics.getMetrics()));
Assert.assertTrue(contains("integration.channel.errorChannel.errorRate.mean", applicationMetrics.getMetrics()));
Assert.assertTrue(contains("integration.channel.errorChannel.errorRate.mean",
applicationMetrics.getMetrics()));
Assert.assertFalse(CollectionUtils.isEmpty(applicationMetrics.getProperties()));
Assert.assertTrue(applicationMetrics.getProperties().get("spring.test.env.syntax").equals("testing"));
Assert.assertTrue(applicationMetrics.getProperties().get("spring.test.env.syntax")
.equals("testing"));
applicationContext.close();
}
@Test
public void overrideAppName() throws Exception {
ConfigurableApplicationContext applicationContext = SpringApplication.run(BinderExporterApplication.class,
"--server.port=0", "--spring.jmx.enabled=false", "--spring.cloud.stream.metrics.delay-millis=500",
"--spring.application.name=foo", "--spring.cloud.stream.instanceIndex=1",
"--spring.cloud.stream.bindings.streamMetrics.destination=foo",
"--spring.cloud.stream.metrics.key=foobarfoo");
Emitter emitterSource = applicationContext.getBean(Emitter.class);
MessageCollector collector = applicationContext.getBean(MessageCollector.class);
Message message = collector.forChannel(emitterSource.metrics()).poll(1000, TimeUnit.MILLISECONDS);
Assert.assertNotNull(message);
ObjectMapper mapper = applicationContext.getBean(ObjectMapper.class);
ApplicationMetrics applicationMetrics = mapper.readValue((String) message.getPayload(),
ApplicationMetrics.class);
Assert.assertTrue(contains("integration.channel.errorChannel.errorRate.mean", applicationMetrics.getMetrics()));
Assert.assertFalse(contains("mem", applicationMetrics.getMetrics()));
Assert.assertEquals("foobarfoo", applicationMetrics.getName());
Assert.assertEquals(1, applicationMetrics.getInstanceIndex());
Assert.assertTrue(CollectionUtils.isEmpty(applicationMetrics.getProperties()));
applicationContext.close();
}
@Test
public void overrideAppName() throws Exception {
ConfigurableApplicationContext applicationContext = SpringApplication.run(
BinderExporterApplication.class, "--server.port=0",
"--spring.jmx.enabled=false", "--spring.metrics.export.delay-millis=500",
"--spring.application.name=foo", "--spring.cloud.stream.instanceIndex=1",
"--spring.cloud.stream.bindings.streamMetrics.destination=foo",
"--spring.cloud.stream.metrics.key=foobarfoo");
Emitter emitterSource = applicationContext.getBean(Emitter.class);
MessageCollector collector = applicationContext.getBean(MessageCollector.class);
Message<?> message = collector.forChannel(emitterSource.metrics()).poll(1000,
TimeUnit.MILLISECONDS);
Assert.assertNotNull(message);
ObjectMapper mapper = applicationContext.getBean(ObjectMapper.class);
ApplicationMetrics applicationMetrics = mapper
.readValue((String) message.getPayload(), ApplicationMetrics.class);
Assert.assertTrue(contains("integration.channel.errorChannel.errorRate.mean",
applicationMetrics.getMetrics()));
Assert.assertFalse(contains("mem", applicationMetrics.getMetrics()));
Assert.assertEquals("foobarfoo", applicationMetrics.getName());
Assert.assertEquals(1, applicationMetrics.getInstanceIndex());
Assert.assertTrue(CollectionUtils.isEmpty(applicationMetrics.getProperties()));
applicationContext.close();
}
private boolean contains(String metric, Collection<Metric> metrics) {
private boolean contains(String metric, Collection<Metric<?>> metrics) {
boolean contains = false;
for (Metric entry : metrics) {
for (Metric<?> entry : metrics) {
contains = entry.getName().equals(metric);
if (contains) {
break;