Skip to content

Commit bb8c0d5

Browse files
authored
Use app.screen.name and allow opt-in for older semconv (#1819)
* set up config and helper to map to old semconv * use `app.screen.name` over `screen.name` with compat flag. * add test * wire up to DSL * wire up to DSL * use method
1 parent 0bed377 commit bb8c0d5

22 files changed

Lines changed: 265 additions & 51 deletions

File tree

android-agent/api/android-agent.api

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,17 @@ public final class io/opentelemetry/android/agent/dsl/OpenTelemetryConfiguration
6969
public final fun httpExport (Lkotlin/jvm/functions/Function1;)V
7070
public final fun instrumentations (Lkotlin/jvm/functions/Function1;)V
7171
public final fun resource (Lkotlin/jvm/functions/Function1;)V
72+
public final fun semanticConventions (Lkotlin/jvm/functions/Function1;)V
7273
public final fun session (Lkotlin/jvm/functions/Function1;)V
7374
public final fun setClock (Lio/opentelemetry/sdk/common/Clock;)V
7475
}
7576

77+
public final class io/opentelemetry/android/agent/dsl/SemanticConventionsConfiguration {
78+
public fun <init> ()V
79+
public final fun getUseLatestExperimental ()Z
80+
public final fun setUseLatestExperimental (Z)V
81+
}
82+
7683
public final class io/opentelemetry/android/agent/dsl/SessionConfiguration {
7784
public final fun getBackgroundInactivityTimeout-UwyO8pc ()J
7885
public final fun getMaxLifetime-UwyO8pc ()J

android-agent/src/main/kotlin/io/opentelemetry/android/agent/dsl/OpenTelemetryConfiguration.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ class OpenTelemetryConfiguration internal constructor(
3131
internal val exportConfig = HttpExportConfiguration()
3232
internal val sessionConfig = SessionConfiguration()
3333
internal val instrumentations = InstrumentationConfiguration(rumConfig, instrumentationLoader)
34+
35+
internal val semanticConventions = SemanticConventionsConfiguration()
3436
internal var resourceAction: ResourceBuilder.() -> Unit = {}
3537

3638
/**
@@ -68,6 +70,13 @@ class OpenTelemetryConfiguration internal constructor(
6870
instrumentations.action()
6971
}
7072

73+
/**
74+
* Configures semantic conventions.
75+
*/
76+
fun semanticConventions(action: SemanticConventionsConfiguration.() -> Unit) {
77+
semanticConventions.action()
78+
}
79+
7180
/**
7281
* Configures session behavior.
7382
*/
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package io.opentelemetry.android.agent.dsl
2+
3+
import io.opentelemetry.android.common.internal.SemconvCompat
4+
5+
/**
6+
* Type-safe DSL configuration of semantic conventions behaviors.
7+
*/
8+
@OpenTelemetryDslMarker
9+
class SemanticConventionsConfiguration {
10+
11+
/**
12+
* Determines if the latest available experimental semantic conventions should
13+
* be used. If set to false, the old, deprecated, or nonstandard semantic convention
14+
* names will continue to be used.
15+
*
16+
* This provides a compatibility path forward for users who need to continue using
17+
* existing values until the next major version bump. This setting does NOT
18+
* imply that only _stable_ semantic conventions are emitted.
19+
*/
20+
var useLatestExperimental: Boolean = true
21+
set(value){
22+
field = value
23+
SemconvCompat.useLatestExperimental = value
24+
}
25+
26+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package io.opentelemetry.android.agent.dsl
2+
3+
import io.mockk.every
4+
import io.mockk.mockk
5+
import io.opentelemetry.android.agent.FakeClock
6+
import io.opentelemetry.android.agent.FakeInstrumentationLoader
7+
import io.opentelemetry.android.common.internal.SemconvCompat
8+
import io.opentelemetry.android.config.OtelRumConfig
9+
import io.opentelemetry.android.features.diskbuffering.DiskBufferingConfig
10+
import org.assertj.core.api.Assertions.assertThat
11+
import org.junit.jupiter.api.AfterEach
12+
import org.junit.jupiter.api.BeforeEach
13+
import org.junit.jupiter.api.Test
14+
15+
class SemanticConventionsConfigurationTest {
16+
17+
var compatState: Boolean = true
18+
19+
@BeforeEach
20+
fun setup(){
21+
compatState = SemconvCompat.useLatestExperimental
22+
}
23+
24+
@AfterEach
25+
fun restore(){
26+
SemconvCompat.useLatestExperimental = compatState
27+
}
28+
29+
@Test
30+
fun `defaults to true`(){
31+
val rumConfig = mockk<OtelRumConfig>()
32+
every { rumConfig.setDiskBufferingConfig(any<DiskBufferingConfig>()) } returns rumConfig
33+
every { rumConfig.suppressInstrumentation(any<String>()) } returns rumConfig
34+
val otelConfig = OpenTelemetryConfiguration(
35+
rumConfig = rumConfig,
36+
instrumentationLoader = FakeInstrumentationLoader(),
37+
clock = FakeClock()
38+
)
39+
assertThat(otelConfig.semanticConventions.useLatestExperimental).isTrue
40+
assertThat(SemconvCompat.useLatestExperimental).isTrue
41+
}
42+
43+
@Test
44+
fun `can disable latest experimental semconv`(){
45+
val rumConfig = mockk<OtelRumConfig>()
46+
every { rumConfig.setDiskBufferingConfig(any<DiskBufferingConfig>()) } returns rumConfig
47+
every { rumConfig.suppressInstrumentation(any<String>()) } returns rumConfig
48+
val otelConfig = OpenTelemetryConfiguration(
49+
rumConfig = rumConfig,
50+
instrumentationLoader = FakeInstrumentationLoader(),
51+
clock = FakeClock()
52+
)
53+
otelConfig.semanticConventions {
54+
useLatestExperimental = false
55+
}
56+
assertThat(SemconvCompat.useLatestExperimental).isFalse
57+
}
58+
59+
}

common/api/common.api

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,16 @@ public final class io/opentelemetry/android/common/RumConstants {
22
public static final field INSTANCE Lio/opentelemetry/android/common/RumConstants;
33
public static final field LAST_SCREEN_NAME_KEY Lio/opentelemetry/api/common/AttributeKey;
44
public static final field OTEL_RUM_LOG_TAG Ljava/lang/String;
5-
public static final field SCREEN_NAME_KEY Lio/opentelemetry/api/common/AttributeKey;
5+
}
6+
7+
public final class io/opentelemetry/android/common/internal/SemconvCompat {
8+
public static final field Companion Lio/opentelemetry/android/common/internal/SemconvCompat$Companion;
9+
}
10+
11+
public final class io/opentelemetry/android/common/internal/SemconvCompat$Companion {
12+
public final fun getUseLatestExperimental ()Z
13+
public final fun map (Ljava/lang/String;)Ljava/lang/String;
14+
public final fun setUseLatestExperimental (Z)V
615
}
716

817
public final class io/opentelemetry/android/common/internal/features/networkattributes/CurrentNetworkAttributesExtractor {

common/src/main/java/io/opentelemetry/android/common/RumConstants.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,4 @@ object RumConstants {
1212

1313
@JvmField
1414
val LAST_SCREEN_NAME_KEY: AttributeKey<String> = AttributeKey.stringKey("last.screen.name")
15-
16-
@JvmField
17-
val SCREEN_NAME_KEY: AttributeKey<String> = AttributeKey.stringKey("screen.name")
1815
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package io.opentelemetry.android.common.internal
2+
3+
import io.opentelemetry.kotlin.semconv.AppAttributes
4+
import io.opentelemetry.kotlin.semconv.IncubatingApi
5+
6+
/**
7+
* Internal class used to map the latest/experimental semantic conventions
8+
* to a previous value. This class is subject to change at any time, and external
9+
* users are highly discouraged from using it.
10+
*/
11+
@OptIn(IncubatingApi::class)
12+
class SemconvCompat internal constructor() {
13+
14+
companion object {
15+
16+
var useLatestExperimental = true
17+
18+
fun map(key: String) : String {
19+
if(useLatestExperimental){
20+
return key
21+
}
22+
return when(key) {
23+
// new -> old
24+
"app.crash" -> "device.crash"
25+
AppAttributes.APP_SCREEN_NAME -> "screen.name"
26+
else -> key
27+
}
28+
}
29+
30+
}
31+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package io.opentelemetry.android.common.internal
2+
3+
import org.assertj.core.api.Assertions.assertThat
4+
import org.junit.jupiter.api.AfterEach
5+
import org.junit.jupiter.api.Test
6+
7+
class SemconvCompatTest {
8+
9+
var compatState: Boolean = true
10+
11+
@AfterEach
12+
fun setup() {
13+
compatState = SemconvCompat.useLatestExperimental
14+
}
15+
16+
@AfterEach
17+
fun restore() {
18+
SemconvCompat.useLatestExperimental = compatState
19+
}
20+
21+
@Test
22+
fun `test compat with legacy`() {
23+
SemconvCompat.useLatestExperimental = false
24+
assertThat(SemconvCompat.map("app.screen.name")).isEqualTo("screen.name")
25+
assertThat(SemconvCompat.map("app.crash")).isEqualTo("device.crash")
26+
assertThat(SemconvCompat.map("rando.semconv.thinger")).isEqualTo("rando.semconv.thinger")
27+
}
28+
29+
@Test
30+
fun `test compat with latest experimental`() {
31+
SemconvCompat.useLatestExperimental = true
32+
assertThat(SemconvCompat.map("app.screen.name")).isEqualTo("app.screen.name")
33+
assertThat(SemconvCompat.map("app.crash")).isEqualTo("app.crash")
34+
assertThat(SemconvCompat.map("rando.semconv.thinger")).isEqualTo("rando.semconv.thinger")
35+
}
36+
37+
}

core/src/main/java/io/opentelemetry/android/ScreenAttributesSpanProcessor.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55

66
package io.opentelemetry.android
77

8-
import io.opentelemetry.android.common.RumConstants
8+
import io.opentelemetry.android.common.internal.SemconvCompat.Companion.map
99
import io.opentelemetry.android.internal.services.visiblescreen.VisibleScreenTracker
10+
import io.opentelemetry.api.common.AttributeKey.stringKey
1011
import io.opentelemetry.context.Context
12+
import io.opentelemetry.kotlin.semconv.AppAttributes.APP_SCREEN_NAME
13+
import io.opentelemetry.kotlin.semconv.IncubatingApi
1114
import io.opentelemetry.sdk.trace.ReadWriteSpan
1215
import io.opentelemetry.sdk.trace.ReadableSpan
1316
import io.opentelemetry.sdk.trace.SpanProcessor
@@ -18,12 +21,13 @@ import io.opentelemetry.sdk.trace.SpanProcessor
1821
internal class ScreenAttributesSpanProcessor(
1922
private val visibleScreenTracker: VisibleScreenTracker,
2023
) : SpanProcessor {
24+
@OptIn(IncubatingApi::class)
2125
override fun onStart(
2226
parentContext: Context,
2327
span: ReadWriteSpan,
2428
) {
2529
val currentScreen = visibleScreenTracker.currentlyVisibleScreen
26-
span.setAttribute(RumConstants.SCREEN_NAME_KEY, currentScreen)
30+
span.setAttribute(stringKey(map(APP_SCREEN_NAME)), currentScreen)
2731
}
2832

2933
override fun isStartRequired(): Boolean = true

core/src/main/java/io/opentelemetry/android/internal/processors/ScreenAttributesLogRecordProcessor.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,24 @@
55

66
package io.opentelemetry.android.internal.processors
77

8-
import io.opentelemetry.android.common.RumConstants
8+
import io.opentelemetry.android.common.internal.SemconvCompat.Companion.map
99
import io.opentelemetry.android.internal.services.visiblescreen.VisibleScreenTracker
10+
import io.opentelemetry.api.common.AttributeKey.stringKey
1011
import io.opentelemetry.context.Context
12+
import io.opentelemetry.kotlin.semconv.AppAttributes.APP_SCREEN_NAME
13+
import io.opentelemetry.kotlin.semconv.IncubatingApi
1114
import io.opentelemetry.sdk.logs.LogRecordProcessor
1215
import io.opentelemetry.sdk.logs.ReadWriteLogRecord
1316

1417
class ScreenAttributesLogRecordProcessor(
1518
val visibleScreenTracker: VisibleScreenTracker,
1619
) : LogRecordProcessor {
20+
@OptIn(IncubatingApi::class)
1721
override fun onEmit(
1822
context: Context,
1923
logRecord: ReadWriteLogRecord,
2024
) {
2125
val currentScreen = visibleScreenTracker.currentlyVisibleScreen
22-
logRecord.setAttribute(RumConstants.SCREEN_NAME_KEY, currentScreen)
26+
logRecord.setAttribute(stringKey(map(APP_SCREEN_NAME)), currentScreen)
2327
}
2428
}

0 commit comments

Comments
 (0)