Skip to content

Commit

Permalink
Merge pull request #665 from tpolecat/topic/coalesce
Browse files Browse the repository at this point in the history
Add support for span suppression and coalescing, simplify span creation API via builder pattern
  • Loading branch information
mpilquist committed Nov 24, 2022
2 parents a41ea95 + 35ebed6 commit 9d74b8f
Show file tree
Hide file tree
Showing 22 changed files with 417 additions and 339 deletions.
2 changes: 1 addition & 1 deletion 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"
Expand Down
88 changes: 70 additions & 18 deletions modules/core/shared/src/main/scala/Span.scala
Expand Up @@ -32,11 +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]]

/** Resource that yields a child span of both this span and the given kernel. */
def span(name: String, kernel: Kernel): 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.
Expand Down Expand Up @@ -75,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)

Expand All @@ -85,18 +82,26 @@ trait Span[F[_]] {
override def traceId: G[Option[String]] = f(outer.traceId)

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)
.map(_.mapK(f))
.mapK(f)
}
}
}

object Span {

abstract class Default[F[_]: Applicative] extends Span[F] {
protected val spanCreationPolicy: Options.SpanCreationPolicy

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 => makeSpan(name, options)
}

/** 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.
*/
def putErrorFields[F[_]: Applicative](span: Resource[F, Span[F]]): Resource[F, Span[F]] =
Expand Down Expand Up @@ -130,14 +135,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 =
Expand All @@ -154,4 +158,52 @@ object Span {
def rootTracing[F[_]: Applicative](ep: EntryPoint[F]): Kleisli[F, Span[F], *] ~> F = resolve(
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
def withoutParentKernel: Options
def withSpanCreationPolicy(p: Options.SpanCreationPolicy): Options
}

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
}

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)

def parentKernel(kernel: Kernel): Options = Defaults.withParentKernel(kernel)
}
}

0 comments on commit 9d74b8f

Please sign in to comment.