Skip to content

Commit

Permalink
Add Tracer#currentSpanOrThrow
Browse files Browse the repository at this point in the history
  • Loading branch information
NthPortal committed Mar 22, 2024
1 parent a66f519 commit 756016a
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 7 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,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 oteljava/metrics/target sdk-exporter/common/.js/target sdk/common/.native/target sdk/common/.js/target core/trace/.js/target semconv/.jvm/target sdk-exporter/all/.jvm/target sdk/trace/.js/target core/common/.jvm/target sdk-exporter/common/.native/target oteljava/common-testkit/target sdk-exporter/trace/.jvm/target unidocs/target oteljava/trace-testkit/target core/metrics/.native/target core/all/.native/target sdk/trace-testkit/.jvm/target sdk/trace-testkit/.native/target core/metrics/.jvm/target core/all/.js/target sdk-exporter/proto/.jvm/target sdk-exporter/proto/.js/target sdk/all/.native/target core/metrics/.js/target core/all/.jvm/target sdk-exporter/trace/.native/target sdk/common/.jvm/target core/trace/.native/target oteljava/metrics-testkit/target sdk/trace/.native/target semconv/.js/target oteljava/common/target scalafix/rules/target sdk-exporter/proto/.native/target core/trace/.jvm/target sdk-exporter/common/.jvm/target sdk-exporter/trace/.js/target core/common/.native/target sdk/trace-testkit/.js/target core/common/.js/target oteljava/trace/target oteljava/testkit/target semconv/.native/target sdk-exporter/all/.js/target sdk/all/.js/target sdk/all/.jvm/target sdk-exporter/all/.native/target oteljava/all/target sdk/trace/.jvm/target project/target
run: mkdir -p oteljava/metrics/target core/trace/.js/target semconv/.jvm/target core/common/.jvm/target oteljava/common-testkit/target unidocs/target oteljava/trace-testkit/target core/metrics/.native/target core/all/.native/target core/metrics/.jvm/target core/all/.js/target core/metrics/.js/target core/all/.jvm/target core/trace/.native/target oteljava/metrics-testkit/target semconv/.js/target oteljava/common/target scalafix/rules/target core/trace/.jvm/target core/common/.native/target core/common/.js/target oteljava/trace/target oteljava/testkit/target semconv/.native/target oteljava/all/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 oteljava/metrics/target sdk-exporter/common/.js/target sdk/common/.native/target sdk/common/.js/target core/trace/.js/target semconv/.jvm/target sdk-exporter/all/.jvm/target sdk/trace/.js/target core/common/.jvm/target sdk-exporter/common/.native/target oteljava/common-testkit/target sdk-exporter/trace/.jvm/target unidocs/target oteljava/trace-testkit/target core/metrics/.native/target core/all/.native/target sdk/trace-testkit/.jvm/target sdk/trace-testkit/.native/target core/metrics/.jvm/target core/all/.js/target sdk-exporter/proto/.jvm/target sdk-exporter/proto/.js/target sdk/all/.native/target core/metrics/.js/target core/all/.jvm/target sdk-exporter/trace/.native/target sdk/common/.jvm/target core/trace/.native/target oteljava/metrics-testkit/target sdk/trace/.native/target semconv/.js/target oteljava/common/target scalafix/rules/target sdk-exporter/proto/.native/target core/trace/.jvm/target sdk-exporter/common/.jvm/target sdk-exporter/trace/.js/target core/common/.native/target sdk/trace-testkit/.js/target core/common/.js/target oteljava/trace/target oteljava/testkit/target semconv/.native/target sdk-exporter/all/.js/target sdk/all/.js/target sdk/all/.jvm/target sdk-exporter/all/.native/target oteljava/all/target sdk/trace/.jvm/target project/target
run: tar cf targets.tar oteljava/metrics/target core/trace/.js/target semconv/.jvm/target core/common/.jvm/target oteljava/common-testkit/target unidocs/target oteljava/trace-testkit/target core/metrics/.native/target core/all/.native/target core/metrics/.jvm/target core/all/.js/target core/metrics/.js/target core/all/.jvm/target core/trace/.native/target oteljava/metrics-testkit/target semconv/.js/target oteljava/common/target scalafix/rules/target core/trace/.jvm/target core/common/.native/target core/common/.js/target oteljava/trace/target oteljava/testkit/target semconv/.native/target oteljava/all/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 Expand Up @@ -243,7 +243,7 @@ jobs:
- name: Submit Dependencies
uses: scalacenter/sbt-dependency-submission@v2
with:
modules-ignore: otel4s-benchmarks_2.13 otel4s-benchmarks_3 otel4s-examples_2.13 otel4s-examples_3 otel4s-sdk-metrics_native0.4_2.13 otel4s-sdk-metrics_native0.4_3 otel4s_2.13 otel4s_3 docs_2.13 docs_3 scalafix-output_2.13 scalafix-input_2.13 otel4s_2.13 otel4s_3 otel4s_2.13 otel4s_3 otel4s-sdk-metrics_2.13 otel4s-sdk-metrics_3 otel4s-sdk-metrics_sjs1_2.13 otel4s-sdk-metrics_sjs1_3 scalafix-tests_2.12
modules-ignore: otel4s-sdk-exporter-common_sjs1_2.13 otel4s-sdk-exporter-common_sjs1_3 otel4s-sdk-common_native0.4_2.13 otel4s-sdk-common_native0.4_3 otel4s-benchmarks_2.13 otel4s-benchmarks_3 otel4s-examples_2.13 otel4s-examples_3 otel4s-sdk-common_sjs1_2.13 otel4s-sdk-common_sjs1_3 otel4s-sdk-exporter_2.13 otel4s-sdk-exporter_3 otel4s-sdk-trace_sjs1_2.13 otel4s-sdk-trace_sjs1_3 otel4s-sdk-exporter-common_native0.4_2.13 otel4s-sdk-exporter-common_native0.4_3 otel4s-sdk-exporter-trace_2.13 otel4s-sdk-exporter-trace_3 otel4s_2.13 otel4s_3 docs_2.13 docs_3 otel4s-sdk-trace-testkit_2.13 otel4s-sdk-trace-testkit_3 scalafix-output_2.13 otel4s-sdk-trace-testkit_native0.4_2.13 otel4s-sdk-trace-testkit_native0.4_3 scalafix-input_2.13 otel4s-sdk-exporter-proto_2.13 otel4s-sdk-exporter-proto_3 otel4s-sdk-exporter-proto_sjs1_2.13 otel4s-sdk-exporter-proto_sjs1_3 otel4s-sdk_native0.4_2.13 otel4s-sdk_native0.4_3 otel4s-sdk-exporter-trace_native0.4_2.13 otel4s-sdk-exporter-trace_native0.4_3 otel4s-sdk-common_2.13 otel4s-sdk-common_3 otel4s_2.13 otel4s_3 otel4s_2.13 otel4s_3 otel4s-sdk-trace_native0.4_2.13 otel4s-sdk-trace_native0.4_3 otel4s-sdk-exporter-proto_native0.4_2.13 otel4s-sdk-exporter-proto_native0.4_3 otel4s-sdk-exporter-common_2.13 otel4s-sdk-exporter-common_3 otel4s-sdk-exporter-trace_sjs1_2.13 otel4s-sdk-exporter-trace_sjs1_3 otel4s-sdk-trace-testkit_sjs1_2.13 otel4s-sdk-trace-testkit_sjs1_3 scalafix-tests_2.12 otel4s-sdk-exporter_sjs1_2.13 otel4s-sdk-exporter_sjs1_3 otel4s-sdk_sjs1_2.13 otel4s-sdk_sjs1_3 otel4s-sdk_2.13 otel4s-sdk_3 otel4s-sdk-exporter_native0.4_2.13 otel4s-sdk-exporter_native0.4_3 otel4s-sdk-trace_2.13 otel4s-sdk-trace_3
configs-ignore: test scala-tool scala-doc-tool test-internal

validate-steward:
Expand Down
22 changes: 20 additions & 2 deletions core/trace/src/main/scala/org/typelevel/otel4s/trace/Tracer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,21 @@ trait Tracer[F[_]] extends TracerMacro[F] {
*/
def currentSpanContext: F[Option[SpanContext]]

/** Returns the current span if one exists in the local scope, or a no-op span
* otherwise.
/** @return
* the current span if one exists in the local scope, or a no-op span
* otherwise
*/
def currentSpanOrNoop: F[Span[F]]

/** @return
* the current span if one exists in the local scope (even if it's a no-op
* span), or raises an error otherwise
*
* @throws IllegalStateException
* when called while not inside a span, indicating programmer error
*/
def currentSpanOrThrow(implicit F: MonadCancelThrow[F]): F[Span[F]]

/** Creates a new [[SpanBuilder]]. The builder can be used to make a fully
* customized [[Span]].
*
Expand Down Expand Up @@ -206,6 +216,10 @@ trait Tracer[F[_]] extends TracerMacro[F] {
}

object Tracer {
private[otel4s] def raiseNoCurrentSpan[F[_]](implicit
F: MonadCancelThrow[F]
): F[Span[F]] =
F.raiseError(new IllegalStateException("not inside a span"))

def apply[F[_]](implicit ev: Tracer[F]): Tracer[F] = ev

Expand Down Expand Up @@ -264,6 +278,8 @@ object Tracer {
val currentSpanContext: F[Option[SpanContext]] = Applicative[F].pure(None)
val currentSpanOrNoop: F[Span[F]] =
Applicative[F].pure(Span.fromBackend(noopBackend))
def currentSpanOrThrow(implicit F: MonadCancelThrow[F]): F[Span[F]] =
currentSpanOrNoop
def rootScope[A](fa: F[A]): F[A] = fa
def noopScope[A](fa: F[A]): F[A] = fa
def childScope[A](parent: SpanContext)(fa: F[A]): F[A] = fa
Expand All @@ -283,6 +299,8 @@ object Tracer {
kt.liftK(tracer.currentSpanContext)
def currentSpanOrNoop: G[Span[G]] =
kt.liftK(tracer.currentSpanOrNoop.map(_.mapK[G]))
def currentSpanOrThrow(implicit F: MonadCancelThrow[G]): G[Span[G]] =
kt.liftK(tracer.currentSpanOrThrow.map(_.mapK[G]))
def spanBuilder(name: String): SpanBuilder[G] =
tracer.spanBuilder(name).mapK[G]
def childScope[A](parent: SpanContext)(ga: G[A]): G[A] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,58 @@ abstract class BaseTracerSuite[Ctx, K[X] <: Key[X]](implicit
}
}

sdkTest("`currentSpanOrThrow` outside of a span (root scope)") { sdk =>
{
for {
tracer <- sdk.provider.get("tracer")
_ <- tracer.currentSpanOrThrow
_ <- IO.raiseError[Unit](
new AssertionError("did not throw for `currentSpanOrThrow`")
)
} yield ()
}.recover { case _: IllegalStateException => () }
}

sdkTest("`currentSpanOrThrow` in noop scope") { sdk =>
for {
tracer <- sdk.provider.get("tracer")
_ <- tracer.noopScope {
for {
currentSpan <- tracer.currentSpanOrThrow
_ <- currentSpan.addAttribute(Attribute("string-attribute", "value"))
} yield assert(!currentSpan.context.isValid)
}
spans <- sdk.finishedSpans
} yield assertEquals(spans.length, 0)
}

sdkTest("`currentSpanOrThrow` inside a span") { sdk =>
def expected(now: FiniteDuration) =
List(SpanTree(SpanInfo("span", now, now)))

val attribute =
Attribute("string-attribute", "value")

TestControl.executeEmbed {
for {
now <- IO.monotonic.delayBy(1.second) // otherwise returns 0
tracer <- sdk.provider.get("tracer")
_ <- tracer.span("span").surround {
for {
currentSpan <- tracer.currentSpanOrThrow
_ <- currentSpan.addAttribute(attribute)
} yield assert(currentSpan.context.isValid)
}
spans <- sdk.finishedSpans
tree <- IO.pure(treeOf(spans))
// _ <- IO.println(tree.map(renderTree).mkString("\n"))
} yield {
assertEquals(tree, expected(now))
assertEquals(spans.map(_.attributes), List(Attributes(attribute)))
}
}
}

sdkTest("create a new scope with a custom parent") { sdk =>
def expected(now: FiniteDuration) =
SpanTree(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.typelevel.otel4s.oteljava.trace

import cats.effect.Sync
import cats.effect.kernel.MonadCancelThrow
import cats.syntax.flatMap._
import cats.syntax.functor._
import io.opentelemetry.api.trace.{Span => JSpan}
Expand Down Expand Up @@ -54,6 +55,15 @@ private[oteljava] class TracerImpl[F[_]: Sync](
)
}

def currentSpanOrThrow(implicit F: MonadCancelThrow[F]): F[Span[F]] =
traceScope.contextReader { ctx =>
Option(JSpan.fromContextOrNull(ctx.underlying))
.map { jSpan =>
F.pure(Span.fromBackend(SpanBackendImpl.fromJSpan(jSpan)))
}
.getOrElse(Tracer.raiseNoCurrentSpan)
}.flatten

def spanBuilder(name: String): SpanBuilder[F] =
new SpanBuilderImpl[F](jTracer, name, runner, traceScope)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package trace

import cats.data.OptionT
import cats.effect.Temporal
import cats.effect.kernel.MonadCancelThrow
import cats.effect.std.Console
import cats.syntax.flatMap._
import cats.syntax.functor._
Expand Down Expand Up @@ -47,13 +48,21 @@ private final class SdkTracer[F[_]: Temporal: Console] private[trace] (
def currentSpanContext: F[Option[SpanContext]] =
traceScope.current.map(current => current.filter(_.isValid))

def currentSpanOrNoop: F[Span[F]] =
private[this] def currentBackend: OptionT[F, Span.Backend[F]] =
OptionT(traceScope.current)
.semiflatMap { ctx =>
OptionT(storage.get(ctx)).getOrElse(Span.Backend.propagating(ctx))
}

def currentSpanOrNoop: F[Span[F]] =
currentBackend
.getOrElse(Span.Backend.noop)
.map(backend => Span.fromBackend(backend))
.map(Span.fromBackend)

def currentSpanOrThrow(implicit F: MonadCancelThrow[F]): F[Span[F]] =
currentBackend
.map(Span.fromBackend)
.getOrElseF(Tracer.raiseNoCurrentSpan)

def spanBuilder(name: String): SpanBuilder[F] =
new SdkSpanBuilder[F](name, scopeInfo, sharedState, traceScope)
Expand Down

0 comments on commit 756016a

Please sign in to comment.