Keep otel4s context in sync with OpenTelemetry Java

Use this page when otel4s code and Java code in the same process need to observe the same current span.

This usually matters when:

OpenTelemetry Java SDK and otel4s use different context propagation mechanisms by default. OpenTelemetry Java uses ThreadLocal state, while otel4s uses Local. Without extra setup, those contexts do not stay aligned automatically.

1. Add the context-storage dependency

This page assumes you already depend on otel4s-oteljava from the JVM setup guide.

Add settings to build.sbt:

libraryDependencies ++= Seq(
  "org.typelevel" %% "otel4s-oteljava-context-storage" % "1.1.0" // <1>
)
javaOptions += "-Dcats.effect.trackFiberContext=true" // <2>

Add directives to the *.scala file:

//> using dep "org.typelevel::otel4s-oteljava-context-storage:1.1.0" // <1>
//> using javaOpt "-Dcats.effect.trackFiberContext=true" // <2>
  1. Add otel4s-oteljava-context-storage
  2. Enable Cats Effect fiber context tracking

2. Provide IOLocalContextStorage.localProvider[IO]

Create OtelJava with a LocalProvider[IO, Context] backed by IOLocalContextStorage.

import cats.effect.{IO, IOApp}
import io.opentelemetry.api.trace.{Span => JSpan}
import org.typelevel.otel4s.context.LocalProvider
import org.typelevel.otel4s.oteljava.OtelJava
import org.typelevel.otel4s.oteljava.context.Context
import org.typelevel.otel4s.oteljava.context.IOLocalContextStorage
import org.typelevel.otel4s.trace.Tracer

object Main extends IOApp.Simple {
  implicit val provider: LocalProvider[IO, Context] =
    IOLocalContextStorage.localProvider[IO]

  def run: IO[Unit] =
    OtelJava.autoConfigured[IO]().use { otel4s =>
      otel4s.tracerProvider.get("auth-service").flatMap { implicit tracer =>
        program
      }
    }

  def program(implicit tracer: Tracer[IO]): IO[Unit] =
    Tracer[IO].span("test").use { span =>
      IO.println(s"jctx: ${JSpan.current().getSpanContext}") *>
        IO.println(s"otel4s: ${span.context}")
    }
}

With that provider in place, the current Java context and the current otel4s span stay aligned while your IO program runs.

Example output:

jctx: SpanContext{traceId=58b8ed50a558ca53fcc64a0d80b5e662, spanId=fc25fe2c9fb41905, ...}
otel4s: SpanContext{traceId=58b8ed50a558ca53fcc64a0d80b5e662, spanId=fc25fe2c9fb41905, ...}

3. Use this setup before tracing across Java boundaries

Once the context storage is configured, you can:

This page covers the setup only. For handler, client, and library boundary patterns, see the tracing how-to pages.

4. Know the limitations

What's next