open-telemetry / opentelemetry-java-instrumentation

OpenTelemetry auto-instrumentation and instrumentation libraries for Java
https://opentelemetry.io
Apache License 2.0
1.96k stars 860 forks source link

SpringWeb instrumentation conflicts with Spring-Guice library #7428

Closed wzy531a closed 1 year ago

wzy531a commented 1 year ago

Describe the bug When the SpringWeb project uses Spring-Guice to implement ioc, there is a conflict with opentelemetry-javaagent. The opentelemetry-javaagent will make the SpringWeb termination.

Steps to reproduce You can run the demo with opentelemetry-javaagent.jar to reproduce this bug.

What did you expect to see?

What did you see instead?

com.google.inject.ConfigurationException: Guice configuration errors:

1) An exception was caught and reported. Message: Cannot read metadata for class class OpenTelemetryHandlerMappingFilter
  at ModuleRegistryConfiguration.postProcessBeanDefinitionRegistry(ModuleRegistryConfiguration.java:133)

1 error

======================
Full classname legend:
======================
ModuleRegistryConfiguration:       "org.springframework.guice.annotation.ModuleRegistryConfiguration"
OpenTelemetryHandlerMappingFilter: "org.springframework.web.servlet.OpenTelemetryHandlerMappingFilter"
========================
End of classname legend:
========================

    at org.springframework.guice.annotation.ModuleRegistryConfiguration.postProcessBeanDefinitionRegistry(ModuleRegistryConfiguration.java:137) ~[spring-guice-2.0.2.jar:na]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311) ~[spring-context-5.3.24.jar:5.3.24]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:142) ~[spring-context-5.3.24.jar:5.3.24]
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746) ~[spring-context-5.3.24.jar:5.3.24]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564) ~[spring-context-5.3.24.jar:5.3.24]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.6.jar:2.7.6]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) [spring-boot-2.7.6.jar:2.7.6]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) [spring-boot-2.7.6.jar:2.7.6]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-2.7.6.jar:2.7.6]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) [spring-boot-2.7.6.jar:2.7.6]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) [spring-boot-2.7.6.jar:2.7.6]
    at com.example.guicetest.GuiceTestApplication.main(GuiceTestApplication.java:13) [classes/:na]
Caused by: java.lang.IllegalStateException: Cannot read metadata for class class org.springframework.web.servlet.OpenTelemetryHandlerMappingFilter
    at org.springframework.guice.module.GuiceModuleMetadata.matches(GuiceModuleMetadata.java:154) ~[spring-guice-2.0.2.jar:na]
    at org.springframework.guice.module.GuiceModuleMetadata.matches(GuiceModuleMetadata.java:101) ~[spring-guice-2.0.2.jar:na]
    at org.springframework.guice.module.SpringModule$CompositeTypeMatcher.matches(SpringModule.java:477) ~[spring-guice-2.0.2.jar:na]
    at org.springframework.guice.module.SpringModule.bindConditionally(SpringModule.java:302) ~[spring-guice-2.0.2.jar:na]
    at org.springframework.guice.module.SpringModule.bind(SpringModule.java:175) ~[spring-guice-2.0.2.jar:na]
    at org.springframework.guice.module.SpringModule.configure(SpringModule.java:130) ~[spring-guice-2.0.2.jar:na]
    at com.google.inject.AbstractModule.configure(AbstractModule.java:66) ~[guice-5.1.0.jar:na]
    at com.google.inject.spi.Elements$RecordingBinder.install(Elements.java:409) ~[guice-5.1.0.jar:na]
    at com.google.inject.spi.Elements.getElements(Elements.java:108) ~[guice-5.1.0.jar:na]
    at org.springframework.guice.annotation.ModuleRegistryConfiguration.postProcessBeanDefinitionRegistry(ModuleRegistryConfiguration.java:133) ~[spring-guice-2.0.2.jar:na]
    ... 11 common frames omitted
Caused by: java.io.FileNotFoundException: class path resource [org/springframework/web/servlet/OpenTelemetryHandlerMappingFilter.class] cannot be opened because it does not exist
    at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:199) ~[spring-core-5.3.24.jar:5.3.24]
    at org.springframework.core.type.classreading.SimpleMetadataReader.getClassReader(SimpleMetadataReader.java:55) ~[spring-core-5.3.24.jar:5.3.24]
    at org.springframework.core.type.classreading.SimpleMetadataReader.<init>(SimpleMetadataReader.java:48) ~[spring-core-5.3.24.jar:5.3.24]
    at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:103) ~[spring-core-5.3.24.jar:5.3.24]
    at org.springframework.core.type.classreading.CachingMetadataReaderFactory.getMetadataReader(CachingMetadataReaderFactory.java:132) ~[spring-core-5.3.24.jar:5.3.24]
    at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:81) ~[spring-core-5.3.24.jar:5.3.24]
    at org.springframework.guice.module.GuiceModuleMetadata.matches(GuiceModuleMetadata.java:146) ~[spring-guice-2.0.2.jar:na]
    ... 20 common frames omitted

What version are you using? jdk 1.8 spring-guice 2.0.2 springboot 2.7.6 otel-javaagent 1.20.2

Environment Linux

Additional context This problem is caused by Otel-Spring-Web-Instrument injecting beans into spring. If @GuiceModule(excludeNames = {"bean"}) of OtelGuiceConfig.java in demo is changed to @GuiceModule(excludeNames = {"bean", "otelAutoDispatcherFilter" }), you can skip this bean. But as a javaagent, is it reasonable to inject beans into user programs? At least Skywalking did not do so when SpringWeb detected.

mateuszrzeszutek commented 1 year ago

Hey @wzy531a ,

I fixed your issue in https://github.com/open-telemetry/opentelemetry-java-instrumentation/pull/7447

But as a javaagent, is it reasonable to inject beans into user programs?

Theoretically, you don't have to do that, you could just inject bytecode into internal Spring classes. We find that approach rather fragile, and if possible we try to use the built-in mechanisms of the instrumented library (if there are any) to add the instrumentation code. In the case of spring web&webmvs, that means injecting a custom servlet Filter just as if it was added by the app, and letting Spring handle the rest.