diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 99d20f9db..c83a69057 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,6 +21,7 @@ detekt = "1.23.8" binaryCompatValidator = "0.18.1" fragment = "1.8.9" koverGradlePlugin = "0.9.2" +opentelemetryKotlin = "0.5.1" [libraries] opentelemetry-platform-alpha = { module = "io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom-alpha", version.ref = "opentelemetry-instrumentation-alpha" } @@ -56,6 +57,8 @@ auto-service-processor = { module = "dev.zacsweers.autoservice:auto-service-ksp" compose = { group = "androidx.compose.ui", name = "ui", version.ref = "compose" } detekt-plugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" } binary-compat-validator = { module = "org.jetbrains.kotlinx:binary-compatibility-validator", version.ref = "binaryCompatValidator" } +opentelemetry-kotlin = { group = "io.embrace.opentelemetry.kotlin", name = "opentelemetry-kotlin", version.ref = "opentelemetryKotlin" } +opentelemetry-kotlin-compat = { group = "io.embrace.opentelemetry.kotlin", name = "opentelemetry-kotlin-compat", version.ref = "opentelemetryKotlin" } #Test tools opentelemetry-sdk-testing = { module = "io.opentelemetry:opentelemetry-sdk-testing" } @@ -75,6 +78,7 @@ assertj-core = "org.assertj:assertj-core:3.27.6" awaitility = "org.awaitility:awaitility:4.3.0" mockwebserver = "com.google.mockwebserver:mockwebserver:20130706" okhttp-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okhttp" } +opentelemetry-kotlin-testing = { group = "io.embrace.opentelemetry.kotlin", name = "opentelemetry-kotlin-testing", version.ref = "opentelemetryKotlin" } #Compilation tools desugarJdkLibs = "com.android.tools:desugar_jdk_libs:2.1.5" diff --git a/instrumentation/crash/build.gradle.kts b/instrumentation/crash/build.gradle.kts index 14f8ba0de..33f41d983 100644 --- a/instrumentation/crash/build.gradle.kts +++ b/instrumentation/crash/build.gradle.kts @@ -28,4 +28,7 @@ dependencies { implementation(libs.opentelemetry.instrumentation.api) testImplementation(libs.awaitility) testImplementation(libs.robolectric) + + implementation(libs.opentelemetry.kotlin) + implementation(libs.opentelemetry.kotlin.compat) } diff --git a/instrumentation/crash/src/main/java/io/opentelemetry/android/instrumentation/crash/CrashReporter.kt b/instrumentation/crash/src/main/java/io/opentelemetry/android/instrumentation/crash/CrashReporter.kt index bd6c12f67..716906c79 100644 --- a/instrumentation/crash/src/main/java/io/opentelemetry/android/instrumentation/crash/CrashReporter.kt +++ b/instrumentation/crash/src/main/java/io/opentelemetry/android/instrumentation/crash/CrashReporter.kt @@ -5,9 +5,13 @@ package io.opentelemetry.android.instrumentation.crash +import io.embrace.opentelemetry.kotlin.ExperimentalApi +import io.embrace.opentelemetry.kotlin.attributes.setAttributes +import io.embrace.opentelemetry.kotlin.getLogger +import io.embrace.opentelemetry.kotlin.toOtelKotlinApi import io.opentelemetry.android.instrumentation.common.EventAttributesExtractor import io.opentelemetry.api.common.Attributes -import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder +import io.opentelemetry.api.common.AttributesBuilder import io.opentelemetry.context.Context import io.opentelemetry.sdk.OpenTelemetrySdk import io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE @@ -37,35 +41,42 @@ internal class CrashReporter( Thread.setDefaultUncaughtExceptionHandler(handler) } + @OptIn(ExperimentalApi::class) private fun processCrash( openTelemetry: OpenTelemetrySdk, crashDetails: CrashDetails, ) { - val logger = openTelemetry.sdkLoggerProvider.loggerBuilder("io.opentelemetry.crash").build() + val otel = openTelemetry.toOtelKotlinApi() + val logger = otel.getLogger("io.opentelemetry.crash") val throwable = crashDetails.cause val thread = crashDetails.thread - val attributesBuilder = - Attributes - .builder() - .put(THREAD_ID, thread.id) - .put(THREAD_NAME, thread.name) - .put(EXCEPTION_MESSAGE, throwable.message) - .put( - EXCEPTION_STACKTRACE, - throwable.stackTraceToString(), - ).put(EXCEPTION_TYPE, throwable.javaClass.name) + + val attributesBuilder = Attributes.builder() for (extractor in extractors) { val extractedAttributes = extractor.extract(Context.current(), crashDetails) attributesBuilder.putAll(extractedAttributes) } - val eventBuilder = - logger.logRecordBuilder() as ExtendedLogRecordBuilder - eventBuilder - .setEventName("device.crash") - .setAllAttributes(attributesBuilder.build()) - .emit() + + logger.log(body = "device.crash") { + // event name not supported yet. Use body as an example instead. + setAttributes( + mapOf( + THREAD_ID.key to thread.id, + THREAD_NAME.key to thread.name, + EXCEPTION_STACKTRACE.key to throwable.stackTraceToString(), + EXCEPTION_TYPE.key to throwable.javaClass.name, + ), + ) + setAttributes(attributesBuilder.toMap()) + + throwable.message?.let { + setStringAttribute(EXCEPTION_MESSAGE.key, it) + } + } } + private fun AttributesBuilder.toMap(): Map = build().asMap().mapKeys { (k, _) -> k.key } + private fun waitForCrashFlush(openTelemetry: OpenTelemetrySdk) { val flushResult = openTelemetry.sdkLoggerProvider.forceFlush() flushResult.join(10, TimeUnit.SECONDS) diff --git a/instrumentation/crash/src/test/java/io/opentelemetry/android/instrumentation/crash/CrashReportIntegrationTest.kt b/instrumentation/crash/src/test/java/io/opentelemetry/android/instrumentation/crash/CrashReportIntegrationTest.kt index 2006a036f..e32ae6f6e 100644 --- a/instrumentation/crash/src/test/java/io/opentelemetry/android/instrumentation/crash/CrashReportIntegrationTest.kt +++ b/instrumentation/crash/src/test/java/io/opentelemetry/android/instrumentation/crash/CrashReportIntegrationTest.kt @@ -187,7 +187,6 @@ internal class CrashReportIntegrationTest { expectedExcType: String, expectedExcMessage: String? = null, ) { - assertEquals("device.crash", eventName) assertEquals(Severity.UNDEFINED_SEVERITY_NUMBER, severity) val attrs = attributes.asMap().mapKeys { it.key.key } diff --git a/instrumentation/slowrendering/build.gradle.kts b/instrumentation/slowrendering/build.gradle.kts index e38370aaa..20c4b10c0 100644 --- a/instrumentation/slowrendering/build.gradle.kts +++ b/instrumentation/slowrendering/build.gradle.kts @@ -27,6 +27,8 @@ dependencies { implementation(libs.androidx.core) implementation(libs.opentelemetry.semconv) implementation(libs.opentelemetry.sdk) + implementation(libs.opentelemetry.kotlin) + implementation(libs.opentelemetry.kotlin.compat) implementation(libs.opentelemetry.instrumentation.api) implementation(libs.opentelemetry.sdk.extension.incubator) testImplementation(libs.robolectric) diff --git a/instrumentation/slowrendering/src/main/java/io/opentelemetry/android/instrumentation/slowrendering/SlowRenderingInstrumentation.kt b/instrumentation/slowrendering/src/main/java/io/opentelemetry/android/instrumentation/slowrendering/SlowRenderingInstrumentation.kt index 573d37d17..4cab18f59 100644 --- a/instrumentation/slowrendering/src/main/java/io/opentelemetry/android/instrumentation/slowrendering/SlowRenderingInstrumentation.kt +++ b/instrumentation/slowrendering/src/main/java/io/opentelemetry/android/instrumentation/slowrendering/SlowRenderingInstrumentation.kt @@ -8,6 +8,9 @@ package io.opentelemetry.android.instrumentation.slowrendering import android.os.Build import android.util.Log import com.google.auto.service.AutoService +import io.embrace.opentelemetry.kotlin.ExperimentalApi +import io.embrace.opentelemetry.kotlin.getTracer +import io.embrace.opentelemetry.kotlin.toOtelKotlinApi import io.opentelemetry.android.common.RumConstants import io.opentelemetry.android.instrumentation.AndroidInstrumentation import io.opentelemetry.android.instrumentation.InstallationContext @@ -64,6 +67,7 @@ class SlowRenderingInstrumentation : AndroidInstrumentation { return this } + @OptIn(ExperimentalApi::class) override fun install(ctx: InstallationContext) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { Log.w( @@ -85,7 +89,8 @@ class SlowRenderingInstrumentation : AndroidInstrumentation { jankReporter = jankReporter.combine(EventJankReporter(logger, FROZEN_THRESHOLD_MS / 1000.0, debugVerbose)) if (useDeprecatedSpan) { - val tracer = ctx.openTelemetry.getTracer("io.opentelemetry.slow-rendering") + val otel = ctx.openTelemetry.toOtelKotlinApi() + val tracer = otel.getTracer("io.opentelemetry.slow-rendering") jankReporter = jankReporter.combine(SpanBasedJankReporter(tracer)) } diff --git a/instrumentation/slowrendering/src/main/java/io/opentelemetry/android/instrumentation/slowrendering/SpanBasedJankReporter.kt b/instrumentation/slowrendering/src/main/java/io/opentelemetry/android/instrumentation/slowrendering/SpanBasedJankReporter.kt index 0712ab068..4363be455 100644 --- a/instrumentation/slowrendering/src/main/java/io/opentelemetry/android/instrumentation/slowrendering/SpanBasedJankReporter.kt +++ b/instrumentation/slowrendering/src/main/java/io/opentelemetry/android/instrumentation/slowrendering/SpanBasedJankReporter.kt @@ -6,14 +6,16 @@ package io.opentelemetry.android.instrumentation.slowrendering import android.util.Log +import io.embrace.opentelemetry.kotlin.ExperimentalApi +import io.embrace.opentelemetry.kotlin.tracing.Tracer import io.opentelemetry.android.common.RumConstants -import io.opentelemetry.api.trace.Span -import io.opentelemetry.api.trace.Tracer import java.time.Instant +import java.util.concurrent.TimeUnit internal const val SLOW_THRESHOLD_MS = 16 internal const val FROZEN_THRESHOLD_MS = 700 +@OptIn(ExperimentalApi::class) internal class SpanBasedJankReporter( private val tracer: Tracer, ) : JankReporter { @@ -57,13 +59,13 @@ internal class SpanBasedJankReporter( slowCount: Int, now: Instant, ) { - val span: Span = - tracer - .spanBuilder(spanName) - .setAttribute("count", slowCount.toLong()) - .setAttribute("activity.name", activityName) - .setStartTimestamp(now) - .startSpan() - span.end(now) + val span = + tracer.createSpan(spanName, startTimestamp = now.toNanoSeconds()) { + setLongAttribute("count", slowCount.toLong()) + setStringAttribute("activity.name", activityName) + } + span.end(now.toNanoSeconds()) } + + private fun Instant.toNanoSeconds(): Long = TimeUnit.MILLISECONDS.convert(toEpochMilli(), TimeUnit.NANOSECONDS) } diff --git a/instrumentation/slowrendering/src/test/java/io/opentelemetry/android/instrumentation/slowrendering/SlowRenderListenerTest.java b/instrumentation/slowrendering/src/test/java/io/opentelemetry/android/instrumentation/slowrendering/SlowRenderListenerTest.java index 9d63b6ac8..f05154002 100644 --- a/instrumentation/slowrendering/src/test/java/io/opentelemetry/android/instrumentation/slowrendering/SlowRenderListenerTest.java +++ b/instrumentation/slowrendering/src/test/java/io/opentelemetry/android/instrumentation/slowrendering/SlowRenderListenerTest.java @@ -25,8 +25,11 @@ import android.os.Handler; import android.view.FrameMetrics; import androidx.test.ext.junit.runners.AndroidJUnit4; +import io.embrace.opentelemetry.kotlin.OpenTelemetryExtKt; +import io.embrace.opentelemetry.kotlin.OtelJavaOpenTelemetryExtKt; +import io.embrace.opentelemetry.kotlin.tracing.Tracer; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.sdk.testing.junit4.OpenTelemetryRule; import io.opentelemetry.sdk.trace.data.SpanData; import java.time.Duration; @@ -122,7 +125,10 @@ public void addAndRemove() { @Test public void removeWithMetrics() { - Tracer tracer = otelTesting.getOpenTelemetry().getTracer("testTracer"); + OpenTelemetry openTelemetry = otelTesting.getOpenTelemetry(); + io.embrace.opentelemetry.kotlin.OpenTelemetry otelKotlin = + OtelJavaOpenTelemetryExtKt.toOtelKotlinApi(openTelemetry); + Tracer tracer = OpenTelemetryExtKt.getTracer(otelKotlin, "testTracer"); jankReporter = new SpanBasedJankReporter(tracer); SlowRenderListener testInstance = new SlowRenderListener( @@ -157,7 +163,10 @@ public void start() { .when(exec) .scheduleWithFixedDelay(any(), eq(1001L), eq(1001L), eq(TimeUnit.MILLISECONDS)); - Tracer tracer = otelTesting.getOpenTelemetry().getTracer("testTracer"); + OpenTelemetry openTelemetry = otelTesting.getOpenTelemetry(); + io.embrace.opentelemetry.kotlin.OpenTelemetry otelKotlin = + OtelJavaOpenTelemetryExtKt.toOtelKotlinApi(openTelemetry); + Tracer tracer = OpenTelemetryExtKt.getTracer(otelKotlin, "testTracer"); jankReporter = new SpanBasedJankReporter(tracer); SlowRenderListener testInstance = new SlowRenderListener( diff --git a/instrumentation/slowrendering/src/test/java/io/opentelemetry/android/instrumentation/slowrendering/SlowRenderingInstrumentationTest.kt b/instrumentation/slowrendering/src/test/java/io/opentelemetry/android/instrumentation/slowrendering/SlowRenderingInstrumentationTest.kt index 4dda30238..c9e502d33 100644 --- a/instrumentation/slowrendering/src/test/java/io/opentelemetry/android/instrumentation/slowrendering/SlowRenderingInstrumentationTest.kt +++ b/instrumentation/slowrendering/src/test/java/io/opentelemetry/android/instrumentation/slowrendering/SlowRenderingInstrumentationTest.kt @@ -7,6 +7,9 @@ package io.opentelemetry.android.instrumentation.slowrendering import android.app.Application import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.embrace.opentelemetry.kotlin.ExperimentalApi +import io.embrace.opentelemetry.kotlin.createCompatOpenTelemetry +import io.embrace.opentelemetry.kotlin.toOtelJavaApi import io.mockk.Called import io.mockk.MockKAnnotations import io.mockk.Runs @@ -100,16 +103,16 @@ class SlowRenderingInstrumentationTest { verify { application.registerActivityLifecycleCallbacks(capture(capturedListener)) } } + @OptIn(ExperimentalApi::class) @Config(sdk = [24, 25]) @Test fun `can use legacy span`() { val capturedListener = slot() - every { openTelemetry.getTracer(any()) }.returns(mockk()) + val otel = createCompatOpenTelemetry().toOtelJavaApi() every { application.registerActivityLifecycleCallbacks(any()) } just Runs - val ctx = InstallationContext(application, openTelemetry, mockk()) + val ctx = InstallationContext(application, otel, mockk()) slowRenderingInstrumentation.enableDeprecatedZeroDurationSpan().install(ctx) - verify { openTelemetry.getTracer("io.opentelemetry.slow-rendering") } verify { application.registerActivityLifecycleCallbacks(capture(capturedListener)) } } } diff --git a/instrumentation/slowrendering/src/test/java/io/opentelemetry/android/instrumentation/slowrendering/SpanBasedJankReporterTest.kt b/instrumentation/slowrendering/src/test/java/io/opentelemetry/android/instrumentation/slowrendering/SpanBasedJankReporterTest.kt index 94336b07b..6a53b3d0c 100644 --- a/instrumentation/slowrendering/src/test/java/io/opentelemetry/android/instrumentation/slowrendering/SpanBasedJankReporterTest.kt +++ b/instrumentation/slowrendering/src/test/java/io/opentelemetry/android/instrumentation/slowrendering/SpanBasedJankReporterTest.kt @@ -6,10 +6,13 @@ package io.opentelemetry.android.instrumentation.slowrendering import android.util.Log +import io.embrace.opentelemetry.kotlin.ExperimentalApi +import io.embrace.opentelemetry.kotlin.getTracer +import io.embrace.opentelemetry.kotlin.toOtelKotlinApi +import io.embrace.opentelemetry.kotlin.tracing.Tracer import io.mockk.every import io.mockk.mockkStatic import io.opentelemetry.api.common.AttributeKey -import io.opentelemetry.api.trace.Tracer import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions import io.opentelemetry.sdk.testing.junit4.OpenTelemetryRule import io.opentelemetry.sdk.trace.data.SpanData @@ -21,6 +24,7 @@ import org.junit.jupiter.api.Test private val COUNT_KEY = AttributeKey.longKey("count") +@OptIn(ExperimentalApi::class) class SpanBasedJankReporterTest { private lateinit var tracer: Tracer @@ -29,7 +33,7 @@ class SpanBasedJankReporterTest { @BeforeEach fun setup() { - tracer = otelTesting.openTelemetry.getTracer("testTracer") + tracer = otelTesting.openTelemetry.toOtelKotlinApi().getTracer("testTracer") } @Test