Skip to content

No metrics with @EnableIntegrationManagement #3643

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

Closed
MartinHochstrasser opened this issue Oct 13, 2021 · 10 comments · Fixed by #3645
Closed

No metrics with @EnableIntegrationManagement #3643

MartinHochstrasser opened this issue Oct 13, 2021 · 10 comments · Fixed by #3645

Comments

@MartinHochstrasser
Copy link

In what version(s) of Spring Integration are you seeing this issue?

5.5.4 (Spring Boot 2.5.5)
5.5.3 (Spring Boot 2.5.4)
5.5.2 (Spring Boot 2.5.3)
5.5.1 (Spring Boot 2.5.2)
5.5.0 (Spring Boot 2.5.1)

5.4.11 (Spring Boot 2.4.10)
5.4.10 (Spring Boot 2.4.10)
5.4.9 (Spring Boot 2.4.9)
5.4.8 (Spring Boot 2.4.9)
5.4.7 (Spring Boot 2.4.7)
5.4.6 (Spring Boot 2.4.5)
5.4.5 (Spring Boot 2.4.4)
(did not test earlier 5.4.x versions)

5.3.9.RELEASE (Spring Boot 2.3.12.RELEASE)
5.3.8.RELEASE (Spring Boot 2.3.12.RELEASE)
5.3.7.RELEASE (Spring Boot 2.3.10.RELEASE)
5.3.6.RELEASE (Spring Boot 2.3.9.RELEASE)
(did not test earlier 5.3.x versions)

maybe also on earlier versions, but did not test.

Describe the bug

If @EnableIntegrationManagement annotation is added to the application configuration to disable logging, Spring Integration no longer provides metrics.

This is probably related to the fixed bug #3420
If the @EnableIntegrationManagement annotation is present, Spring Integration tries to set up the MicrometerMetricsCaptor before spring boot provides the MeterRegistry bean.

To Reproduce

Add @EnableIntegrationManagement annotation to Spring Integration application that provides metrics.

Expected behavior

If a MeterRegistry bean is present (provided through Spring Boot), Spring Integration should provide metrics as described in documentation even if a @EnableIntegrationManagement annotation is present.

Sample

https://github.com/MartinHochstrasser/spring-integration-metrics-issue

@MartinHochstrasser MartinHochstrasser added status: waiting-for-triage The issue need to be evaluated and its future decided type: bug labels Oct 13, 2021
@MartinHochstrasser
Copy link
Author

Possible Workaround

There seems to be a workaround for people who need @EnableIntegrationManagement(defaultLoggingEnabled = "false") to disable logging due to high throughput, but still want the metrics:

Instead of using the @EnableIntegrationManagement annotation the default logging can be disabled by providing a custom IntegrationManagementConfigurer like this:

    @Bean(name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public IntegrationManagementConfigurer managementConfigurer(MeterRegistry meterRegistry) {
        IntegrationManagementConfigurer configurer = new IntegrationManagementConfigurer();
        configurer.setDefaultLoggingEnabled(false);
        configurer.setMetricsCaptor(new MicrometerMetricsCaptor(meterRegistry));
        return configurer;
    }

@artembilan
Copy link
Member

Just for your info: you must not test against those old versions since every single next includes the previous.
So, even if bug is there in the 5.3.7, the fix can make it only into the next in this 5.3.x generation - 5.3.10.
But if the fix is there in the 5.3.9, then there is not reason to look into 5.3.6, since there is just enough to upgrade to 5.3.9.

Now to clarify the problem:
If we explicitly declare @EnableIntegrationManagement, then Spring Integration does not use MeterRegistry from Spring Boot.
Is that what you wanted to say?
I find that "disable logging" a bit misleading.

Thank you for understanding!

@artembilan artembilan added status: waiting-for-reporter Needs a feedback from the reporter and removed status: waiting-for-triage The issue need to be evaluated and its future decided labels Oct 13, 2021
@garyrussell
Copy link
Contributor

Here is another work around - omit the annotation and add this to configure the framework's bean...

@Bean
ApplicationRunner runner(IntegrationManagementConfigurer imc) {
	imc.setDefaultLoggingEnabled(false);
	return args -> {
	};
}

(You don't really need a runner; this can be done in any bean definition).

@garyrussell
Copy link
Contributor

@artembilan Since that's the sole remaining property on that annotation, maybe we should deprecate it too, and support the setting via spring.integration.properties instead; removing all in 6.0.

WDYT?

@artembilan
Copy link
Member

Yeah... We can do that, but it won't help with the problem over here.
Plus this annotation is indeed a condition for several beans to register:

@Import({ MicrometerMetricsCaptorRegistrar.class, IntegrationManagementConfiguration.class })
public @interface EnableIntegrationManagement {

Removing such an annotation would be more complicated, than just advice do not use in Spring Boot.
At the same time I agree that with Spring Boot opinion we have to do something for that single property and really it must be just enough to expose it as a spring.integration.management.logging=true|false to let end-users to avoid searching a workaround with that annotation and bump to fundamental bean definition registration order problem.

@garyrussell
Copy link
Contributor

I wasn't suggesting removing the annotation, just all of its properties.

@artembilan
Copy link
Member

Well, then it is going to be confusing for end-user: they have to declare an annotation and property.
And it is not going to work just with property since beans to take that property are not there.
Spring Boot has its opinion to always register Spring Integration management.
So, just property over there is really enough since it would just supplement whatever is already there.
Although we can expose something like spring.integration.management.enable=true|false over there, too 😄

It is really probably only the way to fix it via property on Spring Boot side for consistency and convenience of configuration on convention.

Or... I can play with DeferredImportSelector to fix an immediate problem in a single place without trying to justify a movement to the property.

However in the end we could do both 😄

@artembilan
Copy link
Member

Ended up with a solution for the framework like this:

@Configuration(proxyBeanMethods = false)
public class MicrometerMetricsCaptorRegistrar {

	/**
	 * A {@code boolean} flag to indicate if the
	 * {@code io.micrometer.core.instrument.MeterRegistry} class is present in the
	 * CLASSPATH to allow a {@link MicrometerMetricsCaptor} bean.
	 */
	public static final boolean METER_REGISTRY_PRESENT =
			ClassUtils.isPresent("io.micrometer.core.instrument.MeterRegistry", null);

	@Bean(name = MicrometerMetricsCaptor.MICROMETER_CAPTOR_NAME)
	public MicrometerMetricsCaptor micrometerMetricsCaptor(ObjectProvider<MeterRegistry> meterRegistries) {
		if (meterRegistries.stream().findAny().isPresent()) {
			return new MicrometerMetricsCaptor(meterRegistries);
		}
		else {
			return null;
		}
	}

}

Plus:

public class MicrometerMetricsCaptorImportSelector implements ImportSelector {

	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		if (MicrometerMetricsCaptorRegistrar.METER_REGISTRY_PRESENT) {
			return new String[] { MicrometerMetricsCaptorRegistrar.class.getName() };
		}
		else {
			return new String[0];
		}
	}

}

Slightly breaking change, but having a workaround we are not going to back-port it to other versions.
Only the problem when you declare your own MicrometerMetricsCaptor.MICROMETER_CAPTOR_NAME bean and use @EnableIntegrationManagement at the same time.
So, no way to determine which bean is going to win in the end.
Spring Boot will reject such a miscofiguration anyway according its allowBeanDefinitionOverriding = true.

@MartinHochstrasser
Copy link
Author

@artembilan Thanks for the quick reply and analysis of the issue.

To answer your initial question: Yes, exactly: As soon as a @EnableIntegrataionManagement is declared, Spring Integration does not pick up the MeterRegistry from Spring Boot.

We use the @EnableIntegrataionManagement annotation in a Spring Boot / Spring Integration project to disable default logging with the defaultLoggingEnabled = "false" property. That is the only reason we declare this annotation.

About the versions i tested: I was just curious if the issue was introduced with a recent combination of Spring Integration and Spring Boot, or if a workaround could have been to just use a slightly older version combination 😄

@garyrussell Thanks for the other workaround!

artembilan added a commit to artembilan/spring-integration that referenced this issue Oct 14, 2021
Fixes spring-projects#3643

The `ImportBeanDefinitionRegistrar` register its bean definition in the early phase.
It causes a problem with Spring Boot when an explicit `@EnableIntegrationManagement`
is declared: the `MeterRegistry` is provided later on via auto-configuration
and `MicrometerMetricsCaptor` has no knowledge about `MeterRegistry` at the
`ImportBeanDefinitionRegistrar` phase.

* Reworks `MicrometerMetricsCaptorRegistrar` to the `MicrometerMetricsCaptorConfiguration`
and use special conditional `MicrometerMetricsCaptorImportSelector` for that
`@EnableIntegrationManagement`.
This way a `MicrometerMetricsCaptor` `@Bean` consults `ObjectProvider<MeterRegistry>`
on its creating time and returns `null` if no `MeterRegistry` beans at all.
@artembilan artembilan added in: core and removed status: waiting-for-reporter Needs a feedback from the reporter labels Oct 14, 2021
@artembilan artembilan added this to the 5.5.5 milestone Oct 14, 2021
@artembilan
Copy link
Member

See also relevant change in Spring Boot: spring-projects/spring-boot#28355

garyrussell pushed a commit that referenced this issue Oct 18, 2021
Fixes #3643

The `ImportBeanDefinitionRegistrar` register its bean definition in the early phase.
It causes a problem with Spring Boot when an explicit `@EnableIntegrationManagement`
is declared: the `MeterRegistry` is provided later on via auto-configuration
and `MicrometerMetricsCaptor` has no knowledge about `MeterRegistry` at the
`ImportBeanDefinitionRegistrar` phase.

* Reworks `MicrometerMetricsCaptorRegistrar` to the `MicrometerMetricsCaptorConfiguration`
and use special conditional `MicrometerMetricsCaptorImportSelector` for that
`@EnableIntegrationManagement`.
This way a `MicrometerMetricsCaptor` `@Bean` consults `ObjectProvider<MeterRegistry>`
on its creating time and returns `null` if no `MeterRegistry` beans at all.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants