TraceWeaveCapturingInputsAndOutputs

com.dwolla.tracing.TraceWeaveCapturingInputsAndOutputs
See theTraceWeaveCapturingInputsAndOutputs companion object

Use this FunctionK when you have an algebra in Weave[F, TraceableValue, TraceableValue, *] and you want each method call on the algebra to introduce a new child span, using the ambient Trace[F]. Each child span will be named using the algebra name and method name as captured in the Instrumentation[F, A], and the parameters given to the method call and its return value will be attached to the span as attributes.

The format of the attributes is controlled via the implementation of the TraceableValue typeclass. There are implementations provided for String, Int, Boolean, and Unit, as well as Option[A] where TraceableValue[A] exists. Other types that have Show or Circe Encoder instances will also be converted.

If a parameter or return value is sensitive, one way to ensure the sensitive value is not included in the trace is to use a newtype for the parameter type, and then manually write a redacted TraceableValue[Newtype] instance. For example, using io.monix::newtypes-core:

import monix.newtypes._, natchez._

type Password = Password.Type

object Password extends NewtypeWrapped[String] {
  implicit val PasswordTraceableValue: TraceableValue[Password] = new TraceableValue[Password] {
    override def toTraceValue(a: Password): TraceValue = "redacted password value"
  }
}

With that implementation of TraceableValue[Password], the span will record "redacted password value" as an attribute, but the actual value will not be recorded. Similar functionality can be achieved using the newtype library of your choice.

Note if you have an algebra Alg[F] for which an Aspect[Alg, TraceableValue, TraceableValue] exists, it can be converted to Alg[Weave[F, TraceableValue, TraceableValue, *]] using Aspect[Alg, TraceableValue, TraceableValue].weave:

import cats.effect._, cats.tagless.aop._, cats.tagless.syntax.all._, cats._

trait Foo[F[_]] {
  def foo(i: Int): F[Unit]
}

object Foo {
  import LowPriorityTraceableValueInstances._
  implicit val fooTracingAspect: Aspect[Foo, TraceableValue, TraceableValue] = { // Derive.aspect
    // TODO reintroduce derived instance when cats-tagless-macros supports Scala 3
    new Aspect[Foo, TraceableValue, TraceableValue] {
      override def weave[F[_]](af: Foo[F]): Foo[Aspect.Weave[F, TraceableValue, TraceableValue, *]] =
        new Foo[Aspect.Weave[F, TraceableValue, TraceableValue, *]] {
          override def foo(i: Int): Aspect.Weave[F, TraceableValue, TraceableValue, Unit] =
            Aspect.Weave[F, TraceableValue, TraceableValue, Unit](
              "Foo",
              List(List(Aspect.Advice.byValue[TraceableValue, Int]("i", i))),
              Aspect.Advice[F, TraceableValue, Unit]("foo", af.foo(i))
            )
        }

      override def mapK[F[_], G[_]](af: Foo[F])(fk: F ~> G): Foo[G] =
        new Foo[G] {
          override def foo(i: Int): G[Unit] = fk(af.foo(i))
        }
    }
  }
}

def myFoo: Foo[IO] = new Foo[IO] {
  def foo(i: Int) = IO.println(s"foo!").replicateA_(i)
}

val wovenFoo: Foo[Aspect.Weave[IO, TraceableValue, TraceableValue, *]] =
  myFoo.weave

Ensure that TraceableValue instances exist for all the method parameter and return types in the algebra, or you'll see compile-time errors similar to this:

 exception during macro expansion:
 scala.reflect.macros.TypecheckException: could not find implicit value for evidence parameter of type TraceableValue[X]
     implicit val fooTracingAspect: Aspect.Domain[Foo, TraceableValue] = Derive.aspect
                                                                              ^
 one error found

Ensure there is a TraceableValue for the missing type (in the example above, it would be X) in the implicit scope where the Aspect is being derived, and then try again. You may have to iterate several times before all the necessary types are defined.

Attributes

Companion
object
Source
TraceWeaveCapturingInputsAndOutputs.scala
Graph
Supertypes
trait FunctionK[[_] =>> Weave[F, TraceableValue, TraceableValue, _$4], F]
trait Serializable
class Object
trait Matchable
class Any

Members list

Value members

Concrete methods

override def apply[A](fa: Weave[F, TraceableValue, TraceableValue, A]): F[A]

Applies this functor transformation from F to G

Applies this functor transformation from F to G

Attributes

Definition Classes
Source
TraceWeaveCapturingInputsAndOutputs.scala

Inherited methods

def and[H[_]](h: FunctionK[[_] =>> Weave[F, TraceableValue, TraceableValue, _$4], H]): FunctionK[F, [_] =>> Tuple2K[G, H, _$9]]

Composes two instances of FunctionK into a new FunctionK that transforms one single functor to a cats.data.Tuple2K of two functors.

Composes two instances of FunctionK into a new FunctionK that transforms one single functor to a cats.data.Tuple2K of two functors.

scala> import cats.arrow.FunctionK
scala> val list2option = λ[FunctionK[List, Option]](_.headOption)
scala> val list2vector = λ[FunctionK[List, Vector]](_.toVector)
scala> val optionAndVector = list2option and list2vector
scala> optionAndVector(List(1,2,3))
res0: cats.data.Tuple2K[Option,Vector,Int] = Tuple2K(Some(1),Vector(1, 2, 3))

Attributes

Inherited from:
FunctionK
Source
FunctionK.scala
def andThen[H[_]](f: FunctionK[F, H]): FunctionK[F, H]

Composes two instances of FunctionK into a new FunctionK with this transformation applied first.

Composes two instances of FunctionK into a new FunctionK with this transformation applied first.

Attributes

Inherited from:
FunctionK
Source
FunctionK.scala
def compose[E[_]](f: FunctionK[E, [_] =>> Weave[F, TraceableValue, TraceableValue, _$4]]): FunctionK[E, G]

Composes two instances of FunctionK into a new FunctionK with this transformation applied last.

Composes two instances of FunctionK into a new FunctionK with this transformation applied last.

Attributes

Inherited from:
FunctionK
Source
FunctionK.scala
def narrow[F0 <: ([x] =>> Weave[F, TraceableValue, TraceableValue, x])]: FunctionK[F0, G]

Narrows the input type of this FunctionK from F to F0

Narrows the input type of this FunctionK from F to F0

Attributes

Inherited from:
FunctionK
Source
FunctionK.scala
def or[H[_]](h: FunctionK[H, F]): FunctionK[[_] =>> EitherK[F, H, _$6], G]

Composes two instances of FunctionK into a new FunctionK that transforms a cats.data.EitherK to a single functor.

Composes two instances of FunctionK into a new FunctionK that transforms a cats.data.EitherK to a single functor.

This transformation will be used to transform left F values while h will be used to transform right H values.

Attributes

Inherited from:
FunctionK
Source
FunctionK.scala
def widen[G0[x]]: FunctionK[F, G0]

Widens the output type of this FunctionK from G to G0

Widens the output type of this FunctionK from G to G0

Attributes

Inherited from:
FunctionK
Source
FunctionK.scala