Hi Ktor team, what is the status of OpenTelemetry ...
# ktor
s
Hi Ktor team, what is the status of OpenTelemetry support for Ktor? I’m trying to configure
KtorServerTelemetry
in Ktor 3.0 using the official samples, but I’ve encountered the following error when using the plugin alongside zero-config instrumentation (the OTel Java agent)
The plugin correctly uses
GlobalOpenTelemetry.get()
, which the agent should set up. What’s the proper way to configure Ktor server/client telemetry when the OTel Java agent is being used?
Copy code
2025-05-14T15:01:03.532 PDT  INFO   [main]  Application - OTel Java agent is active
2025-05-14T15:01:03.551 PDT  ERROR  [main]  main - Failed to start App!: Please make sure that you use unique name for the plugin and don't install it twice. Conflicting application plugin is already installed with the same key as `OpenTelemetry`
io.ktor.server.application.DuplicatePluginException: Please make sure that you use unique name for the plugin and don't install it twice. Conflicting application plugin is already installed with the same key as `OpenTelemetry`
        at io.ktor.server.application.ApplicationPluginKt.install(ApplicationPlugin.kt:137) ~[ktor-server-core-jvm-3.1.3.jar:3.1.3]
        at dev.suresh.plugins.OTelKt.configureOTel(OTel.kt:14) ~[jvm-0.1.0.0+bfceb6a.jar:0.1.0.0+bfceb6a]
        at dev.suresh.AppKt.module(App.kt:26) ~[jvm-0.1.0.0+bfceb6a.jar:0.1.0.0+bfceb6a]
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:565) ~[na:na]
        at kotlin.reflect.jvm.internal.calls.CallerImpl$Method.callMethod(CallerImpl.kt:97) ~[kotlin-reflect-2.2.0-Beta2.jar:2.2.0-Beta2-release-112]
        at kotlin.reflect.jvm.internal.calls.CallerImpl$Method$Static.call(CallerImpl.kt:106) ~[kotlin-reflect-2.2.0-Beta2.jar:2.2.0-Beta2-release-112]
        at kotlin.reflect.jvm.internal.KCallableImpl.callDefaultMethod$kotlin_reflection(KCallableImpl.kt:207) ~[kotlin-reflect-2.2.0-Beta2.jar:2.2.0-Beta2-release-112]
        at kotlin.reflect.jvm.internal.KCallableImpl.callBy(KCallableImpl.kt:112) ~[kotlin-reflect-2.2.0-Beta2.jar:2.2.0-Beta2-release-112]
        at io.ktor.server.engine.internal.CallableUtilsKt.callFunctionWithInjection(CallableUtils.kt:127) ~[ktor-server-core-jvm-3.1.3.jar:3.1.3]
        at io.ktor.server.engine.internal.CallableUtilsKt.executeModuleFunction(CallableUtils.kt:40) ~[ktor-server-core-jvm-3.1.3.jar:3.1.3]
        at io.ktor.server.engine.EmbeddedServer.launchModuleByName$lambda$32(EmbeddedServerJvm.kt:422) ~[ktor-server-core-jvm-3.1.3.jar:3.1.3]
        at io.ktor.server.engine.EmbeddedServer.avoidingDoubleStartupFor(EmbeddedServerJvm.kt:446) ~[ktor-server-core-jvm-3.1.3.jar:3.1.3]
        at io.ktor.server.engine.EmbeddedServer.launchModuleByName(EmbeddedServerJvm.kt
Here is plugin config
Copy code
fun Application.configureOTel() {
  val otelSdk = otelSdk()
  install(KtorServerTelemetry) {
    setOpenTelemetry(otelSdk)
    spanKindExtractor {
      if (httpMethod == <http://HttpMethod.Post|HttpMethod.Post>) {
        SpanKind.PRODUCER
      } else {
        SpanKind.CLIENT
      }
    }

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

context(app: Application)
fun otelSdk(): OpenTelemetry  {
  val globalOtel = GlobalOpenTelemetry.get()
  val isAgentActive = globalOtel !== OpenTelemetry.noop()
  when(isAgentActive) {
      true -> <http://app.log.info|app.log.info>("OTel Java agent is active")
      else -> app.log.warn("OTel Java agent is inactive!!")
  }
  return globalOtel
}
Env
Copy code
Ktor: 3.1.3
agent: 2.15.0
io.opentelemetry.instrumentation:opentelemetry-ktor-3.0:2.15.0-alpha
a
Is the
DuplicatePluginException
thrown on the server startup? Can you share the steps to reproduce the exception?
p
The recent updates to otel agent now inject the plugin automatically, so you shouldn't need to do it yourself. If you do want to manually configure it you'll need to disable the instrumentation in otel config with
OTEL_INSTRUMENTATION_KTOR_ENABLED=false
environment variable
🙏 2
You'll still get the
GlobalOpenTelemetry
configured as part of the agent instrumentation but it won't inject the ktor plugins (you'll need to install the appropriate plugin in your clients too should you want/need to)
s
@phldavies Thanks very much. That’s what I figured out. Wish Ktor had better documentation for OpenTelemetry with a recommended approach.