diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4625c3c..d5744fb5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [8, 11, 17] + java: [11, 17, 21] name: Run with Java ${{ matrix.java }} steps: - uses: actions/checkout@v2 diff --git a/impl/java/tracing/src/main/kotlin/br/com/guiabolso/tracing/engine/opentelemetry/OpenTelemetryTracer.kt b/impl/java/tracing/src/main/kotlin/br/com/guiabolso/tracing/engine/opentelemetry/OpenTelemetryTracer.kt index d7c2af44..d0ea0e55 100644 --- a/impl/java/tracing/src/main/kotlin/br/com/guiabolso/tracing/engine/opentelemetry/OpenTelemetryTracer.kt +++ b/impl/java/tracing/src/main/kotlin/br/com/guiabolso/tracing/engine/opentelemetry/OpenTelemetryTracer.kt @@ -11,6 +11,7 @@ import io.opentelemetry.api.metrics.Meter import io.opentelemetry.api.trace.Span import io.opentelemetry.api.trace.Tracer import io.opentelemetry.context.Context +import io.opentelemetry.instrumentation.api.instrumenter.LocalRootSpan import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRoute import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRouteSource import java.io.Closeable @@ -30,6 +31,7 @@ class OpenTelemetryTracer : TracerEngine, ThreadContextManager { } override fun setOperationName(name: String) { + currentSpan().updateName(name) HttpServerRoute.update( Context.current(), HttpServerRouteSource.CONTROLLER, @@ -39,27 +41,27 @@ class OpenTelemetryTracer : TracerEngine, ThreadContextManager { } override fun addProperty(key: String, value: String?) { - Span.current()?.addProperty(key, value) + currentSpan().addProperty(key, value) } override fun addRootProperty(key: String, value: String?) { - currentSpan()?.addProperty(key, value) + rootSpan().addProperty(key, value) } override fun addProperty(key: String, value: Number?) { - Span.current()?.addProperty(key, value) + currentSpan().addProperty(key, value) } override fun addRootProperty(key: String, value: Number?) { - currentSpan()?.addProperty(key, value) + rootSpan().addProperty(key, value) } override fun addProperty(key: String, value: Boolean?) { - Span.current()?.addProperty(key, value) + currentSpan().addProperty(key, value) } override fun addRootProperty(key: String, value: Boolean?) { - currentSpan()?.addProperty(key, value) + rootSpan().addProperty(key, value) } override fun addProperty(key: String, value: List<*>) { @@ -90,27 +92,19 @@ class OpenTelemetryTracer : TracerEngine, ThreadContextManager { } override fun notifyError(exception: Throwable, expected: Boolean) { - Span.current()?.let { span -> - OpenTelemetryUtils.notifyError(span, exception, expected) - } + OpenTelemetryUtils.notifyError(currentSpan(), exception, expected) } override fun notifyRootError(exception: Throwable, expected: Boolean) { - currentSpan()?.let { span -> - OpenTelemetryUtils.notifyError(span, exception, expected) - } + OpenTelemetryUtils.notifyError(rootSpan(), exception, expected) } override fun notifyError(message: String, params: Map, expected: Boolean) { - Span.current()?.let { span -> - OpenTelemetryUtils.notifyError(span, message, params, expected) - } + OpenTelemetryUtils.notifyError(currentSpan(), message, params, expected) } override fun notifyRootError(message: String, params: Map, expected: Boolean) { - currentSpan()?.let { span -> - OpenTelemetryUtils.notifyError(span, message, params, expected) - } + OpenTelemetryUtils.notifyError(rootSpan(), message, params, expected) } override fun clear() {} @@ -146,7 +140,12 @@ class OpenTelemetryTracer : TracerEngine, ThreadContextManager { else -> resolveByPrimitiveTypeRepresentationOnJvm(tClass, k) } - private fun currentSpan(): Span? = Span.current() + private fun currentSpan(): Span = Span.current() + + private fun rootSpan(): Span { + val root = LocalRootSpan.current().takeIf { it != Span.getInvalid() } + return root ?: Span.current() + } private class Setter(private val key: AttributeKey, private val transformer: (T) -> R) { fun setAttributeIn(span: Span, value: T) { diff --git a/impl/java/tracing/src/test/kotlin/br/com/guiabolso/tracing/engine/opentelemetry/OpenTelemetryTracerTest.kt b/impl/java/tracing/src/test/kotlin/br/com/guiabolso/tracing/engine/opentelemetry/OpenTelemetryTracerTest.kt index 63f9d0c7..bffa5833 100644 --- a/impl/java/tracing/src/test/kotlin/br/com/guiabolso/tracing/engine/opentelemetry/OpenTelemetryTracerTest.kt +++ b/impl/java/tracing/src/test/kotlin/br/com/guiabolso/tracing/engine/opentelemetry/OpenTelemetryTracerTest.kt @@ -1,11 +1,23 @@ package br.com.guiabolso.tracing.engine.opentelemetry +import br.com.guiabolso.tracing.engine.opentelemetry.OpenTelemetryTracer.Companion.TRACER_NAME +import br.com.guiabolso.tracing.utils.opentelemetry.DefaultUnspecifiedException +import io.mockk.clearStaticMockk +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic import io.mockk.spyk import io.mockk.verify import io.opentelemetry.api.GlobalOpenTelemetry import io.opentelemetry.api.OpenTelemetry import io.opentelemetry.api.common.AttributeKey +import io.opentelemetry.api.common.Attributes import io.opentelemetry.api.trace.Span +import io.opentelemetry.api.trace.StatusCode +import io.opentelemetry.instrumentation.api.instrumenter.LocalRootSpan +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertInstanceOf +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test private val otel = OpenTelemetry.noop().apply(GlobalOpenTelemetry::set) @@ -13,6 +25,12 @@ private val otel = OpenTelemetry.noop().apply(GlobalOpenTelemetry::set) class OpenTelemetryTracerTest { private val openTelemetryTracer = OpenTelemetryTracer() + @BeforeEach + fun before() { + clearStaticMockk(Span::class) + clearStaticMockk(LocalRootSpan::class) + } + @Test fun `should add all supported properties successfully`() { val span = currentSpyiedSpan() @@ -69,12 +87,66 @@ class OpenTelemetryTracerTest { } @Test - fun `should set operation name successfully`() { + fun `should update the span name when set operation`() { + mockkStatic(Span::current) + mockkStatic(LocalRootSpan::current) + val span = mockk(relaxed = true) + every { LocalRootSpan.current() } returns span + every { Span.current() } returns span + openTelemetryTracer.setOperationName("my-operation") + + verify { + span.updateName("my-operation") + } + } + + @Test + fun `should add property to root span`() { + mockkStatic(LocalRootSpan::current) + val span = mockk(relaxed = true) + every { LocalRootSpan.current() } returns span + + openTelemetryTracer.addRootProperty("number", 1) + openTelemetryTracer.addRootProperty("bool", true) + openTelemetryTracer.addRootProperty("string", "my-string") + + verify(exactly = 1) { + span.setAttribute(AttributeKey.longKey("number"), 1L) + span.setAttribute(AttributeKey.booleanKey("bool"), true) + span.setAttribute(AttributeKey.stringKey("string"), "my-string") + } + } + + @Test + fun `should report error on root span`() { + mockkStatic(LocalRootSpan::current) + + val span = mockk(relaxed = true) + every { LocalRootSpan.current() } returns span + + val ex = NotImplementedError() + openTelemetryTracer.notifyRootError(ex, expected = false) + openTelemetryTracer.notifyRootError("my error", mapOf("tag" to "1"), expected = false) + + verify(exactly = 2) { + span.setStatus(StatusCode.ERROR) + } + + verify { + span.recordException(ex) + span.recordException( + withArg { + assertInstanceOf(DefaultUnspecifiedException::class.java, it) + assertEquals("my error", it.message) + }, + Attributes.builder().put("tag", "1").build() + ) + } } private fun currentSpyiedSpan(): Span { - return otel.getTracer(OpenTelemetryTracer.TRACER_NAME) + return otel.getTracer(TRACER_NAME) .spanBuilder("name") .setNoParent() .startSpan()