INT-4418: Micrometer docs and polishing

JIRA: https://jira.spring.io/browse/INT-4418

Add docs for the rework and optimize `Meter` creation.

Polishing

* Polishing according PR comments

* Fix typo in `dsl.adoc`
This commit is contained in:
Gary Russell
2018-02-28 10:53:30 -05:00
committed by Artem Bilan
parent cb0d43db6b
commit 82a8982857
11 changed files with 186 additions and 98 deletions

View File

@@ -92,6 +92,10 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport
private MeterRegistry meterRegistry;
private Timer successTimer;
private Timer failureTimer;
public AbstractMessageChannel() {
this.interceptors = new ChannelInterceptorList(logger);
}
@@ -445,7 +449,7 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport
logger.debug("preSend on channel '" + this + "', message: " + message);
}
if (interceptors.getSize() > 0) {
interceptorStack = new ArrayDeque<ChannelInterceptor>();
interceptorStack = new ArrayDeque<>();
message = interceptors.preSend(message, this, interceptorStack);
if (message == null) {
return false;
@@ -458,13 +462,7 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport
}
sent = doSend(message, timeout);
if (sample != null) {
sample.stop(Timer.builder(SEND_TIMER_NAME)
.tag("type", "channel")
.tag("name", getComponentName() == null ? "unknown" : getComponentName())
.tag("result", sent ? "success" : "failure")
.tag("exception", "none")
.description("Subflow process time")
.register(this.meterRegistry));
sample.stop(sendTimer(sent));
}
channelMetrics.afterSend(metrics, sent);
metricsProcessed = true;
@@ -485,13 +483,7 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport
catch (Exception e) {
if (countsEnabled && !metricsProcessed) {
if (sample != null) {
sample.stop(Timer.builder(SEND_TIMER_NAME)
.tag("type", "channel")
.tag("name", getComponentName() == null ? "unknown" : getComponentName())
.tag("result", "failure")
.tag("exception", e.getClass().getSimpleName())
.description("Subflow process time")
.register(this.meterRegistry));
sample.stop(buildSendTimer(false, e.getClass().getSimpleName()));
}
channelMetrics.afterSend(metrics, false);
}
@@ -506,6 +498,31 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport
}
}
private Timer sendTimer(boolean sent) {
if (sent) {
if (this.successTimer == null) {
this.successTimer = buildSendTimer(true, "none");
}
return this.successTimer;
}
else {
if (this.failureTimer == null) {
this.failureTimer = buildSendTimer(false, "none");
}
return this.failureTimer;
}
}
private Timer buildSendTimer(boolean success, String exception) {
return Timer.builder(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);
}
private Message<?> convertPayloadIfNecessary(Message<?> message) {
// first pass checks if the payload type already matches any of the datatypes
for (Class<?> datatype : this.datatypes) {

View File

@@ -42,6 +42,8 @@ public abstract class AbstractPollableChannel extends AbstractMessageChannel
private volatile int executorInterceptorsSize;
private Counter receiveCounter;
@Override
public int getReceiveCount() {
return getMetrics().getReceiveCount();
@@ -107,13 +109,7 @@ public abstract class AbstractPollableChannel extends AbstractMessageChannel
Message<?> message = this.doReceive(timeout);
if (countsEnabled && message != null) {
if (getMeterRegistry() != null) {
Counter.builder(RECEIVE_COUNTER_NAME)
.tag("name", getComponentName())
.tag("type", "channel")
.tag("result", "success")
.tag("exception", "none")
.description("Messages received")
.register(getMeterRegistry()).increment();
incrementReceiveCounter();
}
getMetrics().afterReceive();
counted = true;
@@ -134,12 +130,13 @@ public abstract class AbstractPollableChannel extends AbstractMessageChannel
if (countsEnabled && !counted) {
if (getMeterRegistry() != null) {
Counter.builder(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()).increment();
.tag("name", getComponentName() == null ? "unknown" : getComponentName())
.tag("type", "channel")
.tag("result", "failure")
.tag("exception", e.getClass().getSimpleName())
.description("Messages received")
.register(getMeterRegistry())
.increment();
}
getMetrics().afterError();
}
@@ -150,6 +147,19 @@ public abstract class AbstractPollableChannel extends AbstractMessageChannel
}
}
private void incrementReceiveCounter() {
if (this.receiveCounter == null) {
this.receiveCounter = Counter.builder(RECEIVE_COUNTER_NAME)
.tag("name", getComponentName())
.tag("type", "channel")
.tag("result", "success")
.tag("exception", "none")
.description("Messages received")
.register(getMeterRegistry());
}
this.receiveCounter.increment();
}
@Override
public void setInterceptors(List<ChannelInterceptor> interceptors) {
super.setInterceptors(interceptors);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-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.
@@ -45,6 +45,7 @@ import io.micrometer.core.instrument.Timer;
*
* @author Mark Fisher
* @author Gary Russell
* @author Artyem Bilan
*/
@IntegrationManagedResource
public class NullChannel implements PollableChannel, MessageChannelMetrics,
@@ -66,6 +67,8 @@ public class NullChannel implements PollableChannel, MessageChannelMetrics,
private MeterRegistry meterRegistry;
private Timer successTimer;
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
@@ -221,6 +224,11 @@ public class NullChannel implements PollableChannel, MessageChannelMetrics,
return this.managementOverrides;
}
@Override
public boolean send(Message<?> message, long timeout) {
return send(message);
}
@Override
public boolean send(Message<?> message) {
if (this.loggingEnabled && this.logger.isDebugEnabled()) {
@@ -228,22 +236,24 @@ public class NullChannel implements PollableChannel, MessageChannelMetrics,
}
if (this.countsEnabled) {
if (this.meterRegistry != null) {
Timer.builder(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).record(0, TimeUnit.MILLISECONDS);
sendTimer().record(0, TimeUnit.MILLISECONDS);
}
this.channelMetrics.afterSend(this.channelMetrics.beforeSend(), true);
}
return true;
}
@Override
public boolean send(Message<?> message, long timeout) {
return this.send(message);
private Timer sendTimer() {
if (this.successTimer == null) {
this.successTimer = Timer.builder(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);
}
return this.successTimer;
}
@Override

View File

@@ -60,14 +60,14 @@ public abstract class AbstractMessageSource<T> extends AbstractExpressionEvaluat
private String managedName;
private Counter counter;
private volatile boolean countsEnabled;
private volatile boolean loggingEnabled = true;
private MeterRegistry meterRegistry;
private Counter receiveCounter;
public void setHeaderExpressions(Map<String, Expression> headerExpressions) {
this.headerExpressions = (headerExpressions != null)
? headerExpressions : Collections.emptyMap();
@@ -130,11 +130,6 @@ public abstract class AbstractMessageSource<T> extends AbstractExpressionEvaluat
this.managementOverrides.loggingConfigured = true;
}
@Override
public void setCounter(Counter counter) {
this.counter = counter;
}
@Override
public void reset() {
this.messageCount.set(0);
@@ -200,19 +195,26 @@ public abstract class AbstractMessageSource<T> extends AbstractExpressionEvaluat
}
if (this.countsEnabled && message != null) {
if (this.meterRegistry != null) {
Counter.builder(RECEIVE_COUNTER_NAME)
.tag("name", getComponentName() == null ? "unknown" : getComponentName())
.tag("type", "source")
.tag("result", "success")
.tag("exception", "none")
.description("Messages received")
.register(this.meterRegistry).increment();
incrementReceiveCounter();
}
this.messageCount.incrementAndGet();
}
return message;
}
private void incrementReceiveCounter() {
if (this.receiveCounter == null) {
this.receiveCounter = Counter.builder(RECEIVE_COUNTER_NAME)
.tag("name", getComponentName() == null ? "unknown" : getComponentName())
.tag("type", "source")
.tag("result", "success")
.tag("exception", "none")
.description("Messages received")
.register(this.meterRegistry);
}
this.receiveCounter.increment();
}
private Map<String, Object> evaluateHeaders() {
Map<String, Object> results = new HashMap<>();
for (Map.Entry<String, Expression> entry : this.headerExpressions.entrySet()) {

View File

@@ -50,11 +50,12 @@ import reactor.core.CoreSubscriber;
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
*/
@IntegrationManagedResource
public abstract class AbstractMessageHandler extends IntegrationObjectSupport implements MessageHandler,
MessageHandlerMetrics, ConfigurableMetricsAware<AbstractMessageHandlerMetrics>, TrackableComponent, Orderable,
CoreSubscriber<Message<?>> {
public abstract class AbstractMessageHandler extends IntegrationObjectSupport
implements MessageHandler, MessageHandlerMetrics, ConfigurableMetricsAware<AbstractMessageHandlerMetrics>,
TrackableComponent, Orderable, CoreSubscriber<Message<?>> {
private final ManagementOverrides managementOverrides = new ManagementOverrides();
@@ -76,6 +77,8 @@ public abstract class AbstractMessageHandler extends IntegrationObjectSupport im
private MeterRegistry meterRegistry;
private Timer successTimer;
@Override
public boolean isLoggingEnabled() {
return this.loggingEnabled;
@@ -147,19 +150,13 @@ public abstract class AbstractMessageHandler extends IntegrationObjectSupport im
}
try {
if (this.shouldTrack) {
message = MessageHistory.write(message, this, this.getMessageBuilderFactory());
message = MessageHistory.write(message, this, getMessageBuilderFactory());
}
if (countsEnabled) {
start = handlerMetrics.beforeHandle();
handleMessageInternal(message);
if (this.meterRegistry != null) {
sample.stop(Timer.builder(SEND_TIMER_NAME)
.tag("type", "handler")
.tag("name", getComponentName() == null ? "unknown" : getComponentName())
.tag("result", "success")
.tag("exception", "none")
.description("Subflow process time")
.register(this.meterRegistry));
sample.stop(sendTimer());
}
handlerMetrics.afterHandle(start, true);
}
@@ -169,13 +166,7 @@ public abstract class AbstractMessageHandler extends IntegrationObjectSupport im
}
catch (Exception e) {
if (sample != null) {
sample.stop(Timer.builder(SEND_TIMER_NAME)
.tag("type", "handler")
.tag("name", getComponentName() == null ? "unknown" : getComponentName())
.tag("result", "failure")
.tag("exception", e.getClass().getSimpleName())
.description("Subflow process time")
.register(this.meterRegistry));
sample.stop(buildSendTimer(false, e.getClass().getSimpleName()));
}
if (countsEnabled) {
handlerMetrics.afterHandle(start, false);
@@ -187,6 +178,23 @@ public abstract class AbstractMessageHandler extends IntegrationObjectSupport im
}
}
private Timer 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)
.tag("type", "handler")
.tag("name", getComponentName() == null ? "unknown" : getComponentName())
.tag("result", success ? "success" : "failure")
.tag("exception", exception)
.description("Send processing time")
.register(this.meterRegistry);
}
@Override
public void onSubscribe(Subscription subscription) {
Assert.notNull(subscription, "'subscription' must not be null");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-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.
@@ -20,14 +20,14 @@ 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;
/**
* A {@link MessageSourceMetrics} that exposes in addition the {@link Lifecycle} interface. The lifecycle methods can
* be used to start and stop polling endpoints, for instance, in a live system.
* A {@link MessageSourceMetrics} that exposes in addition the {@link Lifecycle} interface.
* The lifecycle methods can be used to start and stop polling endpoints, for instance, in a live system.
*
* @author Dave Syer
* @author Gary Russell
* @author Artem Bilan
*
* @since 2.0
*/
@IntegrationManagedResource
@@ -126,9 +126,4 @@ public class LifecycleMessageSourceMetrics implements MessageSourceMetrics, Life
return this.delegate.getOverrides();
}
@Override
public void setCounter(Counter counter) {
this.delegate.setCounter(counter);
}
}

View File

@@ -24,6 +24,8 @@ import io.micrometer.core.instrument.Counter;
/**
* @author Dave Syer
* @author Gary Russell
* @author Artem Bilan
*
* @since 2.0
*/
public interface MessageSourceMetrics extends IntegrationManagement {
@@ -53,7 +55,10 @@ public interface MessageSourceMetrics extends IntegrationManagement {
* Set a micrometer counter to count messages produced.
* @param counter the counter.
* @since 5.0.2
* @deprecated in favor of built-in counter registration via {@code MeterRegistry} callbacks.
* Will be remove in the next release.
*/
@Deprecated
default void setCounter(Counter counter) {
// no op
}

View File

@@ -32,6 +32,7 @@ import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.integration.channel.AbstractPollableChannel;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.NullChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.config.EnableIntegrationManagement;
@@ -75,6 +76,9 @@ public class MicrometerMetricsTests {
@Autowired
private PollableChannel badPoll;
@Autowired
private NullChannel nullChannel;
@Test
public void testSend() {
GenericMessage<String> message = new GenericMessage<>("foo");
@@ -97,6 +101,7 @@ public class MicrometerMetricsTests {
catch (RuntimeException e) {
assertThat(e.getMessage()).isEqualTo("badPoll");
}
nullChannel.send(message);
MeterRegistry registry = this.meterRegistry;
assertThat(registry.get("spring.integration.channels").gauge().value()).isEqualTo(5);
assertThat(registry.get("spring.integration.handlers").gauge().value()).isEqualTo(2);
@@ -137,6 +142,11 @@ public class MicrometerMetricsTests {
.tag("result", "success")
.counter().count()).isEqualTo(1);
assertThat(registry.get("spring.integration.send")
.tag("name", "nullChannel")
.tag("result", "success")
.timer().count()).isEqualTo(1);
BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) this.context.getBeanFactory();
beanFactory.registerBeanDefinition("newChannel",
BeanDefinitionBuilder.genericBeanDefinition(DirectChannel.class).getRawBeanDefinition());

View File

@@ -124,7 +124,7 @@ public MessageChannel priorityChannel() {
}
----
The same `MessageChannels` builder factory can be used in the `channel()` EIP-method from `IntegrationFlowBuilder` to wire endpoints similar to an`input-channel`/`output-channel` pair in the XML configuration.
The same `MessageChannels` builder factory can be used in the `channel()` EIP-method from `IntegrationFlowBuilder` to wire endpoints similar to an `input-channel`/`output-channel` pair in the XML configuration.
By default endpoints are wired via `DirectChannel` s where the bean name is based on the pattern: `[IntegrationFlow.beanName].channel#[channelNameIndex]`.
This rule is applied for unnamed channels produced by inline `MessageChannels` builder factory usage, too.
However all `MessageChannels` methods have a `channelId` -aware variant to create the bean names for `MessageChannel` s.

View File

@@ -63,7 +63,6 @@ Default `false`.
<3> Enable or disable statistical metrics for components not matching one of the patterns in <5>.
Only applied if you have not explicitly configured the setting in a bean definition.
Ignored if <<micrometer-integration, Micrometer>> is being used.
Default 'false'.
<4> A comma-delimited list of patterns for beans for which counts should be enabled; negate the pattern with `!`.
@@ -76,7 +75,6 @@ with `!`.
First match wins (positive or negative).
In the unlikely event that you have a bean name starting with `!`, escape the `!` in the pattern: `\!foo` positively
matches a bean named `!foo`.
Ignored if <<micrometer-integration, Micrometer>> is being used.
Stats implies counts.
<6> A reference to a `MetricsFactory`.
@@ -100,31 +98,62 @@ Starting with _version 5.0.2_, the framework will automatically detect if there
[[micrometer-integration]]
==== Micrometer Integration
Starting with _version 5.0.2_, adding a `MicrometerMetricsFactory` to the application context will switch to using https://micrometer.io/[Micrometer] metrics instead of the inbuilt metrics.
Simply add the bean, configured with a `MeterRegistry` implementation.
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).
[source, java]
----
@Bean
public MicrometerMetricsFactory metricsFactory(MeterRegistry meterRegistry) {
return new MicrometerMetricsFactory(meterRegistry);
}
----
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.
For each `MessageHandler` and `MessageChannel`, a timer and errorCounter are registered.
Simply add a `MeterRegistry` bean of choice to the application context.
For each `MessageHandler` and `MessageChannel`, timers are registered.
For each `MessageSource`, a counter is registered.
This only applies to objects that extend `AbstractMessageHandler`, `AbstractMessageChannel` and `AbstractMessageSource` respectively (which is the case for most framework components).
The factory provides mechanisms to customize the `Meter` names and tags; refer to the Javadocs for more information.
With Micrometer metrics, the `statsEnabled` flag takes no effect, since statistics capture is delegated to Micrometer.
The `countsEnabled` flag controls whether the Micrometer `Meter` s are updated when processing each message.
The `Timer` Meters for send operations on message channels have the following name/tags:
- `name` : `spring.integration.send`
- `tag` : `type:channel`
- `tag` : `name:<componentName>`
- `tag` : `result:(success|failure)`
- `tag` : `exception:(none|exception simple class name)`
- `description` : `Send processing time`
(A `failure` result with a `none` exception means the channel `send()` operation returned `false`).
The `Counter` Meters for receive operations on pollable message channels have the following names/tags:
- `name` : `spring.integration.receive`
- `tag` : `type:channel`
- `tag` : `name:<componentName>`
- `tag` : `result:(success|failure)`
- `tag` : `exception:(none|exception simple class name)`
- `description` : `Messages received`
The `Timer` Meters for operations on message handlers have the following name/tags:
- `name` : `spring.integration.send`
- `tag` : `type:handler`
- `tag` : `name:<componentName>`
- `tag` : `result:(success|failure)`
- `tag` : `exception:(none|exception simple class name)`
- `description` : `Send processing time`
The `Counter` meters for message sources have the following names/tags:
- `name` : `spring.integration.receive`
- `tag` : `type:source`
- `tag` : `name:<componentName>`
- `tag` : `result:success`
- `tag` : `exception:none`
- `description` : `Messages received`
[[mgmt-channel-features]]
==== MessageChannel Metric Features
This only applies if <<micrometer-integration, Micrometer>> is not being used.
These legacy metrics will be removed in a future release; see <<micrometer-integration>>.
Message channels report metrics according to their concrete type.
If you are looking at a `DirectChannel`, you will see statistics for the send operation.
@@ -174,7 +203,7 @@ Error ratio is 1 - success ratio.
[[mgmt-handler-features]]
==== MessageHandler Metric Features
This only applies if <<micrometer-integration, Micrometer>> is not being used.
These legacy metrics will be removed in a future release; see <<micrometer-integration>>.
The following table shows the statistics maintained for message handlers.
Some metrics are simple counters (message count and error count), and one is an estimate of averages of send duration.

View File

@@ -306,3 +306,5 @@ See <<jdbc>> for more information.
http://micrometer.io/[Micrometer] application monitoring is now supported (since _version 5.0.2_).
See <<micrometer-integration>> 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.