Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Local-compatible tracing semantics #107

Merged
merged 21 commits into from
Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ ThisBuild / scalaVersion := Scala213 // the default Scala

val CatsVersion = "2.9.0"
val CatsEffectVersion = "3.4.5"
val CatsMtlVersion = "1.3.0"
val FS2Version = "3.5.0"
val MUnitVersion = "1.0.0-M7"
val MUnitCatsEffectVersion = "2.0.0-M3"
Expand Down Expand Up @@ -176,6 +177,7 @@ lazy val `java-trace` = project
name := "otel4s-java-trace",
libraryDependencies ++= Seq(
"org.typelevel" %%% "cats-effect" % CatsEffectVersion,
"org.typelevel" %%% "cats-mtl" % CatsMtlVersion,
"io.opentelemetry" % "opentelemetry-sdk-testing" % OpenTelemetryVersion % Test,
"org.typelevel" %%% "cats-effect-testkit" % CatsEffectVersion % Test,
"co.fs2" %% "fs2-core" % FS2Version % Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
package org.typelevel.otel4s
package trace

import cats.effect.kernel.Resource

private[otel4s] trait TracerMacro[F[_]] {
self: Tracer[F] =>

Expand Down Expand Up @@ -54,7 +52,7 @@ private[otel4s] trait TracerMacro[F[_]] {
* @param attributes
* the set of attributes to associate with the span
*/
def span(name: String, attributes: Attribute[_]*): Resource[F, Span[F]] =
def span(name: String, attributes: Attribute[_]*): SpanBuilder.Aux[F, Span[F]] =
macro TracerMacro.span

/** Creates a new root span. Even if a parent span is available in the scope,
Expand All @@ -75,9 +73,10 @@ private[otel4s] trait TracerMacro[F[_]] {
def rootSpan(
name: String,
attributes: Attribute[_]*
): Resource[F, Span[F]] =
): SpanBuilder.Aux[F, Span[F]] =
macro TracerMacro.rootSpan

/*
/** Creates a new child span. The span is automatically attached to a parent
* span (based on the scope).
*
Expand All @@ -104,7 +103,8 @@ private[otel4s] trait TracerMacro[F[_]] {
def resourceSpan[A](name: String, attributes: Attribute[_]*)(
resource: Resource[F, A]
): Resource[F, Span.Res[F, A]] =
macro TracerMacro.resourceSpan[F, A]
macro TracerMacro.resourceSpan[F, A]
*/
}

object TracerMacro {
Expand All @@ -115,9 +115,7 @@ object TracerMacro {
attributes: c.Expr[Attribute[_]]*
): c.universe.Tree = {
import c.universe._
val meta = q"${c.prefix}.meta"

q"if ($meta.isEnabled) ${c.prefix}.spanBuilder($name).addAttributes(..$attributes).start else $meta.noopSpan"
q"${c.prefix}.spanBuilder($name).addAttributes(..$attributes)"
}

def rootSpan(c: blackbox.Context)(
Expand All @@ -127,9 +125,10 @@ object TracerMacro {
import c.universe._
val meta = q"${c.prefix}.meta"

q"if ($meta.isEnabled) ${c.prefix}.spanBuilder($name).root.addAttributes(..$attributes).start else $meta.noopSpan"
q"if ($meta.isEnabled) ${c.prefix}.spanBuilder($name).addAttributes(..$attributes) else $meta.noopSpan"
}

/*
def resourceSpan[F[_], A](c: blackbox.Context)(
name: c.Expr[String],
attributes: c.Expr[Attribute[_]]*
Expand All @@ -139,5 +138,6 @@ object TracerMacro {

q"if ($meta.isEnabled) ${c.prefix}.spanBuilder($name).addAttributes(..$attributes).wrapResource($resource).start else $meta.noopResSpan($resource)"
}
*/

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package org.typelevel.otel4s
package trace

import cats.Applicative
import cats.effect.kernel.Resource
import cats.effect.kernel.{MonadCancelThrow, Resource}

import scala.concurrent.duration.FiniteDuration

Expand Down Expand Up @@ -174,6 +174,11 @@ trait SpanBuilder[F[_]] {
*/
def startUnmanaged(implicit ev: Result =:= Span[F]): F[Span[F]]

def use[A](f: Result => F[A]): F[A]

def surround[A](f: F[A]): F[A]

/*
/** Creates a [[Span]]. Unlike [[startUnmanaged]] the lifecycle of the span is
* managed by the [[cats.effect.kernel.Resource Resource]]. That means the
* span is started upon resource allocation and ended upon finalization.
Expand All @@ -193,7 +198,41 @@ trait SpanBuilder[F[_]] {
* }
* }}}
*/
def start: Resource[F, Result]
def start: Resource[F, Span[F]]

/** Creates a [[Span.Res]]. The span is started upon resource allocation and
* ended upon finalization. The allocation and release stages of the
* `resource` are traced by separate spans. Carries a value of the given
* `resource`.
*
* The structure of the inner spans:
* {{{
* > span-name
* > acquire
* > use
* > release
* }}}
*
* The finalization strategy is determined by [[SpanFinalizer.Strategy]]. By
* default, the abnormal termination (error, cancelation) is recorded.
*
* @see
* default finalization strategy [[SpanFinalizer.Strategy.reportAbnormal]]
*
* @example
* {{{
* val tracer: Tracer[F] = ???
* val resource: Resource[F, String] = Resource.eval(Sync[F].delay("string"))
* val ok: F[Unit] =
* tracer.spanBuilder("wrapped-resource").startResource(resource).use { case span @ Span.Res(value) =>
* span.setStatus(Status.Ok, s"all good. resource value: $${value}")
* }
* }}}
* @param resource
* the resource to trace
*/
def startResource[A](resource: Resource[F, A]): Resource[F, Span.Res[F, A]]
*/
}

object SpanBuilder {
Expand All @@ -202,12 +241,12 @@ object SpanBuilder {
type Result = A
}

def noop[F[_]: Applicative](
def noop[F[_]: MonadCancelThrow](
back: Span.Backend[F]
): SpanBuilder.Aux[F, Span[F]] =
make(back, Resource.pure(Span.fromBackend(back)))

private def make[F[_]: Applicative, Res <: Span[F]](
private def make[F[_]: MonadCancelThrow, Res <: Span[F]](
back: Span.Backend[F],
startSpan: Resource[F, Res]
): SpanBuilder.Aux[F, Res] =
Expand Down Expand Up @@ -247,8 +286,21 @@ object SpanBuilder {
def startUnmanaged(implicit ev: Result =:= Span[F]): F[Span[F]] =
Applicative[F].pure(span)

val start: Resource[F, Res] =
startSpan
def use[A](f: Res => F[A]): F[A] =
startSpan.use(res => f(res))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need MonadCancelThrow there


def surround[A](fa: F[A]): F[A] =
fa

/*
val start: Resource[F, Span[F]] =
Resource.pure(span)

def startResource[A](
resource: Resource[F, A]
): Resource[F, Span.Res[F, A]] =
resource.map(a => Span.Res.fromBackend(a, back))
*/
}

}
35 changes: 18 additions & 17 deletions core/trace/src/main/scala/org/typelevel/otel4s/trace/Tracer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package org.typelevel.otel4s
package trace

import cats.Applicative
import cats.effect.kernel.Resource
import cats.effect.kernel.MonadCancelThrow
import org.typelevel.otel4s.meta.InstrumentMeta

trait Tracer[F[_]] extends TracerMacro[F] {
Expand Down Expand Up @@ -56,7 +56,7 @@ trait Tracer[F[_]] extends TracerMacro[F] {
* @param parent
* the span context to use as a parent
*/
def childScope(parent: SpanContext): Resource[F, Unit]
def childScope[A](parent: SpanContext)(fa: F[A]): F[A]

/** Creates a new root tracing scope. The parent span will not be available
* inside. Thus, a span created inside of the scope will be a root one.
Expand All @@ -78,7 +78,7 @@ trait Tracer[F[_]] extends TracerMacro[F] {
* }
* }}}
*/
def rootScope: Resource[F, Unit]
def rootScope[A](fa: F[A]): F[A]

/** Creates a no-op tracing scope. The tracing operations inside of the scope
* are no-op.
Expand All @@ -97,7 +97,7 @@ trait Tracer[F[_]] extends TracerMacro[F] {
* }
* }}}
*/
def noopScope: Resource[F, Unit]
def noopScope[A](fa: F[A]): F[A]

}

Expand All @@ -106,41 +106,42 @@ object Tracer {
def apply[F[_]](implicit ev: Tracer[F]): Tracer[F] = ev

trait Meta[F[_]] extends InstrumentMeta[F] {
def noopSpan: Resource[F, Span[F]]
def noopResSpan[A](resource: Resource[F, A]): Resource[F, Span.Res[F, A]]
def noopSpanBuilder: SpanBuilder[F]
// def noopResSpan[A](resource: Resource[F, A]): Resource[F, Span.Res[F, A]]
}

object Meta {

def enabled[F[_]: Applicative]: Meta[F] = make(true)
def disabled[F[_]: Applicative]: Meta[F] = make(false)
def enabled[F[_]: MonadCancelThrow]: Meta[F] = make(true)
def disabled[F[_]: MonadCancelThrow]: Meta[F] = make(false)

private def make[F[_]: Applicative](enabled: Boolean): Meta[F] =
private def make[F[_]: MonadCancelThrow](enabled: Boolean): Meta[F] =
new Meta[F] {
private val noopBackend = Span.Backend.noop[F]

val isEnabled: Boolean = enabled
val unit: F[Unit] = Applicative[F].unit
val noopSpan: Resource[F, Span[F]] =
Resource.pure(Span.fromBackend(noopBackend))
val noopSpanBuilder: SpanBuilder[F] =
SpanBuilder.noop(noopBackend)

/*
def noopResSpan[A](
resource: Resource[F, A]
): Resource[F, Span.Res[F, A]] =
resource.map(a => Span.Res.fromBackend(a, Span.Backend.noop))
resource.map(a => Span.Res.fromBackend(a, Span.Backend.noop))
*/
}
}

def noop[F[_]: Applicative]: Tracer[F] =
def noop[F[_]: MonadCancelThrow]: Tracer[F] =
new Tracer[F] {
private val noopBackend = Span.Backend.noop
private val builder = SpanBuilder.noop(noopBackend)
private val resourceUnit = Resource.unit[F]
val meta: Meta[F] = Meta.disabled
val currentSpanContext: F[Option[SpanContext]] = Applicative[F].pure(None)
def rootScope: Resource[F, Unit] = resourceUnit
def noopScope: Resource[F, Unit] = resourceUnit
def childScope(parent: SpanContext): Resource[F, Unit] = resourceUnit
def rootScope[A](fa: F[A]): F[A] = fa
def noopScope[A](fa: F[A]): F[A] = fa
def childScope[A](parent: SpanContext)(fa: F[A]): F[A] = fa
def spanBuilder(name: String): SpanBuilder.Aux[F, Span[F]] = builder
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class TracerSuite extends CatsEffectSuite {
_ <- span.setStatus(status, text)
} yield ()
}
_ <- tracer.rootSpan("span", attribute: _*).use { span =>
/*_ <- tracer.rootSpan("span", attribute: _*).use { span =>
for {
_ <- span.addAttributes(attribute: _*)
_ <- span.addEvent(text, attribute: _*)
Expand All @@ -74,7 +74,7 @@ class TracerSuite extends CatsEffectSuite {
_ <- span.setStatus(status)
_ <- span.setStatus(status, text)
} yield ()
}
}*/
} yield assert(!allocated)
}

Expand Down
20 changes: 15 additions & 5 deletions examples/src/main/scala/TracingExample.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,24 @@
* limitations under the License.
*/

import cats.Monad
import cats.effect.IO
import cats.effect.IOApp
import cats.effect.MonadCancelThrow
import cats.effect.Resource
import cats.effect.std.Console
import cats.syntax.all._
import io.opentelemetry.api.GlobalOpenTelemetry
import org.typelevel.otel4s.java.OtelJava
import org.typelevel.otel4s.trace.Tracer
import org.typelevel.otel4s.trace.{Span, SpanBuilder, Tracer}

import scala.concurrent.duration._

trait Work[F[_]] {
def doWork: F[Unit]
}

object Work {
def apply[F[_]: MonadCancelThrow: Tracer: Console]: Work[F] =
def apply[F[_]: Monad: Tracer: Console]: Work[F] =
new Work[F] {
def doWork: F[Unit] =
Tracer[F].span("Work.DoWork").use { span =>
Expand All @@ -39,7 +41,9 @@ object Work {
}

def doWorkInternal =
Console[F].println("Doin' work")
Tracer[F].span("Work.InternalWork").surround(
Console[F].println("Doin' work")
)
}
}

Expand All @@ -52,7 +56,13 @@ object TracingExample extends IOApp.Simple {

def run: IO[Unit] = {
tracerResource.use { implicit tracer: Tracer[IO] =>
Work[IO].doWork
val resource: Resource[IO, Unit] = Resource.make(IO.sleep(50.millis))(_ => IO.sleep(100.millis))
val builder: SpanBuilder.Aux[IO, Span.Res[IO, Unit]] = tracer.span("resource").wrapResource(resource)

builder.use { _ =>
Work[IO].doWork
}

}
}
}
Loading