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

Best-Effort Tracing #65

Merged
merged 2 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ lazy val core = crossProject(JVMPlatform, JSPlatform)
libraryDependencies ++= Seq(
"org.tpolecat" %%% "natchez-core" % "0.3.4",
"org.tpolecat" %%% "natchez-mtl" % "0.3.4",
"org.tpolecat" %%% "natchez-testkit" % "0.3.4" % Test,
"org.typelevel" %%% "cats-tagless-core" % "0.15.0",
"org.typelevel" %%% "cats-mtl" % "1.4.0",
"org.typelevel" %%% "log4cats-noop" % "2.6.0",
"io.circe" %%% "circe-core" % "0.14.6",
"org.tpolecat" %%% "natchez-testkit" % "0.3.4" % Test,
"org.typelevel" %% "munit-cats-effect" % "2.0.0-M4" % Test,
"org.typelevel" %% "scalacheck-effect" % "2.0.0-M2" % Test,
"org.typelevel" %% "scalacheck-effect-munit" % "2.0.0-M2" % Test,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package com.dwolla.tracing

import cats._
import cats.data._
import cats.effect.std._
import cats.effect.syntax.all._
import cats.effect.{IO, Resource, Trace => _, _}
import cats.syntax.all._
import com.dwolla.tracing.instances._
import cats.*
import cats.data.*
import cats.effect.std.*
import cats.effect.syntax.all.*
import cats.effect.{IO, Resource, Trace as _, *}
import cats.syntax.all.*
import com.dwolla.tracing.instances.*
import natchez.{EntryPoint, Span, Trace}
import natchez.mtl.localSpanForKleisli
import org.typelevel.log4cats.Logger
import org.typelevel.log4cats.noop.NoOpLogger

object TraceInitializationExample extends IOApp.Simple {
private implicit def logger[F[_] : Applicative]: Logger[F] = NoOpLogger[F]

private def entryPoint[F[_] : Sync : Env]: Resource[F, EntryPoint[F]] =
OpenTelemetryAtDwolla[F]("TraceInitializationSpec", DwollaEnvironment.Local)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package com.dwolla.tracing.syntax

import cats.effect.{MonadCancelThrow, Resource}
import cats.mtl.Local
import cats.syntax.all._
import cats.syntax.all.*
import com.dwolla.tracing.TraceResourceAcquisition
import natchez.{EntryPoint, Span, Trace}
import org.typelevel.log4cats.noop.NoOpLogger

trait ToTraceResourceLifecycleOps {
implicit def toTraceResourceLifecycleOps[F[_], A](resource: Resource[F, A]): TraceResourceLifecycleOps[F, A] =
Expand Down Expand Up @@ -42,16 +43,21 @@ class TraceResourceLifecycleOps[F[_], A](val resource: Resource[F, A]) extends A
* and finalization phases of the passed `Resource[F, A]` are traced in
* separate root spans.
*
* Unfortunately on Scala 2.12, because of the way extention methods are
* encoded, it is not possible to add an implicit Logger[F] to warn if
* errors occurred when tracing.
*
* @param name the base of the span name to use for acquisition (the finalization span will append ".finalize" to this value to form its span name)
* @param entryPoint an `EntryPoint[F]` capable of creating new root spans
* @param F a `MonadCancelThrow[F]` instance for the given effect type
* @param L a `Local[F, Span[F]]` instance for the given effect type
* @param L1 an implicit `Local[F, Span[F]]` instance for the given effect type
* @param L2 an implicit `Logger[F]` instance for the given effect type
* @return the input `Resource[F, A]` with its acquisition and release phases wrapped in Natchez root spans
*/
def traceResourceLifecycleInRootSpans(name: String,
entryPoint: EntryPoint[F])
(implicit
F: MonadCancelThrow[F],
L: Local[F, Span[F]]): Resource[F, A] =
TraceResourceAcquisition(entryPoint, name, resource)
TraceResourceAcquisition(entryPoint, name, resource)(implicitly, implicitly, NoOpLogger[F])
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.dwolla.tracing.syntax

import cats.effect.{MonadCancelThrow, Resource}
import cats.mtl.Local
import cats.syntax.all.*
import com.dwolla.tracing.TraceResourceAcquisition
import natchez.{EntryPoint, Span, Trace}
import org.typelevel.log4cats.Logger
import org.typelevel.log4cats.noop.NoOpLogger

trait ToTraceResourceLifecycleOps {
implicit def toTraceResourceLifecycleOps[F[_], A](resource: Resource[F, A]): TraceResourceLifecycleOps[F, A] =
new TraceResourceLifecycleOps(resource)
}

class TraceResourceLifecycleOps[F[_], A](val resource: Resource[F, A]) extends AnyVal {
/**
* Wrap the acquisition and release phases of the given `Resource[F, A]`
* in children spans of the given `Trace[F]`.
*
* Note: this requires a `Trace[F]` built from `Local[F, Span[F]]`
* (e.g. using `natchez.mtl.natchezMtlTraceForLocal`) or
* `Trace[Kleisli[F, Span[F], *]]`. Notably, the `natchez.Trace.ioTrace`
* instance is *not* compatible with this method.
*
* @param name the base of the span name to use for acquisition (the finalization span will append ".finalize" to this value to form its span name)
* @param F a `MonadCancelThrow[F]` instance for the given effect type
* @param T a `Trace[F]` for the given effect type. See the note in the description above; not every `Trace[F]` instance will work
* @return the input `Resource[F, A]` with its acquisition and release phases wrapped in Natchez spans
*/
def traceResourceLifecycleAs(name: String)
(implicit F: MonadCancelThrow[F],
T: Trace[F]): Resource[F, A] =
Resource {
Trace[F].spanR(name)
.use(_(resource.allocated))
.map { case (a, finalizer) =>
a -> Trace[F].spanR(s"$name.finalize").use(_(finalizer))
}
}

@deprecated("use variant accepting Logger[F]", "0.2.5")
def traceResourceLifecycleInRootSpans(name: String,
entryPoint: EntryPoint[F],
F: MonadCancelThrow[F],
L: Local[F, Span[F]]): Resource[F, A] =
TraceResourceAcquisition(entryPoint, name, resource)(F, L, NoOpLogger(F))

/**
* Given an entrypoint and a root span name, ensure that the acquisition
* and finalization phases of the passed `Resource[F, A]` are traced in
* separate root spans.
*
* @param name the base of the span name to use for acquisition (the finalization span will append ".finalize" to this value to form its span name)
* @param entryPoint an `EntryPoint[F]` capable of creating new root spans
* @param F a `MonadCancelThrow[F]` instance for the given effect type
* @param L1 an implicit `Local[F, Span[F]]` instance for the given effect type
* @param L2 an implicit `Logger[F]` instance for the given effect type
* @return the input `Resource[F, A]` with its acquisition and release phases wrapped in Natchez root spans
*/
def traceResourceLifecycleInRootSpans(name: String,
entryPoint: EntryPoint[F])
(implicit
F: MonadCancelThrow[F],
L1: Local[F, Span[F]],
L2: Logger[F]): Resource[F, A] =
TraceResourceAcquisition(entryPoint, name, resource)
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.dwolla.tracing

import cats.effect.{Trace => _, _}
import cats.effect.{Trace as _, *}
import cats.mtl.Local
import cats.syntax.all._
import com.dwolla.tracing.syntax._
import natchez._
import natchez.mtl._
import cats.syntax.all.*
import com.dwolla.tracing.syntax.*
import natchez.*
import natchez.mtl.*
import org.typelevel.log4cats.Logger
import org.typelevel.log4cats.noop.NoOpLogger

object TraceResourceAcquisition {
/**
Expand All @@ -17,14 +19,16 @@ object TraceResourceAcquisition {
* @param entryPoint a Natchez `EntryPoint[F]` used to create the new root spans
* @param name the name of the root span. a good default might be `"app initialization"`
* @param resource function that returns a resource to be traced using the ambient `Trace[F]` passed to the function
* @param L an implicit `Local[F, Span[F]]` instance for the given effect type
* @param L1 an implicit `Local[F, Span[F]]` instance for the given effect type
* @param L2 an implicit `Logger[F]` instance for the given effect type
* @tparam F the effect type in which to run
* @tparam A inner type of the `Resource[F, A]`
* @return `Resource[F, A]` that is the same as the one returned by the `resource` function, but with tracing introduced
*/
def apply[F[_] : MonadCancelThrow, A](entryPoint: EntryPoint[F], name: String)
(resource: Trace[F] => Resource[F, A])
(implicit L: Local[F, Span[F]]): Resource[F, A] =
(implicit L1: Local[F, Span[F]],
L2: Logger[F]): Resource[F, A] =
TraceResourceAcquisition(entryPoint, name, Span.Options.Defaults)(resource)

/**
Expand All @@ -37,14 +41,16 @@ object TraceResourceAcquisition {
* @param name the name of the root span. a good default might be `"app initialization"`
* @param resource function that returns a resource to be traced using the ambient `Trace[F]` passed to the function
* @param options options to set on the newly created root span
* @param L an implicit `Local[F, Span[F]]` instance for the given effect type
* @param L1 an implicit `Local[F, Span[F]]` instance for the given effect type
* @param L2 an implicit `Logger[F]` instance for the given effect type
* @tparam F the effect type in which to run
* @tparam A inner type of the `Resource[F, A]`
* @return `Resource[F, A]` that is the same as the one returned by the `resource` function, but with tracing introduced
*/
def apply[F[_] : MonadCancelThrow, A](entryPoint: EntryPoint[F], name: String, options: Span.Options)
(resource: Trace[F] => Resource[F, A])
(implicit L: Local[F, Span[F]]): Resource[F, A] =
(implicit L1: Local[F, Span[F]],
L2: Logger[F]): Resource[F, A] =
TraceResourceAcquisition(entryPoint, name, options, resource(natchezMtlTraceForLocal))

/**
Expand All @@ -55,13 +61,16 @@ object TraceResourceAcquisition {
* @param entryPoint a Natchez `EntryPoint[F]` used to create the new root spans
* @param name the name of the root span. a good default might be `"app initialization"`
* @param resource a resource that was traced using the `Local[F, Span[F]]` passed implicitly as `L`
* @param L an implicit `Local[F, Span[F]]` instance for the given effect type
* @param L1 an implicit `Local[F, Span[F]]` instance for the given effect type
* @param L2 an implicit `Logger[F]` instance for the given effect type
* @tparam F the effect type in which to run
* @tparam A inner type of the `Resource[F, A]`
* @return `Resource[F, A]` that is the same as the one returned by the `resource` function, but with tracing introduced
*/
def apply[F[_] : MonadCancelThrow, A](entryPoint: EntryPoint[F], name: String, resource: Resource[F, A])
(implicit L: Local[F, Span[F]]): Resource[F, A] =
(implicit L1: Local[F, Span[F]],
L2: Logger[F],
): Resource[F, A] =
TraceResourceAcquisition(entryPoint, name, Span.Options.Defaults, resource)

/**
Expand All @@ -73,7 +82,8 @@ object TraceResourceAcquisition {
* @param name the name of the root span. a good default might be `"app initialization"`
* @param resource a resource that was traced using the `Local[F, Span[F]]` passed implicitly as `L`
* @param options options to set on the newly created root span
* @param L an implicit `Local[F, Span[F]]` instance for the given effect type
* @param L1 an implicit `Local[F, Span[F]]` instance for the given effect type
* @param L2 an implicit `Logger[F]` instance for the given effect type
* @tparam F the effect type in which to run
* @tparam A inner type of the `Resource[F, A]`
* @return `Resource[F, A]` that is the same as the one returned by the `resource` function, but with tracing introduced
Expand All @@ -82,7 +92,8 @@ object TraceResourceAcquisition {
name: String,
options: Span.Options,
resource: Resource[F, A])
(implicit L: Local[F, Span[F]]): Resource[F, A] =
(implicit L1: Local[F, Span[F]],
L2: Logger[F]): Resource[F, A] =
Resource {
entryPoint
.runInRoot(name, options) {
Expand All @@ -97,4 +108,39 @@ object TraceResourceAcquisition {
}
}
}

@deprecated("use variant accepting Logger[F]", "0.2.5")
def apply[F[_], A](entryPoint: EntryPoint[F],
name: String,
options: Span.Options,
resource: Resource[F, A],
F: MonadCancelThrow[F],
L: Local[F, Span[F]]): Resource[F, A] =
apply(entryPoint, name, options, resource)(F, L, NoOpLogger(F))

@deprecated("use variant accepting Logger[F]", "0.2.5")
def apply[F[_], A](entryPoint: EntryPoint[F],
name: String,
resource: Resource[F, A],
F: MonadCancelThrow[F],
L: Local[F, Span[F]]): Resource[F, A] =
apply(entryPoint, name, Span.Options.Defaults, resource)(F, L, NoOpLogger(F))

@deprecated("use variant accepting Logger[F]", "0.2.5")
def apply[F[_], A](entryPoint: EntryPoint[F],
name: String,
options: Span.Options,
resource: Trace[F] => Resource[F, A],
F: MonadCancelThrow[F],
L: Local[F, Span[F]]): Resource[F, A] =
apply(entryPoint, name, options, resource(natchezMtlTraceForLocal(L, F)))(F, L, NoOpLogger(F))

@deprecated("use variant accepting Logger[F]", "0.2.5")
def apply[F[_], A](entryPoint: EntryPoint[F],
name: String,
resource: Trace[F] => Resource[F, A],
F: MonadCancelThrow[F],
L: Local[F, Span[F]]): Resource[F, A] =
apply(entryPoint, name, Span.Options.Defaults, resource, F, L)

}
Loading