From 4155f14202878ef2d86335bfae26b85409e1db44 Mon Sep 17 00:00:00 2001 From: Maksym Ochenashko Date: Tue, 9 Apr 2024 10:56:53 +0300 Subject: [PATCH] trace: add `addLink` to `Span` --- .../typelevel/otel4s/trace/SpanMacro.scala | 56 +++++++++++++++++++ .../typelevel/otel4s/trace/SpanMacro.scala | 47 ++++++++++++++++ .../org/typelevel/otel4s/trace/Span.scala | 15 +++++ .../oteljava/trace/SpanBackendImpl.scala | 12 ++++ .../otel4s/sdk/trace/SdkSpanBackend.scala | 14 ++++- .../sdk/trace/SdkSpanBackendSuite.scala | 15 +++++ .../processor/SimpleSpanProcessorSuite.scala | 6 ++ 7 files changed, 162 insertions(+), 3 deletions(-) diff --git a/core/trace/src/main/scala-2/org/typelevel/otel4s/trace/SpanMacro.scala b/core/trace/src/main/scala-2/org/typelevel/otel4s/trace/SpanMacro.scala index 54abe69af..2e6f0d9ce 100644 --- a/core/trace/src/main/scala-2/org/typelevel/otel4s/trace/SpanMacro.scala +++ b/core/trace/src/main/scala-2/org/typelevel/otel4s/trace/SpanMacro.scala @@ -119,6 +119,42 @@ private[otel4s] trait SpanMacro[F[_]] { ): F[Unit] = macro SpanMacro.addEventWithTimestampColl + /** Adds a link to the span. + * + * Links are used to link spans in different traces. Used (for example) in + * batching operations, where a single batch handler processes multiple + * requests from different traces or the same trace. + * + * @param spanContext + * the context of the linked span + * + * @param attributes + * the set of attributes to associated with the link + */ + def addLink( + spanContext: SpanContext, + attributes: Attribute[_]* + ): F[Unit] = + macro SpanMacro.addLink + + /** Adds a link to the span. + * + * Links are used to link spans in different traces. Used (for example) in + * batching operations, where a single batch handler processes multiple + * requests from different traces or the same trace. + * + * @param spanContext + * the context of the linked span + * + * @param attributes + * the set of attributes to associated with the link + */ + def addLink( + spanContext: SpanContext, + attributes: immutable.Iterable[Attribute[_]] + ): F[Unit] = + macro SpanMacro.addLinkColl + /** Records information about the `Throwable` to the span. * * @param exception @@ -227,6 +263,26 @@ object SpanMacro { q"if ($meta.isEnabled) $backend.addEvent($name, $attributes) else $meta.unit" } + def addLink(c: blackbox.Context)( + spanContext: c.Expr[SpanContext], + attributes: c.Expr[Attribute[_]]* + ): c.universe.Tree = { + import c.universe._ + addLinkColl(c)(spanContext, c.Expr(q"_root_.scala.Seq(..$attributes)")) + } + + def addLinkColl(c: blackbox.Context)( + spanContext: c.Expr[SpanContext], + attributes: c.Expr[immutable.Iterable[Attribute[_]]] + ): c.universe.Tree = { + import c.universe._ + + val backend = q"${c.prefix}.backend" + val meta = q"$backend.meta" + + q"if ($meta.isEnabled) $backend.addLink($spanContext, $attributes) else $meta.unit" + } + def addEventWithTimestamp(c: blackbox.Context)( name: c.Expr[String], timestamp: c.Expr[FiniteDuration], diff --git a/core/trace/src/main/scala-3/org/typelevel/otel4s/trace/SpanMacro.scala b/core/trace/src/main/scala-3/org/typelevel/otel4s/trace/SpanMacro.scala index 26e518571..26d4b9cfc 100644 --- a/core/trace/src/main/scala-3/org/typelevel/otel4s/trace/SpanMacro.scala +++ b/core/trace/src/main/scala-3/org/typelevel/otel4s/trace/SpanMacro.scala @@ -125,6 +125,42 @@ private[otel4s] trait SpanMacro[F[_]] { ): F[Unit] = ${ SpanMacro.addEvent('self, 'name, 'timestamp, 'attributes) } + /** Adds a link to the span. + * + * Links are used to link spans in different traces. Used (for example) in + * batching operations, where a single batch handler processes multiple + * requests from different traces or the same trace. + * + * @param spanContext + * the context of the linked span + * + * @param attributes + * the set of attributes to associated with the link + */ + inline def addLink( + spanContext: SpanContext, + attributes: Attribute[_]* + ): F[Unit] = + ${ SpanMacro.addLink('self, 'spanContext, 'attributes) } + + /** Adds a link to the span. + * + * Links are used to link spans in different traces. Used (for example) in + * batching operations, where a single batch handler processes multiple + * requests from different traces or the same trace. + * + * @param spanContext + * the context of the linked span + * + * @param attributes + * the set of attributes to associated with the link + */ + inline def addLink( + spanContext: SpanContext, + attributes: immutable.Iterable[Attribute[_]] + ): F[Unit] = + ${ SpanMacro.addLink('self, 'spanContext, 'attributes) } + /** Records information about the `Throwable` to the span. * * @param exception @@ -228,6 +264,17 @@ object SpanMacro { else $span.backend.meta.unit } + def addLink[F[_]]( + span: Expr[Span[F]], + spanContext: Expr[SpanContext], + attributes: Expr[immutable.Iterable[Attribute[_]]] + )(using Quotes, Type[F]) = + '{ + if ($span.backend.meta.isEnabled) + $span.backend.addLink($spanContext, $attributes) + else $span.backend.meta.unit + } + def recordException[F[_]]( span: Expr[Span[F]], exception: Expr[Throwable], 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 6af5912ca..32b7348e1 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 @@ -141,6 +141,11 @@ object Span { attributes: immutable.Iterable[Attribute[_]] ): F[Unit] + def addLink( + spanContext: SpanContext, + attributes: immutable.Iterable[Attribute[_]] + ): F[Unit] + def recordException( exception: Throwable, attributes: immutable.Iterable[Attribute[_]] @@ -206,6 +211,11 @@ object Span { attributes: immutable.Iterable[Attribute[_]] ): F[Unit] = unit + def addLink( + spanContext: SpanContext, + attributes: immutable.Iterable[Attribute[_]] + ): F[Unit] = unit + def recordException( exception: Throwable, attributes: immutable.Iterable[Attribute[_]] @@ -239,6 +249,11 @@ object Span { attributes: immutable.Iterable[Attribute[_]] ): G[Unit] = f(backend.addEvent(name, timestamp, attributes)) + def addLink( + spanContext: SpanContext, + attributes: immutable.Iterable[Attribute[_]] + ): G[Unit] = + f(backend.addLink(spanContext, attributes)) def recordException( exception: Throwable, attributes: immutable.Iterable[Attribute[_]] diff --git a/oteljava/trace/src/main/scala/org/typelevel/otel4s/oteljava/trace/SpanBackendImpl.scala b/oteljava/trace/src/main/scala/org/typelevel/otel4s/oteljava/trace/SpanBackendImpl.scala index 03878140e..9f3bcfec2 100644 --- a/oteljava/trace/src/main/scala/org/typelevel/otel4s/oteljava/trace/SpanBackendImpl.scala +++ b/oteljava/trace/src/main/scala/org/typelevel/otel4s/oteljava/trace/SpanBackendImpl.scala @@ -78,6 +78,18 @@ private[oteljava] class SpanBackendImpl[F[_]: Sync]( () } + def addLink( + spanContext: SpanContext, + attributes: immutable.Iterable[Attribute[_]] + ): F[Unit] = + Sync[F].delay { + jSpan.addLink( + SpanContextConversions.toJava(spanContext), + attributes.toJavaAttributes + ) + () + } + def setStatus(status: StatusCode): F[Unit] = Sync[F].delay { jSpan.setStatus(toJStatus(status)) diff --git a/sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkSpanBackend.scala b/sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkSpanBackend.scala index 7aa0cca74..97f6b5277 100644 --- a/sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkSpanBackend.scala +++ b/sdk/trace/src/main/scala/org/typelevel/otel4s/sdk/trace/SdkSpanBackend.scala @@ -106,6 +106,14 @@ private final class SdkSpanBackend[F[_]: Monad: Clock: Console] private ( EventData(name, timestamp, attributes.to(Attributes)) ) + def addLink( + context: SpanContext, + attributes: immutable.Iterable[Attribute[_]] + ): F[Unit] = + updateState("addLink") { s => + s.copy(links = s.links :+ LinkData(context, attributes.to(Attributes))) + }.void + def recordException( exception: Throwable, attributes: immutable.Iterable[Attribute[_]] @@ -192,7 +200,7 @@ private final class SdkSpanBackend[F[_]: Monad: Clock: Console] private ( status = state.status, attributes = state.attributes, events = state.events, - links = immutableState.links, + links = state.links, instrumentationScope = immutableState.scopeInfo, resource = immutableState.resource ) @@ -267,7 +275,6 @@ private object SdkSpanBackend { kind = kind, parentContext = parentContext, resource = resource, - links = links, startTimestamp = startTimestamp ) @@ -275,6 +282,7 @@ private object SdkSpanBackend { name = name, status = StatusData.Unset, attributes = attributes, + links = links, events = Vector.empty, endTimestamp = None ) @@ -293,7 +301,6 @@ private object SdkSpanBackend { kind: SpanKind, parentContext: Option[SpanContext], resource: TelemetryResource, - links: Vector[LinkData], startTimestamp: FiniteDuration ) @@ -304,6 +311,7 @@ private object SdkSpanBackend { status: StatusData, attributes: Attributes, events: Vector[EventData], + links: Vector[LinkData], endTimestamp: Option[FiniteDuration] ) diff --git a/sdk/trace/src/test/scala/org/typelevel/otel4s/sdk/trace/SdkSpanBackendSuite.scala b/sdk/trace/src/test/scala/org/typelevel/otel4s/sdk/trace/SdkSpanBackendSuite.scala index faf225459..f5cc1089d 100644 --- a/sdk/trace/src/test/scala/org/typelevel/otel4s/sdk/trace/SdkSpanBackendSuite.scala +++ b/sdk/trace/src/test/scala/org/typelevel/otel4s/sdk/trace/SdkSpanBackendSuite.scala @@ -91,6 +91,21 @@ class SdkSpanBackendSuite extends CatsEffectSuite with ScalaCheckEffectSuite { } } + test(".addLink(:SpanContext, :Attribute[_]*)") { + PropF.forAllF { (spanContext: SpanContext, attrs: Attributes) => + val link = LinkData(spanContext, attrs) + + TestControl.executeEmbed { + for { + span <- start() + _ <- assertIO(span.toSpanData.map(_.links), Vector.empty) + _ <- span.addLink(spanContext, attrs) + _ <- assertIO(span.toSpanData.map(_.links), Vector(link)) + } yield () + } + } + } + test(".updateName(:String)") { PropF.forAllF { (name: String, nextName: String) => for { diff --git a/sdk/trace/src/test/scala/org/typelevel/otel4s/sdk/trace/processor/SimpleSpanProcessorSuite.scala b/sdk/trace/src/test/scala/org/typelevel/otel4s/sdk/trace/processor/SimpleSpanProcessorSuite.scala index 3e8000bc2..f1ca8fde7 100644 --- a/sdk/trace/src/test/scala/org/typelevel/otel4s/sdk/trace/processor/SimpleSpanProcessorSuite.scala +++ b/sdk/trace/src/test/scala/org/typelevel/otel4s/sdk/trace/processor/SimpleSpanProcessorSuite.scala @@ -169,6 +169,12 @@ class SimpleSpanProcessorSuite ): IO[Unit] = noopBackend.addEvent(name, timestamp, attributes) + def addLink( + spanContext: SpanContext, + attributes: immutable.Iterable[Attribute[_]] + ): IO[Unit] = + noopBackend.addLink(spanContext, attributes) + def recordException( exception: Throwable, attributes: immutable.Iterable[Attribute[_]]