Micrometer added a new MeterBinder for virtual thread metrics via https://github.com/micrometer-metrics/micrometer/pull/5067 - documentation is at https://docs.micrometer.io/micrometer/reference/reference/jvm.html#_java_21_metrics. It is in a new module micrometer-java21. It would be nice if Spring Boot has auto-configuration for VirtualThreadMetrics.
Comment From: nosan
class file has wrong version 65.0, should be 61.0 Please remove or make sure it appears in the correct subdirectory of the classpath.
micrometer-java21 is built using JDK21, whereas Spring Boot source code is JDK17 source compatible, hence either reflection or multi-release jar should be used to add support for VirtualThreadMetrics.
I've prototyped changes in my branch by using reflection and FactoryBean.
I also checked VirtualThreadMetrics with a native-image, and unfortunately, I got the following exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'virtualThreadMetrics': Instantiation of supplied bean failed
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1239)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1176)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:336)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:288)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:334)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1122)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1093)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1030)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:318)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350)
at task.gh43122.Gh43122Application.main(Gh43122Application.java:12)
at java.base@21.0.5/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH)
Caused by: java.lang.UnsatisfiedLinkError: jdk.jfr.internal.JVM.getPid()Ljava/lang/String; [symbol: Java_jdk_jfr_internal_JVM_getPid or Java_jdk_jfr_internal_JVM_getPid__]
at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.access.JNINativeLinkage.getOrFindEntryPoint(JNINativeLinkage.java:152)
at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.JNIGeneratedMethodSupport.nativeCallAddress(JNIGeneratedMethodSupport.java:54)
at jdk.jfr@21.0.5/jdk.jfr.internal.JVM.getPid(Native Method)
at jdk.jfr@21.0.5/jdk.jfr.internal.Repository.createRepository(Repository.java:110)
at jdk.jfr@21.0.5/jdk.jfr.internal.Repository.setBasePath(Repository.java:65)
at jdk.jfr@21.0.5/jdk.jfr.internal.Repository.ensureRepository(Repository.java:79)
at jdk.jfr@21.0.5/jdk.jfr.internal.PlatformRecorder.<init>(PlatformRecorder.java:81)
at jdk.jfr@21.0.5/jdk.jfr.FlightRecorder.getFlightRecorder(FlightRecorder.java:171)
at jdk.jfr@21.0.5/jdk.jfr.Recording.<init>(Recording.java:107)
at jdk.jfr@21.0.5/jdk.jfr.Recording.<init>(Recording.java:131)
at jdk.jfr@21.0.5/jdk.jfr.consumer.RecordingStream.<init>(RecordingStream.java:108)
at jdk.jfr@21.0.5/jdk.jfr.consumer.RecordingStream.<init>(RecordingStream.java:101)
at io.micrometer.java21.instrument.binder.jdk.VirtualThreadMetrics.createRecordingStream(VirtualThreadMetrics.java:78)
at io.micrometer.java21.instrument.binder.jdk.VirtualThreadMetrics.<init>(VirtualThreadMetrics.java:57)
at io.micrometer.java21.instrument.binder.jdk.VirtualThreadMetrics.<init>(VirtualThreadMetrics.java:49)
at task.gh43122.Gh43122Application.virtualThreadMetrics(Gh43122Application.java:17)
at task.gh43122.Gh43122Application$$SpringCGLIB$$0.CGLIB$virtualThreadMetrics$0(<generated>)
at task.gh43122.Gh43122Application$$SpringCGLIB$$FastClass$$1.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:348)
at task.gh43122.Gh43122Application$$SpringCGLIB$$0.virtualThreadMetrics(<generated>)
at task.gh43122.Gh43122Application__BeanDefinitions.lambda$getVirtualThreadMetricsInstanceSupplier$0(Gh43122Application__BeanDefinitions.java:32)
at org.springframework.util.function.ThrowingFunction.apply(ThrowingFunction.java:63)
at org.springframework.util.function.ThrowingFunction.apply(ThrowingFunction.java:51)
at org.springframework.beans.factory.aot.BeanInstanceSupplier.lambda$get$1(BeanInstanceSupplier.java:219)
at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58)
at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiateWithFactoryMethod(SimpleInstantiationStrategy.java:88)
at org.springframework.beans.factory.aot.BeanInstanceSupplier.invokeBeanSupplier(BeanInstanceSupplier.java:256)
at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:219)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.obtainInstanceFromSupplier(DefaultListableBeanFactory.java:979)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1233)
... 20 more
Sample to reproduce: gh-43122.zip
mvn clean spring-boot:build-image -Pnative
docker run -p 8080:8080 docker.io/library/gh-43122:0.0.1-SNAPSHOT
Comment From: oleksiizaitsev7
Hi it can be done with some conditions as we have here. But I'm not sure if that is the right way or preferable strategy for the java 21 support
Comment From: nosan
Thanks for the suggestion, @oleksiizaitsev7
The problem is that using the VirtualThreadMetrics class results in a compile error, so adding @EnabledForJreRange won't resolve the issue.
Comment From: mhalbritter
Superseded by #43852.