Skip to content

Protobuf version incompatibility with OtlpMeterRegistry in newly-generated Micronaut projects since Micronaut 4.9.0 #1102

@jon-signal

Description

@jon-signal

Expected Behavior

Attempting to inject a MeterRegistry with OtlpMeterRegistry on the classpath in an otherwise-minimal project should work without issue.

Actual Behaviour

Attempting to inject a MeterRegistry with OtlpMeterRegistry on the classpath in an otherwise-minimal project results in the following exception:

io.micronaut.context.exceptions.BeanInstantiationException: 
Error instantiating bean of type  [io.micrometer.core.instrument.composite.CompositeMeterRegistry]

Message: com/google/protobuf/RuntimeVersion$RuntimeDomain
Path Taken:
c.e.OtlpMeterRegistryDependencyDemoTest#meterRegistry
\---> @i.m.c.a.Primary @j.i.Singleton i.m.c.i.c.CompositeMeterRegistry i.m.c.m.m.MeterRegistryFactory.compositeMeterRegistry#compositeMeterRegistry([List<MeterRegistry> registries], List<MeterRegistryConfigurer<MeterRegistry>> configurers)
	at io.micronaut.context.DefaultBeanContext.resolveByBeanFactory(DefaultBeanContext.java:2357)
	at io.micronaut.context.DefaultBeanContext.createRegistration(DefaultBeanContext.java:3162)
	at io.micronaut.context.SingletonScope.getOrCreate(SingletonScope.java:80)
	at io.micronaut.context.DefaultBeanContext.resolveBeanRegistration(DefaultBeanContext.java:3012)
	at io.micronaut.context.DefaultBeanContext.addCandidateToList(DefaultBeanContext.java:3625)
	at io.micronaut.context.DefaultBeanContext.resolveBeanRegistrations(DefaultBeanContext.java:3580)
	at io.micronaut.context.DefaultBeanContext.getBeanRegistrations(DefaultBeanContext.java:3554)
	at io.micronaut.context.DefaultBeanContext.getBeansOfType(DefaultBeanContext.java:1480)
	at io.micronaut.context.AbstractBeanResolutionContext.getBeansOfType(AbstractBeanResolutionContext.java:240)
	at io.micronaut.context.AbstractInitializableBeanDefinition.resolveBeansOfType(AbstractInitializableBeanDefinition.java:2226)
	at io.micronaut.context.AbstractInitializableBeanDefinition.getBeansOfTypeForConstructorArgument(AbstractInitializableBeanDefinition.java:1515)
	at io.micronaut.configuration.metrics.micrometer.$MeterRegistryFactory$CompositeMeterRegistry0$Definition.instantiate(Unknown Source)
	at io.micronaut.context.DefaultBeanContext.resolveByBeanFactory(DefaultBeanContext.java:2342)
	at io.micronaut.context.DefaultBeanContext.createRegistration(DefaultBeanContext.java:3162)
	at io.micronaut.context.SingletonScope.getOrCreate(SingletonScope.java:80)
	at io.micronaut.context.DefaultBeanContext.resolveBeanRegistration(DefaultBeanContext.java:3012)
	at io.micronaut.context.DefaultBeanContext.resolveBeanRegistration(DefaultBeanContext.java:2774)
	at io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:1786)
	at io.micronaut.context.AbstractBeanResolutionContext.getBean(AbstractBeanResolutionContext.java:210)
	at io.micronaut.context.AbstractInitializableBeanDefinition.resolveBean(AbstractInitializableBeanDefinition.java:2137)
	at io.micronaut.context.AbstractInitializableBeanDefinition.getBeanForField(AbstractInitializableBeanDefinition.java:1711)
	at com.example.$OtlpMeterRegistryDependencyDemoTest$Definition.inject(Unknown Source)
	at io.micronaut.context.DefaultBeanContext.doInjectAndInitialize(DefaultBeanContext.java:2679)
	at io.micronaut.context.DefaultBeanContext.inject(DefaultBeanContext.java:1014)
	at io.micronaut.test.extensions.AbstractMicronautExtension.beforeEach(AbstractMicronautExtension.java:476)
	at io.micronaut.test.extensions.junit5.MicronautJunit5Extension.beforeEach(MicronautJunit5Extension.java:236)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
Caused by: java.lang.NoClassDefFoundError: com/google/protobuf/RuntimeVersion$RuntimeDomain
	at io.opentelemetry.proto.resource.v1.Resource.<clinit>(Resource.java:21)
	at io.micrometer.registry.otlp.OtlpMeterRegistry.<init>(OtlpMeterRegistry.java:130)
	at io.micrometer.registry.otlp.OtlpMeterRegistry.<init>(OtlpMeterRegistry.java:119)
	at io.micrometer.registry.otlp.OtlpMeterRegistry.<init>(OtlpMeterRegistry.java:108)
	at io.micronaut.configuration.metrics.micrometer.otlp.OtlpMeterRegistryFactory.otlpMeterRegistry(OtlpMeterRegistryFactory.java:51)
	at io.micronaut.configuration.metrics.micrometer.otlp.$OtlpMeterRegistryFactory$OtlpMeterRegistry0$Definition.instantiate(Unknown Source)
	at io.micronaut.context.DefaultBeanContext.resolveByBeanFactory(DefaultBeanContext.java:2342)
	... 27 more
Caused by: java.lang.ClassNotFoundException: com.google.protobuf.RuntimeVersion$RuntimeDomain
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:580)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
	... 34 more

The problem is that OtlpMeterRegistry ultimately depends on com.google.protobuf.RuntimeVersion, which exists in protobuf-java 4.x, but not 3.x. The micronaut-parent POM sets the protobuf version to 3.25.8.

This has been a problem since Micronaut 4.9.0, which bumped from a transitive dependency on io.opentelemetry.proto:opentelemetry-proto:1.2.0-alpha to ...:1.5.0-alpha, which is what introduces the protobuf-java 4.x dependency.

Steps To Reproduce

  1. Create a new Micronaut project:
mn create-app --build=maven --jdk=21 --lang=java --test=junit com.example.otlp-meter-registry-dependency-demo
  1. Add io.micronaut.micrometer:micronaut-micrometer-registry-otlp as a dependency and add @Inject MeterRegistry meterRegistry to the auto-generated test class:
diff --git a/pom.xml b/pom.xml
index 83be2e2..0080cd2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -36,6 +36,10 @@
       <artifactId>micronaut-http-server-netty</artifactId>
       <scope>compile</scope>
     </dependency>
+    <dependency>
+      <groupId>io.micronaut.micrometer</groupId>
+      <artifactId>micronaut-micrometer-registry-otlp</artifactId>
+    </dependency>
     <dependency>
       <groupId>io.micronaut.serde</groupId>
       <artifactId>micronaut-serde-jackson</artifactId>
diff --git a/src/test/java/com/example/OtlpMeterRegistryDependencyDemoTest.java b/src/test/java/com/example/OtlpMeterRegistryDependencyDemoTest.java
index 9a4d6c2..78f3630 100644
--- a/src/test/java/com/example/OtlpMeterRegistryDependencyDemoTest.java
+++ b/src/test/java/com/example/OtlpMeterRegistryDependencyDemoTest.java
@@ -1,5 +1,6 @@
 package com.example;

+import io.micrometer.core.instrument.MeterRegistry;
 import io.micronaut.runtime.EmbeddedApplication;
 import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
 import org.junit.jupiter.api.Test;
@@ -13,6 +14,9 @@ class OtlpMeterRegistryDependencyDemoTest {
     @Inject
     EmbeddedApplication<?> application;

+    @Inject
+    MeterRegistry meterRegistry;
+
     @Test
     void testItWorks() {
         Assertions.assertTrue(application.isRunning());
  1. Run the test:
./mvnw clean test
...
[ERROR] Errors: 
[ERROR]   OtlpMeterRegistryDependencyDemoTest.testItWorks » BeanInstantiation Error instantiating bean of type  [io.micrometer.core.instrument.composite.CompositeMeterRegistry]

Message: com/google/protobuf/RuntimeVersion$RuntimeDomain
Path Taken:
c.e.OtlpMeterRegistryDependencyDemoTest#meterRegistry
\---> @i.m.c.a.Primary @j.i.Singleton i.m.c.i.c.CompositeMeterRegistry i.m.c.m.m.MeterRegistryFactory.compositeMeterRegistry#compositeMeterRegistry([List<MeterRegistry> registries], List<MeterRegistryConfigurer<MeterRegistry>> configurers)

Environment Information

Reproducible in all environments with Micronaut 4.9.0 or later.

Example Application

Please find an example application attached: otlp-meter-registry-dependency-demo.zip

…though the attached project is produced by creating a project with the CLI invocation given above and applying the patch included above.

Version

Micronaut 4.9.0 or later

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions