From 11bd3008a798c3d9447b1694f96587c5c668ac78 Mon Sep 17 00:00:00 2001 From: Maksym Ochenashko Date: Tue, 12 Dec 2023 14:36:11 +0200 Subject: [PATCH 1/2] sdk-trace: add `SdkTraceScope` --- .../otel4s/sdk/trace/SdkContextKeys.scala | 41 +++ .../otel4s/sdk/trace/SdkTraceScope.scala | 335 ++++++++++++++++++ .../otel4s/sdk/trace/SdkTraceScopeSuite.scala | 310 ++++++++++++++++ 3 files changed, 686 insertions(+) create mode 100644 sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkContextKeys.scala create mode 100644 sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkTraceScope.scala create mode 100644 sdk/trace/src/test/scala/org/typelevel/otel4s/sdk/trace/SdkTraceScopeSuite.scala diff --git a/sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkContextKeys.scala b/sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkContextKeys.scala new file mode 100644 index 000000000..42dc2e868 --- /dev/null +++ b/sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkContextKeys.scala @@ -0,0 +1,41 @@ +/* + * Copyright 2023 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.sdk.trace + +import cats.effect.SyncIO +import org.typelevel.otel4s.sdk.context.Context +import org.typelevel.otel4s.trace.SpanContext + +/** The keys that can be used by a third-party integrations. + */ +object SdkContextKeys { + + /** The [[org.typelevel.otel4s.trace.SpanContext SpanContext]] is stored under + * this key in the [[org.typelevel.otel4s.sdk.context.Context Context]]. + * + * To retrieve the context use: + * {{{ + * val context: Context = ??? + * val spanContext: Option[SpanContext] = context.get(SdkContextKeys.SpanContextKey) + * }}} + */ + val SpanContextKey: Context.Key[SpanContext] = + Context.Key + .unique[SyncIO, SpanContext]("otel4s-trace-span-context-key") + .unsafeRunSync() + +} diff --git a/sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkTraceScope.scala b/sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkTraceScope.scala new file mode 100644 index 000000000..c11a4458f --- /dev/null +++ b/sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkTraceScope.scala @@ -0,0 +1,335 @@ +/* + * Copyright 2023 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.sdk.trace + +import cats.mtl.Local +import cats.~> +import org.typelevel.otel4s.sdk.context.Context +import org.typelevel.otel4s.trace.SpanContext + +/** The context-manipulation interface. + * + * The implementation uses [[Local]] to propagate [[Context]] within the + * fibers. + * + * The [[Context]] is a type-safe key-value storage. It may carry + * [[SpanContext]], baggage attributes, and so on. + * + * In terms of the span propagation, there can be 3 scenarios: + * - the [[SpanContext]] is missing in the context + * - the '''valid''' [[SpanContext]] is present in the context + * - the '''invalid''' [[SpanContext]] is present in the context + * + * @tparam F + * the higher-kinded type of a polymorphic effect + */ +private[trace] trait SdkTraceScope[F[_]] { + + /** Returns a [[SpanContext]] if it's available in the current scope. + */ + def current: F[Option[SpanContext]] + + /** Creates a new scope using the given `spanContext` context if the + * requirement are met. + * + * ==The propagation logic== + * + * The propagation is based on the presence and the validity of the + * [[SpanContext]]. + * + * All context values except for the [[SpanContext]] remains intact. To + * demonstrate that values remain intact, the context has some baggage data. + * + * ===The [[SpanContext]] is missing=== + * + * We use the given span context. The baggage data remains. + * + * {{{ + * ┌───────────────────────┐ ┌──────────────────────────────────────────────┐ + * │ Context │ │ Context │ + * │ │ │ │ + * │ ┌─────────────────┐ │ │ ┌─────────────────┐ ┌─────────────────┐ │ + * │ │ │ │ │ │ │ │ │ │ + * │ │ Baggage │ │ ---> │ │ SpanContext │ │ Baggage │ │ + * │ │ │ │ │ │ span_id = "new" │ │ │ │ + * │ └─────────────────┘ │ │ └─────────────────┘ └─────────────────┘ │ + * │ │ │ │ + * └───────────────────────┘ └──────────────────────────────────────────────┘ + * }}} + * + * ===The [[SpanContext]] is valid=== + * + * We override the existing span context with the given one. The baggage data + * remains. + * + * {{{ + * ┌──────────────────────────────────────────────┐ ┌──────────────────────────────────────────────┐ + * │ Context │ │ Context │ + * │ │ │ │ + * │ ┌─────────────────┐ ┌─────────────────┐ │ │ ┌─────────────────┐ ┌─────────────────┐ │ + * │ │ │ │ │ │ │ │ │ │ │ │ + * │ │ SpanContext │ │ Baggage │ │ ---> │ │ SpanContext │ │ Baggage │ │ + * │ │ span_id = "old" │ │ │ │ │ │ span_id = "new" │ │ │ │ + * │ └─────────────────┘ └─────────────────┘ │ │ └─────────────────┘ └─────────────────┘ │ + * │ │ │ │ + * └──────────────────────────────────────────────┘ └──────────────────────────────────────────────┘ + * }}} + * + * ===The [[SpanContext]] is invalid=== + * + * The propagation logic is no-op. We keep the context as is. The baggage + * data remains. + * + * {{{ + * ┌──────────────────────────────────────────────┐ ┌──────────────────────────────────────────────┐ + * │ Context │ │ Context │ + * │ │ │ │ + * │ ┌─────────────────┐ ┌─────────────────┐ │ │ ┌─────────────────┐ ┌─────────────────┐ │ + * │ │ │ │ │ │ │ │ │ │ │ │ + * │ │ SpanContext │ │ Baggage │ │ ---> │ │ SpanContext │ │ Baggage │ │ + * │ │ invalid │ │ │ │ │ │ invalid │ │ │ │ + * │ └─────────────────┘ └─────────────────┘ │ │ └─────────────────┘ └─────────────────┘ │ + * │ │ │ │ + * └──────────────────────────────────────────────┘ └──────────────────────────────────────────────┘ + * }}} + * + * @param spanContext + * the span context to use + */ + def makeScope(spanContext: SpanContext): F[F ~> F] + + /** Creates a root scope. The difference with the [[makeScope]] is that we + * override the whole [[Context]], rather then only a [[SpanContext]] within + * the context. + * + * ==The propagation logic== + * + * The propagation is based on the presence and the validity of the + * [[SpanContext]]. + * + * To demonstrate that the whole context may be replaced, the context has + * some baggage data. + * + * ===The [[SpanContext]] is missing=== + * + * The scope is already root, so we keep the context as is. + * + * {{{ + * ┌───────────────────────┐ ┌───────────────────────┐ + * │ Context │ │ Context │ + * │ │ │ │ + * │ ┌─────────────────┐ │ │ │ + * │ │ │ │ │ │ + * │ │ Baggage │ │ ---> │ │ + * │ │ │ │ │ │ + * │ └─────────────────┘ │ │ │ + * │ │ │ │ + * └───────────────────────┘ └───────────────────────┘ + * }}} + * + * ===The [[SpanContext]] is valid=== + * + * There is a valid span, we forcefully use [[Context.root]]. + * + * {{{ + * ┌──────────────────────────────────────────────┐ ┌───────────────────────┐ + * │ Context │ │ Context │ + * │ │ │ │ + * │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ + * │ │ │ │ │ │ │ │ + * │ │ SpanContext │ │ Baggage │ │ ---> │ │ + * │ │ span_id = "abc" │ │ │ │ │ │ + * │ └─────────────────┘ └─────────────────┘ │ │ │ + * │ │ │ │ + * └──────────────────────────────────────────────┘ └───────────────────────┘ + * }}} + * + * ===The [[SpanContext]] is invalid=== + * + * The current propagation strategy is no-op, so we keep the context as is. + * The baggage data remains. + * + * {{{ + * ┌──────────────────────────────────────────────┐ ┌──────────────────────────────────────────────┐ + * │ Context │ │ Context │ + * │ │ │ │ + * │ ┌─────────────────┐ ┌─────────────────┐ │ │ ┌─────────────────┐ ┌─────────────────┐ │ + * │ │ │ │ │ │ │ │ │ │ │ │ + * │ │ SpanContext │ │ Baggage │ │ ---> │ │ SpanContext │ │ Baggage │ │ + * │ │ invalid │ │ │ │ │ │ invalid │ │ │ │ + * │ └─────────────────┘ └─────────────────┘ │ │ └─────────────────┘ └─────────────────┘ │ + * │ │ │ │ + * └──────────────────────────────────────────────┘ └──────────────────────────────────────────────┘ + * }}} + */ + def rootScope: F[F ~> F] + + /** Creates a no-op scope. + * + * No-op scope means the tracing operations are no-op and the spans created + * within this scope will not be exported anywhere. + * + * We use [[SpanContext.invalid]] as a mark that the propagation logic is + * no-op. + * + * A shortcut for `makeScope(SpanContext.invalid)`. + * + * ==The propagation logic== + * + * All context values except for the [[SpanContext]] remains intact. To + * demonstrate that values remain intact, the context has some baggage data. + * + * ===The [[SpanContext]] is missing=== + * + * The scope is already root, so we keep the context as is. The baggage data + * remains. + * + * {{{ + * ┌───────────────────────┐ ┌──────────────────────────────────────────────┐ + * │ Context │ │ Context │ + * │ │ │ │ + * │ ┌─────────────────┐ │ │ ┌─────────────────┐ ┌─────────────────┐ │ + * │ │ │ │ │ │ │ │ │ │ + * │ │ Baggage │ │ ---> │ │ SpanContext │ │ Baggage │ │ + * │ │ │ │ │ │ invalid │ │ │ │ + * │ └─────────────────┘ │ │ └─────────────────┘ └─────────────────┘ │ + * │ │ │ │ + * └───────────────────────┘ └──────────────────────────────────────────────┘ + * }}} + * + * ===The [[SpanContext]] is valid=== + * + * There is a valid span, we forcefully use [[SpanContext.invalid]]. The + * baggage data remains. + * + * {{{ + * ┌──────────────────────────────────────────────┐ ┌──────────────────────────────────────────────┐ + * │ Context │ │ Context │ + * │ │ │ │ + * │ ┌─────────────────┐ ┌─────────────────┐ │ │ ┌─────────────────┐ ┌─────────────────┐ │ + * │ │ │ │ │ │ │ │ │ │ │ │ + * │ │ SpanContext │ │ Baggage │ │ ---> │ │ SpanContext │ │ Baggage │ │ + * │ │ span_id = "abc" │ │ │ │ │ │ invalid │ │ │ │ + * │ └─────────────────┘ └─────────────────┘ │ │ └─────────────────┘ └─────────────────┘ │ + * │ │ │ │ + * └──────────────────────────────────────────────┘ └──────────────────────────────────────────────┘ + * }}} + * + * ===The [[SpanContext]] is invalid=== + * + * The current propagation strategy is already no-op, so we keep the context + * as is. The baggage data remains. + * + * {{{ + * ┌──────────────────────────────────────────────┐ ┌──────────────────────────────────────────────┐ + * │ Context │ │ Context │ + * │ │ │ │ + * │ ┌─────────────────┐ ┌─────────────────┐ │ │ ┌─────────────────┐ ┌─────────────────┐ │ + * │ │ │ │ │ │ │ │ │ │ │ │ + * │ │ SpanContext │ │ Baggage │ │ ---> │ │ SpanContext │ │ Baggage │ │ + * │ │ invalid │ │ │ │ │ │ invalid │ │ │ │ + * │ └─────────────────┘ └─────────────────┘ │ │ └─────────────────┘ └─────────────────┘ │ + * │ │ │ │ + * └──────────────────────────────────────────────┘ └──────────────────────────────────────────────┘ + * }}} + */ + def noopScope: F ~> F + + /** Creates a scope that uses the given `context`. + * + * A shortcut for + * {{{ + * Local[F, Context].scope(context) + * }}} + * + * @param context + * the context to use + */ + def withContext(context: Context): F ~> F + + /** Runs the `f` with the current [[Context]]. + * + * A shortcut for + * {{{ + * Local[F, Context].reader(f) + * }}} + */ + def contextReader[A](f: Context => A): F[A] +} + +private[trace] object SdkTraceScope { + + def fromLocal[F[_]](implicit L: Local[F, Context]): SdkTraceScope[F] = + new SdkTraceScope[F] { + import SdkContextKeys.SpanContextKey + + def current: F[Option[SpanContext]] = + L.reader(_.get(SpanContextKey)) + + def makeScope(span: SpanContext): F[F ~> F] = + L.applicative.map(current) { context => + createScope(nextScope(context, span)) + } + + def rootScope: F[F ~> F] = + L.reader { context => + val ctx = context.get(SpanContextKey) match { + // the SpanContext exist and it's invalid. + // It means, the propagation strategy is noop and we should keep the current context + case Some(ctx) if !ctx.isValid => context + // the SpanContext exist and it's valid, hence we start with the fresh one + case Some(_) => Context.root + // there is no existing SpanContext, we start with the fresh one + case None => Context.root + } + + withContext(ctx) + } + + def noopScope: F ~> F = + createScope(SpanContext.invalid) + + def withContext(context: Context): F ~> F = + new (F ~> F) { + def apply[A](fa: F[A]): F[A] = + L.scope(fa)(context) + } + + def contextReader[A](f: Context => A): F[A] = + L.reader(f) + + private def createScope(spanContext: SpanContext): F ~> F = + new (F ~> F) { + def apply[A](fa: F[A]): F[A] = + L.local(fa)(context => context.updated(SpanContextKey, spanContext)) + } + + // the context propagation logic + private def nextScope( + current: Option[SpanContext], + next: SpanContext + ): SpanContext = + current match { + // the current span context is valid, so we can switch to the next one + case Some(value) if value.isValid => next + // the current span context is invalid, so we cannot switch and keep the current one + case Some(value) => value + // the current span context does not exist, so we start with the next one + case None => next + } + } +} diff --git a/sdk/trace/src/test/scala/org/typelevel/otel4s/sdk/trace/SdkTraceScopeSuite.scala b/sdk/trace/src/test/scala/org/typelevel/otel4s/sdk/trace/SdkTraceScopeSuite.scala new file mode 100644 index 000000000..3868b3bdf --- /dev/null +++ b/sdk/trace/src/test/scala/org/typelevel/otel4s/sdk/trace/SdkTraceScopeSuite.scala @@ -0,0 +1,310 @@ +/* + * Copyright 2023 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.sdk.trace + +import cats.Applicative +import cats.Functor +import cats.effect.IO +import cats.effect.IOLocal +import cats.effect.LiftIO +import cats.effect.MonadCancelThrow +import cats.mtl.Local +import munit.CatsEffectSuite +import munit.ScalaCheckEffectSuite +import org.scalacheck.Arbitrary +import org.scalacheck.effect.PropF +import org.typelevel.otel4s.sdk.context.Context +import org.typelevel.otel4s.trace.SpanContext + +class SdkTraceScopeSuite extends CatsEffectSuite with ScalaCheckEffectSuite { + + private implicit val spanContextArbitrary: Arbitrary[SpanContext] = + Arbitrary(Gens.spanContext) + + test("current - return None when there is no SpanContext") { + for { + scope <- createTraceScope + current <- scope.current + } yield assertEquals(current, None) + } + + test("rootScope - return None when there is no SpanContext") { + for { + scope <- createTraceScope + rootScope <- scope.rootScope + current <- rootScope(scope.current) + } yield assertEquals(current, None) + } + + test("rootScope - use Context.root when there is no SpanContext") { + val value = "value" + + for { + scope <- createTraceScope + _ <- assertIO(scope.current, None) + + key <- Context.Key.unique[IO, String]("some-key") + ctxWithKey <- scope.contextReader(_.updated(key, value)) + + // the context wasn't modified yet, so it should be empty + _ <- assertIO(scope.contextReader(_.get(key)), None) + + // here we use an explicit context with a custom key + _ <- scope.withContext(ctxWithKey) { + for { + _ <- assertIO(scope.contextReader(_.get(key)), Some(value)) + rootScope <- scope.rootScope + _ <- assertIO(rootScope(scope.current), None) + _ <- assertIO(rootScope(scope.contextReader(_.get(key))), None) + } yield () + } + + rootScope <- scope.rootScope + _ <- assertIO(rootScope(scope.current), None) + } yield () + } + + test( + "rootScope - keep the current context when there is an invalid SpanContext" + ) { + val invalid = SpanContext.invalid + val value = "value" + + for { + scope <- createTraceScope + _ <- assertIO(scope.current, None) + + key <- Context.Key.unique[IO, String]("some-key") + ctxWithKey <- scope.contextReader(_.updated(key, value)) + + // the context wasn't modified yet, so it should be empty + _ <- assertIO(scope.contextReader(_.get(key)), None) + + // here we use an explicit context with a custom key + _ <- scope.withContext(ctxWithKey) { + scope.noopScope { + for { + _ <- assertIO(scope.contextReader(_.get(key)), Some(value)) + _ <- assertIO(scope.current, Some(invalid)) + rootScope <- scope.rootScope + _ <- assertIO(rootScope(scope.current), Some(invalid)) + _ <- assertIO( + rootScope(scope.contextReader(_.get(key))), + Some(value) + ) + } yield () + } + } + + // the context must be reset + _ <- assertIO(scope.current, None) + } yield () + } + + test( + "rootScope - use Context.root when there is a valid SpanContext" + ) { + PropF.forAllF { (ctx: SpanContext) => + val value = "value" + + for { + scope <- createTraceScope + _ <- assertIO(scope.current, None) + + key <- Context.Key.unique[IO, String]("some-key") + ctxWithKey <- scope.contextReader(_.updated(key, value)) + + // the context wasn't modified yet, so it should be empty + _ <- assertIO(scope.contextReader(_.get(key)), None) + + _ <- scope.withContext(ctxWithKey) { + for { + _ <- assertIO(scope.contextReader(_.get(key)), Some(value)) + scope1 <- scope.makeScope(ctx) + _ <- scope1 { + for { + // the key must be present within the custom scope + _ <- assertIO(scope.contextReader(_.get(key)), Some(value)) + _ <- assertIO(scope.current, Some(ctx)) + + // the key must be present within the root scope, because we use empty span context + rootScope <- scope.rootScope + _ <- assertIO(rootScope(scope.current), None) + _ <- assertIO(rootScope(scope.contextReader(_.get(key))), None) + } yield () + } + } yield () + } + + // the context must be reset + _ <- assertIO(scope.current, None) + } yield () + } + } + + test("makeScope - use the given context when there is no SpanContext") { + PropF.forAllF { (ctx: SpanContext) => + for { + scope <- createTraceScope + + _ <- assertIO(scope.current, None) + + lift <- scope.makeScope(ctx) + _ <- lift(assertIO(scope.current, Some(ctx))) + + // the context must be reset + _ <- assertIO(scope.current, None) + } yield () + } + } + + test("makeScope - use the given context when there is a valid SpanContext") { + PropF.forAllF { (ctx1: SpanContext, ctx2: SpanContext) => + for { + scope <- createTraceScope + + _ <- assertIO(scope.current, None) + + scope1 <- scope.makeScope(ctx1) + _ <- scope1 { + for { + _ <- assertIO(scope.current, Some(ctx1)) + scope2 <- scope.makeScope(ctx2) + _ <- scope2(assertIO(scope.current, Some(ctx2))) + _ <- assertIO(scope.current, Some(ctx1)) + } yield () + } + + // the context must be reset + _ <- assertIO(scope.current, None) + } yield () + } + } + + test("makeScope - use invalid context when there is an valid SpanContext") { + val invalid = SpanContext.invalid + + PropF.forAllF { (valid: SpanContext) => + for { + scope <- createTraceScope + + _ <- assertIO(scope.current, None) + + scope1 <- scope.makeScope(invalid) + _ <- scope1 { + for { + _ <- assertIO(scope.current, Some(invalid)) + scope2 <- scope.makeScope(valid) + _ <- scope2(assertIO(scope.current, Some(invalid))) + _ <- assertIO(scope.current, Some(invalid)) + } yield () + } + + // the context must be reset + _ <- assertIO(scope.current, None) + } yield () + } + } + + test("noopScope - always use invalid context") { + val invalid = SpanContext.invalid + + PropF.forAllF { (ctx: SpanContext) => + for { + scope <- createTraceScope + + _ <- assertIO(scope.current, None) + + // the context is empty + _ <- assertIO(scope.noopScope(scope.current), Some(invalid)) + + scope1 <- scope.makeScope(ctx) + + // the context has a valid span + _ <- scope1 { + for { + _ <- assertIO(scope.current, Some(ctx)) + _ <- assertIO(scope.noopScope(scope.current), Some(invalid)) + _ <- assertIO(scope.current, Some(ctx)) + } yield () + } + + // the context must be reset + _ <- assertIO(scope.current, None) + } yield () + } + } + + test("withContext - use the given context") { + PropF.forAllF { (value: String) => + for { + scope <- createTraceScope + key <- Context.Key.unique[IO, String]("some-key") + + _ <- assertIO(scope.contextReader(identity), Context.root) + + _ <- scope.withContext(Context.root.updated(key, value)) { + assertIO( + scope.contextReader(_.updated(key, value)).map(_.get(key)), + Some(value) + ) + } + + // the context must remain unchanged + _ <- assertIO(scope.contextReader(identity), Context.root) + } yield () + } + } + + test("contextReader - use the given context") { + PropF.forAllF { (value: String) => + for { + scope <- createTraceScope + key <- Context.Key.unique[IO, String]("some-key") + + _ <- assertIO(scope.contextReader(identity), Context.root) + + _ <- assertIO( + scope.contextReader(_.updated(key, value)).map(_.get(key)), + Some(value) + ) + + // the context must remain unchanged + _ <- assertIO(scope.contextReader(identity), Context.root) + } yield () + } + } + + private def createTraceScope: IO[SdkTraceScope[IO]] = + IOLocal(Context.root).map { implicit ioLocal => + SdkTraceScope.fromLocal[IO] + } + + private implicit def localForIoLocal[F[_]: MonadCancelThrow: LiftIO, E]( + implicit ioLocal: IOLocal[E] + ): Local[F, E] = + new Local[F, E] { + def applicative = + Applicative[F] + def ask[E2 >: E] = + Functor[F].widen[E, E2](ioLocal.get.to[F]) + def local[A](fa: F[A])(f: E => E): F[A] = + MonadCancelThrow[F].bracket(ioLocal.modify(e => (f(e), e)).to[F])(_ => + fa + )(ioLocal.set(_).to[F]) + } +} From 55be98e5c7daaca82bf31bbc6cc7794077e0a461 Mon Sep 17 00:00:00 2001 From: Maksym Ochenashko Date: Sat, 16 Dec 2023 11:23:03 +0200 Subject: [PATCH 2/2] rename `makeScope` -> `childScope` --- .../otel4s/sdk/trace/SdkTraceScope.scala | 8 ++++---- .../otel4s/sdk/trace/SdkTraceScopeSuite.scala | 20 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkTraceScope.scala b/sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkTraceScope.scala index c11a4458f..5405910a4 100644 --- a/sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkTraceScope.scala +++ b/sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkTraceScope.scala @@ -110,9 +110,9 @@ private[trace] trait SdkTraceScope[F[_]] { * @param spanContext * the span context to use */ - def makeScope(spanContext: SpanContext): F[F ~> F] + def childScope(spanContext: SpanContext): F[F ~> F] - /** Creates a root scope. The difference with the [[makeScope]] is that we + /** Creates a root scope. The difference with the [[childScope]] is that we * override the whole [[Context]], rather then only a [[SpanContext]] within * the context. * @@ -186,7 +186,7 @@ private[trace] trait SdkTraceScope[F[_]] { * We use [[SpanContext.invalid]] as a mark that the propagation logic is * no-op. * - * A shortcut for `makeScope(SpanContext.invalid)`. + * A shortcut for `childScope(SpanContext.invalid)`. * * ==The propagation logic== * @@ -280,7 +280,7 @@ private[trace] object SdkTraceScope { def current: F[Option[SpanContext]] = L.reader(_.get(SpanContextKey)) - def makeScope(span: SpanContext): F[F ~> F] = + def childScope(span: SpanContext): F[F ~> F] = L.applicative.map(current) { context => createScope(nextScope(context, span)) } diff --git a/sdk/trace/src/test/scala/org/typelevel/otel4s/sdk/trace/SdkTraceScopeSuite.scala b/sdk/trace/src/test/scala/org/typelevel/otel4s/sdk/trace/SdkTraceScopeSuite.scala index 3868b3bdf..7bf4747f7 100644 --- a/sdk/trace/src/test/scala/org/typelevel/otel4s/sdk/trace/SdkTraceScopeSuite.scala +++ b/sdk/trace/src/test/scala/org/typelevel/otel4s/sdk/trace/SdkTraceScopeSuite.scala @@ -134,7 +134,7 @@ class SdkTraceScopeSuite extends CatsEffectSuite with ScalaCheckEffectSuite { _ <- scope.withContext(ctxWithKey) { for { _ <- assertIO(scope.contextReader(_.get(key)), Some(value)) - scope1 <- scope.makeScope(ctx) + scope1 <- scope.childScope(ctx) _ <- scope1 { for { // the key must be present within the custom scope @@ -156,14 +156,14 @@ class SdkTraceScopeSuite extends CatsEffectSuite with ScalaCheckEffectSuite { } } - test("makeScope - use the given context when there is no SpanContext") { + test("childScope - use the given context when there is no SpanContext") { PropF.forAllF { (ctx: SpanContext) => for { scope <- createTraceScope _ <- assertIO(scope.current, None) - lift <- scope.makeScope(ctx) + lift <- scope.childScope(ctx) _ <- lift(assertIO(scope.current, Some(ctx))) // the context must be reset @@ -172,18 +172,18 @@ class SdkTraceScopeSuite extends CatsEffectSuite with ScalaCheckEffectSuite { } } - test("makeScope - use the given context when there is a valid SpanContext") { + test("childScope - use the given context when there is a valid SpanContext") { PropF.forAllF { (ctx1: SpanContext, ctx2: SpanContext) => for { scope <- createTraceScope _ <- assertIO(scope.current, None) - scope1 <- scope.makeScope(ctx1) + scope1 <- scope.childScope(ctx1) _ <- scope1 { for { _ <- assertIO(scope.current, Some(ctx1)) - scope2 <- scope.makeScope(ctx2) + scope2 <- scope.childScope(ctx2) _ <- scope2(assertIO(scope.current, Some(ctx2))) _ <- assertIO(scope.current, Some(ctx1)) } yield () @@ -195,7 +195,7 @@ class SdkTraceScopeSuite extends CatsEffectSuite with ScalaCheckEffectSuite { } } - test("makeScope - use invalid context when there is an valid SpanContext") { + test("childScope - use invalid context when there is an valid SpanContext") { val invalid = SpanContext.invalid PropF.forAllF { (valid: SpanContext) => @@ -204,11 +204,11 @@ class SdkTraceScopeSuite extends CatsEffectSuite with ScalaCheckEffectSuite { _ <- assertIO(scope.current, None) - scope1 <- scope.makeScope(invalid) + scope1 <- scope.childScope(invalid) _ <- scope1 { for { _ <- assertIO(scope.current, Some(invalid)) - scope2 <- scope.makeScope(valid) + scope2 <- scope.childScope(valid) _ <- scope2(assertIO(scope.current, Some(invalid))) _ <- assertIO(scope.current, Some(invalid)) } yield () @@ -232,7 +232,7 @@ class SdkTraceScopeSuite extends CatsEffectSuite with ScalaCheckEffectSuite { // the context is empty _ <- assertIO(scope.noopScope(scope.current), Some(invalid)) - scope1 <- scope.makeScope(ctx) + scope1 <- scope.childScope(ctx) // the context has a valid span _ <- scope1 {