Skip to content

Ktor 3 Sentry OpenTelemetry Agentless ClassNotFoundException #4345

Open

Description

@Ynnck123

Environment

SaaS (https://sentry.io/)

Steps to Reproduce

Potentially relevant used plugin versions:

Ktor Server version 3.1.2
Kotlin 2.1.20
Sentry jvm gradle plugin 5.3.0

I added the following to my dependencies { .. } block in my build.gradle.kts

    implementation(platform("io.sentry:sentry-bom:8.8.0"))
    implementation("io.sentry:sentry")
    implementation("io.sentry:sentry-opentelemetry-agentless")

    implementation("io.opentelemetry.instrumentation:opentelemetry-ktor-3.0:2.15.0-alpha")

I start sentry in my Ktor application and then configure the ktor 3 plugin in the following way:

fun Application.configureMonitoring() {

        install(KtorServerTelemetry) {
            val openTelemetry = createOpenTelemetry()
            setOpenTelemetry(openTelemetry)

            attributesExtractor {
                onStart {
                    attributes.put("start-time", Clock.System.now().toEpochMilliseconds())
                }
                onEnd {
                    attributes.put("end-time", Clock.System.now().toEpochMilliseconds())
                }
            }
        }
}

private fun createOpenTelemetry(): OpenTelemetry =
    AutoConfiguredOpenTelemetrySdk.builder().addResourceCustomizer { oldResource, _ ->
        oldResource.toBuilder()
            .putAll(oldResource.attributes)
            .put(ServiceAttributes.SERVICE_NAME, "my-app-backend")
            .build()
    }
        .build()
        .openTelemetrySdk

This was what I came up with based on this Ktor opentelemetry example and the Sentry Agentless Opentelemetry docs.

Expected Result

The app starts and reports the traces to Sentry cloud.

Actual Result

My application doesn't come up, I see the following crash in my logs:

java.lang.ClassNotFoundException: io.opentelemetry.api.incubator.events.EventLoggerProvider
at jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:521)
at io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk.builder(AutoConfiguredOpenTelemetrySdk.java:44)
at io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk.initialize(AutoConfiguredOpenTelemetrySdk.java:36)
at myktorapp.com.plugins.MonitoringKt.configureMonitoring$lambda$3(Monitoring.kt:28)
at io.ktor.server.application.CreatePluginUtilsKt.createRouteScopedPluginInstance(CreatePluginUtils.kt:277)
at io.ktor.server.application.CreatePluginUtilsKt.access$createRouteScopedPluginInstance(CreatePluginUtils.kt:1)
at io.ktor.server.application.RouteScopedPluginImpl.install(CreatePluginUtils.kt:182)
at io.ktor.server.application.RouteScopedPluginImpl.install(CreatePluginUtils.kt:164)
at io.ktor.server.application.ApplicationPluginKt.install(ApplicationPlugin.kt:121)
at myktorapp.com.plugins.MonitoringKt.configureMonitoring(Monitoring.kt:26)
at myktorapp.com.ApplicationKt.module(Application.kt:51)
        at myktorapp.com.ApplicationKt.module$default(Application.kt:43)
        at myktorapp.com.ApplicationKt.initialize(Application.kt:30)
        at jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
        at java.lang.reflect.Method.invoke(Method.java:578)
        at kotlin.reflect.jvm.internal.calls.CallerImpl$Method.callMethod(CallerImpl.kt:97)
        at kotlin.reflect.jvm.internal.calls.CallerImpl$Method$Static.call(CallerImpl.kt:106)
        at kotlin.reflect.jvm.internal.KCallableImpl.callDefaultMethod$kotlin_reflection(KCallableImpl.kt:207)
        at kotlin.reflect.jvm.internal.KCallableImpl.callBy(KCallableImpl.kt:112)
        at io.ktor.server.engine.internal.CallableUtilsKt.callFunctionWithInjection(CallableUtils.kt:127)
        at io.ktor.server.engine.internal.CallableUtilsKt.executeModuleFunction(CallableUtils.kt:40)
        at io.ktor.server.engine.EmbeddedServer.launchModuleByName$lambda$32(EmbeddedServerJvm.kt:422)
        at io.ktor.server.engine.EmbeddedServer.avoidingDoubleStartupFor(EmbeddedServerJvm.kt:446)
        at io.ktor.server.engine.EmbeddedServer.launchModuleByName(EmbeddedServerJvm.kt:421)
        at io.ktor.server.engine.EmbeddedServer.dynamicModule$lambda$26(EmbeddedServerJvm.kt:381)
        at io.ktor.server.engine.EmbeddedServer.instantiateAndConfigureApplication$lambda$25(EmbeddedServerJvm.kt:371)
        at io.ktor.server.engine.EmbeddedServer.avoidingDoubleStartup(EmbeddedServerJvm.kt:428)
        at io.ktor.server.engine.EmbeddedServer.instantiateAndConfigureApplication(EmbeddedServerJvm.kt:370)
        at io.ktor.server.engine.EmbeddedServer.createApplication(EmbeddedServerJvm.kt:171)
        at io.ktor.server.engine.EmbeddedServer.start(EmbeddedServerJvm.kt:301)
        at io.ktor.server.netty.EngineMain.main(EngineMain.kt:25)
        at myktorapp.com.ApplicationKt.main(Application.kt:17)
        java.lang.NoClassDefFoundError: io/opentelemetry/api/incubator/events/EventLoggerProvider
        at io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk.builder(AutoConfiguredOpenTelemetrySdk.java:44)
        at io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk.initialize(AutoConfiguredOpenTelemetrySdk.java:36)
        at myktorapp.com.plugins.MonitoringKt.configureMonitoring$lambda$3(Monitoring.kt:28)
        at io.ktor.server.application.CreatePluginUtilsKt.createRouteScopedPluginInstance(CreatePluginUtils.kt:277)
        at io.ktor.server.application.CreatePluginUtilsKt.access$createRouteScopedPluginInstance(CreatePluginUtils.kt:1)
        at io.ktor.server.application.RouteScopedPluginImpl.install(CreatePluginUtils.kt:182)
        at io.ktor.server.application.RouteScopedPluginImpl.install(CreatePluginUtils.kt:164)
        at io.ktor.server.application.ApplicationPluginKt.install(ApplicationPlugin.kt:121)
        at myktorapp.com.plugins.MonitoringKt.configureMonitoring(Monitoring.kt:26)
        at myktorapp.com.ApplicationKt.module(Application.kt:51)
        at myktorapp.com.ApplicationKt.module$default(Application.kt:43)
        at myktorapp.com.ApplicationKt.initialize(Application.kt:30)
        at jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
        at java.lang.reflect.Method.invoke(Method.java:578)
        at kotlin.reflect.jvm.internal.calls.CallerImpl$Method.callMethod(CallerImpl.kt:97)
        at kotlin.reflect.jvm.internal.calls.CallerImpl$Method$Static.call(CallerImpl.kt:106)
        at kotlin.reflect.jvm.internal.KCallableImpl.callDefaultMethod$kotlin_reflection(KCallableImpl.kt:207)
        at kotlin.reflect.jvm.internal.KCallableImpl.callBy(KCallableImpl.kt:112)
        at io.ktor.server.engine.internal.CallableUtilsKt.callFunctionWithInjection(CallableUtils.kt:127)
        at io.ktor.server.engine.internal.CallableUtilsKt.executeModuleFunction(CallableUtils.kt:40)
        at io.ktor.server.engine.EmbeddedServer.launchModuleByName$lambda$32(EmbeddedServerJvm.kt:422)
        at io.ktor.server.engine.EmbeddedServer.avoidingDoubleStartupFor(EmbeddedServerJvm.kt:446)
        at io.ktor.server.engine.EmbeddedServer.launchModuleByName(EmbeddedServerJvm.kt:421)
        at io.ktor.server.engine.EmbeddedServer.dynamicModule$lambda$26(EmbeddedServerJvm.kt:381)
        at io.ktor.server.engine.EmbeddedServer.instantiateAndConfigureApplication$lambda$25(EmbeddedServerJvm.kt:371)
        at io.ktor.server.engine.EmbeddedServer.avoidingDoubleStartup(EmbeddedServerJvm.kt:428)
        at io.ktor.server.engine.EmbeddedServer.instantiateAndConfigureApplication(EmbeddedServerJvm.kt:370)
        at io.ktor.server.engine.EmbeddedServer.createApplication(EmbeddedServerJvm.kt:171)
        at io.ktor.server.engine.EmbeddedServer.start(EmbeddedServerJvm.kt:301)
        at io.ktor.server.netty.EngineMain.main(EngineMain.kt:25)
        at myktorapp.com.ApplicationKt.main(Application.kt:17)

Product Area

Performance

Link

No response

DSN

No response

Version

No response

Activity

  1. getsantry commented on Apr 17, 2025

    @getsantry

    Assigning to @getsentry/support for routing ⏲️

  2. moved this to Waiting for: Support in GitHub Issues with 👀 3on Apr 17, 2025
  3. Ynnck123 commented on Apr 17, 2025

    @Ynnck123
    Author

    An addition, the issue seems to be based on the Ktor-3 OTel plugin io.opentelemetry.instrumentation:opentelemetry-ktor-3 which seems to introduce an version inconsistency in combination with the setup mentioned.

  4. moved this from Waiting for: Support to Waiting for: Product Owner in GitHub Issues with 👀 3on Apr 19, 2025
  5. adinauer commented on Apr 22, 2025

    @adinauer
    Member

    Hello @Ynnck123 can you please retry with implementation("io.opentelemetry.instrumentation:opentelemetry-ktor-3.0:2.10.0-alpha")?

    If you need 2.15.0 then we'd have to bump the OpenTelemetry version that Sentry uses.

  6. moved this from Waiting for: Product Owner to No status in GitHub Issues with 👀 3on Apr 22, 2025
  7. Ynnck123 commented on Apr 28, 2025

    @Ynnck123
    Author

    @adinauer thank you very much, that indeed worked now!

    If anyone else stumbles up this problem, in comparison to my initial code-snippet which was based on v2.15.0-alpha, I slightly had to adopt the syntax for the plugin installation for v2.10.0-alpha like so :

    fun Application.configureMonitoring() {
    
            install(KtorServerTracing) {
                setOpenTelemetry(createOpenTelemetry())
    
                attributeExtractor {
                    onStart {
                        attributes.put("start-time", Clock.System.now().toEpochMilliseconds())
                    }
                    onEnd {
                        attributes.put("end-time", Clock.System.now().toEpochMilliseconds())
                    }
                }
            }

    However, while this solves my problem for now, maybe its possible to provide a compatibility matrix somewhere?

  8. moved this to Waiting for: Product Owner in GitHub Issues with 👀 3on Apr 28, 2025
  9. adinauer commented on Apr 29, 2025

    @adinauer
    Member

    Nice! It's always the same version that Sentry SDK uses which should be used when manually adding OpenTelemetry dependencies. Unfortunately there's multiple versions, depending on what you're adding. Until we come up with a better way to surface the versions, you can take a look at

    object OpenTelemetry {
    val otelVersion = "1.44.1"
    val otelAlphaVersion = "$otelVersion-alpha"
    val otelInstrumentationVersion = "2.10.0"
    val otelInstrumentationAlphaVersion = "$otelInstrumentationVersion-alpha"
    val otelSemanticConvetionsVersion = "1.28.0-alpha" // check https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/dependencyManagement/build.gradle.kts#L49 for release version above to find a compatible version
    val otelSdk = "io.opentelemetry:opentelemetry-sdk:$otelVersion"
    val otelSemconv = "io.opentelemetry.semconv:opentelemetry-semconv:$otelSemanticConvetionsVersion"
    val otelSemconvIncubating = "io.opentelemetry.semconv:opentelemetry-semconv-incubating:$otelSemanticConvetionsVersion"
    val otelJavaAgent = "io.opentelemetry.javaagent:opentelemetry-javaagent:$otelInstrumentationVersion"
    val otelJavaAgentExtensionApi = "io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api:$otelInstrumentationAlphaVersion"
    val otelJavaAgentTooling = "io.opentelemetry.javaagent:opentelemetry-javaagent-tooling:$otelInstrumentationAlphaVersion"
    val otelExtensionAutoconfigureSpi = "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:$otelVersion"
    val otelExtensionAutoconfigure = "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:$otelVersion"
    val otelInstrumentationBom = "io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom:$otelInstrumentationVersion"
    }
    for the version of Sentry you're using - sorry for the inconvenience.

  10. moved this from Waiting for: Product Owner to No status in GitHub Issues with 👀 3on Apr 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions