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

[WIP] Mostly complete Tracer impl for F -> OptionT #261

Closed
wants to merge 1 commit into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.typelevel.otel4s.meta

import cats.Applicative
import cats.data.OptionT

trait InstrumentMeta[F[_]] {

Expand Down Expand Up @@ -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)
}
}
55 changes: 55 additions & 0 deletions core/trace/src/main/scala/org/typelevel/otel4s/trace/Span.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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] =
Expand Down Expand Up @@ -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)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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]] =
???

Comment on lines +239 to +243
Copy link
Contributor Author

@NthPortal NthPortal Jun 29, 2023

Choose a reason for hiding this comment

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

this method in particular seems impossible to implement for multiple reasons. for one, the underlying builder expects Resource[F, A], but all we have is Resource[OptionT[F, *], A]. our resource may not actually contain a value—how would we pass OptionT(F[None]) to the underlying builder? on top of this, the current implementation/method only handles Span, not Span.Res. I made an attempt at generifying it as S[F] <: Span[F] (happy to provide this branch if wanted), but that ends up not working either because you no longer have a clear underlying builder.Result type (among other complications).

thoughts?

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))
}
}
}
33 changes: 33 additions & 0 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,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
Expand Down Expand Up @@ -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))
}
}