Metrics refactoring
- Change the metrics channel name to `applicationMetricsChannel` - Update documentation - Remove `instanceIndex` to reduce coupling to Spring Cloud Stream semantics and because it can be sent via the `properties` metadata set
This commit is contained in:
@@ -1840,11 +1840,13 @@ This module allow operators to collect metrics from stream applications without
|
||||
|
||||
HTTP polling can be challenging on cloud environments since applications could be on a private network or behind a Load balancer that prevents per instance access.
|
||||
|
||||
The module is activated when you set the destination name for its channel, `spring.cloud.stream.bindings.aggregateMetricsChannel.destination=<DESTINATION_NAME>`.
|
||||
The module is activated when you set the destination name for its channel, `spring.cloud.stream.bindings.applicationMetricsChannel.destination=<DESTINATION_NAME>`.
|
||||
`applicationMetricsChannel` can be configured in a similar fashion to any other bound output channel.
|
||||
The default `contentType` setting of `applicationMetricsChannel` is `application/json`.
|
||||
|
||||
By default the module is configured to only send Spring Integration message channel metrics.
|
||||
|
||||
Available properties for customization using the prefix `spring.cloud.stream.metrics`.
|
||||
Available properties for customization using the prefix `spring.cloud.stream.applicationMetricsChannel`.
|
||||
|
||||
key::
|
||||
The name of the metric being emitted. Should be an unique value per application.
|
||||
@@ -1875,11 +1877,6 @@ properties::
|
||||
Just like the `includes` option, it allows white listing application properties that will be added to the metrics payload
|
||||
+
|
||||
Default: null.
|
||||
+
|
||||
spring.cloud.stream.bindings.aggregateMetricsChannel.contentType::
|
||||
Content-Type of the message
|
||||
+
|
||||
Default: `application/json`
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
|
||||
@@ -35,19 +35,15 @@ public class ApplicationMetrics {
|
||||
private final Date createdTime;
|
||||
|
||||
private String name;
|
||||
|
||||
private int instanceIndex;
|
||||
|
||||
|
||||
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) {
|
||||
this.name = name;
|
||||
this.instanceIndex = instanceIndex;
|
||||
this.metrics = metrics;
|
||||
this.createdTime = new Date();
|
||||
}
|
||||
@@ -60,14 +56,6 @@ public class ApplicationMetrics {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getInstanceIndex() {
|
||||
return instanceIndex;
|
||||
}
|
||||
|
||||
public void setInstanceIndex(int instanceIndex) {
|
||||
this.instanceIndex = instanceIndex;
|
||||
}
|
||||
|
||||
public Collection<Metric<?>> getMetrics() {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public class ApplicationMetricsExporter implements Exporter {
|
||||
@Override
|
||||
public void export() {
|
||||
ApplicationMetrics appMetrics = new ApplicationMetrics(
|
||||
this.properties.getMetricName(), this.properties.getInstanceIndex(),
|
||||
this.properties.getMetricName(),
|
||||
filter());
|
||||
appMetrics.setProperties(this.properties.getExportProperties());
|
||||
source.send(MessageBuilder.withPayload(appMetrics).build());
|
||||
|
||||
@@ -44,9 +44,6 @@ public class ApplicationMetricsProperties
|
||||
@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;
|
||||
@@ -80,14 +77,6 @@ public class ApplicationMetricsProperties
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public int getInstanceIndex() {
|
||||
return instanceIndex;
|
||||
}
|
||||
|
||||
public void setInstanceIndex(int instanceIndex) {
|
||||
this.instanceIndex = instanceIndex;
|
||||
}
|
||||
|
||||
public String[] getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@@ -34,14 +34,14 @@ import org.springframework.context.annotation.Configuration;
|
||||
@ConditionalOnClass(Binder.class)
|
||||
@EnableBinding(Emitter.class)
|
||||
@EnableConfigurationProperties(ApplicationMetricsProperties.class)
|
||||
@ConditionalOnProperty("spring.cloud.stream.bindings." + Emitter.METRICS_CHANNEL_NAME
|
||||
@ConditionalOnProperty("spring.cloud.stream.bindings." + Emitter.APPLICATION_METRICS
|
||||
+ ".destination")
|
||||
public class BinderMetricsAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public ApplicationMetricsExporter aggregateMetricsExporter(MetricsEndpoint endpoint,
|
||||
Emitter emitter, ApplicationMetricsProperties properties) {
|
||||
return new ApplicationMetricsExporter(endpoint, emitter.metrics(), properties);
|
||||
return new ApplicationMetricsExporter(endpoint, emitter.applicationMetrics(), properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -29,15 +29,12 @@ 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."+Emitter.METRICS_CHANNEL_NAME+".contentType",
|
||||
propertiesToAdd.put("spring.cloud.stream.bindings." + Emitter.APPLICATION_METRICS + ".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));
|
||||
environment.getPropertySources()
|
||||
.addLast(new MapPropertySource("binderMetricsDefaultProperties", propertiesToAdd));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ import org.springframework.messaging.MessageChannel;
|
||||
*/
|
||||
public interface Emitter {
|
||||
|
||||
String METRICS_CHANNEL_NAME = "aggregateMetricsChannel";
|
||||
String APPLICATION_METRICS = "applicationMetricsChannel";
|
||||
|
||||
@Output(METRICS_CHANNEL_NAME)
|
||||
MessageChannel metrics();
|
||||
@Output(APPLICATION_METRICS)
|
||||
MessageChannel applicationMetrics();
|
||||
|
||||
}
|
||||
|
||||
@@ -73,21 +73,20 @@ public class ApplicationMetricsExporterTests {
|
||||
ConfigurableApplicationContext applicationContext = SpringApplication.run(
|
||||
BinderExporterApplication.class, "--server.port=0",
|
||||
"--spring.jmx.enabled=false", "--spring.metrics.export.delay-millis=500",
|
||||
"--spring.cloud.stream.bindings.aggregateMetricsChannel.destination=foo");
|
||||
"--spring.cloud.stream.bindings.applicationMetricsChannel.destination=foo");
|
||||
Emitter emitterSource = applicationContext.getBean(Emitter.class);
|
||||
MessageCollector collector = applicationContext.getBean(MessageCollector.class);
|
||||
Message<?> message = collector.forChannel(emitterSource.metrics()).poll(1000,
|
||||
Message<?> message = collector.forChannel(emitterSource.applicationMetrics()).poll(1000,
|
||||
TimeUnit.MILLISECONDS);
|
||||
Assert.assertNotNull(message);
|
||||
ObjectMapper mapper = applicationContext.getBean(ObjectMapper.class);
|
||||
ApplicationMetrics applicationMetrics = mapper
|
||||
ApplicationMetrics applicationMetricsChannel = 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());
|
||||
Assert.assertTrue(CollectionUtils.isEmpty(applicationMetrics.getProperties()));
|
||||
applicationMetricsChannel.getMetrics()));
|
||||
Assert.assertFalse(contains("mem", applicationMetricsChannel.getMetrics()));
|
||||
Assert.assertEquals("application", applicationMetricsChannel.getName());
|
||||
Assert.assertTrue(CollectionUtils.isEmpty(applicationMetricsChannel.getProperties()));
|
||||
applicationContext.close();
|
||||
}
|
||||
|
||||
@@ -96,22 +95,21 @@ public class ApplicationMetricsExporterTests {
|
||||
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.aggregateMetricsChannel.destination=foo");
|
||||
"--spring.application.name=foo",
|
||||
"--spring.cloud.stream.bindings.applicationMetricsChannel.destination=foo");
|
||||
Emitter emitterSource = applicationContext.getBean(Emitter.class);
|
||||
MessageCollector collector = applicationContext.getBean(MessageCollector.class);
|
||||
Message<?> message = collector.forChannel(emitterSource.metrics()).poll(1000,
|
||||
Message<?> message = collector.forChannel(emitterSource.applicationMetrics()).poll(1000,
|
||||
TimeUnit.MILLISECONDS);
|
||||
Assert.assertNotNull(message);
|
||||
ObjectMapper mapper = applicationContext.getBean(ObjectMapper.class);
|
||||
ApplicationMetrics applicationMetrics = mapper
|
||||
ApplicationMetrics applicationMetricsChannel = 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());
|
||||
Assert.assertTrue(CollectionUtils.isEmpty(applicationMetrics.getProperties()));
|
||||
applicationMetricsChannel.getMetrics()));
|
||||
Assert.assertFalse(contains("mem", applicationMetricsChannel.getMetrics()));
|
||||
Assert.assertEquals("foo", applicationMetricsChannel.getName());
|
||||
Assert.assertTrue(CollectionUtils.isEmpty(applicationMetricsChannel.getProperties()));
|
||||
applicationContext.close();
|
||||
}
|
||||
|
||||
@@ -121,22 +119,20 @@ public class ApplicationMetricsExporterTests {
|
||||
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.aggregateMetricsChannel.destination=foo");
|
||||
"--spring.cloud.stream.bindings.applicationMetricsChannel.destination=foo");
|
||||
Emitter emitterSource = applicationContext.getBean(Emitter.class);
|
||||
MessageCollector collector = applicationContext.getBean(MessageCollector.class);
|
||||
Message<?> message = collector.forChannel(emitterSource.metrics()).poll(1000,
|
||||
Message<?> message = collector.forChannel(emitterSource.applicationMetrics()).poll(1000,
|
||||
TimeUnit.MILLISECONDS);
|
||||
Assert.assertNotNull(message);
|
||||
ObjectMapper mapper = applicationContext.getBean(ObjectMapper.class);
|
||||
ApplicationMetrics applicationMetrics = mapper
|
||||
ApplicationMetrics applicationMetricsChannel = 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());
|
||||
Assert.assertTrue(CollectionUtils.isEmpty(applicationMetrics.getProperties()));
|
||||
applicationMetricsChannel.getMetrics()));
|
||||
Assert.assertFalse(contains("mem", applicationMetricsChannel.getMetrics()));
|
||||
Assert.assertEquals("foo.application", applicationMetricsChannel.getName());
|
||||
Assert.assertTrue(CollectionUtils.isEmpty(applicationMetricsChannel.getProperties()));
|
||||
applicationContext.close();
|
||||
}
|
||||
|
||||
@@ -145,21 +141,21 @@ public class ApplicationMetricsExporterTests {
|
||||
ConfigurableApplicationContext applicationContext = SpringApplication.run(
|
||||
BinderExporterApplication.class, "--server.port=0",
|
||||
"--spring.jmx.enabled=false", "--spring.metrics.export.delay-millis=500",
|
||||
"--spring.cloud.stream.bindings.aggregateMetricsChannel.destination=foo",
|
||||
"--spring.cloud.stream.bindings.applicationMetricsChannel.destination=foo",
|
||||
"--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,
|
||||
Message<?> message = collector.forChannel(emitterSource.applicationMetrics()).poll(1000,
|
||||
TimeUnit.MILLISECONDS);
|
||||
Assert.assertNotNull(message);
|
||||
ObjectMapper mapper = applicationContext.getBean(ObjectMapper.class);
|
||||
ApplicationMetrics applicationMetrics = mapper
|
||||
ApplicationMetrics applicationMetricsChannel = 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()));
|
||||
applicationMetricsChannel.getMetrics()));
|
||||
Assert.assertTrue(contains("mem", applicationMetricsChannel.getMetrics()));
|
||||
Assert.assertTrue(CollectionUtils.isEmpty(applicationMetricsChannel.getProperties()));
|
||||
applicationContext.close();
|
||||
}
|
||||
|
||||
@@ -168,22 +164,22 @@ public class ApplicationMetricsExporterTests {
|
||||
ConfigurableApplicationContext applicationContext = SpringApplication.run(
|
||||
BinderExporterApplication.class, "--server.port=0",
|
||||
"--spring.jmx.enabled=false", "--spring.metrics.export.delay-millis=500",
|
||||
"--spring.cloud.stream.bindings.aggregateMetricsChannel.destination=foo",
|
||||
"--spring.cloud.stream.bindings.applicationMetricsChannel.destination=foo",
|
||||
"--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,
|
||||
Message<?> message = collector.forChannel(emitterSource.applicationMetrics()).poll(1000,
|
||||
TimeUnit.MILLISECONDS);
|
||||
Assert.assertNotNull(message);
|
||||
ObjectMapper mapper = applicationContext.getBean(ObjectMapper.class);
|
||||
ApplicationMetrics applicationMetrics = mapper
|
||||
ApplicationMetrics applicationMetricsChannel = mapper
|
||||
.readValue((String) message.getPayload(), ApplicationMetrics.class);
|
||||
Assert.assertFalse(contains("mem", applicationMetrics.getMetrics()));
|
||||
Assert.assertFalse(contains("mem", applicationMetricsChannel.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")
|
||||
applicationMetricsChannel.getMetrics()));
|
||||
Assert.assertFalse(CollectionUtils.isEmpty(applicationMetricsChannel.getProperties()));
|
||||
Assert.assertTrue(applicationMetricsChannel.getProperties().get("spring.test.env.syntax")
|
||||
.equals("testing"));
|
||||
applicationContext.close();
|
||||
}
|
||||
@@ -193,23 +189,22 @@ public class ApplicationMetricsExporterTests {
|
||||
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.aggregateMetricsChannel.destination=foo",
|
||||
"--spring.application.name=foo",
|
||||
"--spring.cloud.stream.bindings.applicationMetricsChannel.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,
|
||||
Message<?> message = collector.forChannel(emitterSource.applicationMetrics()).poll(1000,
|
||||
TimeUnit.MILLISECONDS);
|
||||
Assert.assertNotNull(message);
|
||||
ObjectMapper mapper = applicationContext.getBean(ObjectMapper.class);
|
||||
ApplicationMetrics applicationMetrics = mapper
|
||||
ApplicationMetrics applicationMetricsChannel = 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()));
|
||||
applicationMetricsChannel.getMetrics()));
|
||||
Assert.assertFalse(contains("mem", applicationMetricsChannel.getMetrics()));
|
||||
Assert.assertEquals("foobarfoo", applicationMetricsChannel.getName());
|
||||
Assert.assertTrue(CollectionUtils.isEmpty(applicationMetricsChannel.getProperties()));
|
||||
applicationContext.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -33,24 +33,24 @@ public class RelaxedPropertiesUtilsTests {
|
||||
RelaxedNames springEnv = new RelaxedNames("SPRING_APPLICATION_NAME");
|
||||
RelaxedNames springDot = new RelaxedNames("spring.application.name");
|
||||
RelaxedNames springCamel = new RelaxedNames("springApplicationName");
|
||||
RelaxedNames contentType = new RelaxedNames("spring.cloud.stream.bindings.aggregateMetricsChannel.contentType");
|
||||
RelaxedNames contentTypeEnv = new RelaxedNames("SPRING_CLOUD_STREAM_BINDINGS_AGGREGATE-METRICS-CHANNEL_CONTENT-TYPE");
|
||||
RelaxedNames contentType = new RelaxedNames("spring.cloud.stream.bindings.applicationMetricsChannel.contentType");
|
||||
RelaxedNames contentTypeEnv = new RelaxedNames("SPRING_CLOUD_STREAM_BINDINGS_APPLICATION-METRICS-CHANNEL_CONTENT-TYPE");
|
||||
RelaxedNames xyz = new RelaxedNames("My.X.Is");
|
||||
RelaxedNames springMetrics = new RelaxedNames("spring.cloud.stream.aggregateMetricsChannel");
|
||||
RelaxedNames springMetricsEnv = new RelaxedNames("spring.cloud.stream.aggregate-metrics-channel");
|
||||
RelaxedNames springMetrics = new RelaxedNames("spring.cloud.stream.applicationMetricsChannel");
|
||||
RelaxedNames springMetricsEnv = new RelaxedNames("spring.cloud.stream.application-metrics-channel");
|
||||
Assert.assertEquals("java.home", RelaxedPropertiesUtils.findCanonicalFormat(javaHome));
|
||||
Assert.assertEquals("os", RelaxedPropertiesUtils.findCanonicalFormat(os));
|
||||
Assert.assertEquals("spring.application.name", RelaxedPropertiesUtils.findCanonicalFormat(springEnv));
|
||||
Assert.assertEquals("spring.application.name", RelaxedPropertiesUtils.findCanonicalFormat(springDot));
|
||||
Assert.assertEquals("spring.application.name", RelaxedPropertiesUtils.findCanonicalFormat(springCamel));
|
||||
Assert.assertEquals("spring.cloud.stream.bindings.aggregateMetricsChannel.contentType",
|
||||
Assert.assertEquals("spring.cloud.stream.bindings.applicationMetricsChannel.contentType",
|
||||
RelaxedPropertiesUtils.findCanonicalFormat(contentType));
|
||||
Assert.assertEquals("spring.cloud.stream.bindings.aggregateMetricsChannel.contentType",
|
||||
Assert.assertEquals("spring.cloud.stream.bindings.applicationMetricsChannel.contentType",
|
||||
RelaxedPropertiesUtils.findCanonicalFormat(contentTypeEnv));
|
||||
Assert.assertEquals("My.X.Is", RelaxedPropertiesUtils.findCanonicalFormat(xyz));
|
||||
Assert.assertEquals("spring.cloud.stream.aggregateMetricsChannel",
|
||||
Assert.assertEquals("spring.cloud.stream.applicationMetricsChannel",
|
||||
RelaxedPropertiesUtils.findCanonicalFormat(springMetrics));
|
||||
Assert.assertEquals("spring.cloud.stream.aggregateMetricsChannel",
|
||||
Assert.assertEquals("spring.cloud.stream.applicationMetricsChannel",
|
||||
RelaxedPropertiesUtils.findCanonicalFormat(springMetricsEnv));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user