From 7e192231b4368695bb09f170e4a6c01cf4dba60f Mon Sep 17 00:00:00 2001 From: mpilquist Date: Wed, 23 Nov 2022 10:38:30 -0500 Subject: [PATCH 1/8] Initial draft of span coalescing --- modules/core/shared/src/main/scala/Span.scala | 59 ++++++++++-- .../core/shared/src/main/scala/Trace.scala | 90 +++++++++---------- .../core/shared/src/test/scala/InMemory.scala | 27 +++--- .../src/test/scala/SpanCoalesceTest.scala | 66 ++++++++++++++ 4 files changed, 174 insertions(+), 68 deletions(-) create mode 100644 modules/core/shared/src/test/scala/SpanCoalesceTest.scala diff --git a/modules/core/shared/src/main/scala/Span.scala b/modules/core/shared/src/main/scala/Span.scala index 273d56fd..fca6b1fd 100644 --- a/modules/core/shared/src/main/scala/Span.scala +++ b/modules/core/shared/src/main/scala/Span.scala @@ -35,8 +35,9 @@ trait Span[F[_]] { /** Resource that yields a child span with the given name. */ def span(name: String): Resource[F, Span[F]] + // TODO /** Resource that yields a child span of both this span and the given kernel. */ - def span(name: String, kernel: Kernel): Resource[F, Span[F]] + def span(name: String, options: Span.Options): Resource[F, Span[F]] /** A unique ID for the trace of this span, if available. * This can be useful to include in error messages for example, so you can quickly find the associated trace. @@ -86,9 +87,8 @@ trait Span[F[_]] { override def traceUri: G[Option[URI]] = f(outer.traceUri) - /** Create resource with new span and add current span and kernel to parents of new span */ - override def span(name: String, kernel: Kernel): Resource[G, Span[G]] = outer - .span(name, kernel) + override def span(name: String, options: Span.Options): Resource[G, Span[G]] = outer + .span(name, options) .map(_.mapK(f)) .mapK(f) } @@ -97,6 +97,24 @@ trait Span[F[_]] { object Span { + trait Default[F[_]] extends Span[F] { + protected implicit val applciativeInstance: Applicative[F] + protected val spanCreationPolicy: Options.SpanCreationPolicy + + def span(name: String): Resource[F, Span[F]] = + span(name, Options.Defaults) + + def span(name: String, options: Options): Resource[F, Span[F]] = { + spanCreationPolicy match { + case Options.SpanCreationPolicy.Suppress => Resource.pure(Span.noop[F]) + case Options.SpanCreationPolicy.Coalesce => Resource.pure(this) + case Options.SpanCreationPolicy.Default => createSpan(name, options) + } + } + + def createSpan(name: String, options: Options): Resource[F, Span[F]] + } + /** Ensure that Fields mixin data is added to a span when an error is raised. */ def putErrorFields[F[_]: Applicative](span: Resource[F, Span[F]]): Resource[F, Span[F]] = @@ -131,13 +149,13 @@ object Span { private class NoopSpan[F[_]: Applicative] extends EphemeralSpan[F] { def span(name: String): Resource[F, Span[F]] = Resource.pure(this) - override def span(name: String, kernel: Kernel): Resource[F, Span[F]] = Resource.pure(this) + override def span(name: String, options: Span.Options): Resource[F, Span[F]] = Resource.pure(this) } private class RootsSpan[F[_]: Applicative](ep: EntryPoint[F]) extends EphemeralSpan[F] { def span(name: String): Resource[F, Span[F]] = ep.root(name) - override def span(name: String, kernel: Kernel): Resource[F, Span[F]] = - ep.continueOrElseRoot(name, kernel) + override def span(name: String, options: Span.Options): Resource[F, Span[F]] = + options.parentKernel.fold(ep.root(name))(ep.continueOrElseRoot(name, _)) } private def resolve[F[_]](span: Span[F]): Kleisli[F, Span[F], *] ~> F = @@ -154,4 +172,31 @@ object Span { def rootTracing[F[_]: Applicative](ep: EntryPoint[F]): Kleisli[F, Span[F], *] ~> F = resolve( makeRoots(ep) ) + + sealed trait Options { + def parentKernel: Option[Kernel] + def spanCreationPolicy: Options.SpanCreationPolicy + + def withParentKernel(kernel: Kernel): Options + def withoutParentKernel: Options + def withSpanCreationPolicy(p: Options.SpanCreationPolicy): Options + } + + object Options { + sealed trait SpanCreationPolicy + object SpanCreationPolicy { + case object Default extends SpanCreationPolicy + case object Suppress extends SpanCreationPolicy + case object Coalesce extends SpanCreationPolicy + } + + private case class OptionsImpl(parentKernel: Option[Kernel], spanCreationPolicy: SpanCreationPolicy) extends Options { + def withParentKernel(kernel: Kernel): Options = OptionsImpl(Some(kernel), spanCreationPolicy) + def withoutParentKernel: Options = OptionsImpl(None, spanCreationPolicy) + def withSpanCreationPolicy(p: SpanCreationPolicy): Options = OptionsImpl(parentKernel, p) + } + val Defaults: Options = OptionsImpl(None, SpanCreationPolicy.Default) + val Suppress: Options = Defaults.withSpanCreationPolicy(SpanCreationPolicy.Suppress) + val Coalesce: Options = Defaults.withSpanCreationPolicy(SpanCreationPolicy.Coalesce) + } } diff --git a/modules/core/shared/src/main/scala/Trace.scala b/modules/core/shared/src/main/scala/Trace.scala index e0777248..a60b624c 100644 --- a/modules/core/shared/src/main/scala/Trace.scala +++ b/modules/core/shared/src/main/scala/Trace.scala @@ -33,13 +33,13 @@ trait Trace[F[_]] { def kernel: F[Kernel] /** Creates a new span as a resource. */ - def spanR(name: String, kernel: Option[Kernel] = None): Resource[F, F ~> F] + def spanR(name: String, options: Span.Options = Span.Options.Defaults): Resource[F, F ~> F] /** Create a new span, and within it run the continuation `k`. */ def span[A](name: String)(k: F[A]): F[A] - /** Create a new span and add current span and kernel to parents of new span */ - def span[A](name: String, kernel: Kernel)(k: F[A]): F[A] + /** Creates a new span as a resource. */ + def span[A](name: String, options: Span.Options = Span.Options.Defaults)(k: F[A]): F[A] /** A unique ID for this trace, if available. This can be useful to include in error messages for * example, so you can quickly find the associated trace. @@ -76,10 +76,10 @@ object Trace { override def kernel: IO[Kernel] = local.get.flatMap(_.kernel) - override def spanR(name: String, kernel: Option[Kernel]): Resource[IO, IO ~> IO] = + override def spanR(name: String, options: Span.Options): Resource[IO, IO ~> IO] = for { parent <- Resource.eval(local.get) - child <- kernel.fold(parent.span(name))(parent.span(name, _)) + child <- parent.span(name, options) } yield new (IO ~> IO) { def apply[A](fa: IO[A]): IO[A] = local @@ -90,8 +90,8 @@ object Trace { override def span[A](name: String)(k: IO[A]): IO[A] = spanR(name).use(_(k)) - override def span[A](name: String, kernel: Kernel)(k: IO[A]): IO[A] = - spanR(name, Some(kernel)).use(_(k)) + override def span[A](name: String, options: Span.Options)(k: IO[A]): IO[A] = + spanR(name, options).use(_(k)) override def traceId: IO[Option[String]] = local.get.flatMap(_.traceId) @@ -115,10 +115,10 @@ object Trace { override def attachError(err: Throwable): F[Unit] = void override def log(fields: (String, TraceValue)*): F[Unit] = void override def log(event: String): F[Unit] = void - override def spanR(name: String, kernel: Option[Kernel]): Resource[F, F ~> F] = + override def spanR(name: String, options: Span.Options): Resource[F, F ~> F] = Resource.pure(FunctionK.id) override def span[A](name: String)(k: F[A]): F[A] = k - override def span[A](name: String, kernel: Kernel)(k: F[A]): F[A] = k + override def span[A](name: String, options: Span.Options)(k: F[A]): F[A] = k override def traceId: F[Option[String]] = none.pure[F] override def traceUri: F[Option[URI]] = none.pure[F] } @@ -154,11 +154,11 @@ object Trace { override def spanR( name: String, - kernel: Option[Kernel] + options: Span.Options ): Resource[Kleisli[F, Span[F], *], Kleisli[F, Span[F], *] ~> Kleisli[F, Span[F], *]] = Resource( Kleisli((span: Span[F]) => - kernel.fold(span.span(name))(span.span(name, _)).allocated.map { case (child, release) => + span.span(name, options).allocated.map { case (child, release) => new (Kleisli[F, Span[F], *] ~> Kleisli[F, Span[F], *]) { def apply[A](fa: Kleisli[F, Span[F], A]): Kleisli[F, Span[F], A] = fa.local((_: Span[F]) => child).mapF(_.onError { case e => child.attachError(e) }) @@ -170,10 +170,10 @@ object Trace { override def span[A](name: String)(k: Kleisli[F, Span[F], A]): Kleisli[F, Span[F], A] = spanR(name).use(_(k)) - override def span[A](name: String, kernel: Kernel)( + override def span[A](name: String, options: Span.Options)( k: Kleisli[F, Span[F], A] ): Kleisli[F, Span[F], A] = - spanR(name, Some(kernel)).use(_(k)) + spanR(name, options).use(_(k)) def lens[E](f: E => Span[F], g: (E, Span[F]) => E): Trace[Kleisli[F, E, *]] = new Trace[Kleisli[F, E, *]] { @@ -195,11 +195,11 @@ object Trace { override def spanR( name: String, - kernel: Option[Kernel] + options: Span.Options ): Resource[Kleisli[F, E, *], Kleisli[F, E, *] ~> Kleisli[F, E, *]] = Resource( Kleisli((e: E) => - kernel.fold(f(e).span(name))(f(e).span(name, _)).allocated.map { + f(e).span(name, options).allocated.map { case (child, release) => new (Kleisli[F, E, *] ~> Kleisli[F, E, *]) { def apply[A](fa: Kleisli[F, E, A]): Kleisli[F, E, A] = @@ -213,8 +213,8 @@ object Trace { override def span[A](name: String)(k: Kleisli[F, E, A]): Kleisli[F, E, A] = spanR(name).use(_(k)) - override def span[A](name: String, kernel: Kernel)(k: Kleisli[F, E, A]): Kleisli[F, E, A] = - spanR(name, Some(kernel)).use(_(k)) + override def span[A](name: String, options: Span.Options)(k: Kleisli[F, E, A]): Kleisli[F, E, A] = + spanR(name, options).use(_(k)) override def traceId: Kleisli[F, E, Option[String]] = Kleisli(e => f(e).traceId) @@ -252,11 +252,11 @@ object Trace { override def spanR( name: String, - kernel: Option[Kernel] + options: Span.Options ): Resource[Kleisli[F, E, *], Kleisli[F, E, *] ~> Kleisli[F, E, *]] = Resource( Kleisli((e: E) => - trace.spanR(name, kernel).allocated.map { case (f, release) => + trace.spanR(name, options).allocated.map { case (f, release) => f.compose(Kleisli.applyK(e)).andThen(Kleisli.liftK[F, E]) -> Kleisli.liftF[F, E, Unit](f(release)) } @@ -266,8 +266,8 @@ object Trace { override def span[A](name: String)(k: Kleisli[F, E, A]): Kleisli[F, E, A] = Kleisli(e => trace.span[A](name)(k.run(e))) - override def span[A](name: String, kernel: Kernel)(k: ReaderT[F, E, A]): ReaderT[F, E, A] = - Kleisli(e => trace.span[A](name, kernel)(k.run(e))) + override def span[A](name: String, options: Span.Options)(k: ReaderT[F, E, A]): ReaderT[F, E, A] = + Kleisli(e => trace.span[A](name, options)(k.run(e))) override def traceId: Kleisli[F, E, Option[String]] = Kleisli.liftF(trace.traceId) @@ -297,11 +297,11 @@ object Trace { override def spanR( name: String, - kernel: Option[Kernel] + options: Span.Options ): Resource[StateT[F, S, *], StateT[F, S, *] ~> StateT[F, S, *]] = Resource( StateT.liftF( - trace.spanR(name, kernel).allocated.map { case (f, release) => + trace.spanR(name, options).allocated.map { case (f, release) => new (StateT[F, S, *] ~> StateT[F, S, *]) { def apply[A](fa: StateT[F, S, A]): StateT[F, S, A] = StateT.applyF(f(fa.runF)) @@ -314,8 +314,8 @@ object Trace { override def span[A](name: String)(k: StateT[F, S, A]): StateT[F, S, A] = StateT(s => trace.span[(S, A)](name)(k.run(s))) - override def span[A](name: String, kernel: Kernel)(k: StateT[F, S, A]): StateT[F, S, A] = - StateT(s => trace.span[(S, A)](name, kernel)(k.run(s))) + override def span[A](name: String, options: Span.Options)(k: StateT[F, S, A]): StateT[F, S, A] = + StateT(s => trace.span[(S, A)](name, options)(k.run(s))) override def traceId: StateT[F, S, Option[String]] = StateT.liftF(trace.traceId) @@ -346,11 +346,11 @@ object Trace { override def spanR( name: String, - kernel: Option[Kernel] + options: Span.Options ): Resource[EitherT[F, E, *], EitherT[F, E, *] ~> EitherT[F, E, *]] = Resource( EitherT.liftF( - trace.spanR(name, kernel).allocated.map { case (f, release) => + trace.spanR(name, options).allocated.map { case (f, release) => new (EitherT[F, E, *] ~> EitherT[F, E, *]) { def apply[A](fa: EitherT[F, E, A]): EitherT[F, E, A] = EitherT(f(fa.value)) @@ -363,8 +363,8 @@ object Trace { override def span[A](name: String)(k: EitherT[F, E, A]): EitherT[F, E, A] = EitherT(trace.span(name)(k.value)) - override def span[A](name: String, kernel: Kernel)(k: EitherT[F, E, A]): EitherT[F, E, A] = - EitherT(trace.span(name, kernel)(k.value)) + override def span[A](name: String, options: Span.Options)(k: EitherT[F, E, A]): EitherT[F, E, A] = + EitherT(trace.span(name, options)(k.value)) override def traceId: EitherT[F, E, Option[String]] = EitherT.liftF(trace.traceId) @@ -393,11 +393,11 @@ object Trace { override def spanR( name: String, - kernel: Option[Kernel] + options: Span.Options ): Resource[OptionT[F, *], OptionT[F, *] ~> OptionT[F, *]] = Resource( OptionT.liftF( - trace.spanR(name, kernel).allocated.map { case (f, release) => + trace.spanR(name, options).allocated.map { case (f, release) => new (OptionT[F, *] ~> OptionT[F, *]) { def apply[A](fa: OptionT[F, A]): OptionT[F, A] = OptionT(f(fa.value)) @@ -410,8 +410,8 @@ object Trace { override def span[A](name: String)(k: OptionT[F, A]): OptionT[F, A] = OptionT(trace.span(name)(k.value)) - override def span[A](name: String, kernel: Kernel)(k: OptionT[F, A]): OptionT[F, A] = - OptionT(trace.span(name, kernel)(k.value)) + override def span[A](name: String, options: Span.Options)(k: OptionT[F, A]): OptionT[F, A] = + OptionT(trace.span(name, options)(k.value)) override def traceId: OptionT[F, Option[String]] = OptionT.liftF(trace.traceId) @@ -443,11 +443,11 @@ object Trace { override def spanR( name: String, - kernel: Option[Kernel] + options: Span.Options ): Resource[Nested[F, G, *], Nested[F, G, *] ~> Nested[F, G, *]] = Resource( Nested( - trace.spanR(name, kernel).allocated.map { case (f, release) => + trace.spanR(name, options).allocated.map { case (f, release) => ( new (Nested[F, G, *] ~> Nested[F, G, *]) { def apply[A](fa: Nested[F, G, A]): Nested[F, G, A] = @@ -462,8 +462,8 @@ object Trace { override def span[A](name: String)(k: Nested[F, G, A]): Nested[F, G, A] = trace.span(name)(k.value).nested - override def span[A](name: String, kernel: Kernel)(k: Nested[F, G, A]): Nested[F, G, A] = - trace.span(name, kernel)(k.value).nested + override def span[A](name: String, options: Span.Options)(k: Nested[F, G, A]): Nested[F, G, A] = + trace.span(name, options)(k.value).nested override def traceId: Nested[F, G, Option[String]] = trace.traceId.map(_.pure[G]).nested @@ -493,11 +493,11 @@ object Trace { override def spanR( name: String, - kernel: Option[Kernel] + options: Span.Options ): Resource[Resource[F, *], Resource[F, *] ~> Resource[F, *]] = Resource( Resource.eval( - trace.spanR(name, kernel).allocated.map { case (f, release) => + trace.spanR(name, options).allocated.map { case (f, release) => new (Resource[F, *] ~> Resource[F, *]) { def apply[A](fa: Resource[F, A]): Resource[F, A] = fa.mapK(f) @@ -514,8 +514,8 @@ object Trace { }) } - override def span[A](name: String, kernel: Kernel)(k: Resource[F, A]): Resource[F, A] = - trace.spanR(name, kernel.some).flatMap { f => + override def span[A](name: String, options: Span.Options)(k: Resource[F, A]): Resource[F, A] = + trace.spanR(name, options).flatMap { f => Resource(f(k.allocated).map { case (a, release) => a -> f(release) }) @@ -547,11 +547,11 @@ object Trace { override def spanR( name: String, - kernel: Option[Kernel] + options: Span.Options ): Resource[Stream[F, *], Stream[F, *] ~> Stream[F, *]] = Resource( Stream.eval( - trace.spanR(name, kernel).allocated.map { case (f, release) => + trace.spanR(name, options).allocated.map { case (f, release) => new (Stream[F, *] ~> Stream[F, *]) { def apply[A](fa: Stream[F, A]): Stream[F, A] = fa.translate(f) @@ -564,8 +564,8 @@ object Trace { override def span[A](name: String)(k: Stream[F, A]): Stream[F, A] = Stream.resource(trace.spanR(name)).flatMap(k.translate) - override def span[A](name: String, kernel: Kernel)(k: Stream[F, A]): Stream[F, A] = - Stream.resource(trace.spanR(name, kernel.some)).flatMap(k.translate) + override def span[A](name: String, options: Span.Options)(k: Stream[F, A]): Stream[F, A] = + Stream.resource(trace.spanR(name, options)).flatMap(k.translate) override def traceId: Stream[F, Option[String]] = Stream.eval(trace.traceId) diff --git a/modules/core/shared/src/test/scala/InMemory.scala b/modules/core/shared/src/test/scala/InMemory.scala index de31091a..4dcf6c6f 100644 --- a/modules/core/shared/src/test/scala/InMemory.scala +++ b/modules/core/shared/src/test/scala/InMemory.scala @@ -9,13 +9,18 @@ import java.net.URI import cats.data.Chain import cats.effect.{IO, Ref, Resource} +import natchez.Span.Options +import cats.Applicative + object InMemory { class Span( lineage: Lineage, k: Kernel, - ref: Ref[IO, Chain[(Lineage, NatchezCommand)]] - ) extends natchez.Span[IO] { + ref: Ref[IO, Chain[(Lineage, NatchezCommand)]], + val spanCreationPolicy: Options.SpanCreationPolicy + ) extends natchez.Span.Default[IO] { + protected val applciativeInstance: Applicative[IO] = implicitly def put(fields: (String, natchez.TraceValue)*): IO[Unit] = ref.update(_.append(lineage -> NatchezCommand.Put(fields.toList))) @@ -32,20 +37,10 @@ object InMemory { def kernel: IO[Kernel] = ref.update(_.append(lineage -> NatchezCommand.AskKernel(k))).as(k) - def span(name: String): Resource[IO, natchez.Span[IO]] = { - val acquire = ref - .update(_.append(lineage -> NatchezCommand.CreateSpan(name, None))) - .as(new Span(lineage / name, k, ref)) - - val release = ref.update(_.append(lineage -> NatchezCommand.ReleaseSpan(name))) - - Resource.make(acquire)(_ => release) - } - - def span(name: String, kernel: Kernel): Resource[IO, natchez.Span[IO]] = { + def createSpan(name: String, options: Options): Resource[IO, natchez.Span[IO]] = { val acquire = ref - .update(_.append(lineage -> NatchezCommand.CreateSpan(name, Some(kernel)))) - .as(new Span(lineage / name, k, ref)) + .update(_.append(lineage -> NatchezCommand.CreateSpan(name, options.parentKernel))) + .as(new Span(lineage / name, k, ref, options.spanCreationPolicy)) val release = ref.update(_.append(lineage -> NatchezCommand.ReleaseSpan(name))) @@ -77,7 +72,7 @@ object InMemory { private def newSpan(name: String, kernel: Kernel): Resource[IO, Span] = { val acquire = ref .update(_.append(Lineage.Root -> NatchezCommand.CreateRootSpan(name, kernel))) - .as(new Span(Lineage.Root, kernel, ref)) + .as(new Span(Lineage.Root, kernel, ref, Options.SpanCreationPolicy.Default)) val release = ref.update(_.append(Lineage.Root -> NatchezCommand.ReleaseRootSpan(name))) diff --git a/modules/core/shared/src/test/scala/SpanCoalesceTest.scala b/modules/core/shared/src/test/scala/SpanCoalesceTest.scala new file mode 100644 index 00000000..cc7d4ee2 --- /dev/null +++ b/modules/core/shared/src/test/scala/SpanCoalesceTest.scala @@ -0,0 +1,66 @@ +// Copyright (c) 2019-2020 by Rob Norris and Contributors +// This software is licensed under the MIT License (MIT). +// For more information see LICENSE or https://opensource.org/licenses/MIT + +package natchez + +import cats.data.Kleisli +import cats.effect.IO +import munit.CatsEffectSuite + +import InMemory.{Lineage, NatchezCommand} + +class SpanCoalesceTest extends CatsEffectSuite { + + test("suppress - nominal") { + def detailed[F[_]: Trace] = + Trace[F].span("parent")(Trace[F].span("child")(Trace[F].put("answer" -> 42))) + def suppressed[F[_]: Trace] = + Trace[F].span("suppressed", Span.Options.Suppress)(detailed[F]) + + testTraceKleisli(_ => suppressed[Kleisli[IO, Span[IO], *]], List( + (Lineage.Root, NatchezCommand.CreateRootSpan("root", Kernel(Map()))), + (Lineage.Root, NatchezCommand.CreateSpan("suppressed", None)), + (Lineage.Root, NatchezCommand.ReleaseSpan("suppressed")), + (Lineage.Root, NatchezCommand.ReleaseRootSpan("root")) + )) + } + + test("coaslesce - nominal") { + def detailed[F[_]: Trace] = + Trace[F].span("parent")(Trace[F].span("child")(Trace[F].put("answer" -> 42))) + def coaslesced[F[_]: Trace] = + Trace[F].span("coalesced", Span.Options.Coalesce)(detailed[F]) + + testTraceKleisli(_ => coaslesced[Kleisli[IO, Span[IO], *]], List( + (Lineage.Root, NatchezCommand.CreateRootSpan("root", Kernel(Map()))), + (Lineage.Root, NatchezCommand.CreateSpan("coalesced", None)), + (Lineage.Root / "coalesced", NatchezCommand.Put(List("answer" -> 42))), + (Lineage.Root, NatchezCommand.ReleaseSpan("coalesced")), + (Lineage.Root, NatchezCommand.ReleaseRootSpan("root")) + )) + } + + def testTraceKleisli( + traceProgram: Trace[Kleisli[IO, Span[IO], *]] => Kleisli[IO, Span[IO], Unit], + expectedHistory: List[(Lineage, NatchezCommand)] + ) = testTrace[Kleisli[IO, Span[IO], *]](traceProgram, root => + IO.pure(Trace[Kleisli[IO, Span[IO], *]] -> (k => k.run(root))) + , expectedHistory) + + def testTrace[F[_]]( + traceProgram: Trace[F] => F[Unit], + makeTraceAndResolver: Span[IO] => IO[(Trace[F], F[Unit] => IO[Unit])], + expectedHistory: List[(Lineage, NatchezCommand)] + ) = + InMemory.EntryPoint.create.flatMap { ep => + val traced = ep.root("root").use { r => + makeTraceAndResolver(r).flatMap { case (traceInstance, resolve) => + resolve(traceProgram(traceInstance)) + } + } + traced *> ep.ref.get.map { history => + assertEquals(history.toList, expectedHistory) + } + } +} \ No newline at end of file From a5e4d8fd17b42218f80a9ce50a193f40fad5b08b Mon Sep 17 00:00:00 2001 From: mpilquist Date: Wed, 23 Nov 2022 13:59:49 -0500 Subject: [PATCH 2/8] Refactor test suite --- modules/core/shared/src/main/scala/Span.scala | 15 +-- .../core/shared/src/main/scala/Trace.scala | 35 ++++--- .../core/shared/src/test/scala/InMemory.scala | 52 ++++++++++- .../src/test/scala/SpanCoalesceTest.scala | 93 +++++++------------ .../src/test/scala/SpanPropagationTest.scala | 55 ++++------- 5 files changed, 134 insertions(+), 116 deletions(-) diff --git a/modules/core/shared/src/main/scala/Span.scala b/modules/core/shared/src/main/scala/Span.scala index fca6b1fd..bca2e179 100644 --- a/modules/core/shared/src/main/scala/Span.scala +++ b/modules/core/shared/src/main/scala/Span.scala @@ -101,16 +101,15 @@ object Span { protected implicit val applciativeInstance: Applicative[F] protected val spanCreationPolicy: Options.SpanCreationPolicy - def span(name: String): Resource[F, Span[F]] = + def span(name: String): Resource[F, Span[F]] = span(name, Options.Defaults) - def span(name: String, options: Options): Resource[F, Span[F]] = { + def span(name: String, options: Options): Resource[F, Span[F]] = spanCreationPolicy match { case Options.SpanCreationPolicy.Suppress => Resource.pure(Span.noop[F]) case Options.SpanCreationPolicy.Coalesce => Resource.pure(this) - case Options.SpanCreationPolicy.Default => createSpan(name, options) + case Options.SpanCreationPolicy.Default => createSpan(name, options) } - } def createSpan(name: String, options: Options): Resource[F, Span[F]] } @@ -149,7 +148,8 @@ object Span { private class NoopSpan[F[_]: Applicative] extends EphemeralSpan[F] { def span(name: String): Resource[F, Span[F]] = Resource.pure(this) - override def span(name: String, options: Span.Options): Resource[F, Span[F]] = Resource.pure(this) + override def span(name: String, options: Span.Options): Resource[F, Span[F]] = + Resource.pure(this) } private class RootsSpan[F[_]: Applicative](ep: EntryPoint[F]) extends EphemeralSpan[F] { @@ -190,7 +190,10 @@ object Span { case object Coalesce extends SpanCreationPolicy } - private case class OptionsImpl(parentKernel: Option[Kernel], spanCreationPolicy: SpanCreationPolicy) extends Options { + private case class OptionsImpl( + parentKernel: Option[Kernel], + spanCreationPolicy: SpanCreationPolicy + ) extends Options { def withParentKernel(kernel: Kernel): Options = OptionsImpl(Some(kernel), spanCreationPolicy) def withoutParentKernel: Options = OptionsImpl(None, spanCreationPolicy) def withSpanCreationPolicy(p: SpanCreationPolicy): Options = OptionsImpl(parentKernel, p) diff --git a/modules/core/shared/src/main/scala/Trace.scala b/modules/core/shared/src/main/scala/Trace.scala index a60b624c..6db826b6 100644 --- a/modules/core/shared/src/main/scala/Trace.scala +++ b/modules/core/shared/src/main/scala/Trace.scala @@ -199,13 +199,12 @@ object Trace { ): Resource[Kleisli[F, E, *], Kleisli[F, E, *] ~> Kleisli[F, E, *]] = Resource( Kleisli((e: E) => - f(e).span(name, options).allocated.map { - case (child, release) => - new (Kleisli[F, E, *] ~> Kleisli[F, E, *]) { - def apply[A](fa: Kleisli[F, E, A]): Kleisli[F, E, A] = - fa.local((_: E) => g(e, child)) - .mapF(_.onError { case e => child.attachError(e) }) - } -> Kleisli.liftF[F, E, Unit](release) + f(e).span(name, options).allocated.map { case (child, release) => + new (Kleisli[F, E, *] ~> Kleisli[F, E, *]) { + def apply[A](fa: Kleisli[F, E, A]): Kleisli[F, E, A] = + fa.local((_: E) => g(e, child)) + .mapF(_.onError { case e => child.attachError(e) }) + } -> Kleisli.liftF[F, E, Unit](release) } ) ) @@ -213,7 +212,9 @@ object Trace { override def span[A](name: String)(k: Kleisli[F, E, A]): Kleisli[F, E, A] = spanR(name).use(_(k)) - override def span[A](name: String, options: Span.Options)(k: Kleisli[F, E, A]): Kleisli[F, E, A] = + override def span[A](name: String, options: Span.Options)( + k: Kleisli[F, E, A] + ): Kleisli[F, E, A] = spanR(name, options).use(_(k)) override def traceId: Kleisli[F, E, Option[String]] = @@ -266,7 +267,9 @@ object Trace { override def span[A](name: String)(k: Kleisli[F, E, A]): Kleisli[F, E, A] = Kleisli(e => trace.span[A](name)(k.run(e))) - override def span[A](name: String, options: Span.Options)(k: ReaderT[F, E, A]): ReaderT[F, E, A] = + override def span[A](name: String, options: Span.Options)( + k: ReaderT[F, E, A] + ): ReaderT[F, E, A] = Kleisli(e => trace.span[A](name, options)(k.run(e))) override def traceId: Kleisli[F, E, Option[String]] = @@ -297,7 +300,7 @@ object Trace { override def spanR( name: String, - options: Span.Options + options: Span.Options ): Resource[StateT[F, S, *], StateT[F, S, *] ~> StateT[F, S, *]] = Resource( StateT.liftF( @@ -314,7 +317,9 @@ object Trace { override def span[A](name: String)(k: StateT[F, S, A]): StateT[F, S, A] = StateT(s => trace.span[(S, A)](name)(k.run(s))) - override def span[A](name: String, options: Span.Options)(k: StateT[F, S, A]): StateT[F, S, A] = + override def span[A](name: String, options: Span.Options)( + k: StateT[F, S, A] + ): StateT[F, S, A] = StateT(s => trace.span[(S, A)](name, options)(k.run(s))) override def traceId: StateT[F, S, Option[String]] = @@ -363,7 +368,9 @@ object Trace { override def span[A](name: String)(k: EitherT[F, E, A]): EitherT[F, E, A] = EitherT(trace.span(name)(k.value)) - override def span[A](name: String, options: Span.Options)(k: EitherT[F, E, A]): EitherT[F, E, A] = + override def span[A](name: String, options: Span.Options)( + k: EitherT[F, E, A] + ): EitherT[F, E, A] = EitherT(trace.span(name, options)(k.value)) override def traceId: EitherT[F, E, Option[String]] = @@ -462,7 +469,9 @@ object Trace { override def span[A](name: String)(k: Nested[F, G, A]): Nested[F, G, A] = trace.span(name)(k.value).nested - override def span[A](name: String, options: Span.Options)(k: Nested[F, G, A]): Nested[F, G, A] = + override def span[A](name: String, options: Span.Options)( + k: Nested[F, G, A] + ): Nested[F, G, A] = trace.span(name, options)(k.value).nested override def traceId: Nested[F, G, Option[String]] = diff --git a/modules/core/shared/src/test/scala/InMemory.scala b/modules/core/shared/src/test/scala/InMemory.scala index 4dcf6c6f..a9f287e1 100644 --- a/modules/core/shared/src/test/scala/InMemory.scala +++ b/modules/core/shared/src/test/scala/InMemory.scala @@ -6,11 +6,12 @@ package natchez import java.net.URI -import cats.data.Chain +import cats.data.{Chain, Kleisli} import cats.effect.{IO, Ref, Resource} import natchez.Span.Options import cats.Applicative +import munit.CatsEffectSuite object InMemory { @@ -111,3 +112,52 @@ object InMemory { } } + +trait InMemorySuite extends CatsEffectSuite { + type Lineage = InMemory.Lineage + val Lineage = InMemory.Lineage + type NatchezCommand = InMemory.NatchezCommand + val NatchezCommand = InMemory.NatchezCommand + + trait TraceTest { + def program[F[_]: Trace]: F[Unit] + def expectedHistory: List[(Lineage, NatchezCommand)] + } + + def traceTest(name: String, tt: TraceTest) = { + test(s"$name - Kleisli")( + testTraceKleisli(tt.program[Kleisli[IO, Span[IO], *]](_), tt.expectedHistory) + ) + test(s"$name - IOLocal")(testTraceIoLocal(tt.program[IO](_), tt.expectedHistory)) + } + + def testTraceKleisli( + traceProgram: Trace[Kleisli[IO, Span[IO], *]] => Kleisli[IO, Span[IO], Unit], + expectedHistory: List[(Lineage, NatchezCommand)] + ) = testTrace[Kleisli[IO, Span[IO], *]]( + traceProgram, + root => IO.pure(Trace[Kleisli[IO, Span[IO], *]] -> (k => k.run(root))), + expectedHistory + ) + + def testTraceIoLocal( + traceProgram: Trace[IO] => IO[Unit], + expectedHistory: List[(Lineage, NatchezCommand)] + ) = testTrace[IO](traceProgram, Trace.ioTrace(_).map(_ -> identity), expectedHistory) + + def testTrace[F[_]]( + traceProgram: Trace[F] => F[Unit], + makeTraceAndResolver: Span[IO] => IO[(Trace[F], F[Unit] => IO[Unit])], + expectedHistory: List[(Lineage, NatchezCommand)] + ) = + InMemory.EntryPoint.create.flatMap { ep => + val traced = ep.root("root").use { r => + makeTraceAndResolver(r).flatMap { case (traceInstance, resolve) => + resolve(traceProgram(traceInstance)) + } + } + traced *> ep.ref.get.map { history => + assertEquals(history.toList, expectedHistory) + } + } +} diff --git a/modules/core/shared/src/test/scala/SpanCoalesceTest.scala b/modules/core/shared/src/test/scala/SpanCoalesceTest.scala index cc7d4ee2..a4c693e9 100644 --- a/modules/core/shared/src/test/scala/SpanCoalesceTest.scala +++ b/modules/core/shared/src/test/scala/SpanCoalesceTest.scala @@ -4,63 +4,42 @@ package natchez -import cats.data.Kleisli -import cats.effect.IO -import munit.CatsEffectSuite - -import InMemory.{Lineage, NatchezCommand} - -class SpanCoalesceTest extends CatsEffectSuite { - - test("suppress - nominal") { - def detailed[F[_]: Trace] = - Trace[F].span("parent")(Trace[F].span("child")(Trace[F].put("answer" -> 42))) - def suppressed[F[_]: Trace] = - Trace[F].span("suppressed", Span.Options.Suppress)(detailed[F]) - - testTraceKleisli(_ => suppressed[Kleisli[IO, Span[IO], *]], List( - (Lineage.Root, NatchezCommand.CreateRootSpan("root", Kernel(Map()))), - (Lineage.Root, NatchezCommand.CreateSpan("suppressed", None)), - (Lineage.Root, NatchezCommand.ReleaseSpan("suppressed")), - (Lineage.Root, NatchezCommand.ReleaseRootSpan("root")) - )) - } - - test("coaslesce - nominal") { - def detailed[F[_]: Trace] = - Trace[F].span("parent")(Trace[F].span("child")(Trace[F].put("answer" -> 42))) - def coaslesced[F[_]: Trace] = - Trace[F].span("coalesced", Span.Options.Coalesce)(detailed[F]) - - testTraceKleisli(_ => coaslesced[Kleisli[IO, Span[IO], *]], List( - (Lineage.Root, NatchezCommand.CreateRootSpan("root", Kernel(Map()))), - (Lineage.Root, NatchezCommand.CreateSpan("coalesced", None)), - (Lineage.Root / "coalesced", NatchezCommand.Put(List("answer" -> 42))), - (Lineage.Root, NatchezCommand.ReleaseSpan("coalesced")), - (Lineage.Root, NatchezCommand.ReleaseRootSpan("root")) - )) - } - - def testTraceKleisli( - traceProgram: Trace[Kleisli[IO, Span[IO], *]] => Kleisli[IO, Span[IO], Unit], - expectedHistory: List[(Lineage, NatchezCommand)] - ) = testTrace[Kleisli[IO, Span[IO], *]](traceProgram, root => - IO.pure(Trace[Kleisli[IO, Span[IO], *]] -> (k => k.run(root))) - , expectedHistory) - - def testTrace[F[_]]( - traceProgram: Trace[F] => F[Unit], - makeTraceAndResolver: Span[IO] => IO[(Trace[F], F[Unit] => IO[Unit])], - expectedHistory: List[(Lineage, NatchezCommand)] - ) = - InMemory.EntryPoint.create.flatMap { ep => - val traced = ep.root("root").use { r => - makeTraceAndResolver(r).flatMap { case (traceInstance, resolve) => - resolve(traceProgram(traceInstance)) - } +class SpanCoalesceTest extends InMemorySuite { + + traceTest( + "suppress - nominal", + new TraceTest { + def program[F[_]: Trace] = { + def detailed = + Trace[F].span("parent")(Trace[F].span("child")(Trace[F].put("answer" -> 42))) + Trace[F].span("suppressed", Span.Options.Suppress)(detailed) } - traced *> ep.ref.get.map { history => - assertEquals(history.toList, expectedHistory) + + def expectedHistory = List( + (Lineage.Root, NatchezCommand.CreateRootSpan("root", Kernel(Map()))), + (Lineage.Root, NatchezCommand.CreateSpan("suppressed", None)), + (Lineage.Root, NatchezCommand.ReleaseSpan("suppressed")), + (Lineage.Root, NatchezCommand.ReleaseRootSpan("root")) + ) + } + ) + + traceTest( + "coaslesce - nominal", + new TraceTest { + def program[F[_]: Trace] = { + def detailed = + Trace[F].span("parent")(Trace[F].span("child")(Trace[F].put("answer" -> 42))) + Trace[F].span("coalesced", Span.Options.Coalesce)(detailed) } + + def expectedHistory = List( + (Lineage.Root, NatchezCommand.CreateRootSpan("root", Kernel(Map()))), + (Lineage.Root, NatchezCommand.CreateSpan("coalesced", None)), + (Lineage.Root / "coalesced", NatchezCommand.Put(List("answer" -> 42))), + (Lineage.Root, NatchezCommand.ReleaseSpan("coalesced")), + (Lineage.Root, NatchezCommand.ReleaseRootSpan("root")) + ) } -} \ No newline at end of file + ) +} diff --git a/modules/core/shared/src/test/scala/SpanPropagationTest.scala b/modules/core/shared/src/test/scala/SpanPropagationTest.scala index 74070cb9..13893e74 100644 --- a/modules/core/shared/src/test/scala/SpanPropagationTest.scala +++ b/modules/core/shared/src/test/scala/SpanPropagationTest.scala @@ -4,46 +4,23 @@ package natchez -import cats.data.Kleisli -import cats.effect.IO -import munit.CatsEffectSuite +class SpanPropagationTest extends InMemorySuite { -import InMemory.{Lineage, NatchezCommand} + traceTest( + "propagation", + new TraceTest { + def program[F[_]: Trace] = + Trace[F].span("parent")(Trace[F].span("child")(Trace[F].put("answer" -> 42))) -class SpanPropagationTest extends CatsEffectSuite { - def prg[F[_]: Trace] = - Trace[F].span("parent")(Trace[F].span("child")(Trace[F].put("answer" -> 42))) - - def testPropagation[F[_]](f: Span[IO] => IO[(Trace[F], F[Unit] => IO[Unit])]) = - InMemory.EntryPoint.create.flatMap { ep => - val traced = ep.root("root").use { r => - f(r).flatMap { case (traceInstance, resolve) => - resolve(prg(traceInstance)) - } - } - traced *> ep.ref.get.map { history => - assertEquals( - history.toList, - List( - (Lineage.Root, NatchezCommand.CreateRootSpan("root", Kernel(Map()))), - (Lineage.Root, NatchezCommand.CreateSpan("parent", None)), - (Lineage.Root / "parent", NatchezCommand.CreateSpan("child", None)), - (Lineage.Root / "parent" / "child", NatchezCommand.Put(List("answer" -> 42))), - (Lineage.Root / "parent", NatchezCommand.ReleaseSpan("child")), - (Lineage.Root, NatchezCommand.ReleaseSpan("parent")), - (Lineage.Root, NatchezCommand.ReleaseRootSpan("root")) - ) - ) - } + def expectedHistory = List( + (Lineage.Root, NatchezCommand.CreateRootSpan("root", Kernel(Map()))), + (Lineage.Root, NatchezCommand.CreateSpan("parent", None)), + (Lineage.Root / "parent", NatchezCommand.CreateSpan("child", None)), + (Lineage.Root / "parent" / "child", NatchezCommand.Put(List("answer" -> 42))), + (Lineage.Root / "parent", NatchezCommand.ReleaseSpan("child")), + (Lineage.Root, NatchezCommand.ReleaseSpan("parent")), + (Lineage.Root, NatchezCommand.ReleaseRootSpan("root")) + ) } - - test("kleisli") { - testPropagation[Kleisli[IO, Span[IO], *]](root => - IO.pure(Trace[Kleisli[IO, Span[IO], *]] -> (k => k.run(root))) - ) - } - - test("io") { - testPropagation[IO](root => Trace.ioTrace(root).map(_ -> identity)) - } + ) } From bf00ff7430619051ab792318a6210e41f37fd6ac Mon Sep 17 00:00:00 2001 From: mpilquist Date: Wed, 23 Nov 2022 19:32:35 -0500 Subject: [PATCH 3/8] Implement for each backend --- modules/core/shared/src/main/scala/Span.scala | 8 ++--- .../core/shared/src/main/scala/Trace.scala | 5 +-- .../core/shared/src/test/scala/InMemory.scala | 4 +-- .../datadog/src/main/scala/DDEntryPoint.scala | 4 +-- modules/datadog/src/main/scala/DDSpan.scala | 25 +++++--------- .../src/main/scala/HoneycombSpan.scala | 26 +++++++-------- .../src/main/scala/JaegerEntryPoint.scala | 4 +-- .../jaeger/src/main/scala/JaegerSpan.scala | 26 +++++---------- .../src/main/scala/LightstepEntryPoint.scala | 4 +-- .../src/main/scala/LightstepSpan.scala | 22 ++++--------- modules/log-odin/src/main/scala/LogSpan.scala | 24 +++++++------- .../log/shared/src/main/scala/LogSpan.scala | 31 +++++++++-------- .../shared/src/main/scala/LocalTrace.scala | 15 +++------ .../scala/natchez/newrelic/NewrelicSpan.scala | 25 +++++++------- .../noop/shared/src/main/scala/NoopSpan.scala | 2 +- .../shared/src/main/scala/NoopTrace.scala | 4 +-- .../src/main/scala/OpenCensusSpan.scala | 31 ++++++++--------- .../opentelemetry/OpenTelemetrySpan.scala | 31 ++++++++--------- .../main/scala/natchez/xray/XRaySpan.scala | 33 ++++++++----------- 19 files changed, 147 insertions(+), 177 deletions(-) diff --git a/modules/core/shared/src/main/scala/Span.scala b/modules/core/shared/src/main/scala/Span.scala index bca2e179..f99893fa 100644 --- a/modules/core/shared/src/main/scala/Span.scala +++ b/modules/core/shared/src/main/scala/Span.scala @@ -97,8 +97,7 @@ trait Span[F[_]] { object Span { - trait Default[F[_]] extends Span[F] { - protected implicit val applciativeInstance: Applicative[F] + abstract class Default[F[_]: Applicative] extends Span[F] { protected val spanCreationPolicy: Options.SpanCreationPolicy def span(name: String): Resource[F, Span[F]] = @@ -108,10 +107,11 @@ object Span { spanCreationPolicy match { case Options.SpanCreationPolicy.Suppress => Resource.pure(Span.noop[F]) case Options.SpanCreationPolicy.Coalesce => Resource.pure(this) - case Options.SpanCreationPolicy.Default => createSpan(name, options) + case Options.SpanCreationPolicy.Default => makeSpan(name, options) } - def createSpan(name: String, options: Options): Resource[F, Span[F]] + /** Like `span` but always creates a child span -- i.e., `options.spanCreationPolicy` is ignored. */ + def makeSpan(name: String, options: Options): Resource[F, Span[F]] } /** Ensure that Fields mixin data is added to a span when an error is raised. diff --git a/modules/core/shared/src/main/scala/Trace.scala b/modules/core/shared/src/main/scala/Trace.scala index 6db826b6..1094fc42 100644 --- a/modules/core/shared/src/main/scala/Trace.scala +++ b/modules/core/shared/src/main/scala/Trace.scala @@ -35,10 +35,11 @@ trait Trace[F[_]] { /** Creates a new span as a resource. */ def spanR(name: String, options: Span.Options = Span.Options.Defaults): Resource[F, F ~> F] + // TODO delete? /** Create a new span, and within it run the continuation `k`. */ - def span[A](name: String)(k: F[A]): F[A] + def span[A](name: String)(k: F[A]): F[A] = span(name, Span.Options.Defaults)(k) - /** Creates a new span as a resource. */ + /** Create a new span, and within it run the continuation `k`. */ def span[A](name: String, options: Span.Options = Span.Options.Defaults)(k: F[A]): F[A] /** A unique ID for this trace, if available. This can be useful to include in error messages for diff --git a/modules/core/shared/src/test/scala/InMemory.scala b/modules/core/shared/src/test/scala/InMemory.scala index a9f287e1..eecc8a13 100644 --- a/modules/core/shared/src/test/scala/InMemory.scala +++ b/modules/core/shared/src/test/scala/InMemory.scala @@ -10,7 +10,6 @@ import cats.data.{Chain, Kleisli} import cats.effect.{IO, Ref, Resource} import natchez.Span.Options -import cats.Applicative import munit.CatsEffectSuite object InMemory { @@ -21,7 +20,6 @@ object InMemory { ref: Ref[IO, Chain[(Lineage, NatchezCommand)]], val spanCreationPolicy: Options.SpanCreationPolicy ) extends natchez.Span.Default[IO] { - protected val applciativeInstance: Applicative[IO] = implicitly def put(fields: (String, natchez.TraceValue)*): IO[Unit] = ref.update(_.append(lineage -> NatchezCommand.Put(fields.toList))) @@ -38,7 +36,7 @@ object InMemory { def kernel: IO[Kernel] = ref.update(_.append(lineage -> NatchezCommand.AskKernel(k))).as(k) - def createSpan(name: String, options: Options): Resource[IO, natchez.Span[IO]] = { + def makeSpan(name: String, options: Options): Resource[IO, natchez.Span[IO]] = { val acquire = ref .update(_.append(lineage -> NatchezCommand.CreateSpan(name, options.parentKernel))) .as(new Span(lineage / name, k, ref, options.spanCreationPolicy)) diff --git a/modules/datadog/src/main/scala/DDEntryPoint.scala b/modules/datadog/src/main/scala/DDEntryPoint.scala index dae28280..313bf651 100644 --- a/modules/datadog/src/main/scala/DDEntryPoint.scala +++ b/modules/datadog/src/main/scala/DDEntryPoint.scala @@ -18,7 +18,7 @@ final class DDEntryPoint[F[_]: Sync](tracer: ot.Tracer, uriPrefix: Option[URI]) override def root(name: String): Resource[F, Span[F]] = Resource .make(Sync[F].delay(tracer.buildSpan(name).start()))(s => Sync[F].delay(s.finish())) - .map(DDSpan(tracer, _, uriPrefix)) + .map(DDSpan(tracer, _, uriPrefix, Span.Options.SpanCreationPolicy.Default)) override def continue(name: String, kernel: Kernel): Resource[F, Span[F]] = Resource @@ -31,7 +31,7 @@ final class DDEntryPoint[F[_]: Sync](tracer: ot.Tracer, uriPrefix: Option[URI]) tracer.buildSpan(name).asChildOf(spanContext).start() } )(s => Sync[F].delay(s.finish())) - .map(DDSpan(tracer, _, uriPrefix)) + .map(DDSpan(tracer, _, uriPrefix, Span.Options.SpanCreationPolicy.Default)) override def continueOrElseRoot(name: String, kernel: Kernel): Resource[F, Span[F]] = continue(name, kernel).flatMap { diff --git a/modules/datadog/src/main/scala/DDSpan.scala b/modules/datadog/src/main/scala/DDSpan.scala index caeabd2e..bcf30fb8 100644 --- a/modules/datadog/src/main/scala/DDSpan.scala +++ b/modules/datadog/src/main/scala/DDSpan.scala @@ -23,8 +23,9 @@ import java.net.URI final case class DDSpan[F[_]: Sync]( tracer: ot.Tracer, span: ot.Span, - uriPrefix: Option[URI] -) extends Span[F] { + uriPrefix: Option[URI], + spanCreationPolicy: Span.Options.SpanCreationPolicy +) extends Span.Default[F] { def kernel: F[Kernel] = Sync[F].delay { @@ -52,26 +53,16 @@ final case class DDSpan[F[_]: Sync]( override def log(event: String): F[Unit] = Sync[F].delay(span.log(event)).void - def span(name: String): Resource[F, Span[F]] = + override def makeSpan(name: String, options: Span.Options): Resource[F, Span[F]] = { + val parent = options.parentKernel.map(k => + tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(k.toHeaders.asJava))) Span.putErrorFields( Resource - .makeCase(Sync[F].delay(tracer.buildSpan(name).asChildOf(span).start)) { + .makeCase(Sync[F].delay(tracer.buildSpan(name).asChildOf(parent.orNull).asChildOf(span).start)) { case (span, ExitCase.Errored(e)) => Sync[F].delay(span.log(e.toString).finish()) case (span, _) => Sync[F].delay(span.finish()) } - .map(DDSpan(tracer, _, uriPrefix)) - ) - - def span(name: String, kernel: Kernel): Resource[F, Span[F]] = { - val parent = - tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(kernel.toHeaders.asJava)) - Span.putErrorFields( - Resource - .makeCase(Sync[F].delay(tracer.buildSpan(name).asChildOf(parent).asChildOf(span).start)) { - case (span, ExitCase.Errored(e)) => Sync[F].delay(span.log(e.toString).finish()) - case (span, _) => Sync[F].delay(span.finish()) - } - .map(DDSpan(tracer, _, uriPrefix)) + .map(DDSpan(tracer, _, uriPrefix, options.spanCreationPolicy)) ) } diff --git a/modules/honeycomb/src/main/scala/HoneycombSpan.scala b/modules/honeycomb/src/main/scala/HoneycombSpan.scala index f6a2132b..66c9bc0c 100644 --- a/modules/honeycomb/src/main/scala/HoneycombSpan.scala +++ b/modules/honeycomb/src/main/scala/HoneycombSpan.scala @@ -22,8 +22,9 @@ private[honeycomb] final case class HoneycombSpan[F[_]: Sync]( parentId: Option[UUID], traceUUID: UUID, timestamp: Instant, - fields: Ref[F, Map[String, TraceValue]] -) extends Span[F] { + fields: Ref[F, Map[String, TraceValue]], + spanCreationPolicy: Span.Options.SpanCreationPolicy +) extends Span.Default[F] { import HoneycombSpan._ def get(key: String): F[Option[TraceValue]] = @@ -46,15 +47,10 @@ private[honeycomb] final case class HoneycombSpan[F[_]: Sync]( override def log(event: String): F[Unit] = log("event" -> TraceValue.StringValue(event)) - def span(label: String): Resource[F, Span[F]] = - Span.putErrorFields( - Resource.makeCase(HoneycombSpan.child(this, label))(HoneycombSpan.finish[F]).widen - ) - - override def span(name: String, kernel: Kernel): Resource[F, Span[F]] = + override def makeSpan(name: String, options: Span.Options): Resource[F, Span[F]] = Span.putErrorFields( Resource - .makeCase(HoneycombSpan.fromKernel(client, name, kernel))(HoneycombSpan.finish[F]) + .makeCase(HoneycombSpan.child(this, name, options.spanCreationPolicy))(HoneycombSpan.finish[F]) .widen ) @@ -115,7 +111,8 @@ private[honeycomb] object HoneycombSpan { def child[F[_]: Sync]( parent: HoneycombSpan[F], - name: String + name: String, + spanCreationPolicy: Span.Options.SpanCreationPolicy ): F[HoneycombSpan[F]] = for { spanUUID <- uuid[F] @@ -128,7 +125,8 @@ private[honeycomb] object HoneycombSpan { parentId = Some(parent.spanUUID), traceUUID = parent.traceUUID, timestamp = timestamp, - fields = fields + fields = fields, + spanCreationPolicy = spanCreationPolicy ) def root[F[_]: Sync]( @@ -147,7 +145,8 @@ private[honeycomb] object HoneycombSpan { parentId = None, traceUUID = traceUUID, timestamp = timestamp, - fields = fields + fields = fields, + spanCreationPolicy = Span.Options.SpanCreationPolicy.Default ) def fromKernel[F[_]]( @@ -168,7 +167,8 @@ private[honeycomb] object HoneycombSpan { parentId = Some(parentId), traceUUID = traceUUID, timestamp = timestamp, - fields = fields + fields = fields, + spanCreationPolicy = Span.Options.SpanCreationPolicy.Default ) def fromKernelOrElseRoot[F[_]]( diff --git a/modules/jaeger/src/main/scala/JaegerEntryPoint.scala b/modules/jaeger/src/main/scala/JaegerEntryPoint.scala index 7f671e8d..faaceaee 100644 --- a/modules/jaeger/src/main/scala/JaegerEntryPoint.scala +++ b/modules/jaeger/src/main/scala/JaegerEntryPoint.scala @@ -27,12 +27,12 @@ final class JaegerEntryPoint[F[_]: Sync](tracer: ot.Tracer, uriPrefix: Option[UR tracer.buildSpan(name).asChildOf(p).start() } )(s => Sync[F].delay(s.finish)) - .map(JaegerSpan(tracer, _, uriPrefix)) + .map(JaegerSpan(tracer, _, uriPrefix, Span.Options.SpanCreationPolicy.Default)) def root(name: String): Resource[F, Span[F]] = Resource .make(Sync[F].delay(tracer.buildSpan(name).start()))(s => Sync[F].delay(s.finish)) - .map(JaegerSpan(tracer, _, uriPrefix)) + .map(JaegerSpan(tracer, _, uriPrefix, Span.Options.SpanCreationPolicy.Default)) def continueOrElseRoot(name: String, kernel: Kernel): Resource[F, Span[F]] = continue(name, kernel) diff --git a/modules/jaeger/src/main/scala/JaegerSpan.scala b/modules/jaeger/src/main/scala/JaegerSpan.scala index 96bbd966..21f58808 100644 --- a/modules/jaeger/src/main/scala/JaegerSpan.scala +++ b/modules/jaeger/src/main/scala/JaegerSpan.scala @@ -22,8 +22,9 @@ import java.net.URI private[jaeger] final case class JaegerSpan[F[_]: Sync]( tracer: ot.Tracer, span: ot.Span, - prefix: Option[URI] -) extends Span[F] { + prefix: Option[URI], + spanCreationPolicy: Span.Options.SpanCreationPolicy +) extends Span.Default[F] { import TraceValue._ override def kernel: F[Kernel] = @@ -68,25 +69,16 @@ private[jaeger] final case class JaegerSpan[F[_]: Sync]( override def log(event: String): F[Unit] = Sync[F].delay(span.log(event)).void - override def span(name: String): Resource[F, Span[F]] = - Span.putErrorFields { - Resource.makeCase( - Sync[F] - .delay(tracer.buildSpan(name).asChildOf(span).start) - .map(JaegerSpan(tracer, _, prefix)) - )(JaegerSpan.finish) - } - - override def span(name: String, kernel: Kernel): Resource[F, Span[F]] = + override def makeSpan(name: String, options: Span.Options): Resource[F, Span[F]] = Span.putErrorFields { Resource.makeCase { - val p = tracer.extract( + val p = options.parentKernel.map(k => tracer.extract( Format.Builtin.HTTP_HEADERS, - new TextMapAdapter(kernel.toHeaders.asJava) - ) + new TextMapAdapter(k.toHeaders.asJava) + )) Sync[F] - .delay(tracer.buildSpan(name).asChildOf(p).asChildOf(span).start) - .map(JaegerSpan(tracer, _, prefix)) + .delay(tracer.buildSpan(name).asChildOf(p.orNull).asChildOf(span).start) + .map(JaegerSpan(tracer, _, prefix, options.spanCreationPolicy)) }(JaegerSpan.finish) } diff --git a/modules/lightstep/src/main/scala/LightstepEntryPoint.scala b/modules/lightstep/src/main/scala/LightstepEntryPoint.scala index 6b57a8df..a3c08af9 100644 --- a/modules/lightstep/src/main/scala/LightstepEntryPoint.scala +++ b/modules/lightstep/src/main/scala/LightstepEntryPoint.scala @@ -16,7 +16,7 @@ final class LightstepEntryPoint[F[_]: Sync](tracer: Tracer) extends EntryPoint[F override def root(name: String): Resource[F, Span[F]] = Resource .make(Sync[F].delay(tracer.buildSpan(name).start()))(s => Sync[F].delay(s.finish())) - .map(LightstepSpan(tracer, _)) + .map(LightstepSpan(tracer, _, Span.Options.SpanCreationPolicy.Default)) override def continue(name: String, kernel: Kernel): Resource[F, Span[F]] = Resource @@ -27,7 +27,7 @@ final class LightstepEntryPoint[F[_]: Sync](tracer: Tracer) extends EntryPoint[F tracer.buildSpan(name).asChildOf(p).start() } )(s => Sync[F].delay(s.finish())) - .map(LightstepSpan(tracer, _)) + .map(LightstepSpan(tracer, _, Span.Options.SpanCreationPolicy.Default)) override def continueOrElseRoot(name: String, kernel: Kernel): Resource[F, Span[F]] = continue(name, kernel).flatMap { diff --git a/modules/lightstep/src/main/scala/LightstepSpan.scala b/modules/lightstep/src/main/scala/LightstepSpan.scala index 0b7f938e..0c7ef956 100644 --- a/modules/lightstep/src/main/scala/LightstepSpan.scala +++ b/modules/lightstep/src/main/scala/LightstepSpan.scala @@ -17,8 +17,9 @@ import java.net.URI private[lightstep] final case class LightstepSpan[F[_]: Sync]( tracer: ot.Tracer, - span: ot.Span -) extends Span[F] { + span: ot.Span, + spanCreationPolicy: Span.Options.SpanCreationPolicy +) extends Span.Default[F] { import TraceValue._ @@ -60,26 +61,17 @@ private[lightstep] final case class LightstepSpan[F[_]: Sync]( Sync[F].delay(span.log(map)).void } - override def span(name: String, kernel: Kernel): Resource[F, Span[F]] = { - val p = tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(kernel.toHeaders.asJava)) + override def makeSpan(name: String, options: Span.Options): Resource[F, Span[F]] = { + val p = options.parentKernel.map(k => tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(k.toHeaders.asJava))) Span.putErrorFields( Resource - .make(Sync[F].delay(tracer.buildSpan(name).asChildOf(p).asChildOf(span).start()))(s => + .make(Sync[F].delay(tracer.buildSpan(name).asChildOf(p.orNull).asChildOf(span).start()))(s => Sync[F].delay(s.finish()) ) - .map(LightstepSpan(tracer, _)) + .map(LightstepSpan(tracer, _, options.spanCreationPolicy)) ) } - override def span(name: String): Resource[F, Span[F]] = - Span.putErrorFields( - Resource - .make(Sync[F].delay(tracer.buildSpan(name).asChildOf(span).start()))(s => - Sync[F].delay(s.finish()) - ) - .map(LightstepSpan(tracer, _)) - ) - override def spanId: F[Option[String]] = Sync[F].pure { val rawId = span.context.toSpanId diff --git a/modules/log-odin/src/main/scala/LogSpan.scala b/modules/log-odin/src/main/scala/LogSpan.scala index 24e852f8..80b376a9 100644 --- a/modules/log-odin/src/main/scala/LogSpan.scala +++ b/modules/log-odin/src/main/scala/LogSpan.scala @@ -28,8 +28,9 @@ private[logodin] final case class LogSpan[F[_]: Sync: Logger]( tid: UUID, timestamp: Instant, fields: Ref[F, Map[String, Json]], - children: Ref[F, List[JsonObject]] -) extends Span[F] { + children: Ref[F, List[JsonObject]], + spanCreationPolicy: Span.Options.SpanCreationPolicy +) extends Span.Default[F] { import LogSpan._ def spanId: F[Option[String]] = @@ -68,11 +69,8 @@ private[logodin] final case class LogSpan[F[_]: Sync: Logger]( def log(fields: (String, TraceValue)*): F[Unit] = Applicative[F].unit - def span(label: String): Resource[F, Span[F]] = - Resource.makeCase(LogSpan.child(this, label))(LogSpan.finish[F]).widen - - override def span(name: String, kernel: Kernel): Resource[F, Span[F]] = - Resource.makeCase(LogSpan.fromKernel(service, name, kernel))(LogSpan.finish[F]).widen + def makeSpan(label: String, options: Span.Options): Resource[F, Span[F]] = + Resource.makeCase(LogSpan.child(this, label, options.spanCreationPolicy))(LogSpan.finish[F]).widen def json(finish: Instant, exitCase: ExitCase): F[JsonObject] = (fields.get, children.get).mapN { (fs, cs) => @@ -153,7 +151,8 @@ private[logodin] object LogSpan { def child[F[_]: Sync: Logger]( parent: LogSpan[F], - name: String + name: String, + spanCreationPolicy: Span.Options.SpanCreationPolicy ): F[LogSpan[F]] = for { spanId <- uuid[F] @@ -168,7 +167,8 @@ private[logodin] object LogSpan { tid = parent.tid, timestamp = timestamp, fields = fields, - children = children + children = children, + spanCreationPolicy = spanCreationPolicy ) def root[F[_]: Sync: Logger]( @@ -189,7 +189,8 @@ private[logodin] object LogSpan { tid = traceId, timestamp = timestamp, fields = fields, - children = children + children = children, + spanCreationPolicy = Span.Options.SpanCreationPolicy.Default ) def fromKernel[F[_]: Sync: Logger]( @@ -212,7 +213,8 @@ private[logodin] object LogSpan { tid = traceId, timestamp = timestamp, fields = fields, - children = children + children = children, + spanCreationPolicy = Span.Options.SpanCreationPolicy.Default ) def fromKernelOrElseRoot[F[_]: Sync: Logger]( diff --git a/modules/log/shared/src/main/scala/LogSpan.scala b/modules/log/shared/src/main/scala/LogSpan.scala index aabd2a27..963983e7 100644 --- a/modules/log/shared/src/main/scala/LogSpan.scala +++ b/modules/log/shared/src/main/scala/LogSpan.scala @@ -28,11 +28,13 @@ private[log] final case class LogSpan[F[_]: Sync: Logger]( name: String, sid: UUID, parent: Option[Either[UUID, LogSpan[F]]], + parentKernel: Option[Kernel], traceUUID: UUID, timestamp: Instant, fields: Ref[F, Map[String, Json]], - children: Ref[F, List[JsonObject]] -) extends Span[F] { + children: Ref[F, List[JsonObject]], + spanCreationPolicy: Span.Options.SpanCreationPolicy +) extends Span.Default[F] { import LogSpan._ def parentId: Option[UUID] = @@ -61,8 +63,8 @@ private[log] final case class LogSpan[F[_]: Sync: Logger]( override def log(event: String): F[Unit] = log("event" -> TraceValue.StringValue(event)) - def span(label: String): Resource[F, Span[F]] = - Span.putErrorFields(Resource.makeCase(LogSpan.child(this, label))(LogSpan.finishChild[F]).widen) + def makeSpan(label: String, options: Span.Options): Resource[F, Span[F]] = + Span.putErrorFields(Resource.makeCase(LogSpan.child(this, label, options))(LogSpan.finishChild[F]).widen) def attachError(err: Throwable): F[Unit] = putAny( @@ -99,12 +101,6 @@ private[log] final case class LogSpan[F[_]: Sync: Logger]( sid.toString.some.pure[F] def traceUri: F[Option[URI]] = none.pure[F] - - def span(name: String, kernel: Kernel): Resource[F, Span[F]] = - Span.putErrorFields( - Resource.makeCase(LogSpan.fromKernel(service, name, kernel))(LogSpan.finishChild[F]).widen - ) - } private[log] object LogSpan { @@ -163,7 +159,8 @@ private[log] object LogSpan { def child[F[_]: Sync: Logger]( parent: LogSpan[F], - name: String + name: String, + options: Span.Options ): F[LogSpan[F]] = for { spanId <- uuid[F] @@ -175,10 +172,12 @@ private[log] object LogSpan { name = name, sid = spanId, parent = Some(Right(parent)), + parentKernel = options.parentKernel, traceUUID = parent.traceUUID, timestamp = timestamp, fields = fields, - children = children + children = children, + spanCreationPolicy = options.spanCreationPolicy ) def root[F[_]: Sync: Logger]( @@ -196,10 +195,12 @@ private[log] object LogSpan { name = name, sid = spanId, parent = None, + parentKernel = None, traceUUID = traceUUID, timestamp = timestamp, fields = fields, - children = children + children = children, + spanCreationPolicy = Span.Options.SpanCreationPolicy.Default ) def fromKernel[F[_]: Sync: Logger]( @@ -219,10 +220,12 @@ private[log] object LogSpan { name = name, sid = spanId, parent = Some(Left(parentId)), + parentKernel = None, traceUUID = traceUUID, timestamp = timestamp, fields = fields, - children = children + children = children, + spanCreationPolicy = Span.Options.SpanCreationPolicy.Default ) def fromKernelOrElseRoot[F[_]: Sync: Logger]( diff --git a/modules/mtl/shared/src/main/scala/LocalTrace.scala b/modules/mtl/shared/src/main/scala/LocalTrace.scala index a84ed9e5..1e9505f4 100644 --- a/modules/mtl/shared/src/main/scala/LocalTrace.scala +++ b/modules/mtl/shared/src/main/scala/LocalTrace.scala @@ -32,10 +32,10 @@ private[mtl] class LocalTrace[F[_]](local: Local[F, Span[F]])(implicit override def log(event: String): F[Unit] = local.ask.flatMap(_.log(event)) - override def spanR(name: String, kernel: Option[Kernel]): Resource[F, F ~> F] = + override def spanR(name: String, options: Span.Options): Resource[F, F ~> F] = Resource( local.ask.flatMap(t => - kernel.map(t.span(name, _)).getOrElse(t.span(name)).allocated.map { case (child, release) => + t.span(name, options).allocated.map { case (child, release) => new (F ~> F) { def apply[A](fa: F[A]): F[A] = local.scope(fa)(child) @@ -44,18 +44,13 @@ private[mtl] class LocalTrace[F[_]](local: Local[F, Span[F]])(implicit ) ) - override def span[A](name: String)(k: F[A]): F[A] = + override def span[A](name: String, options: Span.Options)(k: F[A]): F[A] = local.ask.flatMap { span => - span.span(name).use { s => - ev.onError(local.scope(k)(s)) { case err => s.attachError(err) } + span.span(name, options).use { s => + local.scope(k)(s).onError { case err => s.attachError(err) } } } - override def span[A](name: String, kernel: Kernel)(k: F[A]): F[A] = - local.ask.flatMap { span => - span.span(name, kernel).use(local.scope(k)) - } - override def traceId: F[Option[String]] = local.ask.flatMap(_.traceId) diff --git a/modules/newrelic/src/main/scala/natchez/newrelic/NewrelicSpan.scala b/modules/newrelic/src/main/scala/natchez/newrelic/NewrelicSpan.scala index 2e3ac43c..0bcbe3db 100644 --- a/modules/newrelic/src/main/scala/natchez/newrelic/NewrelicSpan.scala +++ b/modules/newrelic/src/main/scala/natchez/newrelic/NewrelicSpan.scala @@ -27,8 +27,9 @@ private[newrelic] final case class NewrelicSpan[F[_]: Sync]( attributes: Ref[F, Attributes], children: Ref[F, List[Span]], parent: Option[Either[String, NewrelicSpan[F]]], - sender: SpanBatchSender -) extends natchez.Span[F] { + sender: SpanBatchSender, + spanCreationPolicy: natchez.Span.Options.SpanCreationPolicy +) extends natchez.Span.Default[F] { override def kernel: F[Kernel] = Sync[F].delay { @@ -55,13 +56,8 @@ private[newrelic] final case class NewrelicSpan[F[_]: Sync]( override def log(event: String): F[Unit] = Sync[F].unit - override def span(name: String, kernel: Kernel): Resource[F, natchez.Span[F]] = - Resource - .make(NewrelicSpan.fromKernel(service, name, kernel)(sender))(NewrelicSpan.finish[F]) - .widen - - override def span(name: String): Resource[F, natchez.Span[F]] = - Resource.make(NewrelicSpan.child(name, this))(NewrelicSpan.finish[F]).widen + override def makeSpan(name: String, options: natchez.Span.Options): Resource[F, natchez.Span[F]] = + Resource.make(NewrelicSpan.child(name, this, options.spanCreationPolicy))(NewrelicSpan.finish[F]).widen override def spanId: F[Option[String]] = id.some.pure[F] @@ -97,7 +93,8 @@ object NewrelicSpan { startTime = timestamp, attributes = attributes, children = children, - sender = sender + sender = sender, + spanCreationPolicy = natchez.Span.Options.SpanCreationPolicy.Default ) def root[F[_]: Sync](service: String, name: String, sender: SpanBatchSender): F[NewrelicSpan[F]] = @@ -116,10 +113,11 @@ object NewrelicSpan { attributes, children, None, - sender + sender, + spanCreationPolicy = natchez.Span.Options.SpanCreationPolicy.Default ) - def child[F[_]: Sync](name: String, parent: NewrelicSpan[F]): F[NewrelicSpan[F]] = + def child[F[_]: Sync](name: String, parent: NewrelicSpan[F], spanCreationPolicy: natchez.Span.Options.SpanCreationPolicy): F[NewrelicSpan[F]] = for { spanId <- Sync[F].delay(UUID.randomUUID().toString) startTime <- Sync[F].delay(System.currentTimeMillis()) @@ -134,7 +132,8 @@ object NewrelicSpan { attributes, children, Some(Right(parent)), - parent.sender + parent.sender, + spanCreationPolicy = spanCreationPolicy ) def finish[F[_]: Sync](nrs: NewrelicSpan[F]): F[Unit] = diff --git a/modules/noop/shared/src/main/scala/NoopSpan.scala b/modules/noop/shared/src/main/scala/NoopSpan.scala index 655126b2..e4535ee9 100644 --- a/modules/noop/shared/src/main/scala/NoopSpan.scala +++ b/modules/noop/shared/src/main/scala/NoopSpan.scala @@ -30,7 +30,7 @@ final case class NoopSpan[F[_]: Applicative]() extends Span[F] { override def span(name: String): Resource[F, Span[F]] = Resource.eval(NoopSpan[F]().pure[F]) - override def span(name: String, kernel: Kernel): Resource[F, Span[F]] = + override def span(name: String, options: Span.Options): Resource[F, Span[F]] = Resource.eval(NoopSpan[F]().pure[F]) // TODO diff --git a/modules/noop/shared/src/main/scala/NoopTrace.scala b/modules/noop/shared/src/main/scala/NoopTrace.scala index 5afa8abc..f916e4ab 100644 --- a/modules/noop/shared/src/main/scala/NoopTrace.scala +++ b/modules/noop/shared/src/main/scala/NoopTrace.scala @@ -26,13 +26,13 @@ final case class NoopTrace[F[_]: Applicative]() extends Trace[F] { override def log(event: String): F[Unit] = Applicative[F].unit - override def spanR(name: String, kernel: Option[Kernel] = None): Resource[F, F ~> F] = + override def spanR(name: String, options: Span.Options): Resource[F, F ~> F] = Resource.pure(FunctionK.id) override def span[A](name: String)(k: F[A]): F[A] = k - override def span[A](name: String, kernel: Kernel)(k: F[A]): F[A] = + override def span[A](name: String, options: Span.Options)(k: F[A]): F[A] = k def traceId: F[Option[String]] = diff --git a/modules/opencensus/src/main/scala/OpenCensusSpan.scala b/modules/opencensus/src/main/scala/OpenCensusSpan.scala index 7b6ddfd4..e54031e6 100644 --- a/modules/opencensus/src/main/scala/OpenCensusSpan.scala +++ b/modules/opencensus/src/main/scala/OpenCensusSpan.scala @@ -21,8 +21,9 @@ import scala.jdk.CollectionConverters._ private[opencensus] final case class OpenCensusSpan[F[_]: Sync]( tracer: Tracer, - span: io.opencensus.trace.Span -) extends Span[F] { + span: io.opencensus.trace.Span, + spanCreationPolicy: Span.Options.SpanCreationPolicy +) extends Span.Default[F] { import OpenCensusSpan._ @@ -56,19 +57,17 @@ private[opencensus] final case class OpenCensusSpan[F[_]: Sync]( Kernel(headers.toMap) } - override def span(name: String, kernel: Kernel): Resource[F, Span[F]] = Span.putErrorFields( + override def makeSpan(name: String, options: Span.Options): Resource[F, Span[F]] = Span.putErrorFields( Resource - .makeCase(OpenCensusSpan.fromKernelWithSpan(tracer, name, kernel, span))( + .makeCase(options.parentKernel match { + case None => OpenCensusSpan.child(this, name, options.spanCreationPolicy) + case Some(k) => OpenCensusSpan.fromKernelWithSpan(tracer, name, k, span, options.spanCreationPolicy) + })( OpenCensusSpan.finish ) .widen ) - override def span(name: String): Resource[F, Span[F]] = - Span.putErrorFields( - Resource.makeCase(OpenCensusSpan.child(this, name))(OpenCensusSpan.finish).widen - ) - def traceId: F[Option[String]] = Sync[F].pure { val rawId = span.getContext.getTraceId.toLowerBase16 @@ -116,7 +115,8 @@ private[opencensus] object OpenCensusSpan { def child[F[_]: Sync]( parent: OpenCensusSpan[F], - name: String + name: String, + spanCreationPolicy: Span.Options.SpanCreationPolicy ): F[OpenCensusSpan[F]] = Sync[F] .delay( @@ -124,7 +124,7 @@ private[opencensus] object OpenCensusSpan { .spanBuilderWithExplicitParent(name, parent.span) .startSpan() ) - .map(OpenCensusSpan(parent.tracer, _)) + .map(OpenCensusSpan(parent.tracer, _, spanCreationPolicy)) def root[F[_]: Sync]( tracer: Tracer, @@ -138,13 +138,14 @@ private[opencensus] object OpenCensusSpan { .setSampler(sampler) .startSpan() ) - .map(OpenCensusSpan(tracer, _)) + .map(OpenCensusSpan(tracer, _, Span.Options.SpanCreationPolicy.Default)) def fromKernelWithSpan[F[_]: Sync]( tracer: Tracer, name: String, kernel: Kernel, - span: io.opencensus.trace.Span + span: io.opencensus.trace.Span, + spanCreationPolicy: Span.Options.SpanCreationPolicy ): F[OpenCensusSpan[F]] = Sync[F] .delay { val ctx = Tracing.getPropagationComponent.getB3Format @@ -154,7 +155,7 @@ private[opencensus] object OpenCensusSpan { .setParentLinks(List(span).asJava) .startSpan() } - .map(OpenCensusSpan(tracer, _)) + .map(OpenCensusSpan(tracer, _, spanCreationPolicy)) def fromKernel[F[_]: Sync]( tracer: Tracer, @@ -167,7 +168,7 @@ private[opencensus] object OpenCensusSpan { .extract(kernel, spanContextGetter) tracer.spanBuilderWithRemoteParent(name, ctx).startSpan() } - .map(OpenCensusSpan(tracer, _)) + .map(OpenCensusSpan(tracer, _, Span.Options.SpanCreationPolicy.Default)) def fromKernelOrElseRoot[F[_]]( tracer: Tracer, diff --git a/modules/opentelemetry/src/main/scala/natchez/opentelemetry/OpenTelemetrySpan.scala b/modules/opentelemetry/src/main/scala/natchez/opentelemetry/OpenTelemetrySpan.scala index 30f2efcd..bf1eb277 100644 --- a/modules/opentelemetry/src/main/scala/natchez/opentelemetry/OpenTelemetrySpan.scala +++ b/modules/opentelemetry/src/main/scala/natchez/opentelemetry/OpenTelemetrySpan.scala @@ -27,8 +27,9 @@ private[opentelemetry] final case class OpenTelemetrySpan[F[_]: Sync]( otel: OTel, tracer: Tracer, span: TSpan, - prefix: Option[URI] -) extends Span[F] { + prefix: Option[URI], + spanCreationPolicy: Span.Options.SpanCreationPolicy +) extends Span.Default[F] { import OpenTelemetrySpan._ @@ -82,19 +83,17 @@ private[opentelemetry] final case class OpenTelemetrySpan[F[_]: Sync]( override def log(event: String): F[Unit] = Sync[F].delay(span.addEvent(event)).void - override def span(name: String, kernel: Kernel): Resource[F, Span[F]] = Span.putErrorFields( + override def makeSpan(name: String, options: Span.Options): Resource[F, Span[F]] = Span.putErrorFields( Resource - .makeCase(OpenTelemetrySpan.fromKernelWithSpan(otel, tracer, name, kernel, span, prefix))( + .makeCase(options.parentKernel match { + case None => OpenTelemetrySpan.child(this, name, options.spanCreationPolicy) + case Some(k) => OpenTelemetrySpan.fromKernelWithSpan(otel, tracer, name, k, span, prefix, options.spanCreationPolicy) + })( OpenTelemetrySpan.finish ) .widen ) - override def span(name: String): Resource[F, Span[F]] = - Span.putErrorFields( - Resource.makeCase(OpenTelemetrySpan.child(this, name))(OpenTelemetrySpan.finish).widen - ) - override def spanId: F[Option[String]] = Sync[F].pure { val rawId = span.getSpanContext.getSpanId @@ -138,7 +137,8 @@ private[opentelemetry] object OpenTelemetrySpan { def child[F[_]: Sync]( parent: OpenTelemetrySpan[F], - name: String + name: String, + spanCreationPolicy: Span.Options.SpanCreationPolicy ): F[OpenTelemetrySpan[F]] = Sync[F] .delay( @@ -147,7 +147,7 @@ private[opentelemetry] object OpenTelemetrySpan { .setParent(Context.current().`with`(parent.span)) .startSpan() ) - .map(OpenTelemetrySpan(parent.otel, parent.tracer, _, parent.prefix)) + .map(OpenTelemetrySpan(parent.otel, parent.tracer, _, parent.prefix, spanCreationPolicy)) def root[F[_]: Sync]( otel: OTel, @@ -161,7 +161,7 @@ private[opentelemetry] object OpenTelemetrySpan { .spanBuilder(name) .startSpan() ) - .map(OpenTelemetrySpan(otel, tracer, _, prefix)) + .map(OpenTelemetrySpan(otel, tracer, _, prefix, Span.Options.SpanCreationPolicy.Default)) def fromKernelWithSpan[F[_]: Sync]( sdk: OTel, @@ -169,14 +169,15 @@ private[opentelemetry] object OpenTelemetrySpan { name: String, kernel: Kernel, span: TSpan, - prefix: Option[URI] + prefix: Option[URI], + spanCreationPolicy: Span.Options.SpanCreationPolicy ): F[OpenTelemetrySpan[F]] = Sync[F] .delay { val ctx = sdk.getPropagators.getTextMapPropagator .extract(Context.current(), kernel, spanContextGetter) tracer.spanBuilder(name).setParent(ctx).addLink(span.getSpanContext).startSpan } - .map(OpenTelemetrySpan(sdk, tracer, _, prefix)) + .map(OpenTelemetrySpan(sdk, tracer, _, prefix, spanCreationPolicy)) def fromKernel[F[_]: Sync]( otel: OTel, @@ -191,7 +192,7 @@ private[opentelemetry] object OpenTelemetrySpan { .extract(Context.current(), kernel, spanContextGetter) tracer.spanBuilder(name).setParent(ctx).startSpan() } - .map(OpenTelemetrySpan(otel, tracer, _, prefix)) + .map(OpenTelemetrySpan(otel, tracer, _, prefix, Span.Options.SpanCreationPolicy.Default)) def fromKernelOrElseRoot[F[_]]( otel: OTel, diff --git a/modules/xray/src/main/scala/natchez/xray/XRaySpan.scala b/modules/xray/src/main/scala/natchez/xray/XRaySpan.scala index 66dcbc3a..c4ed81b1 100644 --- a/modules/xray/src/main/scala/natchez/xray/XRaySpan.scala +++ b/modules/xray/src/main/scala/natchez/xray/XRaySpan.scala @@ -34,8 +34,9 @@ private[xray] final case class XRaySpan[F[_]: Concurrent: Clock: Random]( startTime: FiniteDuration, fields: Ref[F, Map[String, Json]], children: Ref[F, List[JsonObject]], - sampled: Boolean -) extends Span[F] { + sampled: Boolean, + spanCreationPolicy: Span.Options.SpanCreationPolicy +) extends Span.Default[F] { import XRaySpan._ def put(fields: (String, TraceValue)*): F[Unit] = { @@ -53,10 +54,9 @@ private[xray] final case class XRaySpan[F[_]: Concurrent: Clock: Random]( def log(fields: (String, TraceValue)*): F[Unit] = Applicative[F].unit - def span(name: String): Resource[F, Span[F]] = - Resource.makeCase(XRaySpan.child(this, name))( - XRaySpan.finish[F](_, entry, _) - ) + override def makeSpan(name: String, options: Span.Options): Resource[F, Span[F]] = + Resource.makeCase(XRaySpan.child(this, name, options.spanCreationPolicy))(XRaySpan.finish[F](_, entry, _)) + def traceId: F[Option[String]] = xrayTraceId.some.pure[F] @@ -128,15 +128,6 @@ private[xray] final case class XRaySpan[F[_]: Concurrent: Clock: Random]( private def header: String = encodeHeader(xrayTraceId, Some(segmentId), sampled) - - override def span(name: String, kernel: Kernel): Resource[F, Span[F]] = - Resource.makeCase( - kernel.toHeaders - .get(Header) - .flatMap(parseHeader) - .traverse(XRaySpan.fromHeader(name, _, entry)) - .map(_.getOrElse(this)) - )(XRaySpan.finish[F](_, entry, _)) } private[xray] object XRaySpan { @@ -231,7 +222,8 @@ private[xray] object XRaySpan { fields = fields, children = children, parent = header.parentId.map(_.asLeft), - sampled = header.sampled + sampled = header.sampled, + spanCreationPolicy = Span.Options.SpanCreationPolicy.Default ) } @@ -285,13 +277,15 @@ private[xray] object XRaySpan { fields = fields, children = children, parent = None, - sampled = true + sampled = true, + spanCreationPolicy = Span.Options.SpanCreationPolicy.Default ) } def child[F[_]: Concurrent: Clock: Random]( parent: XRaySpan[F], - name: String + name: String, + spanCreationPolicy: Span.Options.SpanCreationPolicy ): F[XRaySpan[F]] = ( segmentId[F], @@ -308,7 +302,8 @@ private[xray] object XRaySpan { fields = fields, children = children, parent = Some(Right(parent)), - sampled = parent.sampled + sampled = parent.sampled, + spanCreationPolicy = spanCreationPolicy ) } From c21c64586e9675770710d0e11dae4a131d2b006c Mon Sep 17 00:00:00 2001 From: mpilquist Date: Wed, 23 Nov 2022 19:35:45 -0500 Subject: [PATCH 4/8] Scalafmt --- modules/datadog/src/main/scala/DDSpan.scala | 7 ++++-- .../src/main/scala/HoneycombSpan.scala | 4 +++- .../jaeger/src/main/scala/JaegerSpan.scala | 10 ++++---- .../src/main/scala/LightstepSpan.scala | 8 ++++--- modules/log-odin/src/main/scala/LogSpan.scala | 4 +++- .../log/shared/src/main/scala/LogSpan.scala | 4 +++- .../scala/natchez/newrelic/NewrelicSpan.scala | 10 ++++++-- .../src/main/scala/OpenCensusSpan.scala | 22 ++++++++++-------- .../opentelemetry/OpenTelemetrySpan.scala | 23 +++++++++++-------- .../main/scala/natchez/xray/XRaySpan.scala | 5 ++-- 10 files changed, 61 insertions(+), 36 deletions(-) diff --git a/modules/datadog/src/main/scala/DDSpan.scala b/modules/datadog/src/main/scala/DDSpan.scala index bcf30fb8..25075087 100644 --- a/modules/datadog/src/main/scala/DDSpan.scala +++ b/modules/datadog/src/main/scala/DDSpan.scala @@ -55,10 +55,13 @@ final case class DDSpan[F[_]: Sync]( override def makeSpan(name: String, options: Span.Options): Resource[F, Span[F]] = { val parent = options.parentKernel.map(k => - tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(k.toHeaders.asJava))) + tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(k.toHeaders.asJava)) + ) Span.putErrorFields( Resource - .makeCase(Sync[F].delay(tracer.buildSpan(name).asChildOf(parent.orNull).asChildOf(span).start)) { + .makeCase( + Sync[F].delay(tracer.buildSpan(name).asChildOf(parent.orNull).asChildOf(span).start) + ) { case (span, ExitCase.Errored(e)) => Sync[F].delay(span.log(e.toString).finish()) case (span, _) => Sync[F].delay(span.finish()) } diff --git a/modules/honeycomb/src/main/scala/HoneycombSpan.scala b/modules/honeycomb/src/main/scala/HoneycombSpan.scala index 66c9bc0c..401c6833 100644 --- a/modules/honeycomb/src/main/scala/HoneycombSpan.scala +++ b/modules/honeycomb/src/main/scala/HoneycombSpan.scala @@ -50,7 +50,9 @@ private[honeycomb] final case class HoneycombSpan[F[_]: Sync]( override def makeSpan(name: String, options: Span.Options): Resource[F, Span[F]] = Span.putErrorFields( Resource - .makeCase(HoneycombSpan.child(this, name, options.spanCreationPolicy))(HoneycombSpan.finish[F]) + .makeCase(HoneycombSpan.child(this, name, options.spanCreationPolicy))( + HoneycombSpan.finish[F] + ) .widen ) diff --git a/modules/jaeger/src/main/scala/JaegerSpan.scala b/modules/jaeger/src/main/scala/JaegerSpan.scala index 21f58808..a1a5800c 100644 --- a/modules/jaeger/src/main/scala/JaegerSpan.scala +++ b/modules/jaeger/src/main/scala/JaegerSpan.scala @@ -72,10 +72,12 @@ private[jaeger] final case class JaegerSpan[F[_]: Sync]( override def makeSpan(name: String, options: Span.Options): Resource[F, Span[F]] = Span.putErrorFields { Resource.makeCase { - val p = options.parentKernel.map(k => tracer.extract( - Format.Builtin.HTTP_HEADERS, - new TextMapAdapter(k.toHeaders.asJava) - )) + val p = options.parentKernel.map(k => + tracer.extract( + Format.Builtin.HTTP_HEADERS, + new TextMapAdapter(k.toHeaders.asJava) + ) + ) Sync[F] .delay(tracer.buildSpan(name).asChildOf(p.orNull).asChildOf(span).start) .map(JaegerSpan(tracer, _, prefix, options.spanCreationPolicy)) diff --git a/modules/lightstep/src/main/scala/LightstepSpan.scala b/modules/lightstep/src/main/scala/LightstepSpan.scala index 0c7ef956..a21dc590 100644 --- a/modules/lightstep/src/main/scala/LightstepSpan.scala +++ b/modules/lightstep/src/main/scala/LightstepSpan.scala @@ -62,11 +62,13 @@ private[lightstep] final case class LightstepSpan[F[_]: Sync]( } override def makeSpan(name: String, options: Span.Options): Resource[F, Span[F]] = { - val p = options.parentKernel.map(k => tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(k.toHeaders.asJava))) + val p = options.parentKernel.map(k => + tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(k.toHeaders.asJava)) + ) Span.putErrorFields( Resource - .make(Sync[F].delay(tracer.buildSpan(name).asChildOf(p.orNull).asChildOf(span).start()))(s => - Sync[F].delay(s.finish()) + .make(Sync[F].delay(tracer.buildSpan(name).asChildOf(p.orNull).asChildOf(span).start()))( + s => Sync[F].delay(s.finish()) ) .map(LightstepSpan(tracer, _, options.spanCreationPolicy)) ) diff --git a/modules/log-odin/src/main/scala/LogSpan.scala b/modules/log-odin/src/main/scala/LogSpan.scala index 80b376a9..935ce353 100644 --- a/modules/log-odin/src/main/scala/LogSpan.scala +++ b/modules/log-odin/src/main/scala/LogSpan.scala @@ -70,7 +70,9 @@ private[logodin] final case class LogSpan[F[_]: Sync: Logger]( def log(fields: (String, TraceValue)*): F[Unit] = Applicative[F].unit def makeSpan(label: String, options: Span.Options): Resource[F, Span[F]] = - Resource.makeCase(LogSpan.child(this, label, options.spanCreationPolicy))(LogSpan.finish[F]).widen + Resource + .makeCase(LogSpan.child(this, label, options.spanCreationPolicy))(LogSpan.finish[F]) + .widen def json(finish: Instant, exitCase: ExitCase): F[JsonObject] = (fields.get, children.get).mapN { (fs, cs) => diff --git a/modules/log/shared/src/main/scala/LogSpan.scala b/modules/log/shared/src/main/scala/LogSpan.scala index 963983e7..4841ff76 100644 --- a/modules/log/shared/src/main/scala/LogSpan.scala +++ b/modules/log/shared/src/main/scala/LogSpan.scala @@ -64,7 +64,9 @@ private[log] final case class LogSpan[F[_]: Sync: Logger]( log("event" -> TraceValue.StringValue(event)) def makeSpan(label: String, options: Span.Options): Resource[F, Span[F]] = - Span.putErrorFields(Resource.makeCase(LogSpan.child(this, label, options))(LogSpan.finishChild[F]).widen) + Span.putErrorFields( + Resource.makeCase(LogSpan.child(this, label, options))(LogSpan.finishChild[F]).widen + ) def attachError(err: Throwable): F[Unit] = putAny( diff --git a/modules/newrelic/src/main/scala/natchez/newrelic/NewrelicSpan.scala b/modules/newrelic/src/main/scala/natchez/newrelic/NewrelicSpan.scala index 0bcbe3db..922b9494 100644 --- a/modules/newrelic/src/main/scala/natchez/newrelic/NewrelicSpan.scala +++ b/modules/newrelic/src/main/scala/natchez/newrelic/NewrelicSpan.scala @@ -57,7 +57,9 @@ private[newrelic] final case class NewrelicSpan[F[_]: Sync]( override def log(event: String): F[Unit] = Sync[F].unit override def makeSpan(name: String, options: natchez.Span.Options): Resource[F, natchez.Span[F]] = - Resource.make(NewrelicSpan.child(name, this, options.spanCreationPolicy))(NewrelicSpan.finish[F]).widen + Resource + .make(NewrelicSpan.child(name, this, options.spanCreationPolicy))(NewrelicSpan.finish[F]) + .widen override def spanId: F[Option[String]] = id.some.pure[F] @@ -117,7 +119,11 @@ object NewrelicSpan { spanCreationPolicy = natchez.Span.Options.SpanCreationPolicy.Default ) - def child[F[_]: Sync](name: String, parent: NewrelicSpan[F], spanCreationPolicy: natchez.Span.Options.SpanCreationPolicy): F[NewrelicSpan[F]] = + def child[F[_]: Sync]( + name: String, + parent: NewrelicSpan[F], + spanCreationPolicy: natchez.Span.Options.SpanCreationPolicy + ): F[NewrelicSpan[F]] = for { spanId <- Sync[F].delay(UUID.randomUUID().toString) startTime <- Sync[F].delay(System.currentTimeMillis()) diff --git a/modules/opencensus/src/main/scala/OpenCensusSpan.scala b/modules/opencensus/src/main/scala/OpenCensusSpan.scala index e54031e6..a4e28bed 100644 --- a/modules/opencensus/src/main/scala/OpenCensusSpan.scala +++ b/modules/opencensus/src/main/scala/OpenCensusSpan.scala @@ -57,16 +57,18 @@ private[opencensus] final case class OpenCensusSpan[F[_]: Sync]( Kernel(headers.toMap) } - override def makeSpan(name: String, options: Span.Options): Resource[F, Span[F]] = Span.putErrorFields( - Resource - .makeCase(options.parentKernel match { - case None => OpenCensusSpan.child(this, name, options.spanCreationPolicy) - case Some(k) => OpenCensusSpan.fromKernelWithSpan(tracer, name, k, span, options.spanCreationPolicy) - })( - OpenCensusSpan.finish - ) - .widen - ) + override def makeSpan(name: String, options: Span.Options): Resource[F, Span[F]] = + Span.putErrorFields( + Resource + .makeCase(options.parentKernel match { + case None => OpenCensusSpan.child(this, name, options.spanCreationPolicy) + case Some(k) => + OpenCensusSpan.fromKernelWithSpan(tracer, name, k, span, options.spanCreationPolicy) + })( + OpenCensusSpan.finish + ) + .widen + ) def traceId: F[Option[String]] = Sync[F].pure { diff --git a/modules/opentelemetry/src/main/scala/natchez/opentelemetry/OpenTelemetrySpan.scala b/modules/opentelemetry/src/main/scala/natchez/opentelemetry/OpenTelemetrySpan.scala index bf1eb277..fc3ecffd 100644 --- a/modules/opentelemetry/src/main/scala/natchez/opentelemetry/OpenTelemetrySpan.scala +++ b/modules/opentelemetry/src/main/scala/natchez/opentelemetry/OpenTelemetrySpan.scala @@ -83,16 +83,19 @@ private[opentelemetry] final case class OpenTelemetrySpan[F[_]: Sync]( override def log(event: String): F[Unit] = Sync[F].delay(span.addEvent(event)).void - override def makeSpan(name: String, options: Span.Options): Resource[F, Span[F]] = Span.putErrorFields( - Resource - .makeCase(options.parentKernel match { - case None => OpenTelemetrySpan.child(this, name, options.spanCreationPolicy) - case Some(k) => OpenTelemetrySpan.fromKernelWithSpan(otel, tracer, name, k, span, prefix, options.spanCreationPolicy) - })( - OpenTelemetrySpan.finish - ) - .widen - ) + override def makeSpan(name: String, options: Span.Options): Resource[F, Span[F]] = + Span.putErrorFields( + Resource + .makeCase(options.parentKernel match { + case None => OpenTelemetrySpan.child(this, name, options.spanCreationPolicy) + case Some(k) => + OpenTelemetrySpan + .fromKernelWithSpan(otel, tracer, name, k, span, prefix, options.spanCreationPolicy) + })( + OpenTelemetrySpan.finish + ) + .widen + ) override def spanId: F[Option[String]] = Sync[F].pure { diff --git a/modules/xray/src/main/scala/natchez/xray/XRaySpan.scala b/modules/xray/src/main/scala/natchez/xray/XRaySpan.scala index c4ed81b1..665b71e1 100644 --- a/modules/xray/src/main/scala/natchez/xray/XRaySpan.scala +++ b/modules/xray/src/main/scala/natchez/xray/XRaySpan.scala @@ -55,8 +55,9 @@ private[xray] final case class XRaySpan[F[_]: Concurrent: Clock: Random]( def log(fields: (String, TraceValue)*): F[Unit] = Applicative[F].unit override def makeSpan(name: String, options: Span.Options): Resource[F, Span[F]] = - Resource.makeCase(XRaySpan.child(this, name, options.spanCreationPolicy))(XRaySpan.finish[F](_, entry, _)) - + Resource.makeCase(XRaySpan.child(this, name, options.spanCreationPolicy))( + XRaySpan.finish[F](_, entry, _) + ) def traceId: F[Option[String]] = xrayTraceId.some.pure[F] From 1b792c2ea340f28732c190be38f4adee6db58e72 Mon Sep 17 00:00:00 2001 From: mpilquist Date: Wed, 23 Nov 2022 19:44:36 -0500 Subject: [PATCH 5/8] Bump base version --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index ff9a216e..b26d9618 100644 --- a/build.sbt +++ b/build.sbt @@ -1,6 +1,6 @@ import com.typesafe.tools.mima.core._ -ThisBuild / tlBaseVersion := "0.2" +ThisBuild / tlBaseVersion := "0.3" val scala212Version = "2.12.17" val scala213Version = "2.13.10" From b5574cd7a365546da55a95934374064802a46735 Mon Sep 17 00:00:00 2001 From: mpilquist Date: Wed, 23 Nov 2022 20:24:41 -0500 Subject: [PATCH 6/8] Remove unnecessary overloads --- modules/core/shared/src/main/scala/Span.scala | 19 ++------- .../core/shared/src/main/scala/Trace.scala | 39 ------------------- .../noop/shared/src/main/scala/NoopSpan.scala | 3 -- .../shared/src/main/scala/NoopTrace.scala | 3 -- 4 files changed, 4 insertions(+), 60 deletions(-) diff --git a/modules/core/shared/src/main/scala/Span.scala b/modules/core/shared/src/main/scala/Span.scala index f99893fa..dee4c89d 100644 --- a/modules/core/shared/src/main/scala/Span.scala +++ b/modules/core/shared/src/main/scala/Span.scala @@ -32,12 +32,8 @@ trait Span[F[_]] { */ def kernel: F[Kernel] - /** Resource that yields a child span with the given name. */ - def span(name: String): Resource[F, Span[F]] - - // TODO - /** Resource that yields a child span of both this span and the given kernel. */ - def span(name: String, options: Span.Options): Resource[F, Span[F]] + /** Resource that yields a child span of this span. */ + def span(name: String, options: Span.Options = Span.Options.Defaults): Resource[F, Span[F]] /** A unique ID for the trace of this span, if available. * This can be useful to include in error messages for example, so you can quickly find the associated trace. @@ -76,8 +72,8 @@ trait Span[F[_]] { override def log(fields: (String, TraceValue)*) = f(outer.log(fields: _*)) - override def span(name: String): Resource[G, Span[G]] = outer - .span(name) + override def span(name: String, options: Span.Options): Resource[G, Span[G]] = outer + .span(name, options) .map(_.mapK(f)) .mapK(f) @@ -86,11 +82,6 @@ trait Span[F[_]] { override def traceId: G[Option[String]] = f(outer.traceId) override def traceUri: G[Option[URI]] = f(outer.traceUri) - - override def span(name: String, options: Span.Options): Resource[G, Span[G]] = outer - .span(name, options) - .map(_.mapK(f)) - .mapK(f) } } } @@ -147,13 +138,11 @@ object Span { } private class NoopSpan[F[_]: Applicative] extends EphemeralSpan[F] { - def span(name: String): Resource[F, Span[F]] = Resource.pure(this) override def span(name: String, options: Span.Options): Resource[F, Span[F]] = Resource.pure(this) } private class RootsSpan[F[_]: Applicative](ep: EntryPoint[F]) extends EphemeralSpan[F] { - def span(name: String): Resource[F, Span[F]] = ep.root(name) override def span(name: String, options: Span.Options): Resource[F, Span[F]] = options.parentKernel.fold(ep.root(name))(ep.continueOrElseRoot(name, _)) } diff --git a/modules/core/shared/src/main/scala/Trace.scala b/modules/core/shared/src/main/scala/Trace.scala index 1094fc42..a80f0bf1 100644 --- a/modules/core/shared/src/main/scala/Trace.scala +++ b/modules/core/shared/src/main/scala/Trace.scala @@ -35,10 +35,6 @@ trait Trace[F[_]] { /** Creates a new span as a resource. */ def spanR(name: String, options: Span.Options = Span.Options.Defaults): Resource[F, F ~> F] - // TODO delete? - /** Create a new span, and within it run the continuation `k`. */ - def span[A](name: String)(k: F[A]): F[A] = span(name, Span.Options.Defaults)(k) - /** Create a new span, and within it run the continuation `k`. */ def span[A](name: String, options: Span.Options = Span.Options.Defaults)(k: F[A]): F[A] @@ -88,9 +84,6 @@ object Trace { .bracket(_ => fa.onError(child.attachError(_)))(_ => local.set(parent)) } - override def span[A](name: String)(k: IO[A]): IO[A] = - spanR(name).use(_(k)) - override def span[A](name: String, options: Span.Options)(k: IO[A]): IO[A] = spanR(name, options).use(_(k)) @@ -118,7 +111,6 @@ object Trace { override def log(event: String): F[Unit] = void override def spanR(name: String, options: Span.Options): Resource[F, F ~> F] = Resource.pure(FunctionK.id) - override def span[A](name: String)(k: F[A]): F[A] = k override def span[A](name: String, options: Span.Options)(k: F[A]): F[A] = k override def traceId: F[Option[String]] = none.pure[F] override def traceUri: F[Option[URI]] = none.pure[F] @@ -168,9 +160,6 @@ object Trace { ) ) - override def span[A](name: String)(k: Kleisli[F, Span[F], A]): Kleisli[F, Span[F], A] = - spanR(name).use(_(k)) - override def span[A](name: String, options: Span.Options)( k: Kleisli[F, Span[F], A] ): Kleisli[F, Span[F], A] = @@ -210,9 +199,6 @@ object Trace { ) ) - override def span[A](name: String)(k: Kleisli[F, E, A]): Kleisli[F, E, A] = - spanR(name).use(_(k)) - override def span[A](name: String, options: Span.Options)( k: Kleisli[F, E, A] ): Kleisli[F, E, A] = @@ -265,9 +251,6 @@ object Trace { ) ) - override def span[A](name: String)(k: Kleisli[F, E, A]): Kleisli[F, E, A] = - Kleisli(e => trace.span[A](name)(k.run(e))) - override def span[A](name: String, options: Span.Options)( k: ReaderT[F, E, A] ): ReaderT[F, E, A] = @@ -315,9 +298,6 @@ object Trace { ) ) - override def span[A](name: String)(k: StateT[F, S, A]): StateT[F, S, A] = - StateT(s => trace.span[(S, A)](name)(k.run(s))) - override def span[A](name: String, options: Span.Options)( k: StateT[F, S, A] ): StateT[F, S, A] = @@ -366,9 +346,6 @@ object Trace { ) ) - override def span[A](name: String)(k: EitherT[F, E, A]): EitherT[F, E, A] = - EitherT(trace.span(name)(k.value)) - override def span[A](name: String, options: Span.Options)( k: EitherT[F, E, A] ): EitherT[F, E, A] = @@ -415,9 +392,6 @@ object Trace { ) ) - override def span[A](name: String)(k: OptionT[F, A]): OptionT[F, A] = - OptionT(trace.span(name)(k.value)) - override def span[A](name: String, options: Span.Options)(k: OptionT[F, A]): OptionT[F, A] = OptionT(trace.span(name, options)(k.value)) @@ -467,9 +441,6 @@ object Trace { ) ) - override def span[A](name: String)(k: Nested[F, G, A]): Nested[F, G, A] = - trace.span(name)(k.value).nested - override def span[A](name: String, options: Span.Options)( k: Nested[F, G, A] ): Nested[F, G, A] = @@ -517,13 +488,6 @@ object Trace { ) ) - override def span[A](name: String)(k: Resource[F, A]): Resource[F, A] = - trace.spanR(name).flatMap { f => - Resource(f(k.allocated).map { case (a, release) => - a -> f(release) - }) - } - override def span[A](name: String, options: Span.Options)(k: Resource[F, A]): Resource[F, A] = trace.spanR(name, options).flatMap { f => Resource(f(k.allocated).map { case (a, release) => @@ -571,9 +535,6 @@ object Trace { ) ) - override def span[A](name: String)(k: Stream[F, A]): Stream[F, A] = - Stream.resource(trace.spanR(name)).flatMap(k.translate) - override def span[A](name: String, options: Span.Options)(k: Stream[F, A]): Stream[F, A] = Stream.resource(trace.spanR(name, options)).flatMap(k.translate) diff --git a/modules/noop/shared/src/main/scala/NoopSpan.scala b/modules/noop/shared/src/main/scala/NoopSpan.scala index e4535ee9..4633217c 100644 --- a/modules/noop/shared/src/main/scala/NoopSpan.scala +++ b/modules/noop/shared/src/main/scala/NoopSpan.scala @@ -27,9 +27,6 @@ final case class NoopSpan[F[_]: Applicative]() extends Span[F] { override def kernel: F[Kernel] = Applicative[F].pure(Kernel(Map.empty)) - override def span(name: String): Resource[F, Span[F]] = - Resource.eval(NoopSpan[F]().pure[F]) - override def span(name: String, options: Span.Options): Resource[F, Span[F]] = Resource.eval(NoopSpan[F]().pure[F]) diff --git a/modules/noop/shared/src/main/scala/NoopTrace.scala b/modules/noop/shared/src/main/scala/NoopTrace.scala index f916e4ab..d2e6b87b 100644 --- a/modules/noop/shared/src/main/scala/NoopTrace.scala +++ b/modules/noop/shared/src/main/scala/NoopTrace.scala @@ -29,9 +29,6 @@ final case class NoopTrace[F[_]: Applicative]() extends Trace[F] { override def spanR(name: String, options: Span.Options): Resource[F, F ~> F] = Resource.pure(FunctionK.id) - override def span[A](name: String)(k: F[A]): F[A] = - k - override def span[A](name: String, options: Span.Options)(k: F[A]): F[A] = k From e3daa4b04022782fa2d6706744d69cfd819c63be Mon Sep 17 00:00:00 2001 From: mpilquist Date: Wed, 23 Nov 2022 20:33:27 -0500 Subject: [PATCH 7/8] Docs --- modules/core/shared/src/main/scala/Span.scala | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/modules/core/shared/src/main/scala/Span.scala b/modules/core/shared/src/main/scala/Span.scala index dee4c89d..40cd7127 100644 --- a/modules/core/shared/src/main/scala/Span.scala +++ b/modules/core/shared/src/main/scala/Span.scala @@ -162,8 +162,17 @@ object Span { makeRoots(ep) ) + /** Options for creating a new span. */ sealed trait Options { + + /** Optional parent kernel for the child span, in addition to the parent span. + * + * Some backends do not support multiple parents, in which case the + * parent span is preferred and this parent kernel is ignored. + */ def parentKernel: Option[Kernel] + + /** Specifies how additional span creation requests are handled on the new span. */ def spanCreationPolicy: Options.SpanCreationPolicy def withParentKernel(kernel: Kernel): Options @@ -174,8 +183,14 @@ object Span { object Options { sealed trait SpanCreationPolicy object SpanCreationPolicy { + + /** Span creation behaves normally. */ case object Default extends SpanCreationPolicy + + /** Requests for span creation are ignored and any information provided to the returned span are also ignored. */ case object Suppress extends SpanCreationPolicy + + /** Requests for span creation are ignored but information provided to the returned span are attached to the original span. */ case object Coalesce extends SpanCreationPolicy } @@ -187,8 +202,11 @@ object Span { def withoutParentKernel: Options = OptionsImpl(None, spanCreationPolicy) def withSpanCreationPolicy(p: SpanCreationPolicy): Options = OptionsImpl(parentKernel, p) } + val Defaults: Options = OptionsImpl(None, SpanCreationPolicy.Default) val Suppress: Options = Defaults.withSpanCreationPolicy(SpanCreationPolicy.Suppress) val Coalesce: Options = Defaults.withSpanCreationPolicy(SpanCreationPolicy.Coalesce) + + def parentKernel(kernel: Kernel): Options = Defaults.withParentKernel(kernel) } } From 35ebed6394fa95aff2e06800d2bb9afcc91fc9b2 Mon Sep 17 00:00:00 2001 From: mpilquist Date: Wed, 23 Nov 2022 20:58:31 -0500 Subject: [PATCH 8/8] Remove unnused span method --- modules/core/shared/src/main/scala/Span.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/core/shared/src/main/scala/Span.scala b/modules/core/shared/src/main/scala/Span.scala index 40cd7127..cef0640c 100644 --- a/modules/core/shared/src/main/scala/Span.scala +++ b/modules/core/shared/src/main/scala/Span.scala @@ -91,9 +91,6 @@ object Span { abstract class Default[F[_]: Applicative] extends Span[F] { protected val spanCreationPolicy: Options.SpanCreationPolicy - def span(name: String): Resource[F, Span[F]] = - span(name, Options.Defaults) - def span(name: String, options: Options): Resource[F, Span[F]] = spanCreationPolicy match { case Options.SpanCreationPolicy.Suppress => Resource.pure(Span.noop[F])