Skip to content

Missing Spring Integration metrics due to the MeterRegistry bean being looked for before it has been defined #24095

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

artembilan
Copy link
Member

Related to spring-projects/spring-integration#3420

After fixing spring-projects/spring-integration#3376
to make sure that Spring Integration has a dependency on the MeterRegistry
for the proper lifecycle on shutdown, it turns out that Spring Integration
metrics features must be configured after the MeterRegistry is
provided properly by Spring Boot

Note: we start observing the wrong order problem when we also have an
IntegrationGraphEndpointAutoConfiguration which is ordered alphabetically
(therefore before MetricsAutoConfiguration) and pushes an
IntegrationAutoConfiguration up - before MetricsAutoConfiguration

  • Add @AutoConfigureBefore(IntegrationAutoConfiguration.class)
    to the CompositeMeterRegistryAutoConfiguration to ensure that MeterRegistry
    bean is provided before @EnableIntegrationManagement logic

Back-port to master moving @AutoConfigureBefore(IntegrationAutoConfiguration.class)
onto the MeterRegistryAutoConfiguration already

Related to spring-projects/spring-integration#3420

After fixing spring-projects/spring-integration#3376
to make sure that Spring Integration has a dependency on the `MeterRegistry`
for the proper lifecycle on shutdown, it turns out that Spring Integration
metrics features must be configured *after* the `MeterRegistry` is
provided properly by Spring Boot

Note: we start observing the wrong order problem when we also have an
`IntegrationGraphEndpointAutoConfiguration` which is ordered alphabetically
(therefore before `MetricsAutoConfiguration`) and pushes an
`IntegrationAutoConfiguration` up - before `MetricsAutoConfiguration`

* Add `@AutoConfigureBefore(IntegrationAutoConfiguration.class)`
to the `CompositeMeterRegistryAutoConfiguration` to ensure that `MeterRegistry`
bean is provided before `@EnableIntegrationManagement` logic

**Back-port to `master` moving `@AutoConfigureBefore(IntegrationAutoConfiguration.class)`
onto the `MeterRegistryAutoConfiguration` already**
* @since 2.0.0
*/
@Configuration(proxyBeanMethods = false)
@Import({ NoOpMeterRegistryConfiguration.class, CompositeMeterRegistryConfiguration.class })
@ConditionalOnClass(CompositeMeterRegistry.class)
@AutoConfigureBefore(IntegrationAutoConfiguration.class)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it make sense to flip the dependency so integration does auto configure after metrics?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you mean, but I feel like I have done exactly what you just said: the IntegrationAutoConfiguration is going to be auto-configured exactly after metrics.
If you are talking about having an @AutoConfigureAfter on the IntegrationAutoConfiguration instead, then we would introduce a logical dependency between spring-boot-autoconfigure and spring-boot-actuator-autoconfigure modules even if we would use a FQCN for the mentioned CompositeMeterRegistryAutoConfiguration with MeterRegistry definition.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, didn't realize the cross module bit

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'll need to find another approach if we can. The conceptual relationship is backwards if the general metrics auto-configuration knows about Integration auto-config.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah... Probably another way is to extract an IntegrationAutoConfiguration.IntegrationManagementConfiguration into a separate class under the spring-boot-actuator-autoconfigure and make it already dependent on the MetricsAutoConfiguration as it is done in many other *MetricsAutoConfiguration, e.g. RabbitMetricsAutoConfiguration or KafkaMetricsAutoConfiguration.

However it is still not clean solution since @EnableIntegrationManagement has its own value even without Micrometer.
On the other hand in Spring Integration 6.0 we are going to get rid off MetricsCaptor facade alltogether to make integration metrics directly based on the Micrometer API. So, our dependency order will become more tighter.
Although that one (I believe) will let us to remove the MicrometerMetricsCaptorRegistrar at all and we won't have that beanFactory.getBeanNamesForType(METER_REGISTRY_CLASS, false, false) logic any more, therefore MeterRegistry detection will be deferred to later phase when we already try to resolve an ObjectProvider<MeterRegistry>.

We probably can trick it exactly on that "guilty" IntegrationGraphEndpointAutoConfiguration which is already here in the spring-boot-actuator-autoconfigure leaving the natural alphabetic order for those IntegrationAutoConfiguration and MetricsAutoConfiguration...

Any other thoughts?

Thank you for looking into this!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ordering could be changed with an auto-configuration that does nothing other than influencing the ordering. Something like this:

package org.springframework.boot.actuate.autoconfigure.metrics.integration;

import io.micrometer.core.instrument.MeterRegistry;

import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration;
import org.springframework.context.annotation.Configuration;

/**
 * {@link EnableAutoConfiguration Auto-configuration} for Spring Integration's metrics.
 * Orders auto-configuration classes to ensure that the {@link MeterRegistry} bean has
 * been defined before Spring Integration's Micrometer support queries the bean factory
 * for it.
 *
 * @author Andy Wilkinson
 */
@AutoConfigureAfter({ MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class })
@AutoConfigureBefore(IntegrationAutoConfiguration.class)
@Configuration(proxyBeanMethods = false)
public class IntegrationMetricsAutoConfiguration {

}

This improves the situation but it doesn't fix the test. It fails as the tag hasn't been applied to the spring.integration.channels meter. The tag is missing due to a number of beans not being post-processed:

2020-11-10 10:10:07.483  INFO   --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'integrationChannelResolver' of type [org.springframework.integration.support.channel.BeanFactoryChannelResolver] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-11-10 10:10:07.484  INFO   --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'integrationDisposableAutoCreatedBeans' of type [org.springframework.integration.config.annotation.Disposables] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-11-10 10:10:07.497  INFO   --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.integration.config.IntegrationManagementConfiguration' of type [org.springframework.integration.config.IntegrationManagementConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-11-10 10:10:07.521  INFO   --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'simpleMetricsExportAutoConfiguration' of type [org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-11-10 10:10:07.535  INFO   --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'management.metrics.export.simple-org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleProperties' of type [org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-11-10 10:10:07.537  INFO   --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'simpleConfig' of type [org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimplePropertiesConfigAdapter] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-11-10 10:10:07.538  INFO   --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'metricsAutoConfiguration' of type [org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-11-10 10:10:07.538  INFO   --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'micrometerClock' of type [io.micrometer.core.instrument.Clock$1] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-11-10 10:10:07.558  INFO   --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'simpleMeterRegistry' of type [io.micrometer.core.instrument.simple.SimpleMeterRegistry] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

I think this is a bug in Spring Integration. SimpleMeterRegistry isn't eligible for post-processing due to the following dependency chain:

  • integrationManagementConfigurer (a BeanPostProcessor)
  • managementConfigurer
  • integrationMicrometerMetricsCaptor
  • simpleMeterRegistry

Generally speaking, injecting dependencies into bean post-processors isn't safe and should be avoided. In this case, it's causing simpleMeterRegistry to be ineligible for post-processing which prevents the meter filter that honours management.metrics.tags from being applied.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, @wilkinsona , for looking into this!

So, I think you found the way it is problematic.
After fixing spring-projects/spring-integration#3425 to rely already on the ContextClosedEvent instead of destroy(), it looks like our attempt to have a proper dependency is not going to be satisfied anyway.
Therefore I will change the IntegrationManagementConfigurer to be based on the ObjectProvider<MetricsCaptor> to avoid that hard dependency for its BeanPostProcessor nature.

From here it sounds like we may not need this fix in Spring Boot again...
Stay tuned: I'll update today!

@wilkinsona wilkinsona added the status: waiting-for-feedback We need additional information before we can continue label Nov 10, 2020
with appropriate auto-configuration order between
Actuator metrics and Spring Integration
* bump SI version to the latest SNAPSHOT
which includes the fix for dependency injection
for the `IntegrationManagementConfigurer`
@artembilan
Copy link
Member Author

So, I pushed the fix according our discussion.
And now this depends on the: spring-projects/spring-integration#3427.

We still need to have an auto-configuration order between metrics and Integration since we have a logic in Spring Integration to register or not some bean when MeterRegistry is present in the context or not.
We probably will get rid of it in the next Spring Integration 6.0.

Thanks

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Nov 10, 2020
wilkinsona pushed a commit to wilkinsona/spring-boot that referenced this pull request Nov 11, 2020
Previously, it was possible for Spring Integration, including its
built-in Micrometer support, to be auto-configured before the
Micrometer auto-configuration had defined the MeterRegistry bean. This
resulted in missing Spring Integration metrics.

Spring Integration is unusual in having its own built-in Micrometer
support that it configures itself. Rather than providing
auto-configuration for Integration's Micrometer support (Which isn't
needed), this commit introduces some auto-configuration that just
affects the ordering of the auto-configuration classes. This ordering
ensures that the MeterRegistry bean has been defined by Spring
Integration is auto-configured. This ensures that the MeterRegistry
bean is known to the BeanFactory when Spring Integration goes looking
for it.

See spring-projectsgh-24095
wilkinsona added a commit to wilkinsona/spring-boot that referenced this pull request Nov 11, 2020
@wilkinsona
Copy link
Member

I've pushed a squashed and slightly polished version of the proposal here. I'd like the rest of the team to take a look before merging as the new auto-configuration class is one-of-a-kind.

@wilkinsona
Copy link
Member

@artembilan I see you've made the Integration change in 5.2 and later but the base branch of this PR is 2.3.x which uses SI 5.3.x. Should we be looking to make the changes proposed here in Boot 2.2.x and later?

@wilkinsona wilkinsona changed the title Auto-configure metrics before Spring Integration Missing Spring Integration metrics due to the MeterRegistry been being looked for before it has been defined Nov 11, 2020
@wilkinsona wilkinsona added for: team-attention An issue we'd like other members of the team to review type: bug A general bug and removed status: feedback-provided Feedback has been provided labels Nov 11, 2020
@wilkinsona wilkinsona changed the title Missing Spring Integration metrics due to the MeterRegistry been being looked for before it has been defined Missing Spring Integration metrics due to the MeterRegistry bean being looked for before it has been defined Nov 11, 2020
@artembilan
Copy link
Member Author

Thank you, @wilkinsona , for your help!
Yes, I think this has to be back-ported to Spring Boot 2.2.x and of course to master.
I missed somehow the fact that Boot 2.2.x is still in support.

tzolov added a commit to spring-cloud/stream-applications that referenced this pull request Nov 18, 2020
 - This is a workaround until spring-projects/spring-boot#24095 makes it to the Spring Boot (e.g. Spring Boot 2.5+)
 - Without this fix the Spring Integration metics are not property configured and as such not sent to the Wavefront TSDB!
 - All Spring Boot 2.4 apps will be affected by this problem and would require to add the following dependency as a workaround:
   org.springframework.cloud.stream.app:stream-applications-micrometer-common:3.+
@philwebb philwebb removed for: team-attention An issue we'd like other members of the team to review status: waiting-for-triage An issue we've not yet triaged labels Nov 18, 2020
@philwebb philwebb added this to the 2.3.x milestone Nov 18, 2020
@wilkinsona wilkinsona modified the milestones: 2.3.x, 2.2.x Nov 20, 2020
wilkinsona pushed a commit that referenced this pull request Nov 20, 2020
Previously, it was possible for Spring Integration, including its
built-in Micrometer support, to be auto-configured before the
Micrometer auto-configuration had defined the MeterRegistry bean. This
resulted in missing Spring Integration metrics.

Spring Integration is unusual in having its own built-in Micrometer
support that it configures itself. Rather than providing
auto-configuration for Integration's Micrometer support (Which isn't
needed), this commit introduces some auto-configuration that just
affects the ordering of the auto-configuration classes. This ordering
ensures that the MeterRegistry bean has been defined by Spring
Integration is auto-configured. This ensures that the MeterRegistry
bean is known to the BeanFactory when Spring Integration goes looking
for it.

See gh-24095
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants