Skip to content

Commit a0bebc5

Browse files
committed
Merge branch 'main' into integration/kotlin-otel-api
2 parents 01f9ed5 + 43943b6 commit a0bebc5

File tree

80 files changed

+893
-951
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+893
-951
lines changed

.github/workflows/release-workflow.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ jobs:
2424
runs-on: ubuntu-latest
2525
timeout-minutes: 60
2626
steps:
27+
- name: Validate access to version data service
28+
uses: embrace-io/public-actions/upload-sdk-version@f4229398f257b24dbbe8b873f78e719f9af6cbbb
29+
with:
30+
platform: 'android'
31+
version: ${{ inputs.rc_version }}
32+
dryRun: true
33+
uploadUrl: ${{ vars.SDK_VERSION_URL }}
34+
env:
35+
SDK_VERSION_TOKEN: ${{ secrets.SDK_VERSION_TOKEN }}
36+
2737
- name: Checkout SDK
2838
uses: actions/checkout@v4
2939
with:
@@ -43,6 +53,16 @@ jobs:
4353
run: |
4454
./gradlew findSonatypeStagingRepository releaseSonatypeStagingRepository -Dorg.gradle.parallel=false --no-build-cache --no-configuration-cache --stacktrace
4555
56+
- name: Record SDK Version History
57+
uses: embrace-io/public-actions/upload-sdk-version@f4229398f257b24dbbe8b873f78e719f9af6cbbb
58+
with:
59+
platform: 'android'
60+
version: ${{ inputs.rc_version }}
61+
dryRun: false
62+
uploadUrl: ${{ vars.SDK_VERSION_URL }}
63+
env:
64+
SDK_VERSION_TOKEN: ${{ secrets.SDK_VERSION_TOKEN }}
65+
4666
publish-api-docs:
4767
name: Publish API Docs to GitHub Pages
4868
uses: ./.github/workflows/publish-api-docs.yml

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Embrace Android SDK Changelog
22

3+
## 7.5.0
4+
*June 9, 2025*
5+
6+
- New configuration option to start the SDK automatically (default off)
7+
- Link Spans with Sessions in which they ended via Span Links
8+
- Bypass instrumentation-time restrictions for Spans and Embrace Logs for non-Embrace Users
9+
- Use `BuildFeatures` API for Gradle 8.5+ instead of a deprecated feature to be removed in Gradle 10
10+
311
## 7.4.0
412
*May 5, 2025*
513

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ nexusPublishing {
1919
sonatype {
2020
username = System.getenv("SONATYPE_USERNAME")
2121
password = System.getenv("SONATYPE_PASSWORD")
22-
nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
22+
nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/"))
2323
}
2424
}
2525
transitionCheckOptions {

embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/OpenTelemetryModule.kt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,6 @@ interface OpenTelemetryModule {
2626
*/
2727
val otelSdkConfig: OtelSdkConfig
2828

29-
/**
30-
* Setup configuration for redacting sensitive keys
31-
*/
32-
fun setupSensitiveKeysBehavior(sensitiveKeysBehavior: SensitiveKeysBehavior)
33-
3429
/**
3530
* Caches span instances that are in progress or completed in the current session
3631
*/
@@ -92,4 +87,12 @@ interface OpenTelemetryModule {
9287
* OpenTelemetry SDK compatible clock based on the internal Embrace clock instance
9388
*/
9489
val openTelemetryClock: io.opentelemetry.sdk.common.Clock
90+
91+
/**
92+
* Setup configuration configuration-dependent behavior
93+
*/
94+
fun applyConfiguration(
95+
sensitiveKeysBehavior: SensitiveKeysBehavior,
96+
bypassValidation: Boolean,
97+
)
9598
}

embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/injection/OpenTelemetryModuleImpl.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import io.embrace.android.embracesdk.internal.otel.impl.EmbOpenTelemetry
99
import io.embrace.android.embracesdk.internal.otel.impl.EmbTracerProvider
1010
import io.embrace.android.embracesdk.internal.otel.logs.LogSink
1111
import io.embrace.android.embracesdk.internal.otel.logs.LogSinkImpl
12+
import io.embrace.android.embracesdk.internal.otel.sdk.DataValidator
1213
import io.embrace.android.embracesdk.internal.otel.sdk.OtelSdkWrapper
1314
import io.embrace.android.embracesdk.internal.otel.spans.EmbraceSpanFactory
1415
import io.embrace.android.embracesdk.internal.otel.spans.EmbraceSpanFactoryImpl
@@ -80,17 +81,23 @@ internal class OpenTelemetryModuleImpl(
8081

8182
private var sensitiveKeysBehavior: SensitiveKeysBehavior? = null
8283

83-
override fun setupSensitiveKeysBehavior(sensitiveKeysBehavior: SensitiveKeysBehavior) {
84+
override fun applyConfiguration(sensitiveKeysBehavior: SensitiveKeysBehavior, bypassValidation: Boolean) {
8485
this.sensitiveKeysBehavior = sensitiveKeysBehavior
86+
this.bypassLimitsValidation = bypassValidation
8587
}
8688

8789
private var internalSpanStopCallback: ((spanId: String) -> Unit)? = null
8890

91+
private var bypassLimitsValidation: Boolean = false
92+
93+
private val dataValidator: DataValidator = DataValidator(bypassValidation = ::bypassLimitsValidation)
94+
8995
private val embraceSpanFactory: EmbraceSpanFactory by singleton {
9096
EmbraceSpanFactoryImpl(
9197
tracer = sdkTracer,
9298
openTelemetryClock = openTelemetryClock,
9399
spanRepository = spanRepository,
100+
dataValidator = dataValidator,
94101
stopCallback = ::spanStopCallbackWrapper,
95102
redactionFunction = ::redactionFunction
96103
)
@@ -113,6 +120,7 @@ internal class OpenTelemetryModuleImpl(
113120
spanRepository = spanRepository,
114121
canStartNewSpan = currentSessionSpan::canStartNewSpan,
115122
initCallback = currentSessionSpan::initializeService,
123+
dataValidator = dataValidator
116124
) { embraceSpanFactory }
117125
}
118126

embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/logs/EmbraceLogService.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import io.embrace.android.embracesdk.internal.otel.attrs.embExceptionHandling
1616
import io.embrace.android.embracesdk.internal.payload.AppFramework
1717
import io.embrace.android.embracesdk.internal.payload.Envelope
1818
import io.embrace.android.embracesdk.internal.session.orchestrator.PayloadStore
19-
import io.embrace.android.embracesdk.internal.utils.PropertyUtils.normalizeProperties
19+
import io.embrace.android.embracesdk.internal.utils.PropertyUtils.sanitizeProperties
2020
import io.embrace.android.embracesdk.internal.utils.Uuid
2121
import io.opentelemetry.semconv.incubating.LogIncubatingAttributes
2222

@@ -31,6 +31,7 @@ class EmbraceLogService(
3131
) : LogService {
3232

3333
private val behavior = configService.logMessageBehavior
34+
private val bypassLimitsValidation = configService.isOnlyUsingOtelExporters()
3435
private val logCounters = mapOf(
3536
Severity.INFO to LogCounter(behavior::getInfoLogLimit),
3637
Severity.WARNING to LogCounter(behavior::getWarnLogLimit),
@@ -45,7 +46,7 @@ class EmbraceLogService(
4546
customLogAttrs: Map<String, String>,
4647
logAttachment: Attachment.EmbraceHosted?,
4748
) {
48-
val redactedProperties = redactSensitiveProperties(normalizeProperties(properties))
49+
val redactedProperties = redactSensitiveProperties(sanitizeProperties(properties, bypassLimitsValidation))
4950
val attrs = createTelemetryAttributes(redactedProperties, customLogAttrs)
5051

5152
val schemaProvider: (TelemetryAttributes) -> SchemaType = when {

embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/utils/PropertyUtils.kt

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,42 +10,21 @@ object PropertyUtils {
1010

1111
const val MAX_PROPERTY_SIZE: Int = 10
1212

13-
/**
14-
* This method will normalize the map by applying the following rules:
15-
*
16-
* - Null key registries will be discarded.
17-
* - Null value registries will be renamed to null as a String.
18-
* - Cap the properties map to a maximum of [PropertyUtils.MAX_PROPERTY_SIZE] properties.
19-
*
20-
* @param properties properties to be normalized.
21-
* @return a normalized Map of the provided properties.
22-
*/
23-
@JvmStatic
24-
fun sanitizeProperties(properties: Map<String, Any?>?): Map<String, Any> {
25-
properties ?: return emptyMap()
26-
27-
return properties.entries
28-
.take(MAX_PROPERTY_SIZE)
29-
.associate { Pair(it.key, checkIfSerializable(it.value)) }
30-
}
31-
32-
@JvmStatic
33-
fun normalizeProperties(properties: Map<String, Any>?): Map<String, Any>? {
34-
var normalizedProperties: Map<String, Any> = HashMap()
35-
if (properties != null) {
36-
runCatching {
37-
normalizedProperties = sanitizeProperties(properties)
38-
}
39-
return normalizedProperties
13+
fun sanitizeProperties(properties: Map<String, Any>?, bypassPropertyLimit: Boolean = false): Map<String, Any> {
14+
return if (properties == null) {
15+
emptyMap()
4016
} else {
41-
return null
17+
runCatching {
18+
if (bypassPropertyLimit) {
19+
properties.entries
20+
} else {
21+
properties.entries.take(MAX_PROPERTY_SIZE)
22+
}.associate { Pair(it.key, checkIfSerializable(it.value)) }
23+
}.getOrDefault(emptyMap())
4224
}
4325
}
4426

45-
private fun checkIfSerializable(value: Any?): Any {
46-
if (value == null) {
47-
return "null"
48-
}
27+
private fun checkIfSerializable(value: Any): Any {
4928
if (!(value is Parcelable || value is Serializable)) {
5029
return "not serializable"
5130
}

embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/capture/session/EmbraceSessionPropertiesTest.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,11 @@ internal class EmbraceSessionPropertiesTest {
150150
}
151151
assertFalse(
152152
"should not be able to add new key when limit is hit",
153-
sessionProperties.add("propNew", VALUE_VALID, true)
153+
sessionProperties.add("propPermNew", VALUE_VALID, true)
154+
)
155+
assertFalse(
156+
"should not be able to add new key when limit is hit",
157+
sessionProperties.add("propTempNew", VALUE_VALID, false)
154158
)
155159
val otherValue = "other"
156160
assertTrue(
@@ -180,7 +184,11 @@ internal class EmbraceSessionPropertiesTest {
180184
}
181185
assertFalse(
182186
"should not be able to add new key when limit is hit",
183-
sessionProperties.add("propNew", VALUE_VALID, true)
187+
sessionProperties.add("propPermNew", VALUE_VALID, true)
188+
)
189+
assertFalse(
190+
"should not be able to add new key when limit is hit",
191+
sessionProperties.add("propTempNew", VALUE_VALID, false)
184192
)
185193
val otherValue = "other"
186194
assertTrue(

embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/utils/PropertiesTest.kt

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

embrace-android-core/src/test/java/io/embrace/android/embracesdk/internal/utils/PropertyUtilsTest.kt

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package io.embrace.android.embracesdk.internal.utils
22

33
import io.embrace.android.embracesdk.internal.utils.PropertyUtils.sanitizeProperties
44
import org.junit.Assert.assertEquals
5+
import org.junit.Assert.assertNotNull
6+
import org.junit.Assert.assertTrue
57
import org.junit.Test
68

79
internal class PropertyUtilsTest {
@@ -19,11 +21,6 @@ internal class PropertyUtilsTest {
1921
assertEquals(expected, sanitizeProperties(input as Map<String, Any>?))
2022
}
2123

22-
@Test
23-
fun testNullValue() {
24-
assertEquals("null", sanitizeProperties(mapOf("a" to null))["a"])
25-
}
26-
2724
@Test
2825
fun testSerializableValue() {
2926
val obj = SerializableClass()
@@ -35,6 +32,41 @@ internal class PropertyUtilsTest {
3532
assertEquals("not serializable", sanitizeProperties(mapOf("a" to UnSerializableClass()))["a"])
3633
}
3734

35+
@Test
36+
fun `bypass limits`() {
37+
val input = (0..20).associateBy { "$it" }
38+
assertEquals(input.size, sanitizeProperties(input, true).size)
39+
}
40+
41+
@Test
42+
fun testPropertiesNormalization() {
43+
val sourceMap: MutableMap<String, Any> = HashMap()
44+
sourceMap[""] = "Empty key"
45+
sourceMap["EmptyValue"] = ""
46+
sourceMap["NullValue"] = ""
47+
for (i in 1..9) {
48+
sourceMap["Key$i"] = "Value$i"
49+
}
50+
val resultMap = sanitizeProperties(sourceMap)
51+
assertTrue(
52+
"Unexpected normalized map size.",
53+
resultMap.size <= PropertyUtils.MAX_PROPERTY_SIZE
54+
)
55+
resultMap.entries.stream()
56+
.peek { (key): Map.Entry<String, Any> ->
57+
assertNotNull(
58+
"Unexpected normalized map key: NULL.",
59+
key
60+
)
61+
}
62+
.peek { (_, value): Map.Entry<String, Any> ->
63+
assertNotNull(
64+
"Unexpected normalized map value: NULL.",
65+
value
66+
)
67+
}
68+
}
69+
3870
private class SerializableClass : java.io.Serializable
3971
private class UnSerializableClass
4072
}

0 commit comments

Comments
 (0)