Skip to content

Commit

Permalink
Merge pull request #214 from typelevel/context-storage
Browse files Browse the repository at this point in the history
Add `IOLocalContextStorage`
  • Loading branch information
iRevive authored Dec 31, 2024
2 parents e050e47 + 5cd592f commit 6783b22
Show file tree
Hide file tree
Showing 12 changed files with 577 additions and 8 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,11 @@ jobs:

- name: Make target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
run: mkdir -p semconv/stable/.jvm/target oteljava/metrics/target instrumentation/metrics/js/target sdk-exporter/common/.js/target sdk/common/native/target sdk/common/js/target core/trace/.js/target semconv/metrics/stable/.jvm/target semconv/metrics/experimental/.jvm/target semconv/metrics/stable/.native/target sdk-exporter/all/.jvm/target sdk-exporter/prometheus/.js/target semconv/experimental/.js/target sdk/trace/.js/target core/common/.jvm/target oteljava/common-testkit/target sdk/metrics/.native/target sdk-exporter/metrics/.jvm/target sdk-exporter/trace/.jvm/target unidocs/target sdk-contrib/aws/resource/.jvm/target oteljava/trace-testkit/target core/metrics/.native/target core/all/.native/target sdk/trace-testkit/.jvm/target sdk/trace-testkit/.native/target sdk/testkit/.native/target sdk-exporter/prometheus/.jvm/target sdk-contrib/aws/resource/.js/target semconv/experimental/.native/target core/metrics/.jvm/target core/all/.js/target sdk-exporter/proto/.jvm/target sdk-exporter/proto/.js/target sdk-exporter/metrics/.js/target semconv/stable/.native/target sdk/all/.native/target sdk/metrics-testkit/.js/target sdk-contrib/aws/xray-propagator/.native/target core/metrics/.js/target sdk/testkit/.js/target core/all/.jvm/target sdk/common/jvm/target core/trace/.native/target oteljava/metrics-testkit/target instrumentation/metrics/jvm/target sdk/trace/.native/target semconv/experimental/.jvm/target sdk/metrics-testkit/.native/target sdk/metrics/.jvm/target oteljava/common/target scalafix/rules/target sdk-exporter/proto/.native/target core/trace/.jvm/target sdk-exporter/common/.jvm/target sdk/metrics-testkit/.jvm/target sdk/metrics/.js/target sdk-exporter/trace/.js/target core/common/.native/target sdk/trace-testkit/.js/target core/common/.js/target oteljava/trace/target semconv/metrics/experimental/.native/target oteljava/testkit/target sdk/testkit/.jvm/target sdk-exporter/all/.js/target sdk-contrib/aws/xray/.native/target sdk-contrib/aws/xray/.js/target sdk-contrib/aws/xray-propagator/.js/target semconv/metrics/experimental/.js/target semconv/metrics/stable/.js/target instrumentation/metrics/native/target sdk/all/.js/target sdk/all/.jvm/target sdk-exporter/all/.native/target oteljava/all/target sdk/trace/.jvm/target sdk-contrib/aws/xray-propagator/.jvm/target semconv/stable/.js/target sdk-contrib/aws/xray/.jvm/target project/target
run: mkdir -p semconv/stable/.jvm/target oteljava/metrics/target oteljava/context-storage/target instrumentation/metrics/js/target sdk/common/native/target core/trace/.js/target semconv/metrics/stable/.jvm/target semconv/metrics/experimental/.jvm/target semconv/metrics/stable/.native/target sdk-exporter/all/.jvm/target semconv/experimental/.js/target core/common/.jvm/target oteljava/common-testkit/target sdk/metrics/.native/target sdk-exporter/metrics/.jvm/target sdk-exporter/trace/.jvm/target unidocs/target sdk-contrib/aws/resource/.jvm/target oteljava/trace-testkit/target core/metrics/.native/target core/all/.native/target sdk/trace-testkit/.jvm/target sdk/trace-testkit/.native/target sdk/testkit/.native/target sdk-exporter/prometheus/.jvm/target semconv/experimental/.native/target core/metrics/.jvm/target core/all/.js/target sdk-exporter/proto/.jvm/target sdk-exporter/proto/.js/target semconv/stable/.native/target sdk/all/.native/target sdk/metrics-testkit/.js/target sdk-contrib/aws/xray-propagator/.native/target core/metrics/.js/target sdk/testkit/.js/target core/all/.jvm/target sdk/common/jvm/target core/trace/.native/target oteljava/metrics-testkit/target instrumentation/metrics/jvm/target sdk/trace/.native/target semconv/experimental/.jvm/target sdk/metrics-testkit/.native/target sdk/metrics/.jvm/target oteljava/common/target scalafix/rules/target sdk-exporter/proto/.native/target core/trace/.jvm/target sdk-exporter/common/.jvm/target sdk/metrics-testkit/.jvm/target core/common/.native/target sdk/trace-testkit/.js/target core/common/.js/target oteljava/trace/target semconv/metrics/experimental/.native/target oteljava/testkit/target sdk/testkit/.jvm/target sdk-exporter/all/.js/target sdk-contrib/aws/xray/.native/target semconv/metrics/experimental/.js/target semconv/metrics/stable/.js/target instrumentation/metrics/native/target sdk/all/.jvm/target sdk-exporter/all/.native/target oteljava/all/target sdk/trace/.jvm/target sdk-contrib/aws/xray-propagator/.jvm/target semconv/stable/.js/target sdk-contrib/aws/xray/.jvm/target project/target

- name: Compress target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
run: tar cf targets.tar semconv/stable/.jvm/target oteljava/metrics/target instrumentation/metrics/js/target sdk-exporter/common/.js/target sdk/common/native/target sdk/common/js/target core/trace/.js/target semconv/metrics/stable/.jvm/target semconv/metrics/experimental/.jvm/target semconv/metrics/stable/.native/target sdk-exporter/all/.jvm/target sdk-exporter/prometheus/.js/target semconv/experimental/.js/target sdk/trace/.js/target core/common/.jvm/target oteljava/common-testkit/target sdk/metrics/.native/target sdk-exporter/metrics/.jvm/target sdk-exporter/trace/.jvm/target unidocs/target sdk-contrib/aws/resource/.jvm/target oteljava/trace-testkit/target core/metrics/.native/target core/all/.native/target sdk/trace-testkit/.jvm/target sdk/trace-testkit/.native/target sdk/testkit/.native/target sdk-exporter/prometheus/.jvm/target sdk-contrib/aws/resource/.js/target semconv/experimental/.native/target core/metrics/.jvm/target core/all/.js/target sdk-exporter/proto/.jvm/target sdk-exporter/proto/.js/target sdk-exporter/metrics/.js/target semconv/stable/.native/target sdk/all/.native/target sdk/metrics-testkit/.js/target sdk-contrib/aws/xray-propagator/.native/target core/metrics/.js/target sdk/testkit/.js/target core/all/.jvm/target sdk/common/jvm/target core/trace/.native/target oteljava/metrics-testkit/target instrumentation/metrics/jvm/target sdk/trace/.native/target semconv/experimental/.jvm/target sdk/metrics-testkit/.native/target sdk/metrics/.jvm/target oteljava/common/target scalafix/rules/target sdk-exporter/proto/.native/target core/trace/.jvm/target sdk-exporter/common/.jvm/target sdk/metrics-testkit/.jvm/target sdk/metrics/.js/target sdk-exporter/trace/.js/target core/common/.native/target sdk/trace-testkit/.js/target core/common/.js/target oteljava/trace/target semconv/metrics/experimental/.native/target oteljava/testkit/target sdk/testkit/.jvm/target sdk-exporter/all/.js/target sdk-contrib/aws/xray/.native/target sdk-contrib/aws/xray/.js/target sdk-contrib/aws/xray-propagator/.js/target semconv/metrics/experimental/.js/target semconv/metrics/stable/.js/target instrumentation/metrics/native/target sdk/all/.js/target sdk/all/.jvm/target sdk-exporter/all/.native/target oteljava/all/target sdk/trace/.jvm/target sdk-contrib/aws/xray-propagator/.jvm/target semconv/stable/.js/target sdk-contrib/aws/xray/.jvm/target project/target
run: tar cf targets.tar semconv/stable/.jvm/target oteljava/metrics/target oteljava/context-storage/target instrumentation/metrics/js/target sdk/common/native/target core/trace/.js/target semconv/metrics/stable/.jvm/target semconv/metrics/experimental/.jvm/target semconv/metrics/stable/.native/target sdk-exporter/all/.jvm/target semconv/experimental/.js/target core/common/.jvm/target oteljava/common-testkit/target sdk/metrics/.native/target sdk-exporter/metrics/.jvm/target sdk-exporter/trace/.jvm/target unidocs/target sdk-contrib/aws/resource/.jvm/target oteljava/trace-testkit/target core/metrics/.native/target core/all/.native/target sdk/trace-testkit/.jvm/target sdk/trace-testkit/.native/target sdk/testkit/.native/target sdk-exporter/prometheus/.jvm/target semconv/experimental/.native/target core/metrics/.jvm/target core/all/.js/target sdk-exporter/proto/.jvm/target sdk-exporter/proto/.js/target semconv/stable/.native/target sdk/all/.native/target sdk/metrics-testkit/.js/target sdk-contrib/aws/xray-propagator/.native/target core/metrics/.js/target sdk/testkit/.js/target core/all/.jvm/target sdk/common/jvm/target core/trace/.native/target oteljava/metrics-testkit/target instrumentation/metrics/jvm/target sdk/trace/.native/target semconv/experimental/.jvm/target sdk/metrics-testkit/.native/target sdk/metrics/.jvm/target oteljava/common/target scalafix/rules/target sdk-exporter/proto/.native/target core/trace/.jvm/target sdk-exporter/common/.jvm/target sdk/metrics-testkit/.jvm/target core/common/.native/target sdk/trace-testkit/.js/target core/common/.js/target oteljava/trace/target semconv/metrics/experimental/.native/target oteljava/testkit/target sdk/testkit/.jvm/target sdk-exporter/all/.js/target sdk-contrib/aws/xray/.native/target semconv/metrics/experimental/.js/target semconv/metrics/stable/.js/target instrumentation/metrics/native/target sdk/all/.jvm/target sdk-exporter/all/.native/target oteljava/all/target sdk/trace/.jvm/target sdk-contrib/aws/xray-propagator/.jvm/target semconv/stable/.js/target sdk-contrib/aws/xray/.jvm/target project/target

- name: Upload target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
Expand Down
29 changes: 26 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ lazy val scalaJSLinkerSettings = Def.settings(
scalaJSLinkerConfig ~= (_.withESFeatures(
_.withESVersion(org.scalajs.linker.interface.ESVersion.ES2018)
)),
Test / scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule))
Test / scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule)),
// the JS artifacts could be quite large and exceed the CI disk space limit
githubWorkflowArtifactUpload := false
)

lazy val scalaNativeSettings = Def.settings(
Expand Down Expand Up @@ -154,7 +156,7 @@ lazy val root = tlCrossRootProject
`oteljava-trace`,
`oteljava-trace-testkit`,
`oteljava-testkit`,
oteljava,
`oteljava-context-storage`,
`semconv-stable`,
`semconv-experimental`,
`semconv-metrics-stable`,
Expand Down Expand Up @@ -722,6 +724,22 @@ lazy val `oteljava-testkit` = project
)
.settings(scalafixSettings)

lazy val `oteljava-context-storage` = project
.in(file("oteljava/context-storage"))
.dependsOn(`oteljava-common`)
.settings(munitDependencies)
.settings(
name := "otel4s-oteljava-context-storage",
libraryDependencies ++= Seq(
"org.typelevel" %%% "cats-effect-testkit" % CatsEffectVersion % Test,
),
Test / javaOptions ++= Seq(
"-Dcats.effect.trackFiberContext=true",
),
Test / fork := true,
)
.settings(scalafixSettings)

lazy val oteljava = project
.in(file("oteljava/all"))
.dependsOn(
Expand Down Expand Up @@ -854,7 +872,7 @@ lazy val benchmarks = project
lazy val examples = project
.enablePlugins(NoPublishPlugin, JavaAgent)
.in(file("examples"))
.dependsOn(core.jvm, oteljava, sdk.jvm, `sdk-exporter`.jvm, `sdk-exporter-prometheus`.jvm)
.dependsOn(core.jvm, oteljava, `oteljava-context-storage`, sdk.jvm, `sdk-exporter`.jvm, `sdk-exporter-prometheus`.jvm)
.settings(
name := "otel4s-examples",
libraryDependencies ++= Seq(
Expand All @@ -869,6 +887,7 @@ lazy val examples = project
javaAgents += "io.opentelemetry.javaagent" % "opentelemetry-javaagent" % OpenTelemetryInstrumentationVersion % Runtime,
run / fork := true,
javaOptions += "-Dotel.java.global-autoconfigure.enabled=true",
javaOptions += "-Dcats.effect.trackFiberContext=true",
envVars ++= Map(
"OTEL_PROPAGATORS" -> "b3multi",
"OTEL_SERVICE_NAME" -> "Trace Example"
Expand All @@ -881,6 +900,7 @@ lazy val docs = project
.enablePlugins(TypelevelSitePlugin)
.dependsOn(
oteljava,
`oteljava-context-storage`,
`oteljava-testkit`,
`instrumentation-metrics`.jvm,
sdk.jvm,
Expand All @@ -905,6 +925,8 @@ lazy val docs = project
"OPEN_TELEMETRY_VERSION" -> OpenTelemetryVersion,
"OPEN_TELEMETRY_INSTRUMENTATION_ALPHA_VERSION" -> OpenTelemetryInstrumentationAlphaVersion
),
run / fork := true,
javaOptions += "-Dcats.effect.trackFiberContext=true",
laikaConfig := {
import laika.config.{ChoiceConfig, Selections, SelectionConfig}

Expand Down Expand Up @@ -975,6 +997,7 @@ lazy val unidocs = project
`oteljava-trace`,
`oteljava-trace-testkit`,
`oteljava-testkit`,
`oteljava-context-storage`,
oteljava,
`semconv-stable`.jvm,
`semconv-experimental`.jvm,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ object LocalProvider extends LocalProviderLowPriority {

/** Cats Effect 3.6 introduced `IOLocal#asLocal`. However, we need a variation for a polymorphic type.
*/
private def localForIOLocal[F[_]: MonadCancelThrow: LiftIO, Ctx](ioLocal: IOLocal[Ctx]): Local[F, Ctx] =
private[otel4s] def localForIOLocal[F[_]: MonadCancelThrow: LiftIO, Ctx](ioLocal: IOLocal[Ctx]): Local[F, Ctx] =
new Local[F, Ctx] {
def applicative: Applicative[F] =
Applicative[F]
Expand Down
1 change: 1 addition & 0 deletions docs/oteljava/directory.conf
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ laika.title = OtelJava
laika.navigationOrder = [
overview.md
metrics-jvm-runtime.md
tracing-context-propagation.md
tracing-java-interop.md
testkit.md
]
85 changes: 85 additions & 0 deletions docs/oteljava/tracing-context-propagation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Tracing | Context propagation

[OpenTelemetry Java SDK][opentelemetry-java] and otel4s rely on different context manipulation approaches,
which aren't interoperable out of the box.
Java SDK utilizes ThreadLocal variables to share tracing information,
otel4s, on the other hand, uses [Local][cats-mtl-local].

Cats Effect 3.6.0 introduced a new method of fiber context tracking,
which can be integrated almost seamlessly with the OpenTelemetry Java SDK.

## Getting started

@:select(build-tool)

@:choice(sbt)

Add settings to the `build.sbt`:

```scala
libraryDependencies ++= Seq(
"org.typelevel" %% "otel4s-oteljava" % "@VERSION@", // <1>
"org.typelevel" %% "otel4s-oteljava-context-storage" % "@VERSION@", // <2>
)
javaOptions += "-Dcats.effect.trackFiberContext=true" // <3>
```

@:choice(scala-cli)

Add directives to the `*.scala` file:

```scala
//> using dep "org.typelevel::otel4s-oteljava:@VERSION@" // <1>
//> using dep "org.typelevel::otel4s-oteljava-context-storage:@VERSION@" // <2>
//> using `java-opt` "-Dcats.effect.trackFiberContext=true" // <3>
```

@:@

1. Add the `otel4s-oteljava` library
2. Add the `otel4s-oteljava-context-storage` library
3. Enable Cats Effect fiber context tracking

## Configuration

You need to use `IOLocalContextStorage.localProvider[IO]` to provide the global context storage, backed by `IOLocal`:
```scala mdoc:silent
import cats.effect.IO
import io.opentelemetry.api.trace.{Span => JSpan}
import org.typelevel.otel4s.context.LocalProvider
import org.typelevel.otel4s.oteljava.IOLocalContextStorage
import org.typelevel.otel4s.oteljava.OtelJava
import org.typelevel.otel4s.oteljava.context.Context
import org.typelevel.otel4s.trace.Tracer

def program(tracer: Tracer[IO]): IO[Unit] =
tracer.span("test").use { span => // start 'test' span using otel4s
println(s"jctx : ${JSpan.current().getSpanContext}") // get a span from a ThreadLocal var
IO.println(s"otel4s: ${span.context}")
}

def run: IO[Unit] = {
implicit val provider: LocalProvider[IO, Context] =
IOLocalContextStorage.localProvider[IO]

OtelJava.autoConfigured[IO]().use { otelJava =>
otelJava.tracerProvider.tracer("com.service").get.flatMap { tracer =>
program(tracer)
}
}
}
```

According to the output, the context is the same:
```
jctx : SpanContext{traceId=58b8ed50a558ca53fcc64a0d80b5e662, spanId=fc25fe2c9fb41905, ...}
otel4s: SpanContext{traceId=58b8ed50a558ca53fcc64a0d80b5e662, spanId=fc25fe2c9fb41905, ...}
```

## Limitations

The `IOLocalContextStorageProvider` doesn't work with [OpenTelemetry Java Agent][opentelemetry-java-agent].

[opentelemetry-java]: https://github.com/open-telemetry/opentelemetry-java
[opentelemetry-java-agent]: https://opentelemetry.io/docs/zero-code/java/agent/
[cats-mtl-local]: https://typelevel.org/cats-mtl/mtl-classes/local.html
39 changes: 39 additions & 0 deletions examples/src/main/scala/ContextStorageExample.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2022 Typelevel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

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

object ContextStorageExample extends IOApp.Simple {

def run: IO[Unit] = {
implicit val provider: LocalProvider[IO, Context] = IOLocalContextStorage.localProvider[IO]
OtelJava.autoConfigured[IO]().use { otelJava =>
otelJava.tracerProvider.tracer("com.service").get.flatMap { tracer =>
tracer.span("test").use { span => // start 'test' span using otel4s
val jSpanContext = JSpan.current().getSpanContext // get a span from a ThreadLocal var
IO.println(s"jCtx: ${jSpanContext}, Otel4s ctx: ${span.context}")
}
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ object Context {
Wrapped(context)

/** The root [[`Context`]], from which all other contexts are derived. */
val root: Context = wrap(JContext.root())
lazy val root: Context = wrap(JContext.root())

implicit object Contextual extends context.Contextual[Context] {
type Key[A] = Context.Key[A]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.typelevel.otel4s.oteljava.IOLocalContextStorageProvider
Loading

0 comments on commit 6783b22

Please sign in to comment.