diff --git a/build.gradle b/build.gradle index c127477bed..aa80016ded 100644 --- a/build.gradle +++ b/build.gradle @@ -331,7 +331,7 @@ project('spring-integration-core') { } compile("io.fastjson:boon:$boonVersion", optional) compile("com.esotericsoftware:kryo-shaded:$kryoShadedVersion", optional) - compile "io.micrometer:micrometer-core:$micrometerVersion" + compile("io.micrometer:micrometer-core:$micrometerVersion", optional) testCompile ("org.aspectj:aspectjweaver:$aspectjVersion") testCompile "io.projectreactor:reactor-test:$reactorVersion" diff --git a/spring-integration-core/src/main/java/org/springframework/integration/channel/AbstractMessageChannel.java b/spring-integration-core/src/main/java/org/springframework/integration/channel/AbstractMessageChannel.java index bc05f994e6..5ff934bf75 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/channel/AbstractMessageChannel.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/channel/AbstractMessageChannel.java @@ -36,8 +36,11 @@ import org.springframework.integration.support.management.ConfigurableMetricsAwa import org.springframework.integration.support.management.DefaultMessageChannelMetrics; import org.springframework.integration.support.management.IntegrationManagedResource; import org.springframework.integration.support.management.MessageChannelMetrics; +import org.springframework.integration.support.management.MetricsCaptor; import org.springframework.integration.support.management.MetricsContext; +import org.springframework.integration.support.management.SampleFacade; import org.springframework.integration.support.management.Statistics; +import org.springframework.integration.support.management.TimerFacade; import org.springframework.integration.support.management.TrackableComponent; import org.springframework.integration.support.utils.IntegrationUtils; import org.springframework.messaging.Message; @@ -48,10 +51,6 @@ import org.springframework.messaging.support.ChannelInterceptor; import org.springframework.util.Assert; import org.springframework.util.StringUtils; -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.Timer; -import io.micrometer.core.instrument.Timer.Sample; - /** * Base class for {@link MessageChannel} implementations providing common * properties such as the channel name. Also provides the common functionality @@ -90,11 +89,11 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport private volatile AbstractMessageChannelMetrics channelMetrics = new DefaultMessageChannelMetrics(); - private MeterRegistry meterRegistry; + private MetricsCaptor metricsCaptor; - private Timer successTimer; + private TimerFacade successTimer; - private Timer failureTimer; + private TimerFacade failureTimer; public AbstractMessageChannel() { this.interceptors = new ChannelInterceptorList(logger); @@ -111,12 +110,12 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport } @Override - public void registerMeterRegistry(MeterRegistry registry) { - this.meterRegistry = registry; + public void registerMetricsCaptor(MetricsCaptor metricsCaptor) { + this.metricsCaptor = metricsCaptor; } - protected MeterRegistry getMeterRegistry() { - return this.meterRegistry; + protected MetricsCaptor getMetricsCaptor() { + return this.metricsCaptor; } @Override @@ -422,7 +421,7 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport boolean countsEnabled = this.countsEnabled; ChannelInterceptorList interceptors = this.interceptors; AbstractMessageChannelMetrics channelMetrics = this.channelMetrics; - Sample sample = null; + SampleFacade sample = null; try { if (this.datatypes.length > 0) { message = this.convertPayloadIfNecessary(message); @@ -440,8 +439,8 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport } if (countsEnabled) { metrics = channelMetrics.beforeSend(); - if (this.meterRegistry != null) { - sample = Timer.start(this.meterRegistry); + if (this.metricsCaptor != null) { + sample = this.metricsCaptor.start(); } sent = doSend(message, timeout); if (sample != null) { @@ -478,7 +477,7 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport } } - private Timer sendTimer(boolean sent) { + private TimerFacade sendTimer(boolean sent) { if (sent) { if (this.successTimer == null) { this.successTimer = buildSendTimer(true, "none"); @@ -493,14 +492,14 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport } } - private Timer buildSendTimer(boolean success, String exception) { - return Timer.builder(SEND_TIMER_NAME) + private TimerFacade buildSendTimer(boolean success, String exception) { + return this.metricsCaptor.timerBuilder(SEND_TIMER_NAME) .tag("type", "channel") .tag("name", getComponentName() == null ? "unknown" : getComponentName()) .tag("result", success ? "success" : "failure") .tag("exception", exception) .description("Send processing time") - .register(this.meterRegistry); + .build(); } private Message convertPayloadIfNecessary(Message message) { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/channel/AbstractPollableChannel.java b/spring-integration-core/src/main/java/org/springframework/integration/channel/AbstractPollableChannel.java index 54164736bb..6ffaab08e2 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/channel/AbstractPollableChannel.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/channel/AbstractPollableChannel.java @@ -20,6 +20,7 @@ import java.util.ArrayDeque; import java.util.Deque; import java.util.List; +import org.springframework.integration.support.management.CounterFacade; import org.springframework.integration.support.management.PollableChannelManagement; import org.springframework.messaging.Message; import org.springframework.messaging.PollableChannel; @@ -27,8 +28,6 @@ import org.springframework.messaging.support.ChannelInterceptor; import org.springframework.messaging.support.ExecutorChannelInterceptor; import org.springframework.util.CollectionUtils; -import io.micrometer.core.instrument.Counter; - /** * Base class for all pollable channels. * @@ -42,7 +41,7 @@ public abstract class AbstractPollableChannel extends AbstractMessageChannel private volatile int executorInterceptorsSize; - private Counter receiveCounter; + private CounterFacade receiveCounter; @Override public int getReceiveCount() { @@ -108,7 +107,7 @@ public abstract class AbstractPollableChannel extends AbstractMessageChannel } Message message = this.doReceive(timeout); if (countsEnabled && message != null) { - if (getMeterRegistry() != null) { + if (getMetricsCaptor() != null) { incrementReceiveCounter(); } getMetrics().afterReceive(); @@ -128,14 +127,14 @@ public abstract class AbstractPollableChannel extends AbstractMessageChannel } catch (RuntimeException e) { if (countsEnabled && !counted) { - if (getMeterRegistry() != null) { - Counter.builder(RECEIVE_COUNTER_NAME) + if (getMetricsCaptor() != null) { + getMetricsCaptor().counterBuilder(RECEIVE_COUNTER_NAME) .tag("name", getComponentName() == null ? "unknown" : getComponentName()) .tag("type", "channel") .tag("result", "failure") .tag("exception", e.getClass().getSimpleName()) .description("Messages received") - .register(getMeterRegistry()) + .build() .increment(); } getMetrics().afterError(); @@ -149,13 +148,13 @@ public abstract class AbstractPollableChannel extends AbstractMessageChannel private void incrementReceiveCounter() { if (this.receiveCounter == null) { - this.receiveCounter = Counter.builder(RECEIVE_COUNTER_NAME) + this.receiveCounter = getMetricsCaptor().counterBuilder(RECEIVE_COUNTER_NAME) .tag("name", getComponentName()) .tag("type", "channel") .tag("result", "success") .tag("exception", "none") .description("Messages received") - .register(getMeterRegistry()); + .build(); } this.receiveCounter.increment(); } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/channel/NullChannel.java b/spring-integration-core/src/main/java/org/springframework/integration/channel/NullChannel.java index 0e08416c21..2a2911ccf0 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/channel/NullChannel.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/channel/NullChannel.java @@ -28,15 +28,14 @@ import org.springframework.integration.support.management.ConfigurableMetricsAwa import org.springframework.integration.support.management.DefaultMessageChannelMetrics; import org.springframework.integration.support.management.IntegrationManagedResource; import org.springframework.integration.support.management.MessageChannelMetrics; +import org.springframework.integration.support.management.MetricsCaptor; import org.springframework.integration.support.management.Statistics; +import org.springframework.integration.support.management.TimerFacade; import org.springframework.messaging.Message; import org.springframework.messaging.PollableChannel; import org.springframework.util.Assert; import org.springframework.util.StringUtils; -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.Timer; - /** * A channel implementation that essentially behaves like "/dev/null". * All receive() calls will return null, and all send() calls @@ -65,9 +64,9 @@ public class NullChannel implements PollableChannel, MessageChannelMetrics, private String beanName; - private MeterRegistry meterRegistry; + private MetricsCaptor metricsCaptor; - private Timer successTimer; + private TimerFacade successTimer; @Override public void setBeanName(String beanName) { @@ -97,8 +96,8 @@ public class NullChannel implements PollableChannel, MessageChannelMetrics, } @Override - public void registerMeterRegistry(MeterRegistry registry) { - this.meterRegistry = registry; + public void registerMetricsCaptor(MetricsCaptor registry) { + this.metricsCaptor = registry; } @Override @@ -235,7 +234,7 @@ public class NullChannel implements PollableChannel, MessageChannelMetrics, this.logger.debug("message sent to null channel: " + message); } if (this.countsEnabled) { - if (this.meterRegistry != null) { + if (this.metricsCaptor != null) { sendTimer().record(0, TimeUnit.MILLISECONDS); } this.channelMetrics.afterSend(this.channelMetrics.beforeSend(), true); @@ -243,15 +242,15 @@ public class NullChannel implements PollableChannel, MessageChannelMetrics, return true; } - private Timer sendTimer() { + private TimerFacade sendTimer() { if (this.successTimer == null) { - this.successTimer = Timer.builder(SEND_TIMER_NAME) + this.successTimer = this.metricsCaptor.timerBuilder(SEND_TIMER_NAME) .tag("type", "channel") .tag("name", getComponentName() == null ? "unknown" : getComponentName()) .tag("result", "success") .tag("exception", "none") .description("Subflow process time") - .register(this.meterRegistry); + .build(); } return this.successTimer; } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/endpoint/AbstractMessageSource.java b/spring-integration-core/src/main/java/org/springframework/integration/endpoint/AbstractMessageSource.java index 5f7304d67c..7ca648f1dc 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/endpoint/AbstractMessageSource.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/endpoint/AbstractMessageSource.java @@ -26,16 +26,15 @@ import org.springframework.expression.Expression; import org.springframework.integration.core.MessageSource; import org.springframework.integration.support.AbstractIntegrationMessageBuilder; import org.springframework.integration.support.context.NamedComponent; +import org.springframework.integration.support.management.CounterFacade; import org.springframework.integration.support.management.IntegrationManagedResource; import org.springframework.integration.support.management.MessageSourceMetrics; +import org.springframework.integration.support.management.MetricsCaptor; import org.springframework.integration.util.AbstractExpressionEvaluator; import org.springframework.messaging.Message; import org.springframework.messaging.MessagingException; import org.springframework.util.CollectionUtils; -import io.micrometer.core.instrument.Counter; -import io.micrometer.core.instrument.MeterRegistry; - /** * @author Mark Fisher * @author Oleg Zhurakousky @@ -64,9 +63,9 @@ public abstract class AbstractMessageSource extends AbstractExpressionEvaluat private volatile boolean loggingEnabled = true; - private MeterRegistry meterRegistry; + private MetricsCaptor metricsCaptor; - private Counter receiveCounter; + private CounterFacade receiveCounter; public void setHeaderExpressions(Map headerExpressions) { this.headerExpressions = (headerExpressions != null) @@ -74,8 +73,8 @@ public abstract class AbstractMessageSource extends AbstractExpressionEvaluat } @Override - public void registerMeterRegistry(MeterRegistry registry) { - this.meterRegistry = registry; + public void registerMetricsCaptor(MetricsCaptor metricsCaptor) { + this.metricsCaptor = metricsCaptor; } @Override @@ -194,7 +193,7 @@ public abstract class AbstractMessageSource extends AbstractExpressionEvaluat .build(); } if (this.countsEnabled && message != null) { - if (this.meterRegistry != null) { + if (this.metricsCaptor != null) { incrementReceiveCounter(); } this.messageCount.incrementAndGet(); @@ -204,13 +203,13 @@ public abstract class AbstractMessageSource extends AbstractExpressionEvaluat private void incrementReceiveCounter() { if (this.receiveCounter == null) { - this.receiveCounter = Counter.builder(RECEIVE_COUNTER_NAME) + this.receiveCounter = this.metricsCaptor.counterBuilder(RECEIVE_COUNTER_NAME) .tag("name", getComponentName() == null ? "unknown" : getComponentName()) .tag("type", "source") .tag("result", "success") .tag("exception", "none") .description("Messages received") - .register(this.meterRegistry); + .build(); } this.receiveCounter.increment(); } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/AbstractMessageHandler.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/AbstractMessageHandler.java index 3f15f241af..ee7bcfc0d4 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/handler/AbstractMessageHandler.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/AbstractMessageHandler.java @@ -27,8 +27,11 @@ import org.springframework.integration.support.management.ConfigurableMetricsAwa import org.springframework.integration.support.management.DefaultMessageHandlerMetrics; import org.springframework.integration.support.management.IntegrationManagedResource; import org.springframework.integration.support.management.MessageHandlerMetrics; +import org.springframework.integration.support.management.MetricsCaptor; import org.springframework.integration.support.management.MetricsContext; +import org.springframework.integration.support.management.SampleFacade; import org.springframework.integration.support.management.Statistics; +import org.springframework.integration.support.management.TimerFacade; import org.springframework.integration.support.management.TrackableComponent; import org.springframework.integration.support.utils.IntegrationUtils; import org.springframework.messaging.Message; @@ -36,9 +39,6 @@ import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessagingException; import org.springframework.util.Assert; -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.Timer; -import io.micrometer.core.instrument.Timer.Sample; import reactor.core.CoreSubscriber; /** @@ -75,9 +75,9 @@ public abstract class AbstractMessageHandler extends IntegrationObjectSupport private volatile boolean loggingEnabled = true; - private MeterRegistry meterRegistry; + private MetricsCaptor metricsCaptor; - private Timer successTimer; + private TimerFacade successTimer; @Override public boolean isLoggingEnabled() { @@ -90,9 +90,10 @@ public abstract class AbstractMessageHandler extends IntegrationObjectSupport this.managementOverrides.loggingConfigured = true; } + @SuppressWarnings("unchecked") @Override - public void registerMeterRegistry(MeterRegistry meterRegistry) { - this.meterRegistry = meterRegistry; + public void registerMetricsCaptor(MetricsCaptor metricsCaptor) { + this.metricsCaptor = metricsCaptor; } @Override @@ -144,9 +145,9 @@ public abstract class AbstractMessageHandler extends IntegrationObjectSupport MetricsContext start = null; boolean countsEnabled = this.countsEnabled; AbstractMessageHandlerMetrics handlerMetrics = this.handlerMetrics; - Sample sample = null; - if (countsEnabled && this.meterRegistry != null) { - sample = Timer.start(this.meterRegistry); + SampleFacade sample = null; + if (countsEnabled && this.metricsCaptor != null) { + sample = this.metricsCaptor.start(); } try { if (this.shouldTrack) { @@ -176,21 +177,21 @@ public abstract class AbstractMessageHandler extends IntegrationObjectSupport } } - private Timer sendTimer() { + private TimerFacade sendTimer() { if (this.successTimer == null) { this.successTimer = buildSendTimer(true, "none"); } return this.successTimer; } - private Timer buildSendTimer(boolean success, String exception) { - return Timer.builder(SEND_TIMER_NAME) + private TimerFacade buildSendTimer(boolean success, String exception) { + return this.metricsCaptor.timerBuilder(SEND_TIMER_NAME) .tag("type", "handler") .tag("name", getComponentName() == null ? "unknown" : getComponentName()) .tag("result", success ? "success" : "failure") .tag("exception", exception) .description("Send processing time") - .register(this.meterRegistry); + .build(); } @Override diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/AbstractMessageChannelMetrics.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/AbstractMessageChannelMetrics.java index 3d0ecf5a6f..ab5540ac86 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/management/AbstractMessageChannelMetrics.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/AbstractMessageChannelMetrics.java @@ -19,12 +19,6 @@ package org.springframework.integration.support.management; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; - -import io.micrometer.core.instrument.Counter; -import io.micrometer.core.instrument.Timer; - /** * Abstract base class for channel metrics implementations. * @@ -39,14 +33,6 @@ public abstract class AbstractMessageChannelMetrics implements ConfigurableMetri protected final String name; - private final Timer timer; - - private final Counter errorCounter; - - private final Counter receiveCounter; - - private final Counter receiveErrorCounter; - private volatile boolean fullStatsEnabled; /** @@ -54,32 +40,7 @@ public abstract class AbstractMessageChannelMetrics implements ConfigurableMetri * @param name the name. */ public AbstractMessageChannelMetrics(String name) { - this(name, null, null, null, null); - } - - /** - * Construct an instance with the provided name, timer, error counter, receive counter - * and receive error counter. - * A non-null timer requires a non-null error counter. When a timer is provided, - * Micrometer metrics are used and the legacy metrics are not maintained. - * @param name the name. - * @param timer the timer. - * @param errorCounter the error counter. - * @param receiveCounter the receive counter. - * @param receiveErrorCounter the receive error counter. - * @since 5.0.2 - */ - public AbstractMessageChannelMetrics(String name, Timer timer, Counter errorCounter, Counter receiveCounter, - Counter receiveErrorCounter) { - - if (timer != null) { - Assert.notNull(errorCounter, "'errorCounter' cannot be null if a timer is provided"); - } this.name = name; - this.timer = timer; - this.errorCounter = errorCounter; - this.receiveCounter = receiveCounter; - this.receiveErrorCounter = receiveErrorCounter; } /** @@ -116,46 +77,6 @@ public abstract class AbstractMessageChannelMetrics implements ConfigurableMetri */ public abstract void reset(); - /** - * Return the timer if Micrometer metrics are being used. - * @return the timer, or null to indicate Micrometer is not being used. - * @since 5.0.2 - */ - @Nullable - public Timer getTimer() { - return this.timer; - } - - /** - * Return the error counter if Micrometer metrics are being used. - * @return the counter or null if Micrometer is not being used. - * @since 5.0.2 - */ - @Nullable - public Counter getErrorCounter() { - return this.errorCounter; - } - - /** - * Return the receive counter if Micrometer metrics are being used. - * @return the counter or null if Micrometer is not being used. - * @since 5.0.2 - */ - @Nullable - public Counter getReceiveCounter() { - return this.receiveCounter; - } - - /** - * Return the receive error counter if Micrometer metrics are being used. - * @return the counter or null if Micrometer is not being used. - * @since 5.0.2 - */ - @Nullable - public Counter getReceiveErrorCounter() { - return this.receiveErrorCounter; - } - public abstract int getSendCount(); public abstract long getSendCountLong(); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/AbstractMessageHandlerMetrics.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/AbstractMessageHandlerMetrics.java index d57f97640a..7917f2737a 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/management/AbstractMessageHandlerMetrics.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/AbstractMessageHandlerMetrics.java @@ -19,9 +19,6 @@ package org.springframework.integration.support.management; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import io.micrometer.core.instrument.Counter; -import io.micrometer.core.instrument.Timer; - /** * Abstract base class for handler metrics implementations. * @@ -35,20 +32,10 @@ public abstract class AbstractMessageHandlerMetrics implements ConfigurableMetri protected final String name; - private final Timer timer; - - private final Counter errorCounter; - private volatile boolean fullStatsEnabled; public AbstractMessageHandlerMetrics(String name) { - this(name, null, null); - } - - public AbstractMessageHandlerMetrics(String name, Timer timer, Counter errorCounter) { this.name = name; - this.timer = timer; - this.errorCounter = errorCounter; } /** @@ -79,14 +66,6 @@ public abstract class AbstractMessageHandlerMetrics implements ConfigurableMetri public abstract void reset(); - public Timer getTimer() { - return this.timer; - } - - public Counter getErrorCounter() { - return this.errorCounter; - } - public abstract long getHandleCountLong(); public abstract int getHandleCount(); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/CounterFacade.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/CounterFacade.java new file mode 100644 index 0000000000..ddc2325fe0 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/CounterFacade.java @@ -0,0 +1,28 @@ +/* + * 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; + +/** + * @author Gary Russell + * @since 5.0.4 + * + */ +public interface CounterFacade { + + void increment(); + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/DefaultMessageChannelMetrics.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/DefaultMessageChannelMetrics.java index 79b9fb8d2a..254e262b01 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/management/DefaultMessageChannelMetrics.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/DefaultMessageChannelMetrics.java @@ -18,9 +18,6 @@ package org.springframework.integration.support.management; import java.util.concurrent.atomic.AtomicLong; -import io.micrometer.core.instrument.Counter; -import io.micrometer.core.instrument.Timer; - /** * Default implementation; use the full constructor to customize the moving averages. * @@ -65,30 +62,13 @@ public class DefaultMessageChannelMetrics extends AbstractMessageChannelMetrics * @param name the name. */ public DefaultMessageChannelMetrics(String name) { - this(name, null, null, null, (Counter) null); - } - - /** - * Construct an instance with default metrics with {@code window=10, period=1 second, - * lapsePeriod=1 minute}. - * @param name the name. - * @param timer a timer. - * @param errorCounter a counter. - * @param receiveCounter a counter for receives. - * @param receiveErrorCounter a counter for receive errors. - * @since 5.0.2 - */ - public DefaultMessageChannelMetrics(String name, Timer timer, Counter errorCounter, Counter receiveCounter, - Counter receiveErrorCounter) { - this(name, new ExponentialMovingAverage(DEFAULT_MOVING_AVERAGE_WINDOW, 1000000.), new ExponentialMovingAverageRate( ONE_SECOND_SECONDS, ONE_MINUTE_SECONDS, DEFAULT_MOVING_AVERAGE_WINDOW, true), new ExponentialMovingAverageRatio( ONE_MINUTE_SECONDS, DEFAULT_MOVING_AVERAGE_WINDOW, true), new ExponentialMovingAverageRate( - ONE_SECOND_SECONDS, ONE_MINUTE_SECONDS, DEFAULT_MOVING_AVERAGE_WINDOW, true), - timer, errorCounter, receiveCounter, receiveErrorCounter); + ONE_SECOND_SECONDS, ONE_MINUTE_SECONDS, DEFAULT_MOVING_AVERAGE_WINDOW, true)); } /** @@ -106,30 +86,7 @@ public class DefaultMessageChannelMetrics extends AbstractMessageChannelMetrics ExponentialMovingAverageRate sendErrorRate, ExponentialMovingAverageRatio sendSuccessRatio, ExponentialMovingAverageRate sendRate) { - this(name, sendDuration, sendErrorRate, sendSuccessRatio, sendRate, null, null, null, null); - } - - /** - * Construct an instance with the supplied metrics. For proper representation of metrics, the - * supplied sendDuration must have a {@code factor=1000000.} and the the other arguments - * must be created with the {@code millis} constructor argument set to true. - * @param name the name. - * @param sendDuration an {@link ExponentialMovingAverage} for calculating the send duration. - * @param sendErrorRate an {@link ExponentialMovingAverageRate} for calculating the send error rate. - * @param sendSuccessRatio an {@link ExponentialMovingAverageRatio} for calculating the success ratio. - * @param sendRate an {@link ExponentialMovingAverageRate} for calculating the send rate. - * @param timer a timer. - * @param errorCounter a counter for sends. - * @param receiveCounter a counter for receives. - * @param receiveErrorCounter a counter for receive errors. - * @since 5.0.2 - */ - public DefaultMessageChannelMetrics(String name, ExponentialMovingAverage sendDuration, - ExponentialMovingAverageRate sendErrorRate, ExponentialMovingAverageRatio sendSuccessRatio, - ExponentialMovingAverageRate sendRate, Timer timer, Counter errorCounter, Counter receiveCounter, - Counter receiveErrorCounter) { - - super(name, timer, errorCounter, receiveCounter, receiveErrorCounter); + super(name); this.sendDuration = sendDuration; this.sendErrorRate = sendErrorRate; this.sendSuccessRatio = sendSuccessRatio; @@ -261,22 +218,12 @@ public class DefaultMessageChannelMetrics extends AbstractMessageChannelMetrics @Override public void afterReceive() { - if (getReceiveCounter() != null) { - getReceiveCounter().increment(); - } - else { - this.receiveCount.incrementAndGet(); - } + this.receiveCount.incrementAndGet(); } @Override public void afterError() { - if (getReceiveErrorCounter() != null) { - getReceiveErrorCounter().increment(); - } - else { - this.receiveErrorCount.incrementAndGet(); - } + this.receiveErrorCount.incrementAndGet(); } @Override diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/DefaultMessageHandlerMetrics.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/DefaultMessageHandlerMetrics.java index 787b406174..2680ae0a4c 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/management/DefaultMessageHandlerMetrics.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/DefaultMessageHandlerMetrics.java @@ -18,9 +18,6 @@ package org.springframework.integration.support.management; import java.util.concurrent.atomic.AtomicLong; -import io.micrometer.core.instrument.Counter; -import io.micrometer.core.instrument.Timer; - /** * Default implementation; use the full constructor to customize the moving averages. * @@ -50,19 +47,7 @@ public class DefaultMessageHandlerMetrics extends AbstractMessageHandlerMetrics * @param name the name. */ public DefaultMessageHandlerMetrics(String name) { - this(name, null, null); - } - - /** - * Construct an instance with the default moving average window (10). - * @param name the name. - * @param timer a timer. - * @param errorCounter a counter. - * @since 5.0.2 - */ - public DefaultMessageHandlerMetrics(String name, Timer timer, Counter errorCounter) { - this(name, new ExponentialMovingAverage(DEFAULT_MOVING_AVERAGE_WINDOW, 1000000.), timer, - errorCounter); + this(name, new ExponentialMovingAverage(DEFAULT_MOVING_AVERAGE_WINDOW, 1000000.)); } /** @@ -74,22 +59,7 @@ public class DefaultMessageHandlerMetrics extends AbstractMessageHandlerMetrics * @since 4.2 */ public DefaultMessageHandlerMetrics(String name, ExponentialMovingAverage duration) { - this(name, duration, null, null); - } - - /** - * Construct an instance with the supplied {@link ExponentialMovingAverage} calculating - * the duration of processing by the message handler (and any downstream synchronous - * endpoints). - * @param name the name. - * @param duration an {@link ExponentialMovingAverage} for calculating the duration. - * @param timer a timer. - * @param errorCounter a counter. - * @since 5.0.2 - */ - public DefaultMessageHandlerMetrics(String name, ExponentialMovingAverage duration, Timer timer, - Counter errorCounter) { - super(name, timer, errorCounter); + super(name); this.duration = duration; } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/GaugeFacade.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/GaugeFacade.java new file mode 100644 index 0000000000..0d491a7720 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/GaugeFacade.java @@ -0,0 +1,26 @@ +/* + * 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; + +/** + * @author Gary Russell + * @since 5.0.4 + * + */ +public interface GaugeFacade { + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationManagement.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationManagement.java index dcc3c41264..22810fe5ce 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationManagement.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationManagement.java @@ -19,8 +19,6 @@ package org.springframework.integration.support.management; import org.springframework.jmx.export.annotation.ManagedAttribute; import org.springframework.jmx.export.annotation.ManagedOperation; -import io.micrometer.core.instrument.MeterRegistry; - /** * Base interface for Integration managed components. * @@ -59,11 +57,11 @@ public interface IntegrationManagement { ManagementOverrides getOverrides(); /** - * Inject a micrometer {@link MeterRegistry} - * @param registry the registry. - * @since 5.0.3 + * Inject a {@link MetricsCaptor} + * @param captor the captor. + * @since 5.0.4 */ - default void registerMeterRegistry(MeterRegistry registry) { + default void registerMetricsCaptor(MetricsCaptor captor) { // no op } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationManagementConfigurer.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationManagementConfigurer.java index edaf3f068e..8a6f9d8315 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationManagementConfigurer.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationManagementConfigurer.java @@ -26,22 +26,20 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanNameAware; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.integration.core.MessageSource; import org.springframework.integration.support.management.IntegrationManagement.ManagementOverrides; +import org.springframework.integration.support.management.micrometer.MicrometerMetricsCaptor; import org.springframework.integration.util.PatternMatchUtils; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; -import io.micrometer.core.instrument.Gauge; -import io.micrometer.core.instrument.MeterRegistry; - /** * Configures beans that implement {@link IntegrationManagement}. @@ -61,11 +59,11 @@ public class IntegrationManagementConfigurer implements SmartInitializingSinglet public static final String MANAGEMENT_CONFIGURER_NAME = "integrationManagementConfigurer"; - private final Map channelsByName = new HashMap(); + private final Map channelsByName = new HashMap<>(); - private final Map handlersByName = new HashMap(); + private final Map handlersByName = new HashMap<>(); - private final Map sourcesByName = new HashMap(); + private final Map sourcesByName = new HashMap<>(); private final Map sourceConfigurers = new HashMap<>(); @@ -89,7 +87,7 @@ public class IntegrationManagementConfigurer implements SmartInitializingSinglet private volatile boolean singletonsInstantiated; - private MeterRegistry meterRegistry; + private MetricsCaptor metricsCaptor; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { @@ -214,15 +212,13 @@ public class IntegrationManagementConfigurer implements SmartInitializingSinglet Assert.state(this.applicationContext != null, "'applicationContext' must not be null"); Assert.state(MANAGEMENT_CONFIGURER_NAME.equals(this.beanName), getClass().getSimpleName() + " bean name must be " + MANAGEMENT_CONFIGURER_NAME); - try { - this.meterRegistry = this.applicationContext.getBean(MeterRegistry.class); + if (ClassUtils.isPresent("io.micrometer.core.instrument.MeterRegistry", + IntegrationManagementConfigurer.class.getClassLoader())) { + this.metricsCaptor = MicrometerMetricsCaptor.loadCaptor(this.applicationContext); } - catch (NoSuchBeanDefinitionException e) { - // no op - } - if (this.meterRegistry != null) { - injectRegistry(this.meterRegistry); - registerComponentGauges(this.meterRegistry); + if (this.metricsCaptor != null) { + injectCaptor(); + registerComponentGauges(); } if (this.metricsFactory == null && StringUtils.hasText(this.metricsFactoryBeanName)) { this.metricsFactory = this.applicationContext.getBean(this.metricsFactoryBeanName, MetricsFactory.class); @@ -249,17 +245,14 @@ public class IntegrationManagementConfigurer implements SmartInitializingSinglet this.singletonsInstantiated = true; } - /** - * @param registry - */ - private void injectRegistry(MeterRegistry registry) { + private void injectCaptor() { Map managed = this.applicationContext.getBeansOfType(IntegrationManagement.class); for (Entry entry : managed.entrySet()) { IntegrationManagement bean = entry.getValue(); if (!bean.getOverrides().loggingConfigured) { bean.setLoggingEnabled(this.defaultLoggingEnabled); } - bean.registerMeterRegistry(registry); + bean.registerMetricsCaptor(this.metricsCaptor); } } @@ -267,7 +260,7 @@ public class IntegrationManagementConfigurer implements SmartInitializingSinglet public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (this.singletonsInstantiated) { if (bean instanceof IntegrationManagement) { - ((IntegrationManagement) bean).registerMeterRegistry(this.meterRegistry); + ((IntegrationManagement) bean).registerMetricsCaptor(this.metricsCaptor); } return doConfigureMetrics(bean, beanName); } @@ -370,21 +363,21 @@ public class IntegrationManagementConfigurer implements SmartInitializingSinglet this.sourcesByName.put(bean.getManagedName() != null ? bean.getManagedName() : name, bean); } - private void registerComponentGauges(MeterRegistry meterRegistry) { - Gauge.builder("spring.integration.channels", this, + private void registerComponentGauges() { + this.metricsCaptor.gaugeBuilder("spring.integration.channels", this, (c) -> this.applicationContext.getBeansOfType(MessageChannel.class).size()) .description("The number of message channels") - .register(meterRegistry); + .build(); - Gauge.builder("spring.integration.handlers", this, + this.metricsCaptor.gaugeBuilder("spring.integration.handlers", this, (c) -> this.applicationContext.getBeansOfType(MessageHandler.class).size()) .description("The number of message handlers") - .register(meterRegistry); + .build(); - Gauge.builder("spring.integration.sources", this, + this.metricsCaptor.gaugeBuilder("spring.integration.sources", this, (c) -> this.applicationContext.getBeansOfType(MessageSource.class).size()) .description("The number of message sources") - .register(meterRegistry); + .build(); } public String[] getChannelNames() { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationStatsManagement.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationStatsManagement.java index 49471fc700..670ed333cd 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationStatsManagement.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationStatsManagement.java @@ -18,9 +18,6 @@ package org.springframework.integration.support.management; import org.springframework.jmx.export.annotation.ManagedAttribute; -import io.micrometer.core.instrument.Counter; -import io.micrometer.core.instrument.Timer; - /** * Base interface containing methods to control complete statistics gathering. @@ -37,22 +34,4 @@ public interface IntegrationStatsManagement extends IntegrationManagement { @ManagedAttribute boolean isStatsEnabled(); - /** - * Set a micrometer timer to time operations. - * @param timer the timer. - * @since 5.0.2 - */ - default void setTimer(Timer timer) { - // no op - } - - /** - * Set a micrometer counter to count errors. - * @param counter the counter. - * @since 5.0.2 - */ - default void setErrorCounter(Counter counter) { - // no op - } - } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/LifecycleMessageHandlerMetrics.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/LifecycleMessageHandlerMetrics.java index edb3b52d12..317d0117b9 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/management/LifecycleMessageHandlerMetrics.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/LifecycleMessageHandlerMetrics.java @@ -20,9 +20,6 @@ import org.springframework.context.Lifecycle; import org.springframework.jmx.export.annotation.ManagedAttribute; import org.springframework.jmx.export.annotation.ManagedOperation; -import io.micrometer.core.instrument.Counter; -import io.micrometer.core.instrument.Timer; - /** * A {@link MessageHandlerMetrics} that exposes in addition the {@link Lifecycle} interface. The lifecycle methods can * be used to stop and start polling endpoints, for instance, in a live system. @@ -190,14 +187,4 @@ public class LifecycleMessageHandlerMetrics implements MessageHandlerMetrics, Li return this.delegate.getOverrides(); } - @Override - public void setTimer(Timer timer) { - this.delegate.setTimer(timer); - } - - @Override - public void setErrorCounter(Counter counter) { - this.delegate.setErrorCounter(counter); - } - } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/MessageSourceMetrics.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/MessageSourceMetrics.java index 66491b21e8..17e9fc4a29 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/management/MessageSourceMetrics.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/MessageSourceMetrics.java @@ -19,8 +19,6 @@ package org.springframework.integration.support.management; import org.springframework.jmx.export.annotation.ManagedMetric; import org.springframework.jmx.support.MetricType; -import io.micrometer.core.instrument.Counter; - /** * @author Dave Syer * @author Gary Russell @@ -59,7 +57,7 @@ public interface MessageSourceMetrics extends IntegrationManagement { * Will be remove in the next release. */ @Deprecated - default void setCounter(Counter counter) { + default void setCounter(CounterFacade counter) { // no op } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/MetricsCaptor.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/MetricsCaptor.java new file mode 100644 index 0000000000..f9c41e3360 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/MetricsCaptor.java @@ -0,0 +1,145 @@ +/* + * 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; + +import java.util.function.ToDoubleFunction; + +import org.springframework.lang.Nullable; + +/** + * A metrics facade that delegates to a concrete implementation. + * + * @author Gary Russell + * @since 5.0.4 + * + */ +public interface MetricsCaptor { + + /** + * Create a timer builder for a timer with the provided name. + * @param name the name. + * @return the builder. + */ + TimerBuilder timerBuilder(String name); + + /** + * Create a counter builder for a counter with the provided name. + * @param name the name. + * @return the builder. + */ + CounterBuilder counterBuilder(String name); + + /** + * Create a gauge builder for a gauge with the provided parameters. + * @param name the name. + * @param obj the object with which to invoke the function. + * @param f the function. + * @return the builder. + */ + GaugeBuilder gaugeBuilder(String name, @Nullable Object obj, ToDoubleFunction f); + + /** + * Start a sample collection. + * @return the sample. + */ + SampleFacade start(); + + /** + * A builder for a timer. + */ + interface TimerBuilder { + + /** + * Add a tag. + * @param key the key. + * @param value the value. + * @return the builder. + */ + TimerBuilder tag(String key, String value); + + /** + * Add the description. + * @param desc the description. + * @return the builder. + */ + TimerBuilder description(String desc); + + /** + * Build the timer. + * @return the timer. + */ + TimerFacade build(); + + } + + /** + * A builder for a counter. + */ + interface CounterBuilder { + + /** + * Add a tag. + * @param key the key. + * @param value the value. + * @return the builder. + */ + CounterBuilder tag(String key, String value); + + /** + * Add the description. + * @param desc the description. + * @return the builder. + */ + CounterBuilder description(String desc); + + /** + * Build the counter. + * @return the counter. + */ + CounterFacade build(); + + } + + /** + * A builder for a gauge. + */ + interface GaugeBuilder { + + /** + * Add a tag. + * @param key the key. + * @param value the value. + * @return the builder. + */ + GaugeBuilder tag(String key, String value); + + /** + * Add the description. + * @param desc the description. + * @return the builder. + */ + GaugeBuilder description(String desc); + + /** + * Build the gauge. + * @return the gauge. + */ + GaugeFacade build(); + + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/SampleFacade.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/SampleFacade.java new file mode 100644 index 0000000000..1ddca3c2c5 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/SampleFacade.java @@ -0,0 +1,28 @@ +/* + * 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; + +/** + * @author Gary Russell + * @since 5.0.4 + * + */ +public interface SampleFacade { + + void stop(TimerFacade timer); + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/TimerFacade.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/TimerFacade.java new file mode 100644 index 0000000000..b274847298 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/TimerFacade.java @@ -0,0 +1,30 @@ +/* + * 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; + +import java.util.concurrent.TimeUnit; + +/** + * @author Gary Russell + * @since 5.0.4 + * + */ +public interface TimerFacade { + + void record(long time, TimeUnit unit); + +} 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 new file mode 100644 index 0000000000..93c09fac5a --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/micrometer/MicrometerMetricsCaptor.java @@ -0,0 +1,244 @@ +/* + * 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 java.util.concurrent.TimeUnit; +import java.util.function.ToDoubleFunction; + +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.integration.support.management.CounterFacade; +import org.springframework.integration.support.management.GaugeFacade; +import org.springframework.integration.support.management.MetricsCaptor; +import org.springframework.integration.support.management.SampleFacade; +import org.springframework.integration.support.management.TimerFacade; +import org.springframework.util.Assert; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; + +/** + * The Micrometer implementation of {@link MetricsCaptor}. + * + * @author Gary Russell + * + * @since 5.0.4 + * + */ +public class MicrometerMetricsCaptor implements MetricsCaptor { + + public static final String MICROMETER_CAPTOR_NAME = "integrationMicrometerMetricsCaptor"; + + private final MeterRegistry meterRegistry; + + public MicrometerMetricsCaptor(MeterRegistry meterRegistry) { + Assert.notNull(meterRegistry, "meterRegistry cannot be null"); + this.meterRegistry = meterRegistry; + } + + @Override + public TimerBuilder timerBuilder(String name) { + return new MicroTimerBuilder(this.meterRegistry, name); + } + + @Override + public CounterBuilder counterBuilder(String name) { + return new MicroCounterBuilder(this.meterRegistry, name); + } + + @Override + public GaugeBuilder gaugeBuilder(String name, Object obj, ToDoubleFunction f) { + return new MicroGaugeBuilder(this.meterRegistry, name, obj, f); + } + + @Override + public SampleFacade start() { + return new MicroSample(Timer.start(this.meterRegistry)); + } + + /** + * Add a MicrometerMetricsCaptor to the context if there's a MeterRegistry. + * @param applicationContext the application context. + * @return the instance. + */ + public static MetricsCaptor loadCaptor(ApplicationContext applicationContext) { + try { + MeterRegistry registry = applicationContext.getBean(MeterRegistry.class); + if (applicationContext instanceof GenericApplicationContext + && !applicationContext.containsBean(MICROMETER_CAPTOR_NAME)) { + ((GenericApplicationContext) applicationContext).registerBean(MICROMETER_CAPTOR_NAME, + MicrometerMetricsCaptor.class, + () -> new MicrometerMetricsCaptor(registry)); + return applicationContext.getBean(MicrometerMetricsCaptor.class); + } + } + catch (NoSuchBeanDefinitionException e) { + return null; + } + return null; + } + + private class MicroSample implements SampleFacade { + + private final Timer.Sample sample; + + MicroSample(Timer.Sample sample) { + this.sample = sample; + } + + @Override + public void stop(TimerFacade timer) { + this.sample.stop(((MicroTimer) timer).timer); + } + + } + + private static class MicroTimerBuilder implements TimerBuilder { + + private final MeterRegistry meterRegistry; + + private final Timer.Builder builder; + + MicroTimerBuilder(MeterRegistry meterRegistry, String name) { + this.meterRegistry = meterRegistry; + this.builder = Timer.builder(name); + } + + @Override + public MicroTimerBuilder tag(String key, String value) { + this.builder.tag(key, value); + return this; + } + + @Override + public MicroTimerBuilder description(String desc) { + this.builder.description(desc); + return this; + } + + @Override + public MicroTimer build() { + return new MicroTimer(this.builder.register(this.meterRegistry)); + } + + } + + private static class MicroTimer implements TimerFacade { + + private final Timer timer; + + MicroTimer(Timer timer) { + this.timer = timer; + } + + @Override + public void record(long time, TimeUnit unit) { + this.timer.record(time, unit); + } + + } + + private static class MicroCounterBuilder implements CounterBuilder { + + private final MeterRegistry meterRegistry; + + private final Counter.Builder builder; + + MicroCounterBuilder(MeterRegistry meterRegistry, String name) { + this.meterRegistry = meterRegistry; + this.builder = Counter.builder(name); + } + + @Override + public CounterBuilder tag(String key, String value) { + this.builder.tag(key, value); + return this; + } + + @Override + public CounterBuilder description(String desc) { + this.builder.description(desc); + return this; + } + + @Override + public CounterFacade build() { + return new MicroCounter(this.builder.register(this.meterRegistry)); + } + + } + + private static class MicroCounter implements CounterFacade { + + private final Counter counter; + + MicroCounter(Counter counter) { + this.counter = counter; + } + + @Override + public void increment() { + this.counter.increment(); + } + + } + + private static class MicroGaugeBuilder implements GaugeBuilder { + + private final MeterRegistry meterRegistry; + + private final Gauge.Builder builder; + + MicroGaugeBuilder(MeterRegistry meterRegistry, String name, Object obj, ToDoubleFunction f) { + this.meterRegistry = meterRegistry; + this.builder = Gauge.builder(name, obj, f); + } + + @Override + public GaugeBuilder tag(String key, String value) { + this.builder.tag(key, value); + return this; + } + + @Override + public GaugeBuilder description(String desc) { + this.builder.description(desc); + return this; + } + + @Override + public GaugeFacade build() { + return new MicroGauge(this.builder.register(this.meterRegistry)); + } + + } + + private static class MicroGauge implements GaugeFacade { + + @SuppressWarnings("unused") + private final Gauge gauge; + + MicroGauge(Gauge gauge) { + this.gauge = gauge; + } + + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/management/micrometer/MicrometerMetricsFactory.java b/spring-integration-core/src/main/java/org/springframework/integration/support/management/micrometer/MicrometerMetricsFactory.java index 42c7897988..393973f67e 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/management/micrometer/MicrometerMetricsFactory.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/management/micrometer/MicrometerMetricsFactory.java @@ -22,21 +22,16 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; -import org.springframework.integration.core.MessageSource; import org.springframework.integration.support.management.AbstractMessageChannelMetrics; import org.springframework.integration.support.management.AbstractMessageHandlerMetrics; import org.springframework.integration.support.management.DefaultMessageChannelMetrics; import org.springframework.integration.support.management.DefaultMessageHandlerMetrics; import org.springframework.integration.support.management.MessageSourceMetrics; import org.springframework.integration.support.management.MessageSourceMetricsConfigurer; +import org.springframework.integration.support.management.MetricsCaptor; import org.springframework.integration.support.management.MetricsFactory; -import org.springframework.messaging.MessageChannel; -import org.springframework.messaging.MessageHandler; import org.springframework.util.Assert; -import io.micrometer.core.instrument.Gauge; -import io.micrometer.core.instrument.MeterRegistry; - /** * Micrometer implementation of a {@link MetricsFactory}. Configures the resulting * channel, and handler metrics to use Micrometer metrics instead of the legacy Spring @@ -56,52 +51,20 @@ import io.micrometer.core.instrument.MeterRegistry; public class MicrometerMetricsFactory implements MetricsFactory, MessageSourceMetricsConfigurer, ApplicationContextAware, SmartInitializingSingleton { - private static final Function NO_TAGS = n -> new String[0]; - - private final MeterRegistry meterRegistry; - - private ApplicationContext applicationContext; - - private Function timerNameProvider = n -> n + ".timer"; - - private Function counterNameProvider = n -> n + ".counter"; - - private Function errorCounterNameProvider = n -> n + ".errorCounter"; - - private Function receiveCounterNameProvider = n -> n + ".receive.counter"; - - private Function receiveErrorCounterNameProvider = n -> n + ".receive.errorCounter"; - - private Function timerTagProvider = NO_TAGS; - - private Function counterTagProvider = NO_TAGS; - - private Function errorCounterTagProvider = NO_TAGS; - - private Function receiveCounterTagProvider = NO_TAGS; - - private Function receiveErrorCounterTagProvider = NO_TAGS; - - private Function componentCountTagProvider = NO_TAGS; - /** - * Construct an instance with the provided {@link MeterRegistry}. - * @param meterRegistry the registry. + * Construct an instance with the provided {@link MetricsCaptor}. + * @param captor the registry. */ - public MicrometerMetricsFactory(MeterRegistry meterRegistry) { - Assert.notNull(meterRegistry, "'meterRegistry' cannot be null"); - this.meterRegistry = meterRegistry; + public MicrometerMetricsFactory(MetricsCaptor captor) { + Assert.notNull(captor, "'meterRegistry' cannot be null"); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; } @Override public void afterSingletonsInstantiated() { - Assert.notNull(this.applicationContext, "An application context is required"); - registerComponentGauges(); } /** @@ -111,7 +74,6 @@ public class MicrometerMetricsFactory implements MetricsFactory, MessageSourceMe */ public void setTimerNameProvider(Function timerNameProvider) { Assert.notNull(timerNameProvider, "'timerNameProvider' cannot be null"); - this.timerNameProvider = timerNameProvider; } /** @@ -121,7 +83,6 @@ public class MicrometerMetricsFactory implements MetricsFactory, MessageSourceMe */ public void setCounterNameProvider(Function counterNameProvider) { Assert.notNull(counterNameProvider, "'counterNameProvider' cannot be null"); - this.counterNameProvider = counterNameProvider; } /** @@ -131,7 +92,6 @@ public class MicrometerMetricsFactory implements MetricsFactory, MessageSourceMe */ public void setErrorCounterNameProvider(Function errorCounterNameProvider) { Assert.notNull(errorCounterNameProvider, "'errorCounterNameProvider' cannot be null"); - this.errorCounterNameProvider = errorCounterNameProvider; } /** @@ -141,7 +101,6 @@ public class MicrometerMetricsFactory implements MetricsFactory, MessageSourceMe */ public void setReceiveCounterNameProvider(Function counterNameProvider) { Assert.notNull(counterNameProvider, "'counterNameProvider' cannot be null"); - this.receiveCounterNameProvider = counterNameProvider; } /** @@ -151,7 +110,6 @@ public class MicrometerMetricsFactory implements MetricsFactory, MessageSourceMe */ public void setReceiveErrorCounterNameProvider(Function errorCounterNameProvider) { Assert.notNull(errorCounterNameProvider, "'errorCounterNameProvider' cannot be null"); - this.receiveErrorCounterNameProvider = errorCounterNameProvider; } /** @@ -161,7 +119,6 @@ public class MicrometerMetricsFactory implements MetricsFactory, MessageSourceMe */ public void setTimerTagProvider(Function timerTagProvider) { Assert.notNull(timerTagProvider, "'timerTagProvider' cannot be null"); - this.timerTagProvider = timerTagProvider; } /** @@ -171,7 +128,6 @@ public class MicrometerMetricsFactory implements MetricsFactory, MessageSourceMe */ public void setCounterTagProvider(Function counterTagProvider) { Assert.notNull(counterTagProvider, "'counterTagProvider' cannot be null"); - this.counterTagProvider = counterTagProvider; } /** @@ -181,7 +137,6 @@ public class MicrometerMetricsFactory implements MetricsFactory, MessageSourceMe */ public void setErrorCounterTagProvider(Function counterTagProvider) { Assert.notNull(counterTagProvider, "'counterTagProvider' cannot be null"); - this.errorCounterTagProvider = counterTagProvider; } /** @@ -191,7 +146,6 @@ public class MicrometerMetricsFactory implements MetricsFactory, MessageSourceMe */ public void setReceiveCounterTagProvider(Function counterTagProvider) { Assert.notNull(counterTagProvider, "'counterTagProvider' cannot be null"); - this.receiveCounterTagProvider = counterTagProvider; } /** @@ -201,7 +155,6 @@ public class MicrometerMetricsFactory implements MetricsFactory, MessageSourceMe */ public void setReceiveErrorCounterTagProvider(Function counterTagProvider) { Assert.notNull(counterTagProvider, "'counterTagProvider' cannot be null"); - this.receiveErrorCounterTagProvider = counterTagProvider; } /** @@ -210,61 +163,25 @@ public class MicrometerMetricsFactory implements MetricsFactory, MessageSourceMe * @param componentCountTagProvider the componentCountTagProvider to set */ public void setComponentCountTagProvider(Function componentCountTagProvider) { - this.componentCountTagProvider = componentCountTagProvider; } @Override public AbstractMessageChannelMetrics createChannelMetrics(String name) { - return new DefaultMessageChannelMetrics(name, - this.meterRegistry.timer(this.timerNameProvider.apply(name), this.timerTagProvider.apply(name)), - this.meterRegistry.counter(this.errorCounterNameProvider.apply(name), - this.errorCounterTagProvider.apply(name)), null, null); + return new DefaultMessageChannelMetrics(name); } @Override public AbstractMessageChannelMetrics createPollableChannelMetrics(String name) { - return new DefaultMessageChannelMetrics(name, - this.meterRegistry.timer(this.timerNameProvider.apply(name), this.timerTagProvider.apply(name)), - this.meterRegistry.counter(this.errorCounterNameProvider.apply(name), - this.errorCounterTagProvider.apply(name)), - this.meterRegistry.counter(this.receiveCounterNameProvider.apply(name), - this.receiveCounterTagProvider.apply(name)), - this.meterRegistry.counter(this.receiveErrorCounterNameProvider.apply(name), - this.receiveErrorCounterTagProvider.apply(name))); + return new DefaultMessageChannelMetrics(name); } @Override public AbstractMessageHandlerMetrics createHandlerMetrics(String name) { - return new DefaultMessageHandlerMetrics(name, - this.meterRegistry.timer(this.timerNameProvider.apply(name), this.timerTagProvider.apply(name)), - this.meterRegistry.counter(this.errorCounterNameProvider.apply(name), - this.errorCounterTagProvider.apply(name))); + return new DefaultMessageHandlerMetrics(name); } @Override public void configure(MessageSourceMetrics metrics, String name) { - metrics.setCounter(this.meterRegistry.counter(this.counterNameProvider.apply(name), - this.counterTagProvider.apply(name))); - } - - private void registerComponentGauges() { - Gauge.Builder builder = Gauge.builder("spring.integration.channels", this, - (c) -> this.applicationContext.getBeansOfType(MessageChannel.class).size()); - builder.tags(this.componentCountTagProvider.apply("channels")) - .description("The Number of Message Channels") - .register(this.meterRegistry); - - builder = Gauge.builder("spring.integration.handlers", this, - (c) -> this.applicationContext.getBeansOfType(MessageHandler.class).size()); - builder.tags(this.componentCountTagProvider.apply("handlers")) - .description("The Number of Message Handlers") - .register(this.meterRegistry); - - builder = Gauge.builder("spring.integration.sources", this, - (c) -> this.applicationContext.getBeansOfType(MessageSource.class).size()); - builder.tags(this.componentCountTagProvider.apply("sources")) - .description("The number of Message Sources") - .register(this.meterRegistry); } } diff --git a/src/reference/asciidoc/metrics.adoc b/src/reference/asciidoc/metrics.adoc index 48551c331f..8293115704 100644 --- a/src/reference/asciidoc/metrics.adoc +++ b/src/reference/asciidoc/metrics.adoc @@ -101,8 +101,10 @@ Starting with _version 5.0.2_, the framework will automatically detect if there Starting with _version 5.0.3_, the presence of a https://micrometer.io/[Micrometer] `MeterRegistry` in the application context will trigger support for Micrometer metrics in addition to the inbuilt metrics (inbuilt metrics will be removed in a future release). IMPORTANT: Micrometer was first supported in _version 5.0.2_, but changes were made to the Micrometer `Meters` in _version 5.0.3_ to make them more suitable for use in dimensional systems. +Further changes were made in 5.0.4; if using Micrometer, a minimum of version 5.0.4 is recommended since some of the changes in 5.0.4 were breaking API changes. Simply add a `MeterRegistry` bean of choice to the application context. +If the `IntegrationManagementConfigurer` detects exactly one `MeterRegistry` bean, it will configure a `MicrometerMetricsCaptor` bean with name `integrationMicrometerMetricsCaptor`. For each `MessageHandler` and `MessageChannel`, timers are registered. For each `MessageSource`, a counter is registered. diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc index cece8b94fc..43ec92b21f 100644 --- a/src/reference/asciidoc/whats-new.adoc +++ b/src/reference/asciidoc/whats-new.adoc @@ -308,6 +308,7 @@ http://micrometer.io/[Micrometer] application monitoring is now supported (since See <> for more information. IMPORTANT: Changes were made to the Micrometer `Meters` in _version 5.0.3_ to make them more suitable for use in dimensional systems. +Further changes were made in 5.0.4; if using Micrometer, a minimum of version 5.0.4 is recommended. ==== @EndpointId Annotations