From aa6343f8b961f339cf80b555a54fd4a4cee10c06 Mon Sep 17 00:00:00 2001 From: Marissa/Princess Date: Thu, 1 Jun 2023 11:48:47 -0400 Subject: [PATCH] [WIP] Mostly complete impl for F -> OptionT --- .../otel4s/meta/InstrumentMeta.scala | 8 +++ .../org/typelevel/otel4s/trace/Span.scala | 55 +++++++++++++++++++ .../typelevel/otel4s/trace/SpanBuilder.scala | 51 +++++++++++++++++ .../org/typelevel/otel4s/trace/Tracer.scala | 33 +++++++++++ 4 files changed, 147 insertions(+) diff --git a/core/common/src/main/scala/org/typelevel/otel4s/meta/InstrumentMeta.scala b/core/common/src/main/scala/org/typelevel/otel4s/meta/InstrumentMeta.scala index 946d13076..d8833a4f9 100644 --- a/core/common/src/main/scala/org/typelevel/otel4s/meta/InstrumentMeta.scala +++ b/core/common/src/main/scala/org/typelevel/otel4s/meta/InstrumentMeta.scala @@ -17,6 +17,7 @@ package org.typelevel.otel4s.meta import cats.Applicative +import cats.data.OptionT trait InstrumentMeta[F[_]] { @@ -44,4 +45,11 @@ object InstrumentMeta { val unit: F[Unit] = Applicative[F].unit } + def liftOptionT[F[_]: Applicative]( + meta: InstrumentMeta[F] + ): InstrumentMeta[OptionT[F, *]] = + new InstrumentMeta[OptionT[F, *]] { + def isEnabled: Boolean = meta.isEnabled + def unit: OptionT[F, Unit] = OptionT.liftF(meta.unit) + } } diff --git a/core/trace/src/main/scala/org/typelevel/otel4s/trace/Span.scala b/core/trace/src/main/scala/org/typelevel/otel4s/trace/Span.scala index 991f53e6a..3826aef70 100644 --- a/core/trace/src/main/scala/org/typelevel/otel4s/trace/Span.scala +++ b/core/trace/src/main/scala/org/typelevel/otel4s/trace/Span.scala @@ -18,6 +18,7 @@ package org.typelevel.otel4s package trace import cats.Applicative +import cats.data.OptionT import org.typelevel.otel4s.meta.InstrumentMeta import scala.concurrent.duration.FiniteDuration @@ -151,6 +152,42 @@ object Span { private[otel4s] def end: F[Unit] = unit private[otel4s] def end(timestamp: FiniteDuration): F[Unit] = unit } + + def liftOptionT[F[_]: Applicative]( + backend: Backend[F] + ): Backend[OptionT[F, *]] = + new Backend[OptionT[F, *]] { + def meta: InstrumentMeta[OptionT[F, *]] = + InstrumentMeta.liftOptionT(backend.meta) + def context: SpanContext = + backend.context + def addAttributes(attributes: Attribute[_]*): OptionT[F, Unit] = + OptionT.liftF(backend.addAttributes(attributes: _*)) + def addEvent( + name: String, + attributes: Attribute[_]* + ): OptionT[F, Unit] = + OptionT.liftF(backend.addEvent(name, attributes: _*)) + def addEvent( + name: String, + timestamp: FiniteDuration, + attributes: Attribute[_]* + ): OptionT[F, Unit] = + OptionT.liftF(backend.addEvent(name, timestamp, attributes: _*)) + def recordException( + exception: Throwable, + attributes: Attribute[_]* + ): OptionT[F, Unit] = + OptionT.liftF(backend.recordException(exception, attributes: _*)) + def setStatus(status: Status): OptionT[F, Unit] = + OptionT.liftF(backend.setStatus(status)) + def setStatus(status: Status, description: String): OptionT[F, Unit] = + OptionT.liftF(backend.setStatus(status, description)) + private[otel4s] def end: OptionT[F, Unit] = + OptionT.liftF(backend.end) + private[otel4s] def end(timestamp: FiniteDuration): OptionT[F, Unit] = + OptionT.liftF(backend.end(timestamp)) + } } private[otel4s] def fromBackend[F[_]](back: Backend[F]): Span[F] = @@ -185,5 +222,23 @@ object Span { def value: A = a def backend: Backend[F] = back } + + def liftOptionT[F[_]: Applicative, A]( + res: Span.Res[F, A] + ): Span.Res[OptionT[F, *], A] = + new Span.Res[OptionT[F, *], A] { + def value: A = res.value + def backend: Backend[OptionT[F, *]] = Backend.liftOptionT(res.backend) + } } + + def liftOptionT[F[_]: Applicative](span: Span[F]): Span[OptionT[F, *]] = + span match { + case res: Span.Res[F, _] => Span.Res.liftOptionT(res) + case _ => + new Span[OptionT[F, *]] { + def backend: Backend[OptionT[F, *]] = + Backend.liftOptionT(span.backend) + } + } } diff --git a/core/trace/src/main/scala/org/typelevel/otel4s/trace/SpanBuilder.scala b/core/trace/src/main/scala/org/typelevel/otel4s/trace/SpanBuilder.scala index 3e47b32f4..ee9376717 100644 --- a/core/trace/src/main/scala/org/typelevel/otel4s/trace/SpanBuilder.scala +++ b/core/trace/src/main/scala/org/typelevel/otel4s/trace/SpanBuilder.scala @@ -18,6 +18,7 @@ package org.typelevel.otel4s package trace import cats.Applicative +import cats.data.OptionT import cats.effect.kernel.MonadCancelThrow import cats.effect.kernel.Resource @@ -210,4 +211,54 @@ object SpanBuilder { } } + def liftOptionT[F[_]: MonadCancelThrow]( + builder: Aux[F, Span[F]] + ): Aux[OptionT[F, *], Span[OptionT[F, *]]] = + new SpanBuilder[OptionT[F, *]] { outer => + type Result = Span[OptionT[F, *]] + + def addAttribute[A](attribute: Attribute[A]): Builder = + liftOptionT(builder.addAttribute(attribute)) + def addAttributes(attributes: Attribute[_]*): Builder = + liftOptionT(builder.addAttributes(attributes: _*)) + def addLink( + spanContext: SpanContext, + attributes: Attribute[_]* + ): Builder = + liftOptionT(builder.addLink(spanContext, attributes: _*)) + def withFinalizationStrategy(strategy: SpanFinalizer.Strategy): Builder = + liftOptionT(builder.withFinalizationStrategy(strategy)) + def withSpanKind(spanKind: SpanKind): Builder = + liftOptionT(builder.withSpanKind(spanKind)) + def withStartTimestamp(timestamp: FiniteDuration): Builder = + liftOptionT(builder.withStartTimestamp(timestamp)) + def root: Builder = + liftOptionT(builder.root) + def withParent(parent: SpanContext): Builder = + liftOptionT(builder.withParent(parent)) + def wrapResource[A](resource: Resource[OptionT[F, *], A])(implicit + ev: Result =:= Span[OptionT[F, *]] + ): Aux[OptionT[F, *], Span.Res[OptionT[F, *], A]] = + ??? + + def build: SpanOps.Aux[OptionT[F, *], Result] = + new SpanOps[OptionT[F, *]] { + type Result = outer.Result + + def startUnmanaged(implicit + ev: Result =:= Span[OptionT[F, *]] + ): OptionT[F, Span[OptionT[F, *]]] = + OptionT + .liftF(builder.build.startUnmanaged) + .map(Span.liftOptionT(_)) + def use[A](f: Result => OptionT[F, A]): OptionT[F, A] = + OptionT( + builder.build.use(spanF => f(Span.liftOptionT(spanF)).value) + ) + def use_ : OptionT[F, Unit] = + OptionT.liftF(builder.build.use_) + def surround[A](fa: OptionT[F, A]): OptionT[F, A] = + OptionT(builder.build.surround(fa.value)) + } + } } diff --git a/core/trace/src/main/scala/org/typelevel/otel4s/trace/Tracer.scala b/core/trace/src/main/scala/org/typelevel/otel4s/trace/Tracer.scala index c800a627e..d7ec78480 100644 --- a/core/trace/src/main/scala/org/typelevel/otel4s/trace/Tracer.scala +++ b/core/trace/src/main/scala/org/typelevel/otel4s/trace/Tracer.scala @@ -18,6 +18,7 @@ package org.typelevel.otel4s package trace import cats.Applicative +import cats.data.OptionT import cats.effect.kernel.MonadCancelThrow import cats.effect.kernel.Resource import org.typelevel.otel4s.meta.InstrumentMeta @@ -218,4 +219,36 @@ object Tracer { object Implicits { implicit def noop[F[_]: MonadCancelThrow]: Tracer[F] = Tracer.noop } + + implicit def liftOptionT[F[_]: MonadCancelThrow](implicit + tracer: Tracer[F] + ): Tracer[OptionT[F, *]] = + new Tracer[OptionT[F, *]] { + def meta: Meta[OptionT[F, *]] = + new Meta[OptionT[F, *]] { + def noopSpanBuilder + : SpanBuilder.Aux[OptionT[F, *], Span[OptionT[F, *]]] = + SpanBuilder.liftOptionT(tracer.meta.noopSpanBuilder) + def isEnabled: Boolean = + tracer.meta.isEnabled + def unit: OptionT[F, Unit] = + OptionT.liftF(tracer.meta.unit) + } + def currentSpanContext: OptionT[F, Option[SpanContext]] = + OptionT.liftF(tracer.currentSpanContext) + def spanBuilder( + name: String + ): SpanBuilder.Aux[OptionT[F, *], Span[OptionT[F, *]]] = + SpanBuilder.liftOptionT(tracer.spanBuilder(name)) + def childScope[A](parent: SpanContext)(fa: OptionT[F, A]): OptionT[F, A] = + OptionT(tracer.childScope(parent)(fa.value)) + def joinOrRoot[A, C: TextMapGetter](carrier: C)( + fa: OptionT[F, A] + ): OptionT[F, A] = + OptionT(tracer.joinOrRoot(carrier)(fa.value)) + def rootScope[A](fa: OptionT[F, A]): OptionT[F, A] = + OptionT(tracer.rootScope(fa.value)) + def noopScope[A](fa: OptionT[F, A]): OptionT[F, A] = + OptionT(tracer.noopScope(fa.value)) + } }