Skip to content

Commit

Permalink
OpenTelemetry: propagate ZIO log annotations to OTEL span attributes (#…
Browse files Browse the repository at this point in the history
…827)

* OpenTelemetry: propagate ZIO log annotations to OTEL span attributes

* OpenTelemetry: propagate ZIO log annotations to OTEL span attributes
  • Loading branch information
IvanFinochenko authored May 4, 2024
1 parent da126cc commit 9cbadc7
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ object OpenTelemetry {
def tracing(
instrumentationScopeName: String,
instrumentationVersion: Option[String] = None,
schemaUrl: Option[String] = None
schemaUrl: Option[String] = None,
logAnnotated: Boolean = false
): URLayer[api.OpenTelemetry with ContextStorage, Tracing] = {
val tracerLayer = ZLayer(
ZIO.serviceWith[api.OpenTelemetry] { openTelemetry =>
Expand All @@ -70,7 +71,7 @@ object OpenTelemetry {
}
)

tracerLayer >>> Tracing.live
tracerLayer >>> Tracing.live(logAnnotated)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -516,16 +516,16 @@ trait Tracing { self =>

object Tracing {

def live: URLayer[Tracer with ContextStorage, Tracing] =
def live(logAnnotated: Boolean = false): URLayer[Tracer with ContextStorage, Tracing] =
ZLayer.scoped {
for {
tracer <- ZIO.service[Tracer]
ctxStorage <- ZIO.service[ContextStorage]
tracing <- scoped(tracer, ctxStorage)
tracing <- scoped(tracer, ctxStorage, logAnnotated)
} yield tracing
}

def scoped(tracer: Tracer, ctxStorage: ContextStorage): URIO[Scope, Tracing] = {
def scoped(tracer: Tracer, ctxStorage: ContextStorage, logAnnotated: Boolean = false): URIO[Scope, Tracing] = {
val acquire =
ZIO.succeed {
new Tracing { self =>
Expand Down Expand Up @@ -819,17 +819,18 @@ object Tracing {
links: Seq[SpanContext]
)(implicit trace: Trace): UIO[(UIO[Unit], Context)] =
for {
nanos <- currentNanos
span <- ZIO.succeed(
tracer
.spanBuilder(spanName)
.setNoParent()
.setAllAttributes(attributes)
.setSpanKind(spanKind)
.setStartTimestamp(nanos, TimeUnit.NANOSECONDS)
.addLinks(links)
.startSpan()
)
nanos <- currentNanos
allAttributes <- injectLogAnnotations(attributes)
span <- ZIO.succeed(
tracer
.spanBuilder(spanName)
.setNoParent()
.setAllAttributes(allAttributes)
.setSpanKind(spanKind)
.setStartTimestamp(nanos, TimeUnit.NANOSECONDS)
.addLinks(links)
.startSpan()
)
} yield (endSpan(span), Context.root().`with`(span))

private def createChild(
Expand All @@ -840,17 +841,18 @@ object Tracing {
links: Seq[SpanContext]
)(implicit trace: Trace): UIO[(UIO[Unit], Context)] =
for {
nanos <- currentNanos
span <- ZIO.succeed(
tracer
.spanBuilder(spanName)
.setParent(parentCtx)
.setAllAttributes(attributes)
.setSpanKind(spanKind)
.setStartTimestamp(nanos, TimeUnit.NANOSECONDS)
.addLinks(links)
.startSpan()
)
nanos <- currentNanos
allAttributes <- injectLogAnnotations(attributes)
span <- ZIO.succeed(
tracer
.spanBuilder(spanName)
.setParent(parentCtx)
.setAllAttributes(allAttributes)
.setSpanKind(spanKind)
.setStartTimestamp(nanos, TimeUnit.NANOSECONDS)
.addLinks(links)
.startSpan()
)
} yield (endSpan(span), parentCtx.`with`(span))

private implicit class SpanBuilderOps(spanBuilder: SpanBuilder) {
Expand All @@ -866,13 +868,14 @@ object Tracing {
links: Seq[SpanContext]
)(implicit trace: Trace): UIO[Context] =
for {
nanos <- currentNanos
span <-
nanos <- currentNanos
allAttributes <- injectLogAnnotations(attributes)
span <-
ZIO.succeed(
tracer
.spanBuilder(spanName)
.setParent(parentCtx)
.setAllAttributes(attributes)
.setAllAttributes(allAttributes)
.setSpanKind(spanKind)
.setStartTimestamp(nanos, TimeUnit.NANOSECONDS)
.addLinks(links)
Expand Down Expand Up @@ -907,6 +910,19 @@ object Tracing {
)(implicit trace: Trace): UIO[Unit] =
ZIO.succeed(propagator.instance.inject(ctx, carrier.kernel, carrier))

private def injectLogAnnotations(attributes: Attributes): UIO[Attributes] =
if (logAnnotated) {
for {
annotations <- ZIO.logAnnotations
} yield annotations
.foldLeft(Attributes.builder()) { case (builder, (annotationKey, annotationValue)) =>
builder.put(annotationKey, annotationValue)
}
.putAll(attributes)
.build()
} else {
ZIO.succeed(attributes)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ object LoggingTest extends ZIOSpecDefault {
}
}.provide(
loggingMockLayer("tracing context (fiberRef)"),
TracingTest.tracingMockLayer,
TracingTest.tracingMockLayer(),
ContextStorage.fiberRef
),
test("tracing context (openTelemtryContext)") {
Expand Down Expand Up @@ -167,7 +167,7 @@ object LoggingTest extends ZIOSpecDefault {
}
}.provide(
loggingMockLayer("tracing context (openTelemtryContext)"),
TracingTest.tracingMockLayer,
TracingTest.tracingMockLayer(),
ContextStorage.native
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ object MeterTest extends ZIOSpecDefault {
)
}
}
).provide(inMemoryMetricReaderLayer, meterLayer(), ContextStorage.fiberRef, TracingTest.tracingMockLayer)
).provide(inMemoryMetricReaderLayer, meterLayer(), ContextStorage.fiberRef, TracingTest.tracingMockLayer())

private val logAnnotatedSpec =
suite("log annotated")(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,29 @@ object TracingTest extends ZIOSpecDefault {
ZEnvironment(inMemorySpanExporter).add(tracer)
})

val tracingMockLayer: URLayer[ContextStorage, Tracing with InMemorySpanExporter with Tracer] =
inMemoryTracerLayer >>> (Tracing.live ++ inMemoryTracerLayer)
def tracingMockLayer(
logAnnotated: Boolean = false
): URLayer[ContextStorage, Tracing with InMemorySpanExporter with Tracer] =
inMemoryTracerLayer >>> (Tracing.live(logAnnotated) ++ inMemoryTracerLayer)

def getFinishedSpans: ZIO[InMemorySpanExporter, Nothing, List[SpanData]] =
ZIO
.service[InMemorySpanExporter]
.map(_.getFinishedSpanItems.asScala.toList)
ZIO.serviceWith[InMemorySpanExporter](_.getFinishedSpanItems.asScala.toList)

def spec: Spec[Any, Throwable] =
suite("zio opentelemetry")(
suite("Tracing")(
creationSpec,
spansSpec,
spanScopedSpec
spanScopedSpec,
spanWithLogAnnotationsSpec
)
)

private val creationSpec =
suite("creation")(
test("live") {
for {
_ <- ZIO.scoped(Tracing.live.build)
_ <- ZIO.scoped(Tracing.live().build)
finishedSpans <- getFinishedSpans
} yield assert(finishedSpans)(hasSize(equalTo(0)))
}.provide(inMemoryTracerLayer, ContextStorage.fiberRef)
Expand Down Expand Up @@ -549,7 +550,7 @@ object TracingTest extends ZIOSpecDefault {
} yield assert(ko)(isSome(failureAssertion)) && assert(ok)(isSome(successAssertion))
}
}
).provide(tracingMockLayer, ContextStorage.fiberRef)
).provide(tracingMockLayer(), ContextStorage.fiberRef)

private val spanScopedSpec =
suite("scoped spans")(
Expand Down Expand Up @@ -651,5 +652,49 @@ object TracingTest extends ZIOSpecDefault {
} yield assert(tags.get(AttributeKey.stringKey("string")))(equalTo("bar"))
}
}
).provide(tracingMockLayer, ContextStorage.fiberRef)
).provide(tracingMockLayer(), ContextStorage.fiberRef)

private val spanWithLogAnnotationsSpec = suite("spans with log annotations")(
test("add log annotations") {
ZIO.serviceWithZIO[Tracing] { tracing =>
import tracing.aspects._

for {
_ <- ZIO.logAnnotate("log-attribute", "foo") {
ZIO.unit @@ span("Root", attributes = Attributes(Attribute.string("root-attribute", "bar")))
}
spans <- getFinishedSpans
tags = spans.head.getAttributes
} yield assert(tags.get(AttributeKey.stringKey("root-attribute")))(equalTo("bar")) &&
assert(tags.get(AttributeKey.stringKey("log-attribute")))(equalTo("foo"))
}
}.provide(tracingMockLayer(true), ContextStorage.fiberRef),
test("span attributes override log annotated") {
ZIO.serviceWithZIO[Tracing] { tracing =>
import tracing.aspects._

for {
_ <- ZIO.logAnnotate("some-attribute", "foo") {
ZIO.unit @@ span("Root", attributes = Attributes(Attribute.string("some-attribute", "bar")))
}
spans <- getFinishedSpans
tags = spans.head.getAttributes
} yield assert(tags.get(AttributeKey.stringKey("some-attribute")))(equalTo("bar"))
}
}.provide(tracingMockLayer(true), ContextStorage.fiberRef),
test("not add log annotations") {
ZIO.serviceWithZIO[Tracing] { tracing =>
import tracing.aspects._

for {
_ <- ZIO.logAnnotate("log-attribute", "foo") {
ZIO.unit @@ span("Root", attributes = Attributes(Attribute.string("root-attribute", "bar")))
}
spans <- getFinishedSpans
tags = spans.head.getAttributes
} yield assert(tags.get(AttributeKey.stringKey("root-attribute")))(equalTo("bar")) &&
assert(Option(tags.get(AttributeKey.stringKey("log-attribute"))))(isNone)
}
}.provide(tracingMockLayer(), ContextStorage.fiberRef)
)
}

0 comments on commit 9cbadc7

Please sign in to comment.