Skip to content

Commit

Permalink
Finish implementing IOLocalContextStorage
Browse files Browse the repository at this point in the history
Finish implementing `IOLocalContextStorage` and
`IOLocalContextStorageProvider` for sharing context modifications
between Java and Scala instrumentation.
  • Loading branch information
NthPortal committed Oct 4, 2023
1 parent d10303b commit b48f3b1
Show file tree
Hide file tree
Showing 9 changed files with 387 additions and 52 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,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 testkit/metrics/jvm/target java/metrics/target testkit/common/jvm/target core/trace/.js/target semconv/.jvm/target core/common/.jvm/target java/trace/target unidocs/target core/metrics/.native/target core/all/.native/target core/metrics/.jvm/target core/all/.js/target java/all/target java/common/target core/metrics/.js/target core/all/.jvm/target core/trace/.native/target semconv/.js/target core/trace/.jvm/target core/common/.native/target core/common/.js/target semconv/.native/target testkit/all/jvm/target project/target
run: mkdir -p testkit/metrics/jvm/target java/metrics/target testkit/common/jvm/target core/trace/.js/target semconv/.jvm/target core/common/.jvm/target java/trace/target unidocs/target core/metrics/.native/target core/all/.native/target core/metrics/.jvm/target java/context-storage/target core/all/.js/target java/all/target java/common/target core/metrics/.js/target core/all/.jvm/target core/trace/.native/target semconv/.js/target core/trace/.jvm/target core/common/.native/target core/common/.js/target semconv/.native/target testkit/all/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 testkit/metrics/jvm/target java/metrics/target testkit/common/jvm/target core/trace/.js/target semconv/.jvm/target core/common/.jvm/target java/trace/target unidocs/target core/metrics/.native/target core/all/.native/target core/metrics/.jvm/target core/all/.js/target java/all/target java/common/target core/metrics/.js/target core/all/.jvm/target core/trace/.native/target semconv/.js/target core/trace/.jvm/target core/common/.native/target core/common/.js/target semconv/.native/target testkit/all/jvm/target project/target
run: tar cf targets.tar testkit/metrics/jvm/target java/metrics/target testkit/common/jvm/target core/trace/.js/target semconv/.jvm/target core/common/.jvm/target java/trace/target unidocs/target core/metrics/.native/target core/all/.native/target core/metrics/.jvm/target java/context-storage/target core/all/.js/target java/all/target java/common/target core/metrics/.js/target core/all/.jvm/target core/trace/.native/target semconv/.js/target core/trace/.jvm/target core/common/.native/target core/common/.js/target semconv/.native/target testkit/all/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
21 changes: 20 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ lazy val root = tlCrossRootProject
`testkit-metrics`,
testkit,
`java-common`,
`java-context-storage`,
`java-metrics`,
`java-trace`,
java,
Expand Down Expand Up @@ -288,9 +289,27 @@ lazy val `java-trace` = project
)
.settings(scalafixSettings)

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

lazy val java = project
.in(file("java/all"))
.dependsOn(core.jvm, `java-metrics`, `java-trace`)
.dependsOn(core.jvm, `java-metrics`, `java-trace`, `java-context-storage`)
.settings(
name := "otel4s-java",
libraryDependencies ++= Seq(
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ object 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.java.IOLocalContextStorageProvider
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* 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.
*/

package org.typelevel.otel4s.java

import cats.effect.IOLocal
import cats.effect.LiftIO
import cats.effect.MonadCancelThrow
import cats.effect.unsafe.IOLocals
import io.opentelemetry.context.{Context => JContext}
import io.opentelemetry.context.ContextStorage
import io.opentelemetry.context.Scope
import org.typelevel.otel4s.java.context.Context
import org.typelevel.otel4s.java.context.LocalContext
import org.typelevel.otel4s.java.instances._

/** A `ContextStorage` backed by an [[cats.effect.IOLocal `IOLocal`]] of a
* [[`Context`]] that also provides [[cats.mtl.Local `Local`]] instances that
* reflect the state of the backing `IOLocal`. Usage of `Local` and
* `ContextStorage` methods will be consistent and stay in sync as long as
* effects are threaded properly.
*/
class IOLocalContextStorage(_ioLocal: () => IOLocal[Context])
extends ContextStorage {
private[this] implicit lazy val ioLocal: IOLocal[Context] = _ioLocal()

@inline private[this] def unsafeCurrent: Context =
IOLocals.get(ioLocal)

override def attach(toAttach: JContext): Scope = {
val previous = unsafeCurrent
IOLocals.set(ioLocal, Context.wrap(toAttach))
() => IOLocals.set(ioLocal, previous)
}

override def current(): JContext =
unsafeCurrent.underlying

/** @return
* a [[cats.mtl.Local `Local`]] of a [[`Context`]] that reflects the state
* of the backing `IOLocal`
*/
def local[F[_]: MonadCancelThrow: LiftIO]: LocalContext[F] = implicitly
}

object IOLocalContextStorage {

/** Returns a [[cats.mtl.Local `Local`]] of a [[`Context`]] if an
* [[`IOLocalContextStorage`]] is configured to be used as the
* `ContextStorage` for the Java otel library.
*
* Raises an exception if an [[`IOLocalContextStorage`]] is __not__
* configured to be used as the `ContextStorage` for the Java otel library,
* or if [[cats.effect.IOLocal `IOLocal`]] propagation is not enabled.
*/
def providedLocal[F[_]: LiftIO](implicit
F: MonadCancelThrow[F]
): F[LocalContext[F]] =
ContextStorage.get() match {
case storage: IOLocalContextStorage =>
// TODO: check `IOLocals.arePropagating` instead once our dependencies
// are updated
if (java.lang.Boolean.getBoolean("cats.effect.ioLocalPropagation")) {
F.pure(storage.local)
} else {
F.raiseError(
new IllegalStateException(
"IOLocal propagation must be enabled with: -Dcats.effect.ioLocalPropagation=true"
)
)
}
case _ =>
F.raiseError(
new IllegalStateException(
"IOLocalContextStorage is not configured for use as the ContextStorageProvider"
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,15 @@

package org.typelevel.otel4s.java

import cats.effect.IOLocal
import cats.effect.SyncIO
import cats.syntax.all._
import io.opentelemetry.context.Context
import cats.effect.{IOLocal, LiftIO, MonadCancelThrow, SyncIO}
import cats.syntax.all.*
import io.opentelemetry.context.ContextStorage
import io.opentelemetry.context.ContextStorageProvider
import org.typelevel.otel4s.java.context.{Context, LocalContext}

object IOLocalContextStorageProvider {
val localContext: IOLocal[Context] =
IOLocal[Context](Context.root())
private lazy val localContext: IOLocal[Context] =
IOLocal[Context](Context.root)
.syncStep(100)
.flatMap(
_.leftMap(_ =>
Expand All @@ -37,7 +36,8 @@ object IOLocalContextStorageProvider {
.unsafeRunSync()
}

/** SPI implementation for [[`IOLocalContextStorage`]]. */
class IOLocalContextStorageProvider extends ContextStorageProvider {
def get(): ContextStorage =
new IOLocalContextStorage(IOLocalContextStorageProvider.localContext)
new IOLocalContextStorage(() => IOLocalContextStorageProvider.localContext)
}
Loading

0 comments on commit b48f3b1

Please sign in to comment.