Skip to content

Commit 6893be5

Browse files
committed
Make sure that RabbitMQ metrics are configured early
The ConnectionFactory can be used early in user configuration to configure an `Exchange`. Such connection may not hold the proper MetricCollector and can be cached, leading to missed metrics information. This commit moves the configuration of RabbitMQ metrics to a BeanPostProcessor so that the proper MetricCollector is configured before any connection is created. Closes gh-12855
1 parent 125b597 commit 6893be5

File tree

2 files changed

+101
-41
lines changed

2 files changed

+101
-41
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright 2012-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.metrics.amqp;
18+
19+
import com.rabbitmq.client.ConnectionFactory;
20+
import com.rabbitmq.client.MetricsCollector;
21+
import io.micrometer.core.instrument.MeterRegistry;
22+
import io.micrometer.core.instrument.Tags;
23+
24+
import org.springframework.amqp.rabbit.connection.AbstractConnectionFactory;
25+
import org.springframework.beans.factory.config.BeanPostProcessor;
26+
import org.springframework.boot.actuate.metrics.amqp.RabbitMetrics;
27+
import org.springframework.context.ApplicationContext;
28+
import org.springframework.core.Ordered;
29+
import org.springframework.util.StringUtils;
30+
31+
/**
32+
* {@link BeanPostProcessor} that configures RabbitMQ metrics. Such arrangement is
33+
* necessary because a connection can be eagerly created and cached without a reference
34+
* to a proper {@link MetricsCollector}.
35+
*
36+
* @author Stephane Nicoll
37+
*/
38+
public class RabbitConnectionFactoryMetricsPostProcessor
39+
implements BeanPostProcessor, Ordered {
40+
41+
private static final String CONNECTION_FACTORY_SUFFIX = "connectionFactory";
42+
43+
private final ApplicationContext context;
44+
45+
private volatile MeterRegistry meterRegistry;
46+
47+
RabbitConnectionFactoryMetricsPostProcessor(ApplicationContext context) {
48+
this.context = context;
49+
}
50+
51+
@Override
52+
public Object postProcessAfterInitialization(Object bean, String beanName) {
53+
if (bean instanceof AbstractConnectionFactory) {
54+
bindConnectionFactoryToRegistry(getMeterRegistry(), beanName,
55+
(AbstractConnectionFactory) bean);
56+
}
57+
return bean;
58+
}
59+
60+
private void bindConnectionFactoryToRegistry(MeterRegistry registry, String beanName,
61+
AbstractConnectionFactory connectionFactory) {
62+
ConnectionFactory rabbitConnectionFactory = connectionFactory
63+
.getRabbitConnectionFactory();
64+
String connectionFactoryName = getConnectionFactoryName(beanName);
65+
new RabbitMetrics(rabbitConnectionFactory, Tags.of("name", connectionFactoryName))
66+
.bindTo(registry);
67+
}
68+
69+
/**
70+
* Get the name of a ConnectionFactory based on its {@code beanName}.
71+
* @param beanName the name of the connection factory bean
72+
* @return a name for the given connection factory
73+
*/
74+
private String getConnectionFactoryName(String beanName) {
75+
if (beanName.length() > CONNECTION_FACTORY_SUFFIX.length()
76+
&& StringUtils.endsWithIgnoreCase(beanName, CONNECTION_FACTORY_SUFFIX)) {
77+
return beanName.substring(0,
78+
beanName.length() - CONNECTION_FACTORY_SUFFIX.length());
79+
}
80+
return beanName;
81+
}
82+
83+
private MeterRegistry getMeterRegistry() {
84+
if (this.meterRegistry == null) {
85+
this.meterRegistry = this.context.getBean(MeterRegistry.class);
86+
}
87+
return this.meterRegistry;
88+
}
89+
90+
@Override
91+
public int getOrder() {
92+
return Ordered.HIGHEST_PRECEDENCE;
93+
}
94+
95+
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/amqp/RabbitMetricsAutoConfiguration.java

Lines changed: 6 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,20 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.metrics.amqp;
1818

19-
import java.util.Map;
20-
2119
import com.rabbitmq.client.ConnectionFactory;
2220
import io.micrometer.core.instrument.MeterRegistry;
23-
import io.micrometer.core.instrument.Tags;
2421

2522
import org.springframework.amqp.rabbit.connection.AbstractConnectionFactory;
26-
import org.springframework.beans.factory.annotation.Autowired;
2723
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
2824
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
29-
import org.springframework.boot.actuate.metrics.amqp.RabbitMetrics;
3025
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
3126
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
3227
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
3328
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
3429
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
30+
import org.springframework.context.ApplicationContext;
31+
import org.springframework.context.annotation.Bean;
3532
import org.springframework.context.annotation.Configuration;
36-
import org.springframework.util.StringUtils;
3733

3834
/**
3935
* {@link EnableAutoConfiguration Auto-configuration} for metrics on all available
@@ -49,41 +45,10 @@
4945
@ConditionalOnBean({ AbstractConnectionFactory.class, MeterRegistry.class })
5046
public class RabbitMetricsAutoConfiguration {
5147

52-
private static final String CONNECTION_FACTORY_SUFFIX = "connectionFactory";
53-
54-
private final MeterRegistry registry;
55-
56-
public RabbitMetricsAutoConfiguration(MeterRegistry registry) {
57-
this.registry = registry;
58-
}
59-
60-
@Autowired
61-
public void bindConnectionFactoriesToRegistry(
62-
Map<String, AbstractConnectionFactory> connectionFactories) {
63-
connectionFactories.forEach(this::bindConnectionFactoryToRegistry);
64-
}
65-
66-
private void bindConnectionFactoryToRegistry(String beanName,
67-
AbstractConnectionFactory connectionFactory) {
68-
ConnectionFactory rabbitConnectionFactory = connectionFactory
69-
.getRabbitConnectionFactory();
70-
String connectionFactoryName = getConnectionFactoryName(beanName);
71-
new RabbitMetrics(rabbitConnectionFactory, Tags.of("name", connectionFactoryName))
72-
.bindTo(this.registry);
73-
}
74-
75-
/**
76-
* Get the name of a ConnectionFactory based on its {@code beanName}.
77-
* @param beanName the name of the connection factory bean
78-
* @return a name for the given connection factory
79-
*/
80-
private String getConnectionFactoryName(String beanName) {
81-
if (beanName.length() > CONNECTION_FACTORY_SUFFIX.length()
82-
&& StringUtils.endsWithIgnoreCase(beanName, CONNECTION_FACTORY_SUFFIX)) {
83-
return beanName.substring(0,
84-
beanName.length() - CONNECTION_FACTORY_SUFFIX.length());
85-
}
86-
return beanName;
48+
@Bean
49+
public static RabbitConnectionFactoryMetricsPostProcessor rabbitConnectionFactoryMetricsPostProcessor(
50+
ApplicationContext applicationContext) {
51+
return new RabbitConnectionFactoryMetricsPostProcessor(applicationContext);
8752
}
8853

8954
}

0 commit comments

Comments
 (0)