diff --git a/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java b/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java index ef6867064..ab002634e 100644 --- a/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java +++ b/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java @@ -311,8 +311,9 @@ public OpenTelemetryRum build() { OpenTelemetrySdk.builder() .setTracerProvider( buildTracerProvider(sessionManager, application, spanExporter)) + .setLoggerProvider( + buildLoggerProvider(sessionManager, application, logsExporter)) .setMeterProvider(buildMeterProvider(application)) - .setLoggerProvider(buildLoggerProvider(application, logsExporter)) .setPropagators(buildFinalPropagators()) .build(); @@ -470,13 +471,16 @@ private SdkTracerProvider buildTracerProvider( } private SdkLoggerProvider buildLoggerProvider( - Application application, LogRecordExporter logsExporter) { + SessionProvider sessionProvider, + Application application, + LogRecordExporter logsExporter) { SdkLoggerProviderBuilder loggerProviderBuilder = SdkLoggerProvider.builder() + .setResource(resource) + .addLogRecordProcessor(new SessionIdLogRecordAppender(sessionProvider)) .addLogRecordProcessor( new GlobalAttributesLogRecordAppender( - config.getGlobalAttributesSupplier())) - .setResource(resource); + config.getGlobalAttributesSupplier())); LogRecordProcessor batchLogsProcessor = BatchLogRecordProcessor.builder(logsExporter).build(); loggerProviderBuilder.addLogRecordProcessor(batchLogsProcessor); diff --git a/core/src/main/java/io/opentelemetry/android/SessionIdLogRecordAppender.kt b/core/src/main/java/io/opentelemetry/android/SessionIdLogRecordAppender.kt new file mode 100644 index 000000000..671e71dab --- /dev/null +++ b/core/src/main/java/io/opentelemetry/android/SessionIdLogRecordAppender.kt @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.android + +import io.opentelemetry.android.session.SessionProvider +import io.opentelemetry.context.Context +import io.opentelemetry.sdk.logs.LogRecordProcessor +import io.opentelemetry.sdk.logs.ReadWriteLogRecord +import io.opentelemetry.semconv.incubating.SessionIncubatingAttributes.SESSION_ID + +internal class SessionIdLogRecordAppender(private val sessionProvider: SessionProvider) : + LogRecordProcessor { + override fun onEmit( + context: Context, + logRecord: ReadWriteLogRecord, + ) { + logRecord.setAttribute(SESSION_ID, sessionProvider.getSessionId()) + } +} diff --git a/core/src/test/java/io/opentelemetry/android/OpenTelemetryRumBuilderTest.java b/core/src/test/java/io/opentelemetry/android/OpenTelemetryRumBuilderTest.java index 7d2e066c0..09bcc5177 100644 --- a/core/src/test/java/io/opentelemetry/android/OpenTelemetryRumBuilderTest.java +++ b/core/src/test/java/io/opentelemetry/android/OpenTelemetryRumBuilderTest.java @@ -9,6 +9,7 @@ import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.incubating.SessionIncubatingAttributes.SESSION_ID; import static org.awaitility.Awaitility.await; import static org.mockito.ArgumentMatchers.anyCollection; import static org.mockito.ArgumentMatchers.anyLong; @@ -64,7 +65,6 @@ import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; import io.opentelemetry.sdk.trace.export.SpanExporter; -import io.opentelemetry.semconv.incubating.SessionIncubatingAttributes; import java.io.IOException; import java.time.Duration; import java.util.Collection; @@ -147,8 +147,7 @@ public void shouldBuildTracerProvider() { .hasName("test span") .hasResource(resource) .hasAttributesSatisfyingExactly( - equalTo(SessionIncubatingAttributes.SESSION_ID, sessionId), - equalTo(SCREEN_NAME_KEY, "unknown")); + equalTo(SESSION_ID, sessionId), equalTo(SCREEN_NAME_KEY, "unknown")); } @Test @@ -173,6 +172,7 @@ public void shouldBuildLogRecordProvider() { assertThat(logs).hasSize(1); assertThat(logs.get(0)) .hasAttributesSatisfyingExactly( + equalTo(SESSION_ID, openTelemetryRum.getRumSessionId()), equalTo(stringKey("event.name"), "test.event"), equalTo(stringKey("mega"), "hit")) .hasResource(resource); @@ -311,7 +311,9 @@ public void setLogRecordExporterCustomizer() { assertThat(logs).hasSize(1); assertThat(logs.iterator().next()) .hasBody("foo") - .hasAttributesSatisfyingExactly(equalTo(stringKey("bing"), "bang")) + .hasAttributesSatisfyingExactly( + equalTo(stringKey("bing"), "bang"), + equalTo(SESSION_ID, rum.getRumSessionId())) .hasSeverity(Severity.FATAL3); } @@ -421,6 +423,7 @@ public void verifyGlobalAttrsForLogs() { OpenTelemetryAssertions.assertThat(logRecordData) .hasAttributes( Attributes.builder() + .put(SESSION_ID, rum.getRumSessionId()) .put("someGlobalKey", "someGlobalValue") .put("localAttrKey", "localAttrValue") .build()); diff --git a/core/src/test/java/io/opentelemetry/android/SessionIdLogRecordAppenderTest.kt b/core/src/test/java/io/opentelemetry/android/SessionIdLogRecordAppenderTest.kt new file mode 100644 index 000000000..a315dd686 --- /dev/null +++ b/core/src/test/java/io/opentelemetry/android/SessionIdLogRecordAppenderTest.kt @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.android + +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.verify +import io.opentelemetry.android.session.SessionProvider +import io.opentelemetry.api.common.AttributeKey +import io.opentelemetry.context.Context +import io.opentelemetry.sdk.logs.ReadWriteLogRecord +import io.opentelemetry.semconv.incubating.SessionIncubatingAttributes.SESSION_ID +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +private const val SESSION_ID_VALUE = "0666" + +class SessionIdLogRecordAppenderTest { + @MockK + lateinit var sessionProvider: SessionProvider + + @MockK + lateinit var log: ReadWriteLogRecord + + @BeforeEach + fun setUp() { + MockKAnnotations.init(this) + every { sessionProvider.getSessionId() }.returns(SESSION_ID_VALUE) + every { log.setAttribute(any>(), any()) } returns log + } + + @Test + fun `should set sessionId as log record attribute`() { + val underTest = SessionIdLogRecordAppender(sessionProvider) + + underTest.onEmit(Context.root(), log) + + verify { log.setAttribute(SESSION_ID, SESSION_ID_VALUE) } + } +} diff --git a/instrumentation/crash/src/main/java/io/opentelemetry/android/instrumentation/crash/CrashReporter.java b/instrumentation/crash/src/main/java/io/opentelemetry/android/instrumentation/crash/CrashReporter.java index 7a08c6cda..b5b08bcf7 100644 --- a/instrumentation/crash/src/main/java/io/opentelemetry/android/instrumentation/crash/CrashReporter.java +++ b/instrumentation/crash/src/main/java/io/opentelemetry/android/instrumentation/crash/CrashReporter.java @@ -9,6 +9,7 @@ import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE; +import static io.opentelemetry.semconv.incubating.EventIncubatingAttributes.EVENT_NAME; import static io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes.THREAD_ID; import static io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes.THREAD_NAME; @@ -60,6 +61,8 @@ private void emitCrashEvent(Logger crashReporter, CrashDetails crashDetails) { extractor.onStart(attributesBuilder, Context.current(), crashDetails); } + // TODO: use emitEvent() when available, with event name from semantic conventions. + attributesBuilder.put(EVENT_NAME, "device.crash"); crashReporter.logRecordBuilder().setAllAttributes(attributesBuilder.build()).emit(); }