From eed5fe7a888c6914e21976f1aaee519c8a535de6 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Mon, 22 Oct 2018 19:58:54 -0400 Subject: [PATCH] Extensible MicrometerMetricsCaptor and polishing - change `loadCaptor` to return any existing captor - make builders and facades protected to allow subclassing - change `AbstractMeter.getMeter()` to use generics - add test case demonstrating meter name changes - docs * Polishing - PR Comments, and rebase --- .../micrometer/MicrometerMetricsCaptor.java | 50 ++++--- .../MicrometerCustomMetricsTests.java | 138 ++++++++++++++++++ src/reference/asciidoc/metrics.adoc | 3 + src/reference/asciidoc/whats-new.adoc | 6 + 4 files changed, 173 insertions(+), 24 deletions(-) create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/support/management/micrometer/MicrometerCustomMetricsTests.java diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/micrometer/MicrometerMetricsCaptor.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/micrometer/MicrometerMetricsCaptor.java index fdfd57a5f1..e83d4c9491 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/management/micrometer/MicrometerMetricsCaptor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/micrometer/MicrometerMetricsCaptor.java @@ -48,7 +48,7 @@ public class MicrometerMetricsCaptor implements MetricsCaptor { public static final String MICROMETER_CAPTOR_NAME = "integrationMicrometerMetricsCaptor"; - private final MeterRegistry meterRegistry; + protected final MeterRegistry meterRegistry; // NOSONAR public MicrometerMetricsCaptor(MeterRegistry meterRegistry) { Assert.notNull(meterRegistry, "meterRegistry cannot be null"); @@ -81,7 +81,8 @@ public class MicrometerMetricsCaptor implements MetricsCaptor { } /** - * Add a MicrometerMetricsCaptor to the context if there's a MeterRegistry. + * Add a MicrometerMetricsCaptor to the context if there's a MeterRegistry; if + * there's already a {@link MetricsCaptor} bean, return that. * @param applicationContext the application context. * @return the instance. */ @@ -93,13 +94,12 @@ public class MicrometerMetricsCaptor implements MetricsCaptor { ((GenericApplicationContext) applicationContext).registerBean(MICROMETER_CAPTOR_NAME, MicrometerMetricsCaptor.class, () -> new MicrometerMetricsCaptor(registry)); - return applicationContext.getBean(MicrometerMetricsCaptor.class); } + return applicationContext.getBean(MICROMETER_CAPTOR_NAME, MetricsCaptor.class); } catch (NoSuchBeanDefinitionException e) { return null; } - return null; } private class MicroSample implements SampleFacade { @@ -110,16 +110,17 @@ public class MicrometerMetricsCaptor implements MetricsCaptor { this.sample = sample; } + @SuppressWarnings("unchecked") @Override public void stop(TimerFacade timer) { - this.sample.stop(((MicroTimer) timer).timer); + this.sample.stop(((AbstractMeter) timer).getMeter()); } } - private static class MicroTimerBuilder implements TimerBuilder { + protected static class MicroTimerBuilder implements TimerBuilder { - private final MeterRegistry meterRegistry; + protected final MeterRegistry meterRegistry; // NOSONAR private final Timer.Builder builder; @@ -147,7 +148,7 @@ public class MicrometerMetricsCaptor implements MetricsCaptor { } - private static abstract class AbstractMeter implements MeterFacade { + protected static abstract class AbstractMeter implements MeterFacade { protected final MeterRegistry meterRegistry; // NOSONAR @@ -159,7 +160,7 @@ public class MicrometerMetricsCaptor implements MetricsCaptor { * Get the meter. * @return the meter. */ - protected abstract Meter getMeter(); + protected abstract M getMeter(); @SuppressWarnings("unchecked") @Override @@ -173,17 +174,18 @@ public class MicrometerMetricsCaptor implements MetricsCaptor { } } - private static class MicroTimer extends AbstractMeter implements TimerFacade { + + protected static class MicroTimer extends AbstractMeter implements TimerFacade { private final Timer timer; - MicroTimer(Timer timer, MeterRegistry meterRegistry) { + protected MicroTimer(Timer timer, MeterRegistry meterRegistry) { super(meterRegistry); this.timer = timer; } @Override - protected Meter getMeter() { + protected Timer getMeter() { return this.timer; } @@ -194,13 +196,13 @@ public class MicrometerMetricsCaptor implements MetricsCaptor { } - private static class MicroCounterBuilder implements CounterBuilder { + protected static class MicroCounterBuilder implements CounterBuilder { - private final MeterRegistry meterRegistry; + protected final MeterRegistry meterRegistry; // NOSONAR private final Counter.Builder builder; - MicroCounterBuilder(MeterRegistry meterRegistry, String name) { + protected MicroCounterBuilder(MeterRegistry meterRegistry, String name) { this.meterRegistry = meterRegistry; this.builder = Counter.builder(name); } @@ -224,17 +226,17 @@ public class MicrometerMetricsCaptor implements MetricsCaptor { } - private static class MicroCounter extends AbstractMeter implements CounterFacade { + protected static class MicroCounter extends AbstractMeter implements CounterFacade { private final Counter counter; - MicroCounter(Counter counter, MeterRegistry meterRegistry) { + protected MicroCounter(Counter counter, MeterRegistry meterRegistry) { super(meterRegistry); this.counter = counter; } @Override - protected Meter getMeter() { + protected Counter getMeter() { return this.counter; } @@ -245,13 +247,13 @@ public class MicrometerMetricsCaptor implements MetricsCaptor { } - private static class MicroGaugeBuilder implements GaugeBuilder { + protected static class MicroGaugeBuilder implements GaugeBuilder { - private final MeterRegistry meterRegistry; + protected final MeterRegistry meterRegistry; // NOSONAR private final Gauge.Builder builder; - MicroGaugeBuilder(MeterRegistry meterRegistry, String name, Object obj, ToDoubleFunction f) { + protected MicroGaugeBuilder(MeterRegistry meterRegistry, String name, Object obj, ToDoubleFunction f) { this.meterRegistry = meterRegistry; this.builder = Gauge.builder(name, obj, f); } @@ -275,17 +277,17 @@ public class MicrometerMetricsCaptor implements MetricsCaptor { } - private static class MicroGauge extends AbstractMeter implements GaugeFacade { + protected static class MicroGauge extends AbstractMeter implements GaugeFacade { private final Gauge gauge; - MicroGauge(Gauge gauge, MeterRegistry meterRegistry) { + protected MicroGauge(Gauge gauge, MeterRegistry meterRegistry) { super(meterRegistry); this.gauge = gauge; } @Override - protected Meter getMeter() { + protected Gauge getMeter() { return this.gauge; } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/support/management/micrometer/MicrometerCustomMetricsTests.java b/spring-integration-core/src/test/java/org/springframework/integration/support/management/micrometer/MicrometerCustomMetricsTests.java new file mode 100644 index 0000000000..127dcb21dc --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/support/management/micrometer/MicrometerCustomMetricsTests.java @@ -0,0 +1,138 @@ +/* + * Copyright 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.integration.support.management.micrometer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.config.EnableIntegration; +import org.springframework.integration.config.EnableIntegrationManagement; +import org.springframework.integration.support.management.metrics.MetricsCaptor; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.test.context.junit4.SpringRunner; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.search.MeterNotFoundException; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; + +/** + * @author Gary Russell + * + * @since 5.1 + * + */ +@RunWith(SpringRunner.class) +public class MicrometerCustomMetricsTests { + + @Autowired + private ConfigurableApplicationContext context; + + @Autowired + private MeterRegistry meterRegistry; + + @Autowired + private QueueChannel queue; + + @Test + public void testSend() throws Exception { + GenericMessage message = new GenericMessage<>("foo"); + this.queue.send(message); + this.queue.receive(); + MeterRegistry registry = this.meterRegistry; + assertThat(registry.get("spring.integration.channels").gauge().value()).isEqualTo(3); + + assertThat(registry.get("myTimer") + .tag("standardTimerName", "spring.integration.send") + .tag("name", "queue") + .tag("result", "success") + .timer().count()).isEqualTo(1); + + assertThat(registry.get("myCounter") + .tag("standardCounterName", "spring.integration.receive") + .tag("name", "queue") + .tag("result", "success") + .counter().count()).isEqualTo(1); + + // Test meter removal + this.context.close(); + try { + registry.get("myTimer").timers(); + fail("Expected MeterNotFoundException"); + } + catch (MeterNotFoundException e) { + assertThat(e).hasMessageContaining("No meter with name 'myTimer' was found"); + } + try { + registry.get("myCounter").counters(); + fail("Expected MeterNotFoundException"); + } + catch (MeterNotFoundException e) { + assertThat(e).hasMessageContaining("No meter with name 'myCounter' was found"); + } + } + + @Configuration + @EnableIntegration + @EnableIntegrationManagement + public static class Config { + + @Bean + public MeterRegistry meterRegistry() { + return new SimpleMeterRegistry(); + } + + @Bean + public QueueChannel queue() { + return new QueueChannel(); + } + + @Bean(name = MicrometerMetricsCaptor.MICROMETER_CAPTOR_NAME) + public MetricsCaptor captor() { + return new CustomMetricsCaptor(meterRegistry()); + } + } + + static class CustomMetricsCaptor extends MicrometerMetricsCaptor { + + CustomMetricsCaptor(MeterRegistry meterRegistry) { + super(meterRegistry); + } + + @Override + public TimerBuilder timerBuilder(String name) { + return super.timerBuilder("myTimer") + .tag("standardTimerName", name); + } + + @Override + public CounterBuilder counterBuilder(String name) { + return super.counterBuilder("myCounter") + .tag("standardCounterName", name); + } + + } + +} + diff --git a/src/reference/asciidoc/metrics.adoc b/src/reference/asciidoc/metrics.adoc index 180d674b52..06dbb25688 100644 --- a/src/reference/asciidoc/metrics.adoc +++ b/src/reference/asciidoc/metrics.adoc @@ -157,6 +157,9 @@ In addition, there are three `Gauge` Meters: * `spring.integration.handlers`: The number of `MessageHandlers` in the application. * `spring.integration.sources`: The number of `MessageSources` in the application. +It is possible to customize the names and tags of `Meters` created by integration components by providing a subclass of `MicrometerMetricsCaptor`. +The https://github.com/spring-projects/spring-integration/blob/master/spring-integration-core/src/test/java/org/springframework/integration/support/management/micrometer/MicrometerCustomMetricsTests.java[MicrometerCustomMetricsTests] test case shows a simple example of how to do that. +You can also further customize the meters by overloading the `build()` methods on builder subclasses. [[mgmt-channel-features]] ==== `MessageChannel` Metric Features diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc index 4c2f15e695..b5664ae25a 100644 --- a/src/reference/asciidoc/whats-new.adoc +++ b/src/reference/asciidoc/whats-new.adoc @@ -200,3 +200,9 @@ Object name key values are now quoted if they contain any characters other than e.g. `org.springframework.integration:type=MessageChannel,` `name="input:foo.myGroup.errors"`. This has the side effect that previously "allowed" names, with such characters, will now be quoted. e.g. `org.springframework.integration:type=MessageChannel,` `name="input#foo.myGroup.errors"`. + +[[x51.-micrometer]] +=== Micrometer Support Changes + +It is now simpler to customize the standard Micrometer meters created by the framework. +See <> for more information.