diff --git a/instrumentation/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java b/instrumentation/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java index 338e30279..12f19e195 100644 --- a/instrumentation/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java +++ b/instrumentation/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java @@ -29,6 +29,7 @@ import io.opentelemetry.android.instrumentation.startup.SdkInitializationEvents; import io.opentelemetry.android.internal.features.persistence.DiskManager; import io.opentelemetry.android.internal.features.persistence.SimpleTemporaryFileProvider; +import io.opentelemetry.android.internal.processors.GlobalAttributesLogRecordAppender; import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; import io.opentelemetry.context.propagation.ContextPropagators; @@ -501,7 +502,11 @@ private SdkMeterProvider buildMeterProvider(Application application) { private SdkLoggerProvider buildLoggerProvider(Application application) { SdkLoggerProviderBuilder loggerProviderBuilder = - SdkLoggerProvider.builder().setResource(resource); + SdkLoggerProvider.builder() + .addLogRecordProcessor( + new GlobalAttributesLogRecordAppender( + config.getGlobalAttributesSupplier())) + .setResource(resource); for (BiFunction customizer : loggerProviderCustomizers) { loggerProviderBuilder = customizer.apply(loggerProviderBuilder, application); diff --git a/instrumentation/src/main/java/io/opentelemetry/android/internal/processors/GlobalAttributesLogRecordAppender.kt b/instrumentation/src/main/java/io/opentelemetry/android/internal/processors/GlobalAttributesLogRecordAppender.kt new file mode 100644 index 000000000..3b33b90c1 --- /dev/null +++ b/instrumentation/src/main/java/io/opentelemetry/android/internal/processors/GlobalAttributesLogRecordAppender.kt @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.android.internal.processors + +import io.opentelemetry.api.common.Attributes +import io.opentelemetry.context.Context +import io.opentelemetry.sdk.logs.LogRecordProcessor +import io.opentelemetry.sdk.logs.ReadWriteLogRecord +import java.util.function.Supplier + +internal class GlobalAttributesLogRecordAppender(private val attributesSupplier: Supplier) : + LogRecordProcessor { + override fun onEmit( + context: Context, + logRecord: ReadWriteLogRecord, + ) { + logRecord.setAllAttributes(attributesSupplier.get()) + } +} diff --git a/instrumentation/src/test/java/io/opentelemetry/android/OpenTelemetryRumBuilderTest.java b/instrumentation/src/test/java/io/opentelemetry/android/OpenTelemetryRumBuilderTest.java index db587a3a3..5454668c1 100644 --- a/instrumentation/src/test/java/io/opentelemetry/android/OpenTelemetryRumBuilderTest.java +++ b/instrumentation/src/test/java/io/opentelemetry/android/OpenTelemetryRumBuilderTest.java @@ -34,13 +34,20 @@ import io.opentelemetry.android.internal.services.PreferencesService; import io.opentelemetry.android.internal.services.Service; import io.opentelemetry.android.internal.services.ServiceManager; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.logs.Logger; import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; import io.opentelemetry.context.propagation.TextMapGetter; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.contrib.disk.buffering.SpanToDiskExporter; +import io.opentelemetry.sdk.logs.data.LogRecordData; +import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor; import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; +import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter; import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; @@ -64,6 +71,7 @@ class OpenTelemetryRumBuilderTest { final Resource resource = Resource.getDefault().toBuilder().put("test.attribute", "abcdef").build(); final InMemorySpanExporter spanExporter = InMemorySpanExporter.create(); + final InMemoryLogRecordExporter logRecordExporter = InMemoryLogRecordExporter.create(); @Mock Application application; @Mock Looper looper; @@ -270,6 +278,36 @@ void diskBufferingDisabled() { assertThat(SignalFromDiskExporter.get()).isNull(); } + @Test + void verifyGlobalAttrsForLogs() { + OtelRumConfig otelRumConfig = buildConfig(); + otelRumConfig.setGlobalAttributes( + () -> Attributes.of(AttributeKey.stringKey("someGlobalKey"), "someGlobalValue")); + + OpenTelemetryRum rum = + OpenTelemetryRum.builder(application, otelRumConfig) + .addLoggerProviderCustomizer( + (sdkLoggerProviderBuilder, application) -> + sdkLoggerProviderBuilder.addLogRecordProcessor( + SimpleLogRecordProcessor.create(logRecordExporter))) + .build(); + + Logger logger = rum.getOpenTelemetry().getLogsBridge().loggerBuilder("LogScope").build(); + logger.logRecordBuilder() + .setAttribute(AttributeKey.stringKey("localAttrKey"), "localAttrValue") + .emit(); + + List recordedLogs = logRecordExporter.getFinishedLogRecordItems(); + assertThat(recordedLogs).hasSize(1); + LogRecordData logRecordData = recordedLogs.get(0); + OpenTelemetryAssertions.assertThat(logRecordData) + .hasAttributes( + Attributes.builder() + .put("someGlobalKey", "someGlobalValue") + .put("localAttrKey", "localAttrValue") + .build()); + } + private static void setUpServiceManager(Service... services) { ServiceManager serviceManager = mock(); for (Service service : services) { diff --git a/instrumentation/src/test/java/io/opentelemetry/android/internal/processors/GlobalAttributesLogRecordAppenderTest.kt b/instrumentation/src/test/java/io/opentelemetry/android/internal/processors/GlobalAttributesLogRecordAppenderTest.kt new file mode 100644 index 000000000..ee261394b --- /dev/null +++ b/instrumentation/src/test/java/io/opentelemetry/android/internal/processors/GlobalAttributesLogRecordAppenderTest.kt @@ -0,0 +1,63 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.android.internal.processors + +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import io.opentelemetry.api.common.Attributes +import io.opentelemetry.context.Context +import io.opentelemetry.sdk.logs.ReadWriteLogRecord +import org.junit.jupiter.api.Test +import java.util.function.Supplier + +class GlobalAttributesLogRecordAppenderTest { + @Test + fun `Add attributes to every logRecord`() { + val attributes = Attributes.builder().put("oneAttr", "oneAttrValue").build() + val attrsSupplier = Supplier { attributes } + val appender = GlobalAttributesLogRecordAppender(attrsSupplier) + val log = createLogRecord() + + appender.onEmit(Context.root(), log) + + verify { + log.setAllAttributes(attributes) + } + } + + @Test + fun `Add updated supplied attrs`() { + val attributes = Attributes.builder().put("oneAttr", "oneAttrValue").build() + val attributes2 = Attributes.builder().put("otherAttr", "otherAttrValue").build() + val attrsSupplier = mockk>() + val appender = GlobalAttributesLogRecordAppender(attrsSupplier) + + // Check first response + val log = createLogRecord() + every { attrsSupplier.get() }.returns(attributes) + appender.onEmit(Context.root(), log) + + verify { + log.setAllAttributes(attributes) + } + + // Check second response + val log2 = createLogRecord() + every { attrsSupplier.get() }.returns(attributes2) + appender.onEmit(Context.root(), log2) + + verify { + log2.setAllAttributes(attributes2) + } + } + + private fun createLogRecord(): ReadWriteLogRecord { + val log = mockk() + every { log.setAllAttributes(any()) }.returns(log) + return log + } +}