Skip to content

Commit 1840225

Browse files
committed
Allow custom resource attributes to be set on otel SDK instance
1 parent 003779b commit 1840225

11 files changed

Lines changed: 181 additions & 120 deletions

File tree

embrace-android-api/api/embrace-android-api.api

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ public abstract interface class io/embrace/android/embracesdk/internal/api/OTelA
106106
public abstract fun addLogRecordExporter (Lio/opentelemetry/sdk/logs/export/LogRecordExporter;)V
107107
public abstract fun addSpanExporter (Lio/opentelemetry/sdk/trace/export/SpanExporter;)V
108108
public abstract fun getOpenTelemetry ()Lio/opentelemetry/api/OpenTelemetry;
109+
public abstract fun setResourceAttribute (Lio/opentelemetry/api/common/AttributeKey;Ljava/lang/String;)V
110+
public abstract fun setResourceAttribute (Ljava/lang/String;Ljava/lang/String;)V
111+
}
112+
113+
public final class io/embrace/android/embracesdk/internal/api/OTelApi$DefaultImpls {
114+
public static fun setResourceAttribute (Lio/embrace/android/embracesdk/internal/api/OTelApi;Lio/opentelemetry/api/common/AttributeKey;Ljava/lang/String;)V
109115
}
110116

111117
public abstract interface class io/embrace/android/embracesdk/internal/api/SdkApi : io/embrace/android/embracesdk/internal/api/BreadcrumbApi, io/embrace/android/embracesdk/internal/api/EmbraceAndroidApi, io/embrace/android/embracesdk/internal/api/InstrumentationApi, io/embrace/android/embracesdk/internal/api/InternalWebViewApi, io/embrace/android/embracesdk/internal/api/LogsApi, io/embrace/android/embracesdk/internal/api/NetworkRequestApi, io/embrace/android/embracesdk/internal/api/OTelApi, io/embrace/android/embracesdk/internal/api/SdkStateApi, io/embrace/android/embracesdk/internal/api/SessionApi, io/embrace/android/embracesdk/internal/api/UserApi, io/embrace/android/embracesdk/spans/TracingApi {
@@ -123,6 +129,7 @@ public final class io/embrace/android/embracesdk/internal/api/SdkApi$DefaultImpl
123129
public static fun recordSpan (Lio/embrace/android/embracesdk/internal/api/SdkApi;Ljava/lang/String;Lio/embrace/android/embracesdk/spans/AutoTerminationMode;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;
124130
public static fun recordSpan (Lio/embrace/android/embracesdk/internal/api/SdkApi;Ljava/lang/String;Lio/embrace/android/embracesdk/spans/EmbraceSpan;Lio/embrace/android/embracesdk/spans/AutoTerminationMode;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;
125131
public static fun recordSpan (Lio/embrace/android/embracesdk/internal/api/SdkApi;Ljava/lang/String;Ljava/util/Map;Ljava/util/List;Lio/embrace/android/embracesdk/spans/AutoTerminationMode;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;
132+
public static fun setResourceAttribute (Lio/embrace/android/embracesdk/internal/api/SdkApi;Lio/opentelemetry/api/common/AttributeKey;Ljava/lang/String;)V
126133
public static fun startSpan (Lio/embrace/android/embracesdk/internal/api/SdkApi;Ljava/lang/String;Lio/embrace/android/embracesdk/spans/AutoTerminationMode;)Lio/embrace/android/embracesdk/spans/EmbraceSpan;
127134
public static fun startSpan (Lio/embrace/android/embracesdk/internal/api/SdkApi;Ljava/lang/String;Lio/embrace/android/embracesdk/spans/EmbraceSpan;Lio/embrace/android/embracesdk/spans/AutoTerminationMode;)Lio/embrace/android/embracesdk/spans/EmbraceSpan;
128135
}

embrace-android-api/src/main/kotlin/io/embrace/android/embracesdk/internal/api/OTelApi.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package io.embrace.android.embracesdk.internal.api
22

33
import io.embrace.android.embracesdk.annotation.InternalApi
44
import io.opentelemetry.api.OpenTelemetry
5+
import io.opentelemetry.api.common.AttributeKey
56
import io.opentelemetry.api.trace.Tracer
67
import io.opentelemetry.sdk.logs.export.LogRecordExporter
8+
import io.opentelemetry.sdk.resources.Resource
79
import io.opentelemetry.sdk.trace.export.SpanExporter
810

911
/**
@@ -27,4 +29,21 @@ public interface OTelApi {
2729
* model.
2830
*/
2931
public fun getOpenTelemetry(): OpenTelemetry
32+
33+
/**
34+
* Set an attribute on the [Resource] used by the OTel SDK instance with the given [AttributeKey] key and String value.
35+
* The value set will override any value set previously or by the Embrace SDK.
36+
* This must be called before the SDK is started in order for it to take effect.
37+
*/
38+
public fun setResourceAttribute(
39+
key: AttributeKey<String>,
40+
value: String
41+
): Unit = setResourceAttribute(key.key, value)
42+
43+
/**
44+
* Set an attribute on the [Resource] used by the OTel SDK instance with the given String key and value.
45+
* The value set will override any value set previously or by the Embrace SDK.
46+
* This must be called before the SDK is started in order for it to take effect.
47+
*/
48+
public fun setResourceAttribute(key: String, value: String)
3049
}

embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/opentelemetry/OpenTelemetryConfiguration.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import io.embrace.android.embracesdk.internal.spans.SpanSink
1313
import io.opentelemetry.sdk.logs.LogRecordProcessor
1414
import io.opentelemetry.sdk.logs.export.LogRecordExporter
1515
import io.opentelemetry.sdk.resources.Resource
16+
import io.opentelemetry.sdk.resources.ResourceBuilder
1617
import io.opentelemetry.sdk.trace.SpanProcessor
1718
import io.opentelemetry.sdk.trace.export.SpanExporter
1819
import io.opentelemetry.semconv.ServiceAttributes
@@ -29,7 +30,7 @@ class OpenTelemetryConfiguration(
2930
) {
3031
val embraceSdkName: String = BuildConfig.LIBRARY_PACKAGE_NAME
3132
val embraceSdkVersion: String = BuildConfig.VERSION_NAME
32-
val resource: Resource = Resource.getDefault().toBuilder()
33+
val resourceBuilder: ResourceBuilder = Resource.getDefault().toBuilder()
3334
.put(ServiceAttributes.SERVICE_NAME, embraceSdkName)
3435
.put(ServiceAttributes.SERVICE_VERSION, embraceSdkVersion)
3536
.put(OsIncubatingAttributes.OS_NAME, systemInfo.osName)
@@ -42,7 +43,6 @@ class OpenTelemetryConfiguration(
4243
.put(DeviceIncubatingAttributes.DEVICE_MODEL_NAME, systemInfo.deviceModel)
4344
.put(TelemetryIncubatingAttributes.TELEMETRY_DISTRO_NAME, embraceSdkName)
4445
.put(TelemetryIncubatingAttributes.TELEMETRY_DISTRO_VERSION, embraceSdkVersion)
45-
.build()
4646

4747
/**
4848
* Unique ID generated for an instance of the app process and not related to the actual process ID assigned by the OS.

embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/opentelemetry/OpenTelemetrySdk.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import io.opentelemetry.api.trace.Tracer
1111
import io.opentelemetry.sdk.OpenTelemetrySdk
1212
import io.opentelemetry.sdk.common.Clock
1313
import io.opentelemetry.sdk.logs.SdkLoggerProvider
14+
import io.opentelemetry.sdk.resources.Resource
1415
import io.opentelemetry.sdk.trace.SdkTracerProvider
1516
import io.opentelemetry.sdk.trace.SpanLimits
1617

@@ -33,7 +34,7 @@ internal class OpenTelemetrySdk(
3334
Systrace.traceSynchronous("otel-tracer-provider-init") {
3435
SdkTracerProvider
3536
.builder()
36-
.addResource(configuration.resource)
37+
.addResource(resource)
3738
.addSpanProcessor(configuration.spanProcessor)
3839
.setSpanLimits(
3940
SpanLimits
@@ -57,6 +58,10 @@ internal class OpenTelemetrySdk(
5758

5859
fun getOpenTelemetryLogger(): Logger = logger
5960

61+
private val resource: Resource by lazy {
62+
configuration.resourceBuilder.build()
63+
}
64+
6065
private val sdk: OpenTelemetrySdk by lazy {
6166
Systrace.traceSynchronous("otel-sdk-init") {
6267
OpenTelemetrySdk
@@ -65,7 +70,7 @@ internal class OpenTelemetrySdk(
6570
.setLoggerProvider(
6671
SdkLoggerProvider
6772
.builder()
68-
.addResource(configuration.resource)
73+
.addResource(resource)
6974
.addLogRecordProcessor(configuration.logProcessor)
7075
.setClock(openTelemetryClock)
7176
.build()

embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/opentelemetry/OpenTelemetryConfigurationTest.kt

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,38 +31,40 @@ internal class OpenTelemetryConfigurationTest {
3131
systemInfo = systemInfo
3232
)
3333

34-
assertEquals(configuration.embraceSdkName, configuration.resource.getAttribute(ServiceAttributes.SERVICE_NAME))
34+
val resource = configuration.resourceBuilder.build()
35+
36+
assertEquals(configuration.embraceSdkName, resource.getAttribute(ServiceAttributes.SERVICE_NAME))
3537
assertEquals(
3638
configuration.embraceSdkVersion,
37-
configuration.resource.getAttribute(ServiceAttributes.SERVICE_VERSION)
39+
resource.getAttribute(ServiceAttributes.SERVICE_VERSION)
3840
)
3941
assertEquals(
4042
configuration.embraceSdkName,
41-
configuration.resource.getAttribute(TelemetryIncubatingAttributes.TELEMETRY_DISTRO_NAME)
43+
resource.getAttribute(TelemetryIncubatingAttributes.TELEMETRY_DISTRO_NAME)
4244
)
4345
assertEquals(
4446
configuration.embraceSdkVersion,
45-
configuration.resource.getAttribute(TelemetryIncubatingAttributes.TELEMETRY_DISTRO_VERSION)
47+
resource.getAttribute(TelemetryIncubatingAttributes.TELEMETRY_DISTRO_VERSION)
4648
)
47-
assertEquals(systemInfo.osName, configuration.resource.getAttribute(OsIncubatingAttributes.OS_NAME))
48-
assertEquals(systemInfo.osVersion, configuration.resource.getAttribute(OsIncubatingAttributes.OS_VERSION))
49-
assertEquals(systemInfo.osType, configuration.resource.getAttribute(OsIncubatingAttributes.OS_TYPE))
50-
assertEquals(systemInfo.osBuild, configuration.resource.getAttribute(OsIncubatingAttributes.OS_BUILD_ID))
49+
assertEquals(systemInfo.osName, resource.getAttribute(OsIncubatingAttributes.OS_NAME))
50+
assertEquals(systemInfo.osVersion, resource.getAttribute(OsIncubatingAttributes.OS_VERSION))
51+
assertEquals(systemInfo.osType, resource.getAttribute(OsIncubatingAttributes.OS_TYPE))
52+
assertEquals(systemInfo.osBuild, resource.getAttribute(OsIncubatingAttributes.OS_BUILD_ID))
5153
assertEquals(
5254
systemInfo.androidOsApiLevel,
53-
configuration.resource.getAttribute(AndroidIncubatingAttributes.ANDROID_OS_API_LEVEL)
55+
resource.getAttribute(AndroidIncubatingAttributes.ANDROID_OS_API_LEVEL)
5456
)
5557
assertEquals(
5658
systemInfo.deviceManufacturer,
57-
configuration.resource.getAttribute(DeviceIncubatingAttributes.DEVICE_MANUFACTURER)
59+
resource.getAttribute(DeviceIncubatingAttributes.DEVICE_MANUFACTURER)
5860
)
5961
assertEquals(
6062
systemInfo.deviceModel,
61-
configuration.resource.getAttribute(DeviceIncubatingAttributes.DEVICE_MODEL_IDENTIFIER)
63+
resource.getAttribute(DeviceIncubatingAttributes.DEVICE_MODEL_IDENTIFIER)
6264
)
6365
assertEquals(
6466
systemInfo.deviceModel,
65-
configuration.resource.getAttribute(DeviceIncubatingAttributes.DEVICE_MODEL_NAME)
67+
resource.getAttribute(DeviceIncubatingAttributes.DEVICE_MODEL_NAME)
6668
)
6769
}
6870
}

embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/LogRecordExporterTest.kt

Lines changed: 0 additions & 46 deletions
This file was deleted.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package io.embrace.android.embracesdk.testcases
2+
3+
import androidx.test.ext.junit.runners.AndroidJUnit4
4+
import io.embrace.android.embracesdk.Severity
5+
import io.embrace.android.embracesdk.fakes.FakeSpanExporter
6+
import io.embrace.android.embracesdk.internal.arch.schema.EmbType
7+
import io.embrace.android.embracesdk.testframework.SdkIntegrationTestRule
8+
import io.opentelemetry.semconv.ServiceAttributes
9+
import org.junit.Assert.assertEquals
10+
import org.junit.Assert.assertNotNull
11+
import org.junit.Assert.assertTrue
12+
import org.junit.Rule
13+
import org.junit.Test
14+
import org.junit.runner.RunWith
15+
16+
@RunWith(AndroidJUnit4::class)
17+
internal class OTelExportTest {
18+
19+
@Rule
20+
@JvmField
21+
val testRule: SdkIntegrationTestRule = SdkIntegrationTestRule()
22+
23+
@Test
24+
fun `session span exported`() {
25+
val fakeSpanExporter = FakeSpanExporter()
26+
testRule.runTest(
27+
preSdkStartAction = {
28+
embrace.setResourceAttribute(ServiceAttributes.SERVICE_NAME, "my.app")
29+
embrace.setResourceAttribute("test", "foo")
30+
embrace.addSpanExporter(fakeSpanExporter)
31+
},
32+
testCaseAction = {
33+
recordSession {
34+
embrace.startSpan("test-span")?.stop()
35+
}
36+
},
37+
assertAction = {
38+
assertNotNull(getSingleSessionEnvelope())
39+
val exportedSpans = fakeSpanExporter.exportedSpans.associateBy { it.name }
40+
assertNotNull(exportedSpans["emb-session"])
41+
},
42+
otelExportAssertion = {
43+
val span = awaitSpans(1) { it.name == "test-span" }
44+
with(span.single()) {
45+
assertEquals("my.app", resource.attributes[ServiceAttributes.SERVICE_NAME])
46+
assertEquals("foo", resource.attributes.asMap().filter { it.key.key == "test" }.values.single())
47+
}
48+
}
49+
)
50+
}
51+
52+
@Test
53+
fun `a SpanExporter added after initialization won't be used`() {
54+
val fakeSpanExporter = FakeSpanExporter()
55+
56+
testRule.runTest(
57+
testCaseAction = {
58+
embrace.addSpanExporter(fakeSpanExporter)
59+
recordSession {
60+
embrace.startSpan("test")?.stop()
61+
}
62+
},
63+
assertAction = {
64+
assertNotNull(getSingleSessionEnvelope())
65+
assertTrue(fakeSpanExporter.exportedSpans.size == 0)
66+
}
67+
)
68+
}
69+
70+
@Suppress("deprecation")
71+
@Test
72+
fun `SDK can receive a LogRecordExporter`() {
73+
var logTimestampNanos = 0L
74+
75+
testRule.runTest(
76+
preSdkStartAction = {
77+
embrace.setResourceAttribute(ServiceAttributes.SERVICE_NAME, "my.app")
78+
embrace.setResourceAttribute("test", "foo")
79+
},
80+
testCaseAction = {
81+
recordSession {
82+
logTimestampNanos = clock.nowInNanos()
83+
embrace.logMessage("test message", Severity.INFO)
84+
}
85+
},
86+
otelExportAssertion = {
87+
val log = awaitLogs(1) { it.attributes.get(EmbType.System.Log.key.attributeKey) == EmbType.System.Log.value }
88+
with(log.single()) {
89+
assertEquals("test message", body.asString())
90+
assertEquals(logTimestampNanos, timestampEpochNanos)
91+
assertEquals(logTimestampNanos, observedTimestampEpochNanos)
92+
assertEquals("my.app", resource.attributes[ServiceAttributes.SERVICE_NAME])
93+
assertEquals("foo", resource.attributes.asMap().filter { it.key.key == "test" }.values.single())
94+
}
95+
}
96+
)
97+
}
98+
}

embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/SpanExporterTest.kt

Lines changed: 0 additions & 56 deletions
This file was deleted.

embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/Embrace.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,10 @@ public class Embrace private constructor(
390390
return impl.getOpenTelemetry()
391391
}
392392

393+
override fun setResourceAttribute(key: String, value: String) {
394+
impl.setResourceAttribute(key, value)
395+
}
396+
393397
/**
394398
* Adds a [LogRecordExporter] to the open telemetry logger.
395399
*

0 commit comments

Comments
 (0)