From b69df7b374c6f09b22da6002328da2c801ebb6a6 Mon Sep 17 00:00:00 2001 From: bidetofevil Date: Thu, 7 Mar 2024 12:33:54 -0800 Subject: [PATCH 01/14] Add session and key name to EmbType and TelemetryType respectively --- .../android/embracesdk/arch/schema/EmbType.kt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/EmbType.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/EmbType.kt index 39c272abb0..be3bc96dde 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/EmbType.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/EmbType.kt @@ -1,16 +1,22 @@ package io.embrace.android.embracesdk.arch.schema +import io.embrace.android.embracesdk.internal.spans.toEmbraceAttributeName + internal sealed class EmbType { /** * Keys that track how fast a time interval is. Only applies to spans. */ - internal sealed class Performance : TelemetryType + internal object Performance : TelemetryType { + override val description: String = "performance" + } /** * Keys that track a point in time & is visual in nature. Applies to spans, logs, and span events. */ internal sealed class Ux(subtype: String) : TelemetryType { + internal object Session : Ux("session") + internal object View : Ux("view") override val description = "ux.$subtype" @@ -35,4 +41,9 @@ internal sealed class EmbType { */ internal interface TelemetryType { val description: String + + /** + * Return the key name used by this attribute when is used inside of OpenTelemetry objects + */ + fun attributeName(): String = "type".toEmbraceAttributeName() } From 948894d8580e9695d8d3b74dafc65e91cfb693f2 Mon Sep 17 00:00:00 2001 From: bidetofevil Date: Sat, 9 Mar 2024 23:58:40 -0800 Subject: [PATCH 02/14] Add NDK service tracing --- .../embracesdk/internal/SharedObjectLoader.kt | 4 +- .../embracesdk/ndk/EmbraceNdkService.kt | 56 ++++++++++--------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/SharedObjectLoader.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/SharedObjectLoader.kt index aae7a49f87..522dc216e6 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/SharedObjectLoader.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/SharedObjectLoader.kt @@ -8,7 +8,9 @@ import io.embrace.android.embracesdk.logging.InternalStaticEmbraceLogger internal class SharedObjectLoader { fun loadEmbraceNative() = try { - System.loadLibrary("embrace-native") + Systrace.traceSynchronous("load-embrace-native-lib") { + System.loadLibrary("embrace-native") + } true } catch (exc: UnsatisfiedLinkError) { InternalStaticEmbraceLogger.logError("Failed to load SO file embrace-native", exc) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/ndk/EmbraceNdkService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/ndk/EmbraceNdkService.kt index abf0137408..06db0e084e 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/ndk/EmbraceNdkService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/ndk/EmbraceNdkService.kt @@ -88,13 +88,14 @@ internal class EmbraceNdkService( null } if (configService.autoDataCaptureBehavior.isNdkEnabled()) { - processStateService.addListener(this) - if (appFramework == AppFramework.UNITY) { - unityCrashId = getEmbUuid() + Systrace.traceSynchronous("init-ndk-service") { + processStateService.addListener(this) + if (appFramework == AppFramework.UNITY) { + unityCrashId = getEmbUuid() + } + startNdk() + cleanOldCrashFiles() } - - Systrace.traceSynchronous("start-ndk-service") { startNdk() } - Systrace.traceSynchronous("clear-stale-crashes") { cleanOldCrashFiles() } } } @@ -230,26 +231,31 @@ internal class EmbraceNdkService( "EmbraceNDKService", "Installing signal handlers. 32bit=$is32bit, crashId=$nativeCrashId" ) - val initialMetaData = serializer.toJson( - NativeCrashMetadata( - metadataService.getLightweightAppInfo(), - metadataService.getLightweightDeviceInfo(), - userService.getUserInfo(), - sessionProperties.get().toMap() + + val initialMetaData = Systrace.traceSynchronous("init-native-crash-metadata") { + serializer.toJson( + NativeCrashMetadata( + metadataService.getLightweightAppInfo(), + metadataService.getLightweightDeviceInfo(), + userService.getUserInfo(), + sessionProperties.get().toMap() + ) ) - ) - delegate._installSignalHandlers( - reportBasePath, - markerFilePath, - initialMetaData, - "null", - metadataService.getAppState(), - nativeCrashId, - Build.VERSION.SDK_INT, - is32bit, - ApkToolsConfig.IS_DEVELOPER_LOGGING_ENABLED - ) - updateDeviceMetaData() + } + Systrace.traceSynchronous("native-install-handlers") { + delegate._installSignalHandlers( + reportBasePath, + markerFilePath, + initialMetaData, + "null", + metadataService.getAppState(), + nativeCrashId, + Build.VERSION.SDK_INT, + is32bit, + ApkToolsConfig.IS_DEVELOPER_LOGGING_ENABLED + ) + } + Systrace.traceSynchronous("update-metadata") { updateDeviceMetaData() } isInstalled = true } From 94eb7bff36581d56eb1792c228045f689502d447 Mon Sep 17 00:00:00 2001 From: bidetofevil Date: Sun, 10 Mar 2024 00:06:23 -0800 Subject: [PATCH 03/14] Defer native module init --- .../ndk/EmbraceNativeThreadSamplerService.kt | 19 +++++++---- .../anr/ndk/NativeThreadSamplerInstaller.kt | 8 +++-- .../embracesdk/internal/SharedObjectLoader.kt | 32 ++++++++++++++----- .../android/embracesdk/ndk/NativeModule.kt | 13 ++++---- .../EmbraceNativeThreadSamplerServiceTest.kt | 7 +++- .../NativeThreadSamplerInstallerTest.kt | 11 ++++--- 6 files changed, 61 insertions(+), 29 deletions(-) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ndk/EmbraceNativeThreadSamplerService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ndk/EmbraceNativeThreadSamplerService.kt index 0134e0c012..8f287d15a5 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ndk/EmbraceNativeThreadSamplerService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ndk/EmbraceNativeThreadSamplerService.kt @@ -3,6 +3,7 @@ package io.embrace.android.embracesdk.anr.ndk import io.embrace.android.embracesdk.config.ConfigService import io.embrace.android.embracesdk.config.behavior.AnrBehavior import io.embrace.android.embracesdk.internal.DeviceArchitecture +import io.embrace.android.embracesdk.internal.SharedObjectLoader import io.embrace.android.embracesdk.logging.InternalEmbraceLogger import io.embrace.android.embracesdk.logging.InternalStaticEmbraceLogger import io.embrace.android.embracesdk.payload.NativeThreadAnrInterval @@ -25,7 +26,8 @@ internal class EmbraceNativeThreadSamplerService @JvmOverloads constructor( private val logger: InternalEmbraceLogger = InternalStaticEmbraceLogger.logger, private val delegate: NdkDelegate = NativeThreadSamplerNdkDelegate(), private val scheduledWorker: ScheduledWorker, - private val deviceArchitecture: DeviceArchitecture + private val deviceArchitecture: DeviceArchitecture, + private val sharedObjectLoader: SharedObjectLoader ) : NativeThreadSamplerService { companion object { @@ -55,11 +57,16 @@ internal class EmbraceNativeThreadSamplerService @JvmOverloads constructor( private var targetThread: Thread = Thread.currentThread() override fun setupNativeSampler(): Boolean { - logger.logDeveloper( - "EmbraceNativeThreadSamplerService", - "Target thread found, attempting to install NativeThreadSampler" - ) - return delegate.setupNativeThreadSampler(deviceArchitecture.is32BitDevice) + return if (sharedObjectLoader.loadEmbraceNative()) { + logger.logDeveloper( + "EmbraceNativeThreadSamplerService", + "Target thread found, attempting to install NativeThreadSampler" + ) + delegate.setupNativeThreadSampler(deviceArchitecture.is32BitDevice) + } else { + logger.logWarning("Embrace native binary load failed. Native thread sampler setup aborted.") + false + } } override fun monitorCurrentThread(): Boolean { diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ndk/NativeThreadSamplerInstaller.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ndk/NativeThreadSamplerInstaller.kt index f279acbcd0..1280eafeec 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ndk/NativeThreadSamplerInstaller.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/anr/ndk/NativeThreadSamplerInstaller.kt @@ -4,6 +4,7 @@ import android.os.Handler import android.os.Looper import io.embrace.android.embracesdk.anr.AnrService import io.embrace.android.embracesdk.config.ConfigService +import io.embrace.android.embracesdk.internal.SharedObjectLoader import io.embrace.android.embracesdk.logging.InternalEmbraceLogger import io.embrace.android.embracesdk.logging.InternalStaticEmbraceLogger import io.embrace.android.embracesdk.payload.NativeThreadAnrSample @@ -17,9 +18,9 @@ internal class NativeThreadSamplerNdkDelegate : EmbraceNativeThreadSamplerServic } internal class NativeThreadSamplerInstaller( - private val logger: InternalEmbraceLogger = InternalStaticEmbraceLogger.logger + private val sharedObjectLoader: SharedObjectLoader, + private val logger: InternalEmbraceLogger = InternalStaticEmbraceLogger.logger, ) { - private val isMonitoring = AtomicBoolean(false) private var targetHandler: Handler? = null @@ -64,6 +65,9 @@ internal class NativeThreadSamplerInstaller( } currentThread = Thread.currentThread() + if (!sharedObjectLoader.loadEmbraceNative()) { + return + } prepareTargetHandler() if (configService.anrBehavior.isNativeThreadAnrSamplingEnabled()) { diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/SharedObjectLoader.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/SharedObjectLoader.kt index 522dc216e6..29f5325bdd 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/SharedObjectLoader.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/SharedObjectLoader.kt @@ -1,19 +1,35 @@ package io.embrace.android.embracesdk.internal import io.embrace.android.embracesdk.logging.InternalStaticEmbraceLogger +import java.util.concurrent.atomic.AtomicBoolean /** - * Loads shared object files. + * Component to load native binaries */ internal class SharedObjectLoader { + val loaded = AtomicBoolean(false) - fun loadEmbraceNative() = try { - Systrace.traceSynchronous("load-embrace-native-lib") { - System.loadLibrary("embrace-native") + /** + * Load Embrace native binary if necessary + */ + fun loadEmbraceNative(): Boolean { + if (loaded.get()) { + return true + } + synchronized(loaded) { + if (!loaded.get()) { + try { + Systrace.traceSynchronous("load-embrace-native-lib") { + System.loadLibrary("embrace-native") + } + loaded.set(true) + } catch (exc: UnsatisfiedLinkError) { + InternalStaticEmbraceLogger.logError("Failed to load SO file embrace-native", exc) + return false + } + } + + return true } - true - } catch (exc: UnsatisfiedLinkError) { - InternalStaticEmbraceLogger.logError("Failed to load SO file embrace-native", exc) - false } } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/ndk/NativeModule.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/ndk/NativeModule.kt index 41e69f8779..76f8768734 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/ndk/NativeModule.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/ndk/NativeModule.kt @@ -10,7 +10,6 @@ import io.embrace.android.embracesdk.injection.DeliveryModule import io.embrace.android.embracesdk.injection.EssentialServiceModule import io.embrace.android.embracesdk.injection.StorageModule import io.embrace.android.embracesdk.injection.singleton -import io.embrace.android.embracesdk.internal.SharedObjectLoader import io.embrace.android.embracesdk.internal.Systrace import io.embrace.android.embracesdk.session.properties.EmbraceSessionProperties import io.embrace.android.embracesdk.worker.WorkerName @@ -59,12 +58,13 @@ internal class NativeModuleImpl( override val nativeThreadSamplerService: NativeThreadSamplerService? by singleton { Systrace.traceSynchronous("native-thread-sampler-init") { - if (nativeThreadSamplingEnabled(essentialServiceModule.configService, essentialServiceModule.sharedObjectLoader)) { + if (nativeThreadSamplingEnabled(essentialServiceModule.configService)) { EmbraceNativeThreadSamplerService( essentialServiceModule.configService, lazy { ndkService.getSymbolsForCurrentArch() }, scheduledWorker = workerThreadModule.scheduledWorker(WorkerName.BACKGROUND_REGISTRATION), - deviceArchitecture = essentialServiceModule.deviceArchitecture + deviceArchitecture = essentialServiceModule.deviceArchitecture, + sharedObjectLoader = essentialServiceModule.sharedObjectLoader ) } else { null @@ -74,16 +74,15 @@ internal class NativeModuleImpl( override val nativeThreadSamplerInstaller: NativeThreadSamplerInstaller? by singleton { Systrace.traceSynchronous("native-thread-sampler-installer-init") { - if (nativeThreadSamplingEnabled(essentialServiceModule.configService, essentialServiceModule.sharedObjectLoader)) { - NativeThreadSamplerInstaller() + if (nativeThreadSamplingEnabled(essentialServiceModule.configService)) { + NativeThreadSamplerInstaller(sharedObjectLoader = essentialServiceModule.sharedObjectLoader) } else { null } } } - private fun nativeThreadSamplingEnabled(configService: ConfigService, sharedObjectLoader: SharedObjectLoader) = - configService.autoDataCaptureBehavior.isNdkEnabled() && sharedObjectLoader.loadEmbraceNative() + private fun nativeThreadSamplingEnabled(configService: ConfigService) = configService.autoDataCaptureBehavior.isNdkEnabled() private val embraceNdkServiceRepository by singleton { EmbraceNdkServiceRepository( diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/EmbraceNativeThreadSamplerServiceTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/EmbraceNativeThreadSamplerServiceTest.kt index bdf7deb625..707639a21a 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/EmbraceNativeThreadSamplerServiceTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/EmbraceNativeThreadSamplerServiceTest.kt @@ -9,6 +9,7 @@ import io.embrace.android.embracesdk.config.remote.AnrRemoteConfig import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeDeviceArchitecture import io.embrace.android.embracesdk.fakes.fakeAnrBehavior +import io.embrace.android.embracesdk.internal.SharedObjectLoader import io.embrace.android.embracesdk.logging.InternalEmbraceLogger import io.embrace.android.embracesdk.payload.NativeThreadAnrInterval import io.embrace.android.embracesdk.payload.NativeThreadAnrSample @@ -32,6 +33,7 @@ import java.util.concurrent.ThreadFactory internal class EmbraceNativeThreadSamplerServiceTest { private lateinit var sampler: EmbraceNativeThreadSamplerService + private lateinit var sharedObjectLoader: SharedObjectLoader private lateinit var configService: ConfigService private lateinit var delegate: EmbraceNativeThreadSamplerService.NdkDelegate private lateinit var random: Random @@ -46,6 +48,7 @@ internal class EmbraceNativeThreadSamplerServiceTest { cfg = AnrRemoteConfig(pctNativeThreadAnrSamplingEnabled = 100f) anrBehavior = fakeAnrBehavior { cfg } configService = FakeConfigService(anrBehavior = anrBehavior) + sharedObjectLoader = mockk(relaxed = true) delegate = mockk(relaxed = true) val logger = InternalEmbraceLogger() random = mockk(relaxed = true) @@ -58,9 +61,11 @@ internal class EmbraceNativeThreadSamplerServiceTest { logger, delegate, ScheduledWorker(executorService), - FakeDeviceArchitecture() + FakeDeviceArchitecture(), + sharedObjectLoader ) every { random.nextInt(any()) } returns 0 + every { sharedObjectLoader.loadEmbraceNative() } returns true } @Test diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/NativeThreadSamplerInstallerTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/NativeThreadSamplerInstallerTest.kt index 018d873f66..e7993c99f4 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/NativeThreadSamplerInstallerTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/NativeThreadSamplerInstallerTest.kt @@ -7,6 +7,7 @@ import io.embrace.android.embracesdk.config.ConfigService import io.embrace.android.embracesdk.config.remote.AnrRemoteConfig import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.fakeAnrBehavior +import io.embrace.android.embracesdk.internal.SharedObjectLoader import io.mockk.every import io.mockk.mockk import io.mockk.verify @@ -19,31 +20,34 @@ import java.util.concurrent.TimeUnit internal class NativeThreadSamplerInstallerTest { private lateinit var sampler: EmbraceNativeThreadSamplerService + private lateinit var sharedObjectLoader: SharedObjectLoader private lateinit var configService: ConfigService private lateinit var anrService: AnrService private lateinit var delegate: EmbraceNativeThreadSamplerService.NdkDelegate private lateinit var cfg: AnrRemoteConfig + private lateinit var installer: NativeThreadSamplerInstaller @Before fun setUp() { + sharedObjectLoader = mockk(relaxed = true) anrService = mockk(relaxed = true) delegate = mockk(relaxed = true) sampler = mockk(relaxed = true) cfg = AnrRemoteConfig(pctNativeThreadAnrSamplingEnabled = 100f) configService = FakeConfigService(anrBehavior = fakeAnrBehavior { cfg }) + installer = NativeThreadSamplerInstaller(sharedObjectLoader = sharedObjectLoader) + every { sharedObjectLoader.loadEmbraceNative() } returns true } @Test fun testInstallDisabled() { - val installer = NativeThreadSamplerInstaller() installer.monitorCurrentThread(sampler, configService, anrService) verify(exactly = 0) { delegate.setupNativeThreadSampler(false) } } @Test fun testInstallEnabledSuccess() { - val installer = NativeThreadSamplerInstaller() every { sampler.setupNativeSampler() } returns true every { sampler.monitorCurrentThread() } returns true @@ -56,7 +60,6 @@ internal class NativeThreadSamplerInstallerTest { @Test fun testInstallEnabledFailure() { - val installer = NativeThreadSamplerInstaller() every { sampler.setupNativeSampler() } returns false every { sampler.monitorCurrentThread() } returns false sampler.setupNativeSampler() @@ -81,7 +84,6 @@ internal class NativeThreadSamplerInstallerTest { @Test fun testConfigListener() { - val installer = NativeThreadSamplerInstaller() every { sampler.setupNativeSampler() } returns true every { sampler.monitorCurrentThread() } returns true sampler.setupNativeSampler() @@ -95,7 +97,6 @@ internal class NativeThreadSamplerInstallerTest { @Test fun testInstallNewThread() { - val installer = NativeThreadSamplerInstaller() every { sampler.setupNativeSampler() } returns true every { sampler.monitorCurrentThread() } returns true From 41b9ac815e6fbcbbb51e1da280d887d79f9cd63c Mon Sep 17 00:00:00 2001 From: bidetofevil Date: Sun, 10 Mar 2024 14:14:12 -0700 Subject: [PATCH 04/14] Defer loading of user info until when it's really necessary --- .../capture/user/EmbraceUserService.kt | 65 ++++++++++++++----- .../embracesdk/EmbraceUserServiceTest.kt | 48 +++++++------- 2 files changed, 71 insertions(+), 42 deletions(-) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/user/EmbraceUserService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/user/EmbraceUserService.kt index c65eb59d27..eaadb618d1 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/user/EmbraceUserService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/user/EmbraceUserService.kt @@ -1,18 +1,22 @@ package io.embrace.android.embracesdk.capture.user +import io.embrace.android.embracesdk.internal.utils.Provider import io.embrace.android.embracesdk.logging.InternalEmbraceLogger import io.embrace.android.embracesdk.payload.UserInfo import io.embrace.android.embracesdk.payload.UserInfo.Companion.ofStored import io.embrace.android.embracesdk.prefs.PreferencesService +import java.util.concurrent.atomic.AtomicReference import java.util.regex.Pattern internal class EmbraceUserService( private val preferencesService: PreferencesService, private val logger: InternalEmbraceLogger ) : UserService { - - @Volatile - internal var info: UserInfo = ofStored(preferencesService) + /** + * Do not access this directly - use [userInfo] and [modifyUserInfo] to get and set this + */ + private val userInfoReference = AtomicReference(DEFAULT_USER) + private val userInfoProvider: Provider = { ofStored(preferencesService) } override fun loadUserInfoFromDisk(): UserInfo? { return try { @@ -23,14 +27,14 @@ internal class EmbraceUserService( } } - override fun getUserInfo(): UserInfo = info.copy() + override fun getUserInfo(): UserInfo = userInfo().copy() override fun setUserIdentifier(userId: String?) { - val currentUserId = info.userId + val currentUserId = userInfo().userId if (currentUserId != null && currentUserId == userId) { return } - info = info.copy(userId = userId) + modifyUserInfo(userInfo().copy(userId = userId)) preferencesService.userIdentifier = userId } @@ -39,11 +43,11 @@ internal class EmbraceUserService( } override fun setUsername(username: String?) { - val currentUserName = info.username + val currentUserName = userInfo().username if (currentUserName != null && currentUserName == username) { return } - info = info.copy(username = username) + modifyUserInfo(userInfo().copy(username = username)) preferencesService.username = username } @@ -52,11 +56,11 @@ internal class EmbraceUserService( } override fun setUserEmail(email: String?) { - val currentEmail = info.email + val currentEmail = userInfo().email if (currentEmail != null && currentEmail == email) { return } - info = info.copy(email = email) + modifyUserInfo(userInfo().copy(email = email)) preferencesService.userEmailAddress = email } @@ -80,7 +84,7 @@ internal class EmbraceUserService( logger.logWarning("Ignoring persona " + persona + " as it does not match " + VALID_PERSONA.pattern()) return } - val currentPersonas = info.personas + val currentPersonas = userInfo().personas if (currentPersonas != null) { if (currentPersonas.size >= PERSONA_LIMIT) { logger.logWarning("Cannot set persona as the limit of " + PERSONA_LIMIT + " has been reached") @@ -91,8 +95,8 @@ internal class EmbraceUserService( } } - val newPersonas: Set = info.personas?.plus(persona) ?: mutableSetOf(persona) - info = info.copy(personas = newPersonas) + val newPersonas: Set = userInfo().personas?.plus(persona) ?: mutableSetOf(persona) + modifyUserInfo(userInfo().copy(personas = newPersonas)) preferencesService.userPersonas = newPersonas } @@ -100,19 +104,19 @@ internal class EmbraceUserService( if (persona == null) { return } - val currentPersonas = info.personas + val currentPersonas = userInfo().personas if (currentPersonas != null && !currentPersonas.contains(persona)) { logger.logWarning("Persona '$persona' is not set") return } - val newPersonas: Set = info.personas?.minus(persona) ?: mutableSetOf() - info = info.copy(personas = newPersonas) + val newPersonas: Set = userInfo().personas?.minus(persona) ?: mutableSetOf() + modifyUserInfo(userInfo().copy(personas = newPersonas)) preferencesService.userPersonas = newPersonas } override fun clearAllUserPersonas() { - val currentPersonas = info.personas + val currentPersonas = userInfo().personas if (currentPersonas != null && currentPersonas.isEmpty()) { return } @@ -123,7 +127,7 @@ internal class EmbraceUserService( if (preferencesService.isUsersFirstDay()) { personas.add(UserInfo.PERSONA_FIRST_DAY_USER) } - info = info.copy(personas = personas) + modifyUserInfo(userInfo().copy(personas = personas)) preferencesService.userPersonas = personas } @@ -134,11 +138,36 @@ internal class EmbraceUserService( clearAllUserPersonas() } + private fun userInfo(): UserInfo { + if (userInfoReference.get() === DEFAULT_USER) { + synchronized(userInfoReference) { + if (userInfoReference.get() === DEFAULT_USER) { + userInfoReference.set(userInfoProvider()) + } + } + } + + return userInfoReference.get() + } + + private fun modifyUserInfo(newUserInfo: UserInfo) { + synchronized(userInfoReference) { + userInfoReference.set(newUserInfo) + } + } + companion object { // Valid persona regex representation. val VALID_PERSONA: Pattern = Pattern.compile("^[a-zA-Z0-9_]{1,32}$") // Maximum number of allowed personas. const val PERSONA_LIMIT = 10 + + private val DEFAULT_USER = UserInfo( + userId = "", + email = "", + username = "", + personas = emptySet() + ) } } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/EmbraceUserServiceTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/EmbraceUserServiceTest.kt index 5173e28f2e..3e7f00cb9d 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/EmbraceUserServiceTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/EmbraceUserServiceTest.kt @@ -49,21 +49,21 @@ internal class EmbraceUserServiceTest { @Test fun testUserInfoNotLoaded() { mockNoUserInfo() - assertNotNull(service.info) - service.info.verifyNoUserInfo() + assertNotNull(service.getUserInfo()) + service.getUserInfo().verifyNoUserInfo() } @Test fun testUserInfoLoaded() { mockUserInfo() - assertNotNull(service.info) - service.info.verifyExpectedUserInfo() + assertNotNull(service.getUserInfo()) + service.getUserInfo().verifyExpectedUserInfo() } @Test fun testUserInfoSessionCopy() { mockUserInfo() - assertNotSame(service.info, service.getUserInfo()) + assertNotSame(service.getUserInfo(), service.getUserInfo()) } @Test @@ -71,11 +71,11 @@ internal class EmbraceUserServiceTest { mockUserInfo() with(service) { - assertEquals("f0a923498c", info.userId) + assertEquals("f0a923498c", getUserInfo().userId) setUserIdentifier("abc") - assertEquals("abc", info.userId) + assertEquals("abc", getUserInfo().userId) service.clearUserIdentifier() - assertNull(info.userId) + assertNull(getUserInfo().userId) } } @@ -84,11 +84,11 @@ internal class EmbraceUserServiceTest { mockUserInfo() with(service) { - assertEquals("Mr Test", info.username) + assertEquals("Mr Test", getUserInfo().username) setUsername("Joe") - assertEquals("Joe", info.username) + assertEquals("Joe", getUserInfo().username) service.clearUsername() - assertNull(info.username) + assertNull(getUserInfo().username) } } @@ -97,11 +97,11 @@ internal class EmbraceUserServiceTest { mockUserInfo() with(service) { - assertEquals("test@example.com", info.email) + assertEquals("test@example.com", getUserInfo().email) setUserEmail("foo@test.com") - assertEquals("foo@test.com", info.email) + assertEquals("foo@test.com", getUserInfo().email) service.clearUserEmail() - assertNull(info.email) + assertNull(getUserInfo().email) } } @@ -110,11 +110,11 @@ internal class EmbraceUserServiceTest { mockUserInfo() with(service) { - assertTrue(checkNotNull(info.personas).contains("payer")) + assertTrue(checkNotNull(getUserInfo().personas).contains("payer")) clearUserAsPayer() - assertFalse(checkNotNull(info.personas).contains("payer")) + assertFalse(checkNotNull(getUserInfo().personas).contains("payer")) setUserAsPayer() - assertTrue(checkNotNull(info.personas).contains("payer")) + assertTrue(checkNotNull(getUserInfo().personas).contains("payer")) } } @@ -123,12 +123,12 @@ internal class EmbraceUserServiceTest { mockUserInfo() with(service) { - info.verifyExpectedUserInfo() + getUserInfo().verifyExpectedUserInfo() service.clearAllUserInfo() - assertNull(info.email) - assertNull(info.userId) - assertNull(info.username) - assertEquals(extraPersonas, info.personas) + assertNull(getUserInfo().email) + assertNull(getUserInfo().userId) + assertNull(getUserInfo().username) + assertEquals(extraPersonas, getUserInfo().personas) } } @@ -137,7 +137,7 @@ internal class EmbraceUserServiceTest { mockUserInfo() val persona = "!@£$$%*(" service.addUserPersona(persona) - val personas = checkNotNull(service.info.personas) + val personas = checkNotNull(service.getUserInfo().personas) assertFalse(personas.contains(persona)) } @@ -148,7 +148,7 @@ internal class EmbraceUserServiceTest { repeat(11) { k -> service.addUserPersona("Persona_$k") } - val personas = checkNotNull(service.info.personas) + val personas = checkNotNull(service.getUserInfo().personas) assertTrue(personas.contains("Persona_1")) assertTrue(personas.contains("Persona_9")) assertFalse(personas.contains("Persona_10")) From abcc1ea7128bd7e72e17f849d6d8c377b61156de Mon Sep 17 00:00:00 2001 From: bidetofevil Date: Sun, 10 Mar 2024 14:26:28 -0700 Subject: [PATCH 05/14] Defer session properties load until it's really needed --- .../properties/EmbraceSessionProperties.kt | 112 ++++++++++-------- 1 file changed, 65 insertions(+), 47 deletions(-) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/properties/EmbraceSessionProperties.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/properties/EmbraceSessionProperties.kt index 620a9ce12f..4c133f214b 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/properties/EmbraceSessionProperties.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/properties/EmbraceSessionProperties.kt @@ -1,9 +1,11 @@ package io.embrace.android.embracesdk.session.properties import io.embrace.android.embracesdk.config.ConfigService +import io.embrace.android.embracesdk.internal.utils.Provider import io.embrace.android.embracesdk.logging.InternalEmbraceLogger import io.embrace.android.embracesdk.logging.InternalStaticEmbraceLogger import io.embrace.android.embracesdk.prefs.PreferencesService +import java.util.concurrent.atomic.AtomicReference internal class EmbraceSessionProperties( private val preferencesService: PreferencesService, @@ -11,16 +13,25 @@ internal class EmbraceSessionProperties( private val logger: InternalEmbraceLogger = InternalStaticEmbraceLogger.logger ) { private val temporary: MutableMap = HashMap() - private var permanent: MutableMap + private val permanentPropertiesReference = AtomicReference(NOT_LOADED) + private val permanentPropertiesProvider: Provider> = { + preferencesService.permanentSessionProperties?.let(::HashMap) ?: HashMap() + } + + private fun permanentProperties(): MutableMap { + if (permanentPropertiesReference.get() === NOT_LOADED) { + synchronized(permanentPropertiesReference) { + if (permanentPropertiesReference.get() === NOT_LOADED) { + permanentPropertiesReference.set(permanentPropertiesProvider()) + } + } + } - init { - // TODO: this blocks on the preferences being successfully read from this. Are we cool with this? - val existingPermanent: Map? = preferencesService.permanentSessionProperties - permanent = existingPermanent?.let(::HashMap) ?: HashMap() + return permanentPropertiesReference.get() } private fun haveKey(key: String): Boolean { - return permanent.containsKey(key) || temporary.containsKey(key) + return permanentProperties().containsKey(key) || temporary.containsKey(key) } private fun isValidKey(key: String?): Boolean { @@ -51,58 +62,64 @@ internal class EmbraceSessionProperties( return value.substring(0, maxLength - endChars.length) + endChars } - @Synchronized fun add(key: String, value: String, isPermanent: Boolean): Boolean { - if (!isValidKey(key)) { - return false - } - val sanitizedKey = enforceLength(key, SESSION_PROPERTY_KEY_LIMIT) - if (!isValidValue(value)) { - return false - } - val sanitizedValue = enforceLength(value, SESSION_PROPERTY_VALUE_LIMIT) - val maxSessionProperties = configService.sessionBehavior.getMaxSessionProperties() - if (size() > maxSessionProperties || size() == maxSessionProperties && !haveKey(sanitizedKey)) { - logger.logError("Session property count is at its limit. Rejecting.") - return false - } + synchronized(permanentPropertiesReference) { + if (!isValidKey(key)) { + return false + } + val sanitizedKey = enforceLength(key, SESSION_PROPERTY_KEY_LIMIT) + if (!isValidValue(value)) { + return false + } + val sanitizedValue = enforceLength(value, SESSION_PROPERTY_VALUE_LIMIT) + val maxSessionProperties = configService.sessionBehavior.getMaxSessionProperties() + if (size() > maxSessionProperties || size() == maxSessionProperties && !haveKey(sanitizedKey)) { + logger.logError("Session property count is at its limit. Rejecting.") + return false + } - // add to selected destination, deleting the key if it exists in the other destination - if (isPermanent) { - permanent[sanitizedKey] = sanitizedValue - temporary.remove(sanitizedKey) - preferencesService.permanentSessionProperties = permanent - } else { - // only save the permanent values if the key existed in the permanent map - if (permanent.remove(sanitizedKey) != null) { - preferencesService.permanentSessionProperties = permanent + // add to selected destination, deleting the key if it exists in the other destination + if (isPermanent) { + permanentProperties()[sanitizedKey] = sanitizedValue + temporary.remove(sanitizedKey) + preferencesService.permanentSessionProperties = permanentProperties() + } else { + // only save the permanent values if the key existed in the permanent map + val newPermanent = permanentProperties() + if (newPermanent.remove(sanitizedKey) != null) { + permanentPropertiesReference.set(newPermanent) + preferencesService.permanentSessionProperties = permanentProperties() + } + temporary[sanitizedKey] = sanitizedValue } - temporary[sanitizedKey] = sanitizedValue + return true } - return true } - @Synchronized fun remove(key: String): Boolean { - if (!isValidKey(key)) { - return false - } - val sanitizedKey = enforceLength(key, SESSION_PROPERTY_KEY_LIMIT) - var existed = false - if (temporary.remove(sanitizedKey) != null) { - existed = true - } - if (permanent.remove(sanitizedKey) != null) { - preferencesService.permanentSessionProperties = permanent - existed = true + synchronized(permanentPropertiesReference) { + if (!isValidKey(key)) { + return false + } + val sanitizedKey = enforceLength(key, SESSION_PROPERTY_KEY_LIMIT) + var existed = false + if (temporary.remove(sanitizedKey) != null) { + existed = true + } + + val newPermanent = permanentProperties() + if (newPermanent.remove(sanitizedKey) != null) { + permanentPropertiesReference.set(newPermanent) + preferencesService.permanentSessionProperties = permanentProperties() + existed = true + } + return existed } - return existed } - @Synchronized - fun get(): Map = permanent.plus(temporary) + fun get(): Map = synchronized(permanentPropertiesReference) { permanentProperties().plus(temporary) } - private fun size(): Int = permanent.size + temporary.size + private fun size(): Int = permanentProperties().size + temporary.size fun clearTemporary() = temporary.clear() @@ -113,5 +130,6 @@ internal class EmbraceSessionProperties( */ private const val SESSION_PROPERTY_KEY_LIMIT = 128 private const val SESSION_PROPERTY_VALUE_LIMIT = 1024 + private val NOT_LOADED = mutableMapOf() } } From 19daf248c87eb28618445526ffddbbacef72c55c Mon Sep 17 00:00:00 2001 From: bidetofevil Date: Sun, 10 Mar 2024 14:27:21 -0700 Subject: [PATCH 06/14] Defer aspects of API service load until necessary --- .../embracesdk/comms/api/EmbraceApiService.kt | 50 ++++++++++++------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/api/EmbraceApiService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/api/EmbraceApiService.kt index 6804878e32..590304ea73 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/api/EmbraceApiService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/api/EmbraceApiService.kt @@ -8,6 +8,7 @@ import io.embrace.android.embracesdk.comms.delivery.DeliveryCacheManager import io.embrace.android.embracesdk.comms.delivery.NetworkStatus import io.embrace.android.embracesdk.comms.delivery.PendingApiCallsSender import io.embrace.android.embracesdk.config.remote.RemoteConfig +import io.embrace.android.embracesdk.internal.Systrace import io.embrace.android.embracesdk.internal.compression.ConditionalGzipOutputStream import io.embrace.android.embracesdk.internal.logs.LogPayload import io.embrace.android.embracesdk.internal.payload.Envelope @@ -38,21 +39,31 @@ internal class EmbraceApiService( networkConnectivityService: NetworkConnectivityService, ) : ApiService, NetworkConnectivityListener { - private val mapper = ApiRequestMapper(urlBuilder, lazyDeviceId, appId) - private val configUrl = urlBuilder.getConfigUrl() + private val mapper = lazy { + Systrace.traceSynchronous("api-request-mapper-init") { + ApiRequestMapper(urlBuilder, lazyDeviceId, appId) + } + } + private val configUrl = lazy { + Systrace.traceSynchronous("config-url-init") { + urlBuilder.getConfigUrl() + } + } private var lastNetworkStatus: NetworkStatus = NetworkStatus.UNKNOWN init { - networkConnectivityService.addNetworkConnectivityListener(this) - lastNetworkStatus = networkConnectivityService.getCurrentNetworkStatus() - pendingApiCallsSender.setSendMethod(this::executePost) + Systrace.traceSynchronous("api-service-init-block") { + networkConnectivityService.addNetworkConnectivityListener(this) + lastNetworkStatus = networkConnectivityService.getCurrentNetworkStatus() + pendingApiCallsSender.setSendMethod(this::executePost) + } } @Throws(IllegalStateException::class) @Suppress("UseCheckOrError") override fun getConfig(): RemoteConfig? { - var request = prepareConfigRequest(configUrl) - val cachedResponse = cachedConfigProvider(configUrl, request) + var request = prepareConfigRequest(configUrl.value) + val cachedResponse = cachedConfigProvider(configUrl.value, request) if (cachedResponse.isValid()) { // only bother if we have a useful response. request = request.copy(eTag = cachedResponse.eTag) } @@ -64,23 +75,28 @@ internal class EmbraceApiService( serializer.fromJson(it, RemoteConfig::class.java) } } + is ApiResponse.NotModified -> { logger.logInfo("Confirmed config has not been modified.") cachedResponse.remoteConfig } + is ApiResponse.TooManyRequests -> { // TODO: We should retry after the retryAfter time or 3 seconds and apply exponential backoff. logger.logWarning("Too many requests. ") null } + is ApiResponse.Failure -> { logger.logInfo("Failed to fetch config (no response).") null } + is ApiResponse.Incomplete -> { logger.logWarning("Failed to fetch config.", response.exception) throw response.exception } + ApiResponse.PayloadTooLarge -> { // Not expected to receive a 413 response for a GET request. null @@ -89,8 +105,8 @@ internal class EmbraceApiService( } override fun getCachedConfig(): CachedConfig { - val request = prepareConfigRequest(configUrl) - return cachedConfigProvider(configUrl, request) + val request = prepareConfigRequest(configUrl.value) + return cachedConfigProvider(configUrl.value, request) } private fun prepareConfigRequest(url: String) = ApiRequest( @@ -106,37 +122,37 @@ internal class EmbraceApiService( } override fun sendLog(eventMessage: EventMessage) { - post(eventMessage, mapper::logRequest) + post(eventMessage, mapper.value::logRequest) } override fun sendLogsEnvelope(logsEnvelope: Envelope) { val parameterizedType = Types.newParameterizedType(Envelope::class.java, LogPayload::class.java) - post(logsEnvelope, mapper::logsEnvelopeRequest, parameterizedType) + post(logsEnvelope, mapper.value::logsEnvelopeRequest, parameterizedType) } override fun sendSessionEnvelope(sessionEnvelope: Envelope) { val parameterizedType = Types.newParameterizedType(Envelope::class.java, SessionPayload::class.java) - post(sessionEnvelope, mapper::sessionEnvelopeRequest, parameterizedType) + post(sessionEnvelope, mapper.value::sessionEnvelopeRequest, parameterizedType) } override fun sendAEIBlob(blobMessage: BlobMessage) { - post(blobMessage, mapper::aeiBlobRequest) + post(blobMessage, mapper.value::aeiBlobRequest) } override fun sendNetworkCall(networkEvent: NetworkEvent) { - post(networkEvent, mapper::networkEventRequest) + post(networkEvent, mapper.value::networkEventRequest) } override fun sendEvent(eventMessage: EventMessage) { - post(eventMessage, mapper::eventMessageRequest) + post(eventMessage, mapper.value::eventMessageRequest) } override fun sendCrash(crash: EventMessage): Future<*> { - return post(crash, mapper::eventMessageRequest) { cacheManager.deleteCrash() } + return post(crash, mapper.value::eventMessageRequest) { cacheManager.deleteCrash() } } override fun sendSession(action: SerializationAction, onFinish: (() -> Unit)?): Future<*> { - return postOnWorker(action, mapper.sessionRequest(), onFinish) + return postOnWorker(action, mapper.value.sessionRequest(), onFinish) } private inline fun post( From 4dbec98ed1ae4625f44be67e1e4fdd08c0289e52 Mon Sep 17 00:00:00 2001 From: Hanson Ho Date: Mon, 11 Mar 2024 08:06:43 -0700 Subject: [PATCH 07/14] Apply suggestions from code review --- .../embrace/android/embracesdk/comms/api/EmbraceApiService.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/api/EmbraceApiService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/api/EmbraceApiService.kt index 590304ea73..46ebe5c231 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/api/EmbraceApiService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/api/EmbraceApiService.kt @@ -39,12 +39,12 @@ internal class EmbraceApiService( networkConnectivityService: NetworkConnectivityService, ) : ApiService, NetworkConnectivityListener { - private val mapper = lazy { + private val mapper by lazy { Systrace.traceSynchronous("api-request-mapper-init") { ApiRequestMapper(urlBuilder, lazyDeviceId, appId) } } - private val configUrl = lazy { + private val configUrl by lazy { Systrace.traceSynchronous("config-url-init") { urlBuilder.getConfigUrl() } From e3c84d860f8a97e09558f7a5ddd48ae483f12757 Mon Sep 17 00:00:00 2001 From: bidetofevil Date: Thu, 7 Mar 2024 13:06:11 -0800 Subject: [PATCH 08/14] Delete EmbraceAttributes.Type and consolidate on EmbType --- .../internal/spans/CurrentSessionSpanImpl.kt | 3 +- .../internal/spans/EmbraceExtensions.kt | 28 ++++--------------- .../internal/spans/EmbraceSpanService.kt | 7 +++-- .../embracesdk/internal/spans/SpanService.kt | 10 ++++--- .../internal/spans/SpanServiceImpl.kt | 7 +++-- .../spans/UninitializedSdkSpanService.kt | 9 +++--- .../FragmentBreadcrumbDataSourceTest.kt | 6 ++-- .../capture/startup/StartupServiceImplTest.kt | 5 ++-- .../embracesdk/fakes/FakeEmbraceSpan.kt | 5 ++-- .../embracesdk/fakes/FakeSpanService.kt | 8 +++--- .../spans/CurrentSessionSpanImplTests.kt | 6 ++-- .../internal/spans/EmbraceSpanServiceTest.kt | 8 ++++-- .../internal/spans/EmbraceTracerTest.kt | 5 ++-- .../internal/spans/InternalTracerTest.kt | 11 ++++---- .../internal/spans/SpanServiceImplTest.kt | 27 +++++++++--------- 15 files changed, 70 insertions(+), 75 deletions(-) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImpl.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImpl.kt index 887301fe3a..15326e5b75 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImpl.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImpl.kt @@ -3,6 +3,7 @@ package io.embrace.android.embracesdk.internal.spans import io.embrace.android.embracesdk.arch.destination.SessionSpanWriter import io.embrace.android.embracesdk.arch.destination.SpanAttributeData import io.embrace.android.embracesdk.arch.destination.SpanEventData +import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.internal.clock.nanosToMillis import io.embrace.android.embracesdk.internal.utils.Provider import io.embrace.android.embracesdk.spans.EmbraceSpan @@ -110,7 +111,7 @@ internal class CurrentSessionSpanImpl( val spanBuilder = createEmbraceSpanBuilder( tracer = tracerSupplier(), name = "session", - type = EmbraceAttributes.Type.SESSION + type = EmbType.Ux.Session ) .setNoParent() .setStartTimestamp(startTimeMs, TimeUnit.MILLISECONDS) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt index f7a1898473..fc507264d7 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.internal.spans +import io.embrace.android.embracesdk.arch.schema.TelemetryType import io.embrace.android.embracesdk.internal.spans.EmbraceAttributes.Attribute import io.embrace.android.embracesdk.internal.utils.Provider import io.embrace.android.embracesdk.spans.EmbraceSpan @@ -68,7 +69,7 @@ internal fun Tracer.embraceSpanBuilder(name: String, internal: Boolean): SpanBui internal fun createEmbraceSpanBuilder( tracer: Tracer, name: String, - type: EmbraceAttributes.Type, + type: TelemetryType, internal: Boolean = true ): SpanBuilder = tracer.embraceSpanBuilder(name, internal).setType(type) @@ -78,15 +79,15 @@ internal fun createEmbraceSpanBuilder( internal fun createRootSpanBuilder( tracer: Tracer, name: String, - type: EmbraceAttributes.Type, + type: TelemetryType, internal: Boolean ): SpanBuilder = createEmbraceSpanBuilder(tracer = tracer, name = name, type = type, internal = internal).setNoParent() /** * Sets and returns the [EmbraceAttributes.Type] attribute for the given [SpanBuilder] */ -internal fun SpanBuilder.setType(value: EmbraceAttributes.Type): SpanBuilder { - setAttribute(value.keyName(), value.typeName) +internal fun SpanBuilder.setType(value: TelemetryType): SpanBuilder { + setAttribute(value.attributeName(), value.description) return this } @@ -233,25 +234,6 @@ internal fun String.toEmbraceUsageAttributeName(): String = EMBRACE_USAGE_ATTRIB * in the Embrace world. Each enum defines the attribute name used in the [Span] and specifies the set of valid values it can be set to. */ internal object EmbraceAttributes { - - /** - * Attribute to categorize a [Span] and give it a distinct semantic meaning. Spans of each [Type] may be treated differently by the - * backend and can be expected to contain a set of attributes to further flesh out the given semantic meanings. - */ - internal enum class Type(val typeName: String) : Attribute { - /** - * Spans that model an Embrace session or background activity. - */ - SESSION("ux.session"), - - /** - * A [Span] created by an SDK user to measure the performance of an operation - */ - PERFORMANCE("performance"); - - override val canonicalName = "type" - } - /** * The reason for the termination of a process span */ diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanService.kt index de6e4ff598..4ec0690fa8 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanService.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.internal.spans +import io.embrace.android.embracesdk.arch.schema.TelemetryType import io.embrace.android.embracesdk.internal.utils.Provider import io.embrace.android.embracesdk.spans.EmbraceSpan import io.embrace.android.embracesdk.spans.EmbraceSpanEvent @@ -42,13 +43,13 @@ internal class EmbraceSpanService( override fun initialized(): Boolean = currentDelegate is SpanServiceImpl - override fun createSpan(name: String, parent: EmbraceSpan?, type: EmbraceAttributes.Type, internal: Boolean): EmbraceSpan? = + override fun createSpan(name: String, parent: EmbraceSpan?, type: TelemetryType, internal: Boolean): EmbraceSpan? = currentDelegate.createSpan(name = name, parent = parent, type = type, internal = internal) override fun recordSpan( name: String, parent: EmbraceSpan?, - type: EmbraceAttributes.Type, + type: TelemetryType, internal: Boolean, attributes: Map, events: List, @@ -68,7 +69,7 @@ internal class EmbraceSpanService( startTimeMs: Long, endTimeMs: Long, parent: EmbraceSpan?, - type: EmbraceAttributes.Type, + type: TelemetryType, internal: Boolean, attributes: Map, events: List, diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/SpanService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/SpanService.kt index c3375c1d1a..1ffb508166 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/SpanService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/SpanService.kt @@ -1,5 +1,7 @@ package io.embrace.android.embracesdk.internal.spans +import io.embrace.android.embracesdk.arch.schema.EmbType +import io.embrace.android.embracesdk.arch.schema.TelemetryType import io.embrace.android.embracesdk.internal.Initializable import io.embrace.android.embracesdk.spans.EmbraceSpan import io.embrace.android.embracesdk.spans.EmbraceSpanEvent @@ -16,7 +18,7 @@ internal interface SpanService : Initializable { fun createSpan( name: String, parent: EmbraceSpan? = null, - type: EmbraceAttributes.Type = EmbraceAttributes.Type.PERFORMANCE, + type: TelemetryType = EmbType.Performance, internal: Boolean = true ): EmbraceSpan? @@ -27,7 +29,7 @@ internal interface SpanService : Initializable { name: String, parent: EmbraceSpan? = null, startTimeMs: Long? = null, - type: EmbraceAttributes.Type = EmbraceAttributes.Type.PERFORMANCE, + type: TelemetryType = EmbType.Performance, internal: Boolean = true ): EmbraceSpan? { createSpan( @@ -51,7 +53,7 @@ internal interface SpanService : Initializable { fun recordSpan( name: String, parent: EmbraceSpan? = null, - type: EmbraceAttributes.Type = EmbraceAttributes.Type.PERFORMANCE, + type: TelemetryType = EmbType.Performance, internal: Boolean = true, attributes: Map = emptyMap(), events: List = emptyList(), @@ -67,7 +69,7 @@ internal interface SpanService : Initializable { startTimeMs: Long, endTimeMs: Long, parent: EmbraceSpan? = null, - type: EmbraceAttributes.Type = EmbraceAttributes.Type.PERFORMANCE, + type: TelemetryType = EmbType.Performance, internal: Boolean = true, attributes: Map = emptyMap(), events: List = emptyList(), diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImpl.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImpl.kt index 13a4af801d..ba9fc8faee 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImpl.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImpl.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.internal.spans +import io.embrace.android.embracesdk.arch.schema.TelemetryType import io.embrace.android.embracesdk.spans.EmbraceSpan import io.embrace.android.embracesdk.spans.EmbraceSpanEvent import io.embrace.android.embracesdk.spans.ErrorCode @@ -27,7 +28,7 @@ internal class SpanServiceImpl( override fun initialized(): Boolean = initialized.get() - override fun createSpan(name: String, parent: EmbraceSpan?, type: EmbraceAttributes.Type, internal: Boolean): EmbraceSpan? { + override fun createSpan(name: String, parent: EmbraceSpan?, type: TelemetryType, internal: Boolean): EmbraceSpan? { return if (EmbraceSpanImpl.inputsValid(name) && currentSessionSpan.canStartNewSpan(parent, internal)) { EmbraceSpanImpl( spanBuilder = createRootSpanBuilder(tracer = tracer, name = name, type = type, internal = internal), @@ -42,7 +43,7 @@ internal class SpanServiceImpl( override fun recordSpan( name: String, parent: EmbraceSpan?, - type: EmbraceAttributes.Type, + type: TelemetryType, internal: Boolean, attributes: Map, events: List, @@ -62,7 +63,7 @@ internal class SpanServiceImpl( startTimeMs: Long, endTimeMs: Long, parent: EmbraceSpan?, - type: EmbraceAttributes.Type, + type: TelemetryType, internal: Boolean, attributes: Map, events: List, diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/UninitializedSdkSpanService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/UninitializedSdkSpanService.kt index b45fc1d856..20fe1f1734 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/UninitializedSdkSpanService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/UninitializedSdkSpanService.kt @@ -1,6 +1,7 @@ package io.embrace.android.embracesdk.internal.spans import io.embrace.android.embracesdk.annotation.InternalApi +import io.embrace.android.embracesdk.arch.schema.TelemetryType import io.embrace.android.embracesdk.spans.EmbraceSpan import io.embrace.android.embracesdk.spans.EmbraceSpanEvent import io.embrace.android.embracesdk.spans.ErrorCode @@ -21,12 +22,12 @@ internal class UninitializedSdkSpanService : SpanService { override fun initialized(): Boolean = true - override fun createSpan(name: String, parent: EmbraceSpan?, type: EmbraceAttributes.Type, internal: Boolean): EmbraceSpan? = null + override fun createSpan(name: String, parent: EmbraceSpan?, type: TelemetryType, internal: Boolean): EmbraceSpan? = null override fun recordSpan( name: String, parent: EmbraceSpan?, - type: EmbraceAttributes.Type, + type: TelemetryType, internal: Boolean, attributes: Map, events: List, @@ -38,7 +39,7 @@ internal class UninitializedSdkSpanService : SpanService { startTimeMs: Long, endTimeMs: Long, parent: EmbraceSpan?, - type: EmbraceAttributes.Type, + type: TelemetryType, internal: Boolean, attributes: Map, events: List, @@ -116,7 +117,7 @@ internal class UninitializedSdkSpanService : SpanService { val startTimeMs: Long, val endTimeMs: Long, val parent: EmbraceSpan?, - val type: EmbraceAttributes.Type, + val type: TelemetryType, val internal: Boolean, val attributes: Map, val events: List, diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/FragmentBreadcrumbDataSourceTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/FragmentBreadcrumbDataSourceTest.kt index 2cf5e414be..6ffa0ae72b 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/FragmentBreadcrumbDataSourceTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/FragmentBreadcrumbDataSourceTest.kt @@ -1,9 +1,9 @@ package io.embrace.android.embracesdk.capture.crumbs +import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeSpanService -import io.embrace.android.embracesdk.internal.spans.EmbraceAttributes import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -35,7 +35,7 @@ internal class FragmentBreadcrumbDataSourceTest { val span = spanService.createdSpans.single() assertEquals("view-breadcrumb", span.name) - assertEquals(EmbraceAttributes.Type.PERFORMANCE, span.type) + assertEquals(EmbType.Performance, span.type) assertTrue(span.isRecording) assertEquals( mapOf( @@ -54,7 +54,7 @@ internal class FragmentBreadcrumbDataSourceTest { val span = spanService.createdSpans.single() assertEquals("view-breadcrumb", span.name) - assertEquals(EmbraceAttributes.Type.PERFORMANCE, span.type) + assertEquals(EmbType.Performance, span.type) assertFalse(span.isRecording) assertEquals( mapOf( diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/startup/StartupServiceImplTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/startup/StartupServiceImplTest.kt index 1e157db4d9..b3a2170c25 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/startup/StartupServiceImplTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/startup/StartupServiceImplTest.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.capture.startup +import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.concurrency.BlockableExecutorService import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.injection.FakeInitModule @@ -50,8 +51,8 @@ internal class StartupServiceImplTest { assertEquals(startTimeMillis, startTimeNanos.nanosToMillis()) assertEquals(endTimeMillis, endTimeNanos.nanosToMillis()) assertEquals( - io.embrace.android.embracesdk.internal.spans.EmbraceAttributes.Type.PERFORMANCE.typeName, - attributes[io.embrace.android.embracesdk.internal.spans.EmbraceAttributes.Type.PERFORMANCE.keyName()] + EmbType.Performance.description, + attributes[EmbType.Performance.attributeName()] ) assertTrue(isPrivate()) assertEquals(StatusCode.OK, status) diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeEmbraceSpan.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeEmbraceSpan.kt index 44ddff240b..7a914b9feb 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeEmbraceSpan.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeEmbraceSpan.kt @@ -1,8 +1,9 @@ package io.embrace.android.embracesdk.fakes import io.embrace.android.embracesdk.arch.destination.SpanEventData +import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.arch.schema.SchemaType -import io.embrace.android.embracesdk.internal.spans.EmbraceAttributes +import io.embrace.android.embracesdk.arch.schema.TelemetryType import io.embrace.android.embracesdk.spans.EmbraceSpan import io.embrace.android.embracesdk.spans.ErrorCode import io.opentelemetry.sdk.trace.IdGenerator @@ -10,7 +11,7 @@ import io.opentelemetry.sdk.trace.IdGenerator internal class FakeEmbraceSpan( override val parent: EmbraceSpan?, val name: String? = null, - val type: EmbraceAttributes.Type = EmbraceAttributes.Type.PERFORMANCE, + val type: TelemetryType = EmbType.Performance, val internal: Boolean = true ) : EmbraceSpan { diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeSpanService.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeSpanService.kt index 770eac38b4..2f2b03d7a0 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeSpanService.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeSpanService.kt @@ -1,6 +1,6 @@ package io.embrace.android.embracesdk.fakes -import io.embrace.android.embracesdk.internal.spans.EmbraceAttributes +import io.embrace.android.embracesdk.arch.schema.TelemetryType import io.embrace.android.embracesdk.internal.spans.SpanService import io.embrace.android.embracesdk.spans.EmbraceSpan import io.embrace.android.embracesdk.spans.EmbraceSpanEvent @@ -18,7 +18,7 @@ internal class FakeSpanService : SpanService { override fun createSpan( name: String, parent: EmbraceSpan?, - type: EmbraceAttributes.Type, + type: TelemetryType, internal: Boolean ): EmbraceSpan = FakeEmbraceSpan(null, name, type, internal).apply { createdSpans.add(this) @@ -27,7 +27,7 @@ internal class FakeSpanService : SpanService { override fun recordSpan( name: String, parent: EmbraceSpan?, - type: EmbraceAttributes.Type, + type: TelemetryType, internal: Boolean, attributes: Map, events: List, @@ -41,7 +41,7 @@ internal class FakeSpanService : SpanService { startTimeMs: Long, endTimeMs: Long, parent: EmbraceSpan?, - type: EmbraceAttributes.Type, + type: TelemetryType, internal: Boolean, attributes: Map, events: List, diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImplTests.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImplTests.kt index 87d6384450..935a9505e5 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImplTests.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImplTests.kt @@ -2,6 +2,7 @@ package io.embrace.android.embracesdk.internal.spans import io.embrace.android.embracesdk.arch.destination.SpanAttributeData import io.embrace.android.embracesdk.arch.destination.SpanEventData +import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.arch.schema.SchemaType import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.injection.FakeInitModule @@ -140,10 +141,7 @@ internal class CurrentSessionSpanImplTests { val lastFlushedSpan = flushedSpans[0] with(lastFlushedSpan) { assertEquals("emb-session", name) - assertEquals( - EmbraceAttributes.Type.SESSION.typeName, - attributes[EmbraceAttributes.Type.SESSION.keyName()] - ) + assertEquals(EmbType.Ux.Session.description, attributes[EmbType.Ux.Session.attributeName()]) assertEquals(StatusCode.OK, status) assertFalse(isKey()) assertEquals(it.name, attributes[it.keyName()]) diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanServiceTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanServiceTest.kt index 852fcc083f..726dc1b41c 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanServiceTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanServiceTest.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.internal.spans +import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.internal.clock.nanosToMillis @@ -58,7 +59,7 @@ internal class EmbraceSpanServiceTest { val expectedName = "test-span" val expectedStartTimeMs = clock.now() val expectedEndTimeMs = expectedStartTimeMs + 100L - val expectedType = EmbraceAttributes.Type.PERFORMANCE + val expectedType = EmbType.Performance val expectedAttributes = mapOf( Pair("attribute1", "value1"), Pair("attribute2", "value2") @@ -86,7 +87,10 @@ internal class EmbraceSpanServiceTest { assertEquals(name, name) assertEquals(expectedStartTimeMs, startTimeNanos.nanosToMillis()) assertEquals(expectedEndTimeMs, endTimeNanos.nanosToMillis()) - assertEquals(expectedType.typeName, attributes[EmbraceAttributes.Type.PERFORMANCE.keyName()]) + assertEquals( + EmbType.Performance.description, + attributes[EmbType.Performance.attributeName()] + ) expectedAttributes.forEach { assertEquals(it.value, attributes[it.key]) } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt index fb41f2095d..bd8c609cb1 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.internal.spans +import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fixtures.TOO_LONG_SPAN_NAME @@ -315,8 +316,8 @@ internal class EmbraceTracerTest { val currentSpan = currentSpans[0] assertEquals(name, currentSpan.name) assertEquals( - EmbraceAttributes.Type.PERFORMANCE.typeName, - currentSpan.attributes[EmbraceAttributes.Type.PERFORMANCE.keyName()] + EmbType.Performance.description, + currentSpan.attributes[EmbType.Performance.attributeName()] ) assertEquals(if (traceRoot) "true" else null, currentSpan.attributes["emb.key"]) assertEquals(errorCode?.name, currentSpan.attributes[errorCode?.keyName()]) diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt index d16098e410..9b0d4afe58 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.internal.spans +import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.internal.clock.millisToNanos @@ -209,7 +210,7 @@ internal class InternalTracerTest { val expectedName = "test-span" val expectedStartTimeMs = clock.now() val expectedEndTimeMs = expectedStartTimeMs + 100L - val expectedType = EmbraceAttributes.Type.PERFORMANCE + val expectedType = EmbType.Performance val expectedAttributes = mapOf( Pair("attribute1", "value1"), Pair("attribute2", "value2") @@ -234,8 +235,8 @@ internal class InternalTracerTest { assertEquals(expectedStartTimeMs, startTimeNanos.nanosToMillis()) assertEquals(expectedEndTimeMs, endTimeNanos.nanosToMillis()) assertEquals( - expectedType.typeName, - attributes[EmbraceAttributes.Type.PERFORMANCE.keyName()] + expectedType.description, + attributes[expectedType.attributeName()] ) assertEquals("true", attributes["emb.key"]) expectedAttributes.forEach { @@ -277,8 +278,8 @@ internal class InternalTracerTest { val currentSpan = currentSpans[0] assertEquals(name, currentSpan.name) assertEquals( - EmbraceAttributes.Type.PERFORMANCE.typeName, - currentSpan.attributes[EmbraceAttributes.Type.PERFORMANCE.keyName()] + EmbType.Performance.description, + currentSpan.attributes[EmbType.Performance.attributeName()] ) assertEquals(if (traceRoot) "true" else null, currentSpan.attributes["emb.key"]) assertEquals(errorCode?.name, currentSpan.attributes[errorCode?.keyName()]) diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt index 617015cf68..6addae46c5 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt @@ -1,6 +1,7 @@ package io.embrace.android.embracesdk.internal.spans import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fixtures.MAX_LENGTH_SPAN_NAME @@ -54,8 +55,8 @@ internal class SpanServiceImplTest { with(verifyAndReturnSoleCompletedSpan("emb-test-span")) { assertEquals(SpanId.getInvalid(), parentSpanId) assertEquals( - EmbraceAttributes.Type.PERFORMANCE.typeName, - attributes[EmbraceAttributes.Type.PERFORMANCE.keyName()] + EmbType.Performance.description, + attributes[EmbType.Performance.attributeName()] ) assertTrue(isKey()) } @@ -75,7 +76,7 @@ internal class SpanServiceImplTest { val embraceSpan = checkNotNull( spansService.createSpan( name = "test-span", - type = EmbraceAttributes.Type.PERFORMANCE + type = EmbType.Performance ) ) assertTrue(embraceSpan.start()) @@ -83,8 +84,8 @@ internal class SpanServiceImplTest { with(verifyAndReturnSoleCompletedSpan("emb-test-span")) { assertEquals(SpanId.getInvalid(), parentSpanId) assertEquals( - EmbraceAttributes.Type.PERFORMANCE.typeName, - attributes[EmbraceAttributes.Type.PERFORMANCE.keyName()] + EmbType.Performance.description, + attributes[EmbType.Performance.attributeName()] ) assertTrue(isKey()) } @@ -167,7 +168,7 @@ internal class SpanServiceImplTest { name = "child-span", parent = parent, startTimeMs = childStartTimeMs, - type = EmbraceAttributes.Type.SESSION, + type = EmbType.Ux.View, internal = true ) ) @@ -179,7 +180,7 @@ internal class SpanServiceImplTest { with(completedSpans[0]) { assertTrue(isPrivate()) assertFalse(isKey()) - assertEquals(EmbraceAttributes.Type.SESSION.typeName, attributes[EmbraceAttributes.Type.SESSION.keyName()]) + assertEquals(EmbType.Ux.View.description, attributes[EmbType.Ux.View.attributeName()]) assertEquals(childStartTimeMs, startTimeNanos.nanosToMillis()) assertEquals(childSpanEndTimeMs, endTimeNanos.nanosToMillis()) } @@ -190,7 +191,7 @@ internal class SpanServiceImplTest { val expectedName = "test-span" val expectedStartTimeMs = clock.now() val expectedEndTimeMs = expectedStartTimeMs + 100L - val expectedType = EmbraceAttributes.Type.PERFORMANCE + val expectedType = EmbType.Performance val expectedAttributes = mapOf( Pair("attribute1", "value1"), Pair("attribute2", "value2") @@ -212,7 +213,10 @@ internal class SpanServiceImplTest { with(verifyAndReturnSoleCompletedSpan("emb-$expectedName")) { assertEquals(expectedStartTimeMs, startTimeNanos.nanosToMillis()) assertEquals(expectedEndTimeMs, endTimeNanos.nanosToMillis()) - assertEquals(expectedType.typeName, attributes[EmbraceAttributes.Type.PERFORMANCE.keyName()]) + assertEquals( + EmbType.Performance.description, + attributes[EmbType.Performance.attributeName()] + ) assertEquals(SpanId.getInvalid(), parentSpanId) assertTrue(isKey()) assertTrue(isPrivate()) @@ -339,10 +343,7 @@ internal class SpanServiceImplTest { assertEquals(returnThis, lambdaReturn) with(verifyAndReturnSoleCompletedSpan("emb-test-span")) { assertEquals(SpanId.getInvalid(), parentSpanId) - assertEquals( - EmbraceAttributes.Type.PERFORMANCE.typeName, - attributes[EmbraceAttributes.Type.PERFORMANCE.keyName()] - ) + assertEquals(EmbType.Performance.description, attributes[EmbType.Performance.attributeName()]) assertTrue(isKey()) assertTrue(isPrivate()) } From 61848e4f39b82f30ba28c5ff49e60808a2ae6e94 Mon Sep 17 00:00:00 2001 From: bidetofevil Date: Thu, 7 Mar 2024 16:55:54 -0800 Subject: [PATCH 09/14] Refactor how Embrace attributes are defined --- .../api/embrace-android-sdk.api | 4 +- embrace-android-sdk/build.gradle | 1 + .../embracesdk/assertions/SpanAssertions.kt | 8 +++- .../arch/schema/AppTerminationCause.kt | 16 ++++++++ .../arch/schema/EmbraceAttribute.kt | 20 ++++++++++ .../arch/schema/ErrorCodeAttribute.kt | 20 ++++++++++ .../internal/spans/CurrentSessionSpan.kt | 3 +- .../internal/spans/CurrentSessionSpanImpl.kt | 7 ++-- .../internal/spans/EmbraceExtensions.kt | 39 ++----------------- .../session/message/PayloadMessageCollator.kt | 5 ++- .../android/embracesdk/spans/ErrorCode.kt | 13 ++++--- .../fakes/FakeCurrentSessionSpan.kt | 4 +- .../spans/CurrentSessionSpanImplTests.kt | 8 ++-- .../internal/spans/EmbraceTracerTest.kt | 8 +++- .../internal/spans/InternalTracerTest.kt | 8 +++- .../internal/spans/SpanServiceImplTest.kt | 26 ++++++++----- 16 files changed, 122 insertions(+), 68 deletions(-) create mode 100644 embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/AppTerminationCause.kt create mode 100644 embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/EmbraceAttribute.kt create mode 100644 embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/ErrorCodeAttribute.kt diff --git a/embrace-android-sdk/api/embrace-android-sdk.api b/embrace-android-sdk/api/embrace-android-sdk.api index e700e6c7e9..31ddfcd2bc 100644 --- a/embrace-android-sdk/api/embrace-android-sdk.api +++ b/embrace-android-sdk/api/embrace-android-sdk.api @@ -375,12 +375,10 @@ public final class io/embrace/android/embracesdk/spans/EmbraceSpanEventJsonAdapt public fun toString ()Ljava/lang/String; } -public final class io/embrace/android/embracesdk/spans/ErrorCode : java/lang/Enum, io/embrace/android/embracesdk/internal/spans/EmbraceAttributes$Attribute { +public final class io/embrace/android/embracesdk/spans/ErrorCode : java/lang/Enum { public static final field FAILURE Lio/embrace/android/embracesdk/spans/ErrorCode; public static final field UNKNOWN Lio/embrace/android/embracesdk/spans/ErrorCode; public static final field USER_ABANDON Lio/embrace/android/embracesdk/spans/ErrorCode; - public fun getCanonicalName ()Ljava/lang/String; - public fun keyName ()Ljava/lang/String; public static fun valueOf (Ljava/lang/String;)Lio/embrace/android/embracesdk/spans/ErrorCode; public static fun values ()[Lio/embrace/android/embracesdk/spans/ErrorCode; } diff --git a/embrace-android-sdk/build.gradle b/embrace-android-sdk/build.gradle index 5045dcc6c9..6b50441cd0 100644 --- a/embrace-android-sdk/build.gradle +++ b/embrace-android-sdk/build.gradle @@ -103,6 +103,7 @@ dependencies { testImplementation "com.squareup.okhttp3:mockwebserver:4.9.3" testImplementation("com.google.protobuf:protobuf-java:3.24.0") testImplementation("com.google.protobuf:protobuf-java-util:3.24.0") + testImplementation "org.jetbrains.kotlin:kotlin-reflect:1.4.32" dokkaHtmlPlugin("org.jetbrains.dokka:kotlin-as-java-plugin:${Versions.dokka}") dokkaHtmlPlugin("org.jetbrains.dokka:android-documentation-plugin:${Versions.dokka}") diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/assertions/SpanAssertions.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/assertions/SpanAssertions.kt index 310cd34cbf..8a6b8ce2bc 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/assertions/SpanAssertions.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/assertions/SpanAssertions.kt @@ -36,7 +36,13 @@ internal fun assertEmbraceSpanData( assertEquals(32, traceId.length) } assertEquals(expectedStatus, status) - assertEquals(errorCode?.name, attributes[errorCode?.keyName()]) + errorCode?.run { + val errorCodeAttribute = fromErrorCode() + assertEquals( + errorCodeAttribute.attributeValue, + attributes[errorCodeAttribute.otelAttributeName()] + ) + } expectedCustomAttributes.forEach { entry -> assertEquals(entry.value, attributes[entry.key]) } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/AppTerminationCause.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/AppTerminationCause.kt new file mode 100644 index 0000000000..beb405fa15 --- /dev/null +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/AppTerminationCause.kt @@ -0,0 +1,16 @@ +package io.embrace.android.embracesdk.arch.schema + +/** + * Attribute that stores the reason an app instance terminated + */ +internal sealed class AppTerminationCause( + override val attributeValue: String +) : EmbraceAttribute { + override val attributeName: String = "termination_cause" + + internal object Crash : AppTerminationCause("crash") + + internal object UserTermination : AppTerminationCause("user_termination") + + internal object Unknown : AppTerminationCause("unknown") +} diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/EmbraceAttribute.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/EmbraceAttribute.kt new file mode 100644 index 0000000000..bd8e425ac2 --- /dev/null +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/EmbraceAttribute.kt @@ -0,0 +1,20 @@ +package io.embrace.android.embracesdk.arch.schema + +import io.embrace.android.embracesdk.internal.spans.toEmbraceAttributeName + +/** + * Instance of a valid value for an attribute that has special meaning in the Embrace platform. + */ +internal interface EmbraceAttribute { + /** + * The unique name given to the attribute + */ + val attributeName: String + + /** + * The value of the particular instance of the attribute + */ + val attributeValue: String + + fun otelAttributeName(): String = attributeName.toEmbraceAttributeName() +} diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/ErrorCodeAttribute.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/ErrorCodeAttribute.kt new file mode 100644 index 0000000000..990da3d1b0 --- /dev/null +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/ErrorCodeAttribute.kt @@ -0,0 +1,20 @@ +package io.embrace.android.embracesdk.arch.schema + +import io.embrace.android.embracesdk.spans.ErrorCode +import java.util.Locale + +/** + * Attribute that stores the [ErrorCode] in an OpenTelemetry span + */ +internal sealed class ErrorCodeAttribute( + errorCode: ErrorCode +) : EmbraceAttribute { + override val attributeName: String = "error_code" + override val attributeValue: String = errorCode.name.toLowerCase(Locale.ENGLISH) + + internal object Failure : ErrorCodeAttribute(ErrorCode.FAILURE) + + internal object UserAbandon : ErrorCodeAttribute(ErrorCode.USER_ABANDON) + + internal object Unknown : ErrorCodeAttribute(ErrorCode.UNKNOWN) +} diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpan.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpan.kt index 777b9aaf2c..60b9cb014e 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpan.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpan.kt @@ -1,6 +1,7 @@ package io.embrace.android.embracesdk.internal.spans import io.embrace.android.embracesdk.arch.destination.SessionSpanWriter +import io.embrace.android.embracesdk.arch.schema.AppTerminationCause import io.embrace.android.embracesdk.internal.Initializable import io.embrace.android.embracesdk.spans.EmbraceSpan @@ -11,7 +12,7 @@ internal interface CurrentSessionSpan : Initializable, SessionSpanWriter { /** * End the current session span and start a new one if the app is not terminating */ - fun endSession(appTerminationCause: EmbraceAttributes.AppTerminationCause? = null): List + fun endSession(appTerminationCause: AppTerminationCause? = null): List /** * Returns true if a span with the given parameters can be started in the current session diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImpl.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImpl.kt index 15326e5b75..359fb9b486 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImpl.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImpl.kt @@ -3,6 +3,7 @@ package io.embrace.android.embracesdk.internal.spans import io.embrace.android.embracesdk.arch.destination.SessionSpanWriter import io.embrace.android.embracesdk.arch.destination.SpanAttributeData import io.embrace.android.embracesdk.arch.destination.SpanEventData +import io.embrace.android.embracesdk.arch.schema.AppTerminationCause import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.internal.clock.nanosToMillis import io.embrace.android.embracesdk.internal.utils.Provider @@ -65,7 +66,7 @@ internal class CurrentSessionSpanImpl( } } - override fun endSession(appTerminationCause: EmbraceAttributes.AppTerminationCause?): List { + override fun endSession(appTerminationCause: AppTerminationCause?): List { val endingSessionSpan = sessionSpan.get() ?: return emptyList() synchronized(sessionSpan) { // Right now, session spans don't survive native crashes and sudden process terminations, @@ -82,8 +83,8 @@ internal class CurrentSessionSpanImpl( sessionSpan.set(startSessionSpan(clock.now().nanosToMillis())) } else { endingSessionSpan.addAttribute( - appTerminationCause.keyName(), - appTerminationCause.name + appTerminationCause.otelAttributeName(), + appTerminationCause.attributeValue ) endingSessionSpan.stop() } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt index fc507264d7..1fdcfa35ae 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt @@ -1,7 +1,6 @@ package io.embrace.android.embracesdk.internal.spans import io.embrace.android.embracesdk.arch.schema.TelemetryType -import io.embrace.android.embracesdk.internal.spans.EmbraceAttributes.Attribute import io.embrace.android.embracesdk.internal.utils.Provider import io.embrace.android.embracesdk.spans.EmbraceSpan import io.embrace.android.embracesdk.spans.EmbraceSpanEvent @@ -84,7 +83,7 @@ internal fun createRootSpanBuilder( ): SpanBuilder = createEmbraceSpanBuilder(tracer = tracer, name = name, type = type, internal = internal).setNoParent() /** - * Sets and returns the [EmbraceAttributes.Type] attribute for the given [SpanBuilder] + * Sets and returns the [TelemetryType] attribute for the given [SpanBuilder] */ internal fun SpanBuilder.setType(value: TelemetryType): SpanBuilder { setAttribute(value.attributeName(), value.description) @@ -182,7 +181,8 @@ internal fun Span.endSpan(errorCode: ErrorCode? = null, endTimeMs: Long? = null) setStatus(StatusCode.OK) } else { setStatus(StatusCode.ERROR) - setAttribute(errorCode.keyName(), errorCode.toString()) + val errorCodeAttribute = errorCode.fromErrorCode() + setAttribute(errorCodeAttribute.otelAttributeName(), errorCodeAttribute.attributeValue) } if (endTimeMs != null) { @@ -228,36 +228,3 @@ internal fun String.toEmbraceAttributeName(): String = EMBRACE_ATTRIBUTE_NAME_PR * Return the appropriate internal Embrace attribute usage name given the current string */ internal fun String.toEmbraceUsageAttributeName(): String = EMBRACE_USAGE_ATTRIBUTE_NAME_PREFIX + this - -/** - * Contains the set of attributes (i.e. implementers of the [Attribute] interface) set on a [Span] by the SDK that has special meaning - * in the Embrace world. Each enum defines the attribute name used in the [Span] and specifies the set of valid values it can be set to. - */ -internal object EmbraceAttributes { - /** - * The reason for the termination of a process span - */ - internal enum class AppTerminationCause : Attribute { - CRASH, - USER_TERMINATION, - UNKNOWN; - - override val canonicalName: String = "termination_cause" - } - - /** - * Denotes an attribute added by the SDK with a restricted set of valid values - */ - internal interface Attribute { - - /** - * The name used to identify this [Attribute] - */ - val canonicalName: String - - /** - * The name used as the key for the [Attribute] in the attributes map - */ - fun keyName(): String = EMBRACE_ATTRIBUTE_NAME_PREFIX + canonicalName - } -} diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/message/PayloadMessageCollator.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/message/PayloadMessageCollator.kt index 51d63e86f3..df77a08f3e 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/message/PayloadMessageCollator.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/message/PayloadMessageCollator.kt @@ -1,6 +1,7 @@ package io.embrace.android.embracesdk.session.message import io.embrace.android.embracesdk.anr.ndk.NativeThreadSamplerService +import io.embrace.android.embracesdk.arch.schema.AppTerminationCause import io.embrace.android.embracesdk.capture.PerformanceInfoService import io.embrace.android.embracesdk.capture.crumbs.BreadcrumbService import io.embrace.android.embracesdk.capture.metadata.MetadataService @@ -12,7 +13,6 @@ import io.embrace.android.embracesdk.config.ConfigService import io.embrace.android.embracesdk.event.EventService import io.embrace.android.embracesdk.event.LogMessageService import io.embrace.android.embracesdk.internal.spans.CurrentSessionSpan -import io.embrace.android.embracesdk.internal.spans.EmbraceAttributes import io.embrace.android.embracesdk.internal.spans.EmbraceSpanData import io.embrace.android.embracesdk.internal.spans.SpanSink import io.embrace.android.embracesdk.internal.utils.Uuid @@ -160,11 +160,12 @@ internal class PayloadMessageCollator( when { !params.isCacheAttempt -> { val appTerminationCause = when { - finalPayload.crashReportId != null -> EmbraceAttributes.AppTerminationCause.CRASH + finalPayload.crashReportId != null -> AppTerminationCause.Crash else -> null } currentSessionSpan.endSession(appTerminationCause) } + else -> spanSink.completedSpans() } } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/spans/ErrorCode.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/spans/ErrorCode.kt index 3db786c8c8..85e07ac3b4 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/spans/ErrorCode.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/spans/ErrorCode.kt @@ -1,14 +1,13 @@ package io.embrace.android.embracesdk.spans import io.embrace.android.embracesdk.annotation.BetaApi -import io.embrace.android.embracesdk.internal.spans.EmbraceAttributes +import io.embrace.android.embracesdk.arch.schema.ErrorCodeAttribute /** - * Attribute to categorize the broad reason a Span completed unsuccessfully. + * Categorize the broad reason a Span completed unsuccessfully. */ @BetaApi -public enum class ErrorCode : EmbraceAttributes.Attribute { - +public enum class ErrorCode { /** * An application failure caused the Span to terminate */ @@ -24,5 +23,9 @@ public enum class ErrorCode : EmbraceAttributes.Attribute { */ UNKNOWN; - override val canonicalName: String = "error_code" + internal fun fromErrorCode(): ErrorCodeAttribute = when (this) { + FAILURE -> ErrorCodeAttribute.Failure + USER_ABANDON -> ErrorCodeAttribute.UserAbandon + UNKNOWN -> ErrorCodeAttribute.Unknown + } } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeCurrentSessionSpan.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeCurrentSessionSpan.kt index e140332d18..98739a5fcc 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeCurrentSessionSpan.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeCurrentSessionSpan.kt @@ -2,8 +2,8 @@ package io.embrace.android.embracesdk.fakes import io.embrace.android.embracesdk.arch.destination.SpanAttributeData import io.embrace.android.embracesdk.arch.destination.SpanEventData +import io.embrace.android.embracesdk.arch.schema.AppTerminationCause import io.embrace.android.embracesdk.internal.spans.CurrentSessionSpan -import io.embrace.android.embracesdk.internal.spans.EmbraceAttributes import io.embrace.android.embracesdk.internal.spans.EmbraceSpanData import io.embrace.android.embracesdk.spans.EmbraceSpan @@ -31,7 +31,7 @@ internal class FakeCurrentSessionSpan : CurrentSessionSpan { return true } - override fun endSession(appTerminationCause: EmbraceAttributes.AppTerminationCause?): List { + override fun endSession(appTerminationCause: AppTerminationCause?): List { return emptyList() } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImplTests.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImplTests.kt index 935a9505e5..43ba4b4634 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImplTests.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImplTests.kt @@ -2,6 +2,7 @@ package io.embrace.android.embracesdk.internal.spans import io.embrace.android.embracesdk.arch.destination.SpanAttributeData import io.embrace.android.embracesdk.arch.destination.SpanEventData +import io.embrace.android.embracesdk.arch.schema.AppTerminationCause import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.arch.schema.SchemaType import io.embrace.android.embracesdk.fakes.FakeClock @@ -131,11 +132,12 @@ internal class CurrentSessionSpanImplTests { @Test fun `flushing with app termination and termination reason flushes session span with right termination type`() { - EmbraceAttributes.AppTerminationCause.values().forEach { + AppTerminationCause::class.sealedSubclasses.forEach { + val cause = checkNotNull(it.objectInstance) val module = FakeInitModule(clock = clock) val sessionSpan = module.openTelemetryModule.currentSessionSpan module.openTelemetryModule.spanService.initializeService(clock.now()) - val flushedSpans = sessionSpan.endSession(it) + val flushedSpans = sessionSpan.endSession(cause) assertEquals(1, flushedSpans.size) val lastFlushedSpan = flushedSpans[0] @@ -144,7 +146,7 @@ internal class CurrentSessionSpanImplTests { assertEquals(EmbType.Ux.Session.description, attributes[EmbType.Ux.Session.attributeName()]) assertEquals(StatusCode.OK, status) assertFalse(isKey()) - assertEquals(it.name, attributes[it.keyName()]) + assertEquals(cause.attributeValue, attributes[cause.otelAttributeName()]) } assertEquals(0, module.openTelemetryModule.spanSink.completedSpans().size) diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt index bd8c609cb1..cf186e72cf 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt @@ -320,7 +320,13 @@ internal class EmbraceTracerTest { currentSpan.attributes[EmbType.Performance.attributeName()] ) assertEquals(if (traceRoot) "true" else null, currentSpan.attributes["emb.key"]) - assertEquals(errorCode?.name, currentSpan.attributes[errorCode?.keyName()]) + errorCode?.run { + val errorCodeAttribute = fromErrorCode() + assertEquals( + errorCodeAttribute.attributeValue, + currentSpan.attributes[errorCodeAttribute.otelAttributeName()] + ) + } assertFalse(currentSpan.isPrivate()) return currentSpan } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt index 9b0d4afe58..f790ed4cd6 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt @@ -282,7 +282,13 @@ internal class InternalTracerTest { currentSpan.attributes[EmbType.Performance.attributeName()] ) assertEquals(if (traceRoot) "true" else null, currentSpan.attributes["emb.key"]) - assertEquals(errorCode?.name, currentSpan.attributes[errorCode?.keyName()]) + errorCode?.run { + val errorCodeAttribute = fromErrorCode() + assertEquals( + errorCodeAttribute.attributeValue, + currentSpan.attributes[errorCodeAttribute.otelAttributeName()] + ) + } assertFalse(currentSpan.isPrivate()) return currentSpan } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt index 6addae46c5..e3c5facb15 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt @@ -1,7 +1,9 @@ package io.embrace.android.embracesdk.internal.spans import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.embrace.android.embracesdk.arch.schema.AppTerminationCause import io.embrace.android.embracesdk.arch.schema.EmbType +import io.embrace.android.embracesdk.arch.schema.ErrorCodeAttribute import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fixtures.MAX_LENGTH_SPAN_NAME @@ -292,17 +294,21 @@ internal class SpanServiceImplTest { @Test fun `record spans with different ending error codes `() { - ErrorCode.values().forEach { + ErrorCode.values().forEach { errorCode -> assertTrue( spansService.recordCompletedSpan( - name = "test${it.name}", + name = "test${errorCode.name}", startTimeMs = 0, endTimeMs = 1, - errorCode = it + errorCode = errorCode ) ) - with(verifyAndReturnSoleCompletedSpan("emb-test${it.name}")) { - assertEquals(it.name, attributes[it.keyName()]) + with(verifyAndReturnSoleCompletedSpan("emb-test${errorCode.name}")) { + val errorCodeAttribute = checkNotNull(errorCode.fromErrorCode()) + assertEquals( + errorCodeAttribute.attributeValue, + attributes[errorCodeAttribute.otelAttributeName()] + ) } spanSink.flushSpans() } @@ -322,7 +328,7 @@ internal class SpanServiceImplTest { @Test fun `cannot record completed span if there is not current session span`() { currentSessionSpan.endSession( - appTerminationCause = EmbraceAttributes.AppTerminationCause.USER_TERMINATION + appTerminationCause = AppTerminationCause.UserTermination ) assertFalse( spansService.recordCompletedSpan( @@ -407,8 +413,8 @@ internal class SpanServiceImplTest { with(verifyAndReturnSoleCompletedSpan("emb-test-span")) { assertEquals( - ErrorCode.FAILURE.name, - attributes[ErrorCode.FAILURE.keyName()] + ErrorCodeAttribute.Failure.attributeValue, + attributes[ErrorCodeAttribute.Failure.otelAttributeName()] ) } } @@ -416,7 +422,7 @@ internal class SpanServiceImplTest { @Test fun `recording span as lambda with no current active session will run code but not log span`() { currentSessionSpan.endSession( - appTerminationCause = EmbraceAttributes.AppTerminationCause.USER_TERMINATION + appTerminationCause = AppTerminationCause.UserTermination ) var executed = false spansService.recordSpan(name = "test-span") { @@ -429,7 +435,7 @@ internal class SpanServiceImplTest { @Test fun `after ending session with app termination, spans cannot be recorded`() { - currentSessionSpan.endSession(EmbraceAttributes.AppTerminationCause.USER_TERMINATION) + currentSessionSpan.endSession(AppTerminationCause.UserTermination) spansService.recordSpan("test-span") { // do thing } From c5267871c91ff08a2c735507511c421db99c0e23 Mon Sep 17 00:00:00 2001 From: bidetofevil Date: Fri, 8 Mar 2024 13:59:05 -0800 Subject: [PATCH 10/14] Port EmbType and TelemetryType over to EmbraceAttribute --- .../arch/destination/LogEventData.kt | 2 +- .../arch/destination/SpanEventData.kt | 2 +- .../arch/destination/StartSpanData.kt | 2 +- .../android/embracesdk/arch/schema/EmbType.kt | 34 ++++++++----------- .../internal/spans/EmbraceExtensions.kt | 2 +- .../embracesdk/internal/spans/SpanService.kt | 8 ++--- .../embracesdk/arch/SpanDataSourceKtTest.kt | 3 +- .../capture/aei/AeiDataSourceImplTest.kt | 3 +- .../crumbs/CustomBreadcrumbDataSourceTest.kt | 3 +- .../FragmentBreadcrumbDataSourceTest.kt | 8 ++--- .../capture/startup/StartupServiceImplTest.kt | 4 +-- .../embracesdk/fakes/FakeEmbraceSpan.kt | 2 +- .../embracesdk/fixtures/SpansTestFixtures.kt | 6 +++- .../internal/logs/EmbraceLogServiceTest.kt | 7 ++-- .../spans/CurrentSessionSpanImplTests.kt | 10 ++++-- .../internal/spans/EmbraceSpanServiceTest.kt | 6 ++-- .../internal/spans/EmbraceTracerTest.kt | 4 +-- .../internal/spans/InternalTracerTest.kt | 10 +++--- .../internal/spans/SpanServiceImplTest.kt | 26 ++++++++------ 19 files changed, 79 insertions(+), 63 deletions(-) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/LogEventData.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/LogEventData.kt index 249ba4cde0..bea879e860 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/LogEventData.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/LogEventData.kt @@ -17,5 +17,5 @@ internal class LogEventData( val severity: Severity, val message: String ) { - val attributes = schemaType.attrs.plus(Pair("emb.type", schemaType.telemetryType.description)) + val attributes = schemaType.attrs.plus(Pair(schemaType.telemetryType.otelAttributeName(), schemaType.telemetryType.attributeValue)) } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/SpanEventData.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/SpanEventData.kt index 4623d03643..72141b8ac5 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/SpanEventData.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/SpanEventData.kt @@ -13,5 +13,5 @@ internal class SpanEventData( val schemaType: SchemaType, val spanStartTimeMs: Long ) { - val attributes = schemaType.attrs.plus(Pair("emb.type", schemaType.telemetryType.description)) + val attributes = schemaType.attrs.plus(Pair(schemaType.telemetryType.otelAttributeName(), schemaType.telemetryType.attributeValue)) } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/StartSpanData.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/StartSpanData.kt index 2570b4730b..ddc32a3ba8 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/StartSpanData.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/StartSpanData.kt @@ -13,5 +13,5 @@ internal class StartSpanData( val schemaType: SchemaType, val spanStartTimeMs: Long, ) { - val attributes = schemaType.attrs.plus(Pair("emb.type", schemaType.telemetryType.description)) + val attributes = schemaType.attrs.plus(Pair(schemaType.telemetryType.otelAttributeName(), schemaType.telemetryType.attributeValue)) } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/EmbType.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/EmbType.kt index be3bc96dde..82385440b7 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/EmbType.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/EmbType.kt @@ -1,36 +1,39 @@ package io.embrace.android.embracesdk.arch.schema -import io.embrace.android.embracesdk.internal.spans.toEmbraceAttributeName - -internal sealed class EmbType { +internal sealed class EmbType(type: String, subtype: String?) : TelemetryType { + override val attributeName: String = "type" + override val attributeValue = type + (subtype?.run { ".$this" } ?: "") /** * Keys that track how fast a time interval is. Only applies to spans. */ - internal object Performance : TelemetryType { - override val description: String = "performance" + internal sealed class Performance(subtype: String?) : EmbType("performance", subtype) { + + internal object Default : Performance(null) + + internal object Network : Performance("network") } /** * Keys that track a point in time & is visual in nature. Applies to spans, logs, and span events. */ - internal sealed class Ux(subtype: String) : TelemetryType { + internal sealed class Ux(subtype: String) : EmbType("ux", subtype) { + internal object Session : Ux("session") internal object View : Ux("view") - - override val description = "ux.$subtype" } /** * Keys that track a point in time that is not visual in nature. Applies to spans, logs, and span events. */ - internal sealed class System(subtype: String) : TelemetryType { + internal sealed class System(subtype: String) : EmbType("system", subtype) { + internal object Breadcrumb : System("breadcrumb") + internal object Log : System("log") - internal object Exit : System("exit") - override val description = "system.$subtype" + internal object Exit : System("exit") } } @@ -39,11 +42,4 @@ internal sealed class EmbType { * a visual event around a UI element. ux is the type, and view is the subtype. This tells the * backend that it can assume the data in the event follows a particular schema. */ -internal interface TelemetryType { - val description: String - - /** - * Return the key name used by this attribute when is used inside of OpenTelemetry objects - */ - fun attributeName(): String = "type".toEmbraceAttributeName() -} +internal interface TelemetryType : EmbraceAttribute diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt index 1fdcfa35ae..f415622fe6 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt @@ -86,7 +86,7 @@ internal fun createRootSpanBuilder( * Sets and returns the [TelemetryType] attribute for the given [SpanBuilder] */ internal fun SpanBuilder.setType(value: TelemetryType): SpanBuilder { - setAttribute(value.attributeName(), value.description) + setAttribute(value.otelAttributeName(), value.attributeValue) return this } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/SpanService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/SpanService.kt index 1ffb508166..ff11096c51 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/SpanService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/SpanService.kt @@ -18,7 +18,7 @@ internal interface SpanService : Initializable { fun createSpan( name: String, parent: EmbraceSpan? = null, - type: TelemetryType = EmbType.Performance, + type: TelemetryType = EmbType.Performance.Default, internal: Boolean = true ): EmbraceSpan? @@ -29,7 +29,7 @@ internal interface SpanService : Initializable { name: String, parent: EmbraceSpan? = null, startTimeMs: Long? = null, - type: TelemetryType = EmbType.Performance, + type: TelemetryType = EmbType.Performance.Default, internal: Boolean = true ): EmbraceSpan? { createSpan( @@ -53,7 +53,7 @@ internal interface SpanService : Initializable { fun recordSpan( name: String, parent: EmbraceSpan? = null, - type: TelemetryType = EmbType.Performance, + type: TelemetryType = EmbType.Performance.Default, internal: Boolean = true, attributes: Map = emptyMap(), events: List = emptyList(), @@ -69,7 +69,7 @@ internal interface SpanService : Initializable { startTimeMs: Long, endTimeMs: Long, parent: EmbraceSpan? = null, - type: TelemetryType = EmbType.Performance, + type: TelemetryType = EmbType.Performance.Default, internal: Boolean = true, attributes: Map = emptyMap(), events: List = emptyList(), diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/arch/SpanDataSourceKtTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/arch/SpanDataSourceKtTest.kt index 99f8ea2436..bf6ccde1db 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/arch/SpanDataSourceKtTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/arch/SpanDataSourceKtTest.kt @@ -2,6 +2,7 @@ package io.embrace.android.embracesdk.arch import io.embrace.android.embracesdk.arch.datasource.startSpanCapture import io.embrace.android.embracesdk.arch.destination.StartSpanData +import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.arch.schema.SchemaType import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.injection.FakeInitModule @@ -25,7 +26,7 @@ internal class SpanDataSourceKtTest { SchemaType.ViewBreadcrumb("my-view"), 1500000000000 ) - assertEquals("ux.view", data.attributes["emb.type"]) + assertEquals(EmbType.Ux.View.attributeValue, data.attributes[EmbType.Ux.View.otelAttributeName()]) assertEquals("my-view", data.attributes["view.name"]) val span = service.startSpanCapture("") { data } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/aei/AeiDataSourceImplTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/aei/AeiDataSourceImplTest.kt index 30711d1e04..43a7ad6285 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/aei/AeiDataSourceImplTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/aei/AeiDataSourceImplTest.kt @@ -4,6 +4,7 @@ import android.app.ActivityManager import android.app.ApplicationExitInfo import com.google.common.util.concurrent.MoreExecutors import io.embrace.android.embracesdk.Severity +import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.config.remote.AppExitInfoConfig import io.embrace.android.embracesdk.config.remote.RemoteConfig import io.embrace.android.embracesdk.fakes.FakeConfigService @@ -360,7 +361,7 @@ internal class AeiDataSourceImplTest { val logEventData = logWriter.logEvents.single() assertEquals("aei-record", logEventData.schemaType.name) assertEquals(Severity.INFO, logEventData.severity) - assertEquals("system.exit", logEventData.attributes["emb.type"]) + assertEquals(EmbType.System.Exit.attributeValue, logEventData.attributes[EmbType.System.Exit.otelAttributeName()]) return logEventData.attributes } } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/CustomBreadcrumbDataSourceTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/CustomBreadcrumbDataSourceTest.kt index 665151b19f..a44a900fb9 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/CustomBreadcrumbDataSourceTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/CustomBreadcrumbDataSourceTest.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.capture.crumbs +import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeCurrentSessionSpan import io.embrace.android.embracesdk.internal.clock.millisToNanos @@ -35,7 +36,7 @@ internal class CustomBreadcrumbDataSourceTest { assertEquals(15000000000.millisToNanos(), spanStartTimeMs) assertEquals( mapOf( - "emb.type" to "system.breadcrumb", + EmbType.System.Breadcrumb.otelAttributeName() to EmbType.System.Breadcrumb.attributeValue, "message" to "Hello, world!" ), attributes diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/FragmentBreadcrumbDataSourceTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/FragmentBreadcrumbDataSourceTest.kt index 6ffa0ae72b..3c919c3dc4 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/FragmentBreadcrumbDataSourceTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/FragmentBreadcrumbDataSourceTest.kt @@ -35,12 +35,12 @@ internal class FragmentBreadcrumbDataSourceTest { val span = spanService.createdSpans.single() assertEquals("view-breadcrumb", span.name) - assertEquals(EmbType.Performance, span.type) + assertEquals(EmbType.Performance.Default, span.type) assertTrue(span.isRecording) assertEquals( mapOf( "view.name" to "my_fragment", - "emb.type" to "ux.view" + EmbType.Ux.View.otelAttributeName() to EmbType.Ux.View.attributeValue, ), span.attributes ) @@ -54,12 +54,12 @@ internal class FragmentBreadcrumbDataSourceTest { val span = spanService.createdSpans.single() assertEquals("view-breadcrumb", span.name) - assertEquals(EmbType.Performance, span.type) + assertEquals(EmbType.Performance.Default, span.type) assertFalse(span.isRecording) assertEquals( mapOf( "view.name" to "my_fragment", - "emb.type" to "ux.view" + EmbType.Ux.View.otelAttributeName() to EmbType.Ux.View.attributeValue, ), span.attributes ) diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/startup/StartupServiceImplTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/startup/StartupServiceImplTest.kt index b3a2170c25..0ff53b0957 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/startup/StartupServiceImplTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/startup/StartupServiceImplTest.kt @@ -51,8 +51,8 @@ internal class StartupServiceImplTest { assertEquals(startTimeMillis, startTimeNanos.nanosToMillis()) assertEquals(endTimeMillis, endTimeNanos.nanosToMillis()) assertEquals( - EmbType.Performance.description, - attributes[EmbType.Performance.attributeName()] + EmbType.Performance.Default.attributeValue, + attributes[EmbType.Performance.Default.otelAttributeName()] ) assertTrue(isPrivate()) assertEquals(StatusCode.OK, status) diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeEmbraceSpan.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeEmbraceSpan.kt index 7a914b9feb..8ec0e242c1 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeEmbraceSpan.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeEmbraceSpan.kt @@ -11,7 +11,7 @@ import io.opentelemetry.sdk.trace.IdGenerator internal class FakeEmbraceSpan( override val parent: EmbraceSpan?, val name: String? = null, - val type: TelemetryType = EmbType.Performance, + val type: TelemetryType = EmbType.Performance.Default, val internal: Boolean = true ) : EmbraceSpan { diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fixtures/SpansTestFixtures.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fixtures/SpansTestFixtures.kt index 3abd470a58..5d817e71cc 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fixtures/SpansTestFixtures.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fixtures/SpansTestFixtures.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.fixtures +import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.internal.spans.EmbraceSpanData import io.embrace.android.embracesdk.internal.spans.EmbraceSpanImpl import io.embrace.android.embracesdk.spans.EmbraceSpanEvent @@ -30,7 +31,10 @@ internal val testSpan = EmbraceSpanData( ) ) ), - attributes = mapOf(Pair("emb.sequence_id", "3"), Pair("emb.type", "performance")) + attributes = mapOf( + Pair("emb.sequence_id", "3"), + EmbType.Performance.Default.otelAttributeName() to EmbType.Performance.Default.attributeValue, + ) ) private fun createMapOfSize(size: Int): Map { diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/logs/EmbraceLogServiceTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/logs/EmbraceLogServiceTest.kt index b8043585f0..c3644c0a3f 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/logs/EmbraceLogServiceTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/logs/EmbraceLogServiceTest.kt @@ -4,6 +4,7 @@ import com.google.common.util.concurrent.MoreExecutors import io.embrace.android.embracesdk.Embrace.AppFramework import io.embrace.android.embracesdk.LogExceptionType import io.embrace.android.embracesdk.Severity +import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.config.ConfigService import io.embrace.android.embracesdk.config.remote.LogRemoteConfig import io.embrace.android.embracesdk.config.remote.RemoteConfig @@ -103,7 +104,7 @@ internal class EmbraceLogServiceTest { assertNotNull(third.attributes["emb.log_id"]) assertEquals("session-123", third.attributes["emb.session_id"]) assertNull(third.attributes["emb.exception_type"]) - assertEquals("system.log", third.attributes["emb.type"]) + assertEquals(EmbType.System.Log.attributeValue, third.attributes[EmbType.System.Log.otelAttributeName()]) } @Test @@ -132,11 +133,11 @@ internal class EmbraceLogServiceTest { assertNotNull(log.attributes["emb.log_id"]) assertEquals("session-123", log.attributes["emb.session_id"]) assertEquals("none", log.attributes["emb.exception_type"]) - assertEquals("system.log", log.attributes["emb.type"]) + assertEquals(EmbType.System.Log.attributeValue, log.attributes[EmbType.System.Log.otelAttributeName()]) } @Test - fun `Embrace properties can not be overriden by custom properties`() { + fun `Embrace properties can not be overridden by custom properties`() { val logService = getLogService() val props = mapOf("emb.session_id" to "session-456") logService.log("Hello world", Severity.INFO, props) diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImplTests.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImplTests.kt index 43ba4b4634..18efa8eee1 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImplTests.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImplTests.kt @@ -143,7 +143,7 @@ internal class CurrentSessionSpanImplTests { val lastFlushedSpan = flushedSpans[0] with(lastFlushedSpan) { assertEquals("emb-session", name) - assertEquals(EmbType.Ux.Session.description, attributes[EmbType.Ux.Session.attributeName()]) + assertEquals(EmbType.Ux.Session.attributeValue, attributes[EmbType.Ux.Session.otelAttributeName()]) assertEquals(StatusCode.OK, status) assertFalse(isKey()) assertEquals(cause.attributeValue, attributes[cause.otelAttributeName()]) @@ -191,7 +191,13 @@ internal class CurrentSessionSpanImplTests { val testEvent = span.events.single() assertEquals("custom-breadcrumb", testEvent.name) assertEquals(1000, testEvent.timestampNanos.nanosToMillis()) - assertEquals(mapOf("emb.type" to "system.breadcrumb", "message" to "test-event"), testEvent.attributes) + assertEquals( + mapOf( + EmbType.System.Breadcrumb.otelAttributeName() to EmbType.System.Breadcrumb.attributeValue, + "message" to "test-event" + ), + testEvent.attributes + ) } @Test diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanServiceTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanServiceTest.kt index 726dc1b41c..6328570f06 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanServiceTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanServiceTest.kt @@ -59,7 +59,7 @@ internal class EmbraceSpanServiceTest { val expectedName = "test-span" val expectedStartTimeMs = clock.now() val expectedEndTimeMs = expectedStartTimeMs + 100L - val expectedType = EmbType.Performance + val expectedType = EmbType.Performance.Default val expectedAttributes = mapOf( Pair("attribute1", "value1"), Pair("attribute2", "value2") @@ -88,8 +88,8 @@ internal class EmbraceSpanServiceTest { assertEquals(expectedStartTimeMs, startTimeNanos.nanosToMillis()) assertEquals(expectedEndTimeMs, endTimeNanos.nanosToMillis()) assertEquals( - EmbType.Performance.description, - attributes[EmbType.Performance.attributeName()] + EmbType.Performance.Default.attributeValue, + attributes[EmbType.Performance.Default.otelAttributeName()] ) expectedAttributes.forEach { assertEquals(it.value, attributes[it.key]) diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt index cf186e72cf..14318f3d78 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt @@ -316,8 +316,8 @@ internal class EmbraceTracerTest { val currentSpan = currentSpans[0] assertEquals(name, currentSpan.name) assertEquals( - EmbType.Performance.description, - currentSpan.attributes[EmbType.Performance.attributeName()] + EmbType.Performance.Default.attributeValue, + currentSpan.attributes[EmbType.Performance.Default.otelAttributeName()] ) assertEquals(if (traceRoot) "true" else null, currentSpan.attributes["emb.key"]) errorCode?.run { diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt index f790ed4cd6..97331f5103 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt @@ -210,7 +210,7 @@ internal class InternalTracerTest { val expectedName = "test-span" val expectedStartTimeMs = clock.now() val expectedEndTimeMs = expectedStartTimeMs + 100L - val expectedType = EmbType.Performance + val expectedType = EmbType.Performance.Default val expectedAttributes = mapOf( Pair("attribute1", "value1"), Pair("attribute2", "value2") @@ -235,8 +235,8 @@ internal class InternalTracerTest { assertEquals(expectedStartTimeMs, startTimeNanos.nanosToMillis()) assertEquals(expectedEndTimeMs, endTimeNanos.nanosToMillis()) assertEquals( - expectedType.description, - attributes[expectedType.attributeName()] + expectedType.attributeValue, + attributes[expectedType.otelAttributeName()] ) assertEquals("true", attributes["emb.key"]) expectedAttributes.forEach { @@ -278,8 +278,8 @@ internal class InternalTracerTest { val currentSpan = currentSpans[0] assertEquals(name, currentSpan.name) assertEquals( - EmbType.Performance.description, - currentSpan.attributes[EmbType.Performance.attributeName()] + EmbType.Performance.Default.attributeValue, + currentSpan.attributes[EmbType.Performance.Default.otelAttributeName()] ) assertEquals(if (traceRoot) "true" else null, currentSpan.attributes["emb.key"]) errorCode?.run { diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt index e3c5facb15..0199843614 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt @@ -57,8 +57,8 @@ internal class SpanServiceImplTest { with(verifyAndReturnSoleCompletedSpan("emb-test-span")) { assertEquals(SpanId.getInvalid(), parentSpanId) assertEquals( - EmbType.Performance.description, - attributes[EmbType.Performance.attributeName()] + EmbType.Performance.Default.attributeValue, + attributes[EmbType.Performance.Default.otelAttributeName()] ) assertTrue(isKey()) } @@ -78,7 +78,7 @@ internal class SpanServiceImplTest { val embraceSpan = checkNotNull( spansService.createSpan( name = "test-span", - type = EmbType.Performance + type = EmbType.Performance.Default ) ) assertTrue(embraceSpan.start()) @@ -86,8 +86,8 @@ internal class SpanServiceImplTest { with(verifyAndReturnSoleCompletedSpan("emb-test-span")) { assertEquals(SpanId.getInvalid(), parentSpanId) assertEquals( - EmbType.Performance.description, - attributes[EmbType.Performance.attributeName()] + EmbType.Performance.Default.attributeValue, + attributes[EmbType.Performance.Default.otelAttributeName()] ) assertTrue(isKey()) } @@ -182,7 +182,10 @@ internal class SpanServiceImplTest { with(completedSpans[0]) { assertTrue(isPrivate()) assertFalse(isKey()) - assertEquals(EmbType.Ux.View.description, attributes[EmbType.Ux.View.attributeName()]) + assertEquals( + EmbType.Ux.View.attributeValue, + attributes[EmbType.Ux.View.otelAttributeName()] + ) assertEquals(childStartTimeMs, startTimeNanos.nanosToMillis()) assertEquals(childSpanEndTimeMs, endTimeNanos.nanosToMillis()) } @@ -193,7 +196,7 @@ internal class SpanServiceImplTest { val expectedName = "test-span" val expectedStartTimeMs = clock.now() val expectedEndTimeMs = expectedStartTimeMs + 100L - val expectedType = EmbType.Performance + val expectedType = EmbType.Performance.Default val expectedAttributes = mapOf( Pair("attribute1", "value1"), Pair("attribute2", "value2") @@ -216,8 +219,8 @@ internal class SpanServiceImplTest { assertEquals(expectedStartTimeMs, startTimeNanos.nanosToMillis()) assertEquals(expectedEndTimeMs, endTimeNanos.nanosToMillis()) assertEquals( - EmbType.Performance.description, - attributes[EmbType.Performance.attributeName()] + EmbType.Performance.Default.attributeValue, + attributes[EmbType.Performance.Default.otelAttributeName()] ) assertEquals(SpanId.getInvalid(), parentSpanId) assertTrue(isKey()) @@ -349,7 +352,10 @@ internal class SpanServiceImplTest { assertEquals(returnThis, lambdaReturn) with(verifyAndReturnSoleCompletedSpan("emb-test-span")) { assertEquals(SpanId.getInvalid(), parentSpanId) - assertEquals(EmbType.Performance.description, attributes[EmbType.Performance.attributeName()]) + assertEquals( + EmbType.Performance.Default.attributeValue, + attributes[EmbType.Performance.Default.otelAttributeName()] + ) assertTrue(isKey()) assertTrue(isPrivate()) } From 587bfd3388a15317c04e63c48e3dcc024c45a004 Mon Sep 17 00:00:00 2001 From: bidetofevil Date: Fri, 8 Mar 2024 22:56:30 -0800 Subject: [PATCH 11/14] Add in extension functions useful when working with EmbraceAttributes --- .../embracesdk/assertions/SpanAssertions.kt | 21 +++-- .../embracesdk/testcases/TracingApiTest.kt | 1 + .../arch/destination/LogEventData.kt | 2 +- .../arch/destination/SpanEventData.kt | 2 +- .../arch/destination/StartSpanData.kt | 2 +- .../arch/schema/EmbraceAttribute.kt | 8 ++ .../android/embracesdk/arch/schema/KeySpan.kt | 9 +++ .../internal/spans/CurrentSessionSpanImpl.kt | 6 +- .../internal/spans/EmbraceExtensions.kt | 29 +++---- .../internal/spans/EmbraceSpanImpl.kt | 6 ++ .../arch/EmbraceAttributeExtensions.kt | 80 +++++++++++++++++++ .../embracesdk/arch/SpanDataSourceKtTest.kt | 2 +- .../capture/aei/AeiDataSourceImplTest.kt | 3 +- .../crumbs/CustomBreadcrumbDataSourceTest.kt | 2 +- .../FragmentBreadcrumbDataSourceTest.kt | 4 +- .../capture/startup/StartupServiceImplTest.kt | 7 +- .../embracesdk/fixtures/SpansTestFixtures.kt | 2 +- .../internal/logs/EmbraceLogServiceTest.kt | 5 +- .../spans/CurrentSessionSpanImplTests.kt | 15 ++-- .../internal/spans/EmbraceSpanServiceTest.kt | 6 +- .../internal/spans/EmbraceTracerTest.kt | 29 ++++--- .../internal/spans/InternalTracerTest.kt | 35 ++++---- .../internal/spans/SpanServiceImplTest.kt | 60 +++++--------- 23 files changed, 211 insertions(+), 125 deletions(-) create mode 100644 embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/KeySpan.kt create mode 100644 embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/arch/EmbraceAttributeExtensions.kt diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/assertions/SpanAssertions.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/assertions/SpanAssertions.kt index 8a6b8ce2bc..b5acb048af 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/assertions/SpanAssertions.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/assertions/SpanAssertions.kt @@ -1,8 +1,11 @@ package io.embrace.android.embracesdk.assertions +import io.embrace.android.embracesdk.arch.assertError +import io.embrace.android.embracesdk.arch.assertIsKeySpan +import io.embrace.android.embracesdk.arch.assertNotKeySpan +import io.embrace.android.embracesdk.arch.assertSuccessful import io.embrace.android.embracesdk.internal.clock.nanosToMillis import io.embrace.android.embracesdk.internal.spans.EmbraceSpanData -import io.embrace.android.embracesdk.internal.spans.isKey import io.embrace.android.embracesdk.internal.spans.isPrivate import io.embrace.android.embracesdk.spans.EmbraceSpanEvent import io.embrace.android.embracesdk.spans.ErrorCode @@ -36,18 +39,20 @@ internal fun assertEmbraceSpanData( assertEquals(32, traceId.length) } assertEquals(expectedStatus, status) - errorCode?.run { - val errorCodeAttribute = fromErrorCode() - assertEquals( - errorCodeAttribute.attributeValue, - attributes[errorCodeAttribute.otelAttributeName()] - ) + if (errorCode == null) { + assertSuccessful() + } else { + assertError(errorCode) } expectedCustomAttributes.forEach { entry -> assertEquals(entry.value, attributes[entry.key]) } assertEquals(expectedEvents, events) assertEquals(private, isPrivate()) - assertEquals(key, isKey()) + if (key) { + assertIsKeySpan() + } else { + assertNotKeySpan() + } } } \ No newline at end of file diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/TracingApiTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/TracingApiTest.kt index 31fcc516a2..35e71b7845 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/TracingApiTest.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/TracingApiTest.kt @@ -204,6 +204,7 @@ internal class TracingApiTest { expectedParentId = traceRootSpan.spanId, expectedTraceId = traceRootSpan.traceId, expectedStatus = StatusCode.ERROR, + errorCode = ErrorCode.FAILURE, expectedCustomAttributes = mapOf(Pair("test-attr", "false")), expectedEvents = listOf( checkNotNull( diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/LogEventData.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/LogEventData.kt index bea879e860..96ac269e75 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/LogEventData.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/LogEventData.kt @@ -17,5 +17,5 @@ internal class LogEventData( val severity: Severity, val message: String ) { - val attributes = schemaType.attrs.plus(Pair(schemaType.telemetryType.otelAttributeName(), schemaType.telemetryType.attributeValue)) + val attributes = schemaType.attrs.plus(schemaType.telemetryType.toOTelKeyValuePair()) } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/SpanEventData.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/SpanEventData.kt index 72141b8ac5..7834089bda 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/SpanEventData.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/SpanEventData.kt @@ -13,5 +13,5 @@ internal class SpanEventData( val schemaType: SchemaType, val spanStartTimeMs: Long ) { - val attributes = schemaType.attrs.plus(Pair(schemaType.telemetryType.otelAttributeName(), schemaType.telemetryType.attributeValue)) + val attributes = schemaType.attrs.plus(schemaType.telemetryType.toOTelKeyValuePair()) } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/StartSpanData.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/StartSpanData.kt index ddc32a3ba8..9d53677fbd 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/StartSpanData.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/destination/StartSpanData.kt @@ -13,5 +13,5 @@ internal class StartSpanData( val schemaType: SchemaType, val spanStartTimeMs: Long, ) { - val attributes = schemaType.attrs.plus(Pair(schemaType.telemetryType.otelAttributeName(), schemaType.telemetryType.attributeValue)) + val attributes = schemaType.attrs.plus(schemaType.telemetryType.toOTelKeyValuePair()) } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/EmbraceAttribute.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/EmbraceAttribute.kt index bd8e425ac2..569e6a1fcf 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/EmbraceAttribute.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/EmbraceAttribute.kt @@ -16,5 +16,13 @@ internal interface EmbraceAttribute { */ val attributeValue: String + /** + * Return the appropriate key name for this attribute to use in an OpenTelemetry attribute + */ fun otelAttributeName(): String = attributeName.toEmbraceAttributeName() + + /** + * Return attribute as a key-value pair appropriate to use as an OpenTelemetry attribute + */ + fun toOTelKeyValuePair() = Pair(otelAttributeName(), attributeValue) } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/KeySpan.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/KeySpan.kt new file mode 100644 index 0000000000..5b603ae6f1 --- /dev/null +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/KeySpan.kt @@ -0,0 +1,9 @@ +package io.embrace.android.embracesdk.arch.schema + +/** + * Denotes an important span to be aggregated and displayed as such in the platform. + */ +internal object KeySpan : EmbraceAttribute { + override val attributeName: String = "key" + override val attributeValue: String = "true" +} diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImpl.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImpl.kt index 359fb9b486..734e41d3be 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImpl.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImpl.kt @@ -6,6 +6,7 @@ import io.embrace.android.embracesdk.arch.destination.SpanEventData import io.embrace.android.embracesdk.arch.schema.AppTerminationCause import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.internal.clock.nanosToMillis +import io.embrace.android.embracesdk.internal.spans.EmbraceSpanImpl.Companion.setEmbraceAttribute import io.embrace.android.embracesdk.internal.utils.Provider import io.embrace.android.embracesdk.spans.EmbraceSpan import io.embrace.android.embracesdk.telemetry.TelemetryService @@ -82,10 +83,7 @@ internal class CurrentSessionSpanImpl( spanRepository.clearCompletedSpans() sessionSpan.set(startSessionSpan(clock.now().nanosToMillis())) } else { - endingSessionSpan.addAttribute( - appTerminationCause.otelAttributeName(), - appTerminationCause.attributeValue - ) + endingSessionSpan.setEmbraceAttribute(appTerminationCause) endingSessionSpan.stop() } return spanSink.flushSpans() diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt index f415622fe6..487bb03556 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt @@ -1,5 +1,7 @@ package io.embrace.android.embracesdk.internal.spans +import io.embrace.android.embracesdk.arch.schema.EmbraceAttribute +import io.embrace.android.embracesdk.arch.schema.KeySpan import io.embrace.android.embracesdk.arch.schema.TelemetryType import io.embrace.android.embracesdk.internal.utils.Provider import io.embrace.android.embracesdk.spans.EmbraceSpan @@ -40,11 +42,6 @@ private const val EMBRACE_USAGE_ATTRIBUTE_NAME_PREFIX = "emb.usage." */ private const val SEQUENCE_ID_ATTRIBUTE_NAME = EMBRACE_ATTRIBUTE_NAME_PREFIX + "sequence_id" -/** - * Denotes an important span to be listed in the Spans listing page in the UI. Currently defined as any spans that are the root of a trace - */ -private const val KEY_SPAN_ATTRIBUTE_NAME = EMBRACE_ATTRIBUTE_NAME_PREFIX + "key" - /** * Denotes a private span logged by Embrace for diagnostic purposes and should not be displayed to customers in the dashboard, but * should be shown to Embrace employees. @@ -70,7 +67,7 @@ internal fun createEmbraceSpanBuilder( name: String, type: TelemetryType, internal: Boolean = true -): SpanBuilder = tracer.embraceSpanBuilder(name, internal).setType(type) +): SpanBuilder = tracer.embraceSpanBuilder(name, internal).setEmbraceAttribute(type) /** * Create a [SpanBuilder] that will create a root [Span] with the appropriate custom Embrace attributes @@ -83,10 +80,10 @@ internal fun createRootSpanBuilder( ): SpanBuilder = createEmbraceSpanBuilder(tracer = tracer, name = name, type = type, internal = internal).setNoParent() /** - * Sets and returns the [TelemetryType] attribute for the given [SpanBuilder] + * Sets an [EmbraceAttribute] on the given [SpanBuilder] and return it */ -internal fun SpanBuilder.setType(value: TelemetryType): SpanBuilder { - setAttribute(value.otelAttributeName(), value.attributeValue) +internal fun SpanBuilder.setEmbraceAttribute(embraceAttribute: EmbraceAttribute): SpanBuilder { + setAttribute(embraceAttribute.otelAttributeName(), embraceAttribute.attributeValue) return this } @@ -95,7 +92,7 @@ internal fun SpanBuilder.setType(value: TelemetryType): SpanBuilder { * for any spans that are the root of a trace. */ internal fun SpanBuilder.makeKey(): SpanBuilder { - setAttribute(KEY_SPAN_ATTRIBUTE_NAME, true) + setEmbraceAttribute(KeySpan) return this } @@ -172,6 +169,10 @@ internal fun Span.setSequenceId(id: Long): Span { return this } +internal fun Span.setEmbraceAttribute(embraceAttribute: EmbraceAttribute) { + setAttribute(embraceAttribute.otelAttributeName(), embraceAttribute.attributeValue) +} + /** * Ends the given [Span], and setting the correct properties per the optional [ErrorCode] passed in. If [errorCode] * is not specified, it means the [Span] completed successfully, and no [ErrorCode] will be set. @@ -181,8 +182,7 @@ internal fun Span.endSpan(errorCode: ErrorCode? = null, endTimeMs: Long? = null) setStatus(StatusCode.OK) } else { setStatus(StatusCode.ERROR) - val errorCodeAttribute = errorCode.fromErrorCode() - setAttribute(errorCodeAttribute.otelAttributeName(), errorCodeAttribute.attributeValue) + setEmbraceAttribute(errorCode.fromErrorCode()) } if (endTimeMs != null) { @@ -204,11 +204,6 @@ internal fun AttributesBuilder.fromMap(attributes: Map): Attribu return this } -/** - * Returns true if the Span is a Key Span - */ -internal fun EmbraceSpanData.isKey(): Boolean = attributes[KEY_SPAN_ATTRIBUTE_NAME] == true.toString() - /** * Returns true if the Span is private */ diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImpl.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImpl.kt index e6771e35c0..cd34caf0a0 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImpl.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanImpl.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.internal.spans +import io.embrace.android.embracesdk.arch.schema.EmbraceAttribute import io.embrace.android.embracesdk.internal.clock.normalizeTimestampAsMillis import io.embrace.android.embracesdk.spans.EmbraceSpan import io.embrace.android.embracesdk.spans.EmbraceSpanEvent @@ -144,5 +145,10 @@ internal class EmbraceSpanImpl( internal fun attributeValid(key: String, value: String) = key.length <= MAX_ATTRIBUTE_KEY_LENGTH && value.length <= MAX_ATTRIBUTE_VALUE_LENGTH + + internal fun EmbraceSpan.setEmbraceAttribute(embraceAttribute: EmbraceAttribute): EmbraceSpan { + addAttribute(embraceAttribute.otelAttributeName(), embraceAttribute.attributeValue) + return this + } } } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/arch/EmbraceAttributeExtensions.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/arch/EmbraceAttributeExtensions.kt new file mode 100644 index 0000000000..88c002de80 --- /dev/null +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/arch/EmbraceAttributeExtensions.kt @@ -0,0 +1,80 @@ +package io.embrace.android.embracesdk.arch + +import io.embrace.android.embracesdk.arch.destination.LogEventData +import io.embrace.android.embracesdk.arch.destination.StartSpanData +import io.embrace.android.embracesdk.arch.schema.EmbType +import io.embrace.android.embracesdk.arch.schema.EmbraceAttribute +import io.embrace.android.embracesdk.arch.schema.ErrorCodeAttribute +import io.embrace.android.embracesdk.arch.schema.KeySpan +import io.embrace.android.embracesdk.arch.schema.TelemetryType +import io.embrace.android.embracesdk.internal.spans.EmbraceSpanData +import io.embrace.android.embracesdk.spans.ErrorCode +import io.opentelemetry.api.trace.StatusCode +import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.assertNull +import org.junit.Assert.assertFalse + +/** + * Assert [EmbraceSpanData] is of type [EmbType.Performance.Default] + */ +internal fun EmbraceSpanData.assertIsTypePerformance() = assertIsType(EmbType.Performance.Default) + +/** + * Assert [EmbraceSpanData] is of type [telemetryType] + */ +internal fun EmbraceSpanData.assertIsType(telemetryType: TelemetryType) = assertHasEmbraceAttribute(telemetryType) + +internal fun EmbraceSpanData.assertIsKeySpan() = assertHasEmbraceAttribute(KeySpan) + +internal fun EmbraceSpanData.assertNotKeySpan() = assertDoesNotHaveEmbraceAttribute(KeySpan) + +/** + * Assert [EmbraceSpanData] has the [EmbraceAttribute] defined by [embraceAttribute] + */ +internal fun EmbraceSpanData.assertHasEmbraceAttribute(embraceAttribute: EmbraceAttribute) { + assertEquals(embraceAttribute.attributeValue, attributes[embraceAttribute.otelAttributeName()]) +} + +internal fun EmbraceSpanData.assertDoesNotHaveEmbraceAttribute(embraceAttribute: EmbraceAttribute) { + assertFalse(attributes[embraceAttribute.otelAttributeName()]?.equals(embraceAttribute.attributeValue) ?: false) +} + +/** + * Assert [EmbraceSpanData] has ended with the error defined by [errorCode] + */ +internal fun EmbraceSpanData.assertError(errorCode: ErrorCode) { + assertEquals(StatusCode.ERROR, status) + assertHasEmbraceAttribute(errorCode.fromErrorCode()) +} + +/** + * Assert [EmbraceSpanData] has ended successfully + */ +internal fun EmbraceSpanData.assertSuccessful() { + assertEquals(StatusCode.OK, status) + assertNull(attributes[ErrorCodeAttribute.Failure.otelAttributeName()]) +} + +/** + * Assert [StartSpanData] is of type [telemetryType] + */ +internal fun StartSpanData.assertIsType(telemetryType: TelemetryType) = assertHasEmbraceAttribute(telemetryType) + +/** + * Assert [StartSpanData] has the [EmbraceAttribute] defined by [embraceAttribute] + */ +internal fun StartSpanData.assertHasEmbraceAttribute(embraceAttribute: EmbraceAttribute) { + assertEquals(embraceAttribute.attributeValue, attributes[embraceAttribute.otelAttributeName()]) +} + +/** + * Assert [LogEventData] is of type [telemetryType] + */ +internal fun LogEventData.assertIsType(telemetryType: TelemetryType) = assertHasEmbraceAttribute(telemetryType) + +/** + * Assert [LogEventData] has the [EmbraceAttribute] defined by [embraceAttribute] + */ +internal fun LogEventData.assertHasEmbraceAttribute(embraceAttribute: EmbraceAttribute) { + assertEquals(embraceAttribute.attributeValue, attributes[embraceAttribute.otelAttributeName()]) +} diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/arch/SpanDataSourceKtTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/arch/SpanDataSourceKtTest.kt index bf6ccde1db..3ded981e7d 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/arch/SpanDataSourceKtTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/arch/SpanDataSourceKtTest.kt @@ -26,7 +26,7 @@ internal class SpanDataSourceKtTest { SchemaType.ViewBreadcrumb("my-view"), 1500000000000 ) - assertEquals(EmbType.Ux.View.attributeValue, data.attributes[EmbType.Ux.View.otelAttributeName()]) + data.assertIsType(EmbType.Ux.View) assertEquals("my-view", data.attributes["view.name"]) val span = service.startSpanCapture("") { data } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/aei/AeiDataSourceImplTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/aei/AeiDataSourceImplTest.kt index 43a7ad6285..216f9417b5 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/aei/AeiDataSourceImplTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/aei/AeiDataSourceImplTest.kt @@ -4,6 +4,7 @@ import android.app.ActivityManager import android.app.ApplicationExitInfo import com.google.common.util.concurrent.MoreExecutors import io.embrace.android.embracesdk.Severity +import io.embrace.android.embracesdk.arch.assertIsType import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.config.remote.AppExitInfoConfig import io.embrace.android.embracesdk.config.remote.RemoteConfig @@ -361,7 +362,7 @@ internal class AeiDataSourceImplTest { val logEventData = logWriter.logEvents.single() assertEquals("aei-record", logEventData.schemaType.name) assertEquals(Severity.INFO, logEventData.severity) - assertEquals(EmbType.System.Exit.attributeValue, logEventData.attributes[EmbType.System.Exit.otelAttributeName()]) + logEventData.assertIsType(EmbType.System.Exit) return logEventData.attributes } } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/CustomBreadcrumbDataSourceTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/CustomBreadcrumbDataSourceTest.kt index a44a900fb9..4bc4f78f8a 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/CustomBreadcrumbDataSourceTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/CustomBreadcrumbDataSourceTest.kt @@ -36,7 +36,7 @@ internal class CustomBreadcrumbDataSourceTest { assertEquals(15000000000.millisToNanos(), spanStartTimeMs) assertEquals( mapOf( - EmbType.System.Breadcrumb.otelAttributeName() to EmbType.System.Breadcrumb.attributeValue, + EmbType.System.Breadcrumb.toOTelKeyValuePair(), "message" to "Hello, world!" ), attributes diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/FragmentBreadcrumbDataSourceTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/FragmentBreadcrumbDataSourceTest.kt index 3c919c3dc4..7e64e788ed 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/FragmentBreadcrumbDataSourceTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/crumbs/FragmentBreadcrumbDataSourceTest.kt @@ -40,7 +40,7 @@ internal class FragmentBreadcrumbDataSourceTest { assertEquals( mapOf( "view.name" to "my_fragment", - EmbType.Ux.View.otelAttributeName() to EmbType.Ux.View.attributeValue, + EmbType.Ux.View.toOTelKeyValuePair(), ), span.attributes ) @@ -59,7 +59,7 @@ internal class FragmentBreadcrumbDataSourceTest { assertEquals( mapOf( "view.name" to "my_fragment", - EmbType.Ux.View.otelAttributeName() to EmbType.Ux.View.attributeValue, + EmbType.Ux.View.toOTelKeyValuePair() ), span.attributes ) diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/startup/StartupServiceImplTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/startup/StartupServiceImplTest.kt index 0ff53b0957..acff21a74d 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/startup/StartupServiceImplTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/startup/StartupServiceImplTest.kt @@ -1,6 +1,6 @@ package io.embrace.android.embracesdk.capture.startup -import io.embrace.android.embracesdk.arch.schema.EmbType +import io.embrace.android.embracesdk.arch.assertIsTypePerformance import io.embrace.android.embracesdk.concurrency.BlockableExecutorService import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.injection.FakeInitModule @@ -50,10 +50,7 @@ internal class StartupServiceImplTest { assertEquals(SpanId.getInvalid(), parentSpanId) assertEquals(startTimeMillis, startTimeNanos.nanosToMillis()) assertEquals(endTimeMillis, endTimeNanos.nanosToMillis()) - assertEquals( - EmbType.Performance.Default.attributeValue, - attributes[EmbType.Performance.Default.otelAttributeName()] - ) + assertIsTypePerformance() assertTrue(isPrivate()) assertEquals(StatusCode.OK, status) } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fixtures/SpansTestFixtures.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fixtures/SpansTestFixtures.kt index 5d817e71cc..5e8df060ed 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fixtures/SpansTestFixtures.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fixtures/SpansTestFixtures.kt @@ -33,7 +33,7 @@ internal val testSpan = EmbraceSpanData( ), attributes = mapOf( Pair("emb.sequence_id", "3"), - EmbType.Performance.Default.otelAttributeName() to EmbType.Performance.Default.attributeValue, + EmbType.Performance.Default.toOTelKeyValuePair(), ) ) diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/logs/EmbraceLogServiceTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/logs/EmbraceLogServiceTest.kt index c3644c0a3f..b5411da97b 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/logs/EmbraceLogServiceTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/logs/EmbraceLogServiceTest.kt @@ -4,6 +4,7 @@ import com.google.common.util.concurrent.MoreExecutors import io.embrace.android.embracesdk.Embrace.AppFramework import io.embrace.android.embracesdk.LogExceptionType import io.embrace.android.embracesdk.Severity +import io.embrace.android.embracesdk.arch.assertIsType import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.config.ConfigService import io.embrace.android.embracesdk.config.remote.LogRemoteConfig @@ -104,7 +105,7 @@ internal class EmbraceLogServiceTest { assertNotNull(third.attributes["emb.log_id"]) assertEquals("session-123", third.attributes["emb.session_id"]) assertNull(third.attributes["emb.exception_type"]) - assertEquals(EmbType.System.Log.attributeValue, third.attributes[EmbType.System.Log.otelAttributeName()]) + third.assertIsType(EmbType.System.Log) } @Test @@ -133,7 +134,7 @@ internal class EmbraceLogServiceTest { assertNotNull(log.attributes["emb.log_id"]) assertEquals("session-123", log.attributes["emb.session_id"]) assertEquals("none", log.attributes["emb.exception_type"]) - assertEquals(EmbType.System.Log.attributeValue, log.attributes[EmbType.System.Log.otelAttributeName()]) + log.assertIsType(EmbType.System.Log) } @Test diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImplTests.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImplTests.kt index 18efa8eee1..29b81331d3 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImplTests.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/CurrentSessionSpanImplTests.kt @@ -1,5 +1,9 @@ package io.embrace.android.embracesdk.internal.spans +import io.embrace.android.embracesdk.arch.assertHasEmbraceAttribute +import io.embrace.android.embracesdk.arch.assertIsType +import io.embrace.android.embracesdk.arch.assertNotKeySpan +import io.embrace.android.embracesdk.arch.assertSuccessful import io.embrace.android.embracesdk.arch.destination.SpanAttributeData import io.embrace.android.embracesdk.arch.destination.SpanEventData import io.embrace.android.embracesdk.arch.schema.AppTerminationCause @@ -9,7 +13,6 @@ import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.internal.clock.nanosToMillis import io.embrace.android.embracesdk.spans.EmbraceSpan -import io.opentelemetry.api.trace.StatusCode import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNotNull @@ -143,10 +146,10 @@ internal class CurrentSessionSpanImplTests { val lastFlushedSpan = flushedSpans[0] with(lastFlushedSpan) { assertEquals("emb-session", name) - assertEquals(EmbType.Ux.Session.attributeValue, attributes[EmbType.Ux.Session.otelAttributeName()]) - assertEquals(StatusCode.OK, status) - assertFalse(isKey()) - assertEquals(cause.attributeValue, attributes[cause.otelAttributeName()]) + assertIsType(EmbType.Ux.Session) + assertSuccessful() + assertNotKeySpan() + assertHasEmbraceAttribute(cause) } assertEquals(0, module.openTelemetryModule.spanSink.completedSpans().size) @@ -193,7 +196,7 @@ internal class CurrentSessionSpanImplTests { assertEquals(1000, testEvent.timestampNanos.nanosToMillis()) assertEquals( mapOf( - EmbType.System.Breadcrumb.otelAttributeName() to EmbType.System.Breadcrumb.attributeValue, + EmbType.System.Breadcrumb.toOTelKeyValuePair(), "message" to "test-event" ), testEvent.attributes diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanServiceTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanServiceTest.kt index 6328570f06..7d5b900727 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanServiceTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceSpanServiceTest.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.internal.spans +import io.embrace.android.embracesdk.arch.assertIsTypePerformance import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.injection.FakeInitModule @@ -87,10 +88,7 @@ internal class EmbraceSpanServiceTest { assertEquals(name, name) assertEquals(expectedStartTimeMs, startTimeNanos.nanosToMillis()) assertEquals(expectedEndTimeMs, endTimeNanos.nanosToMillis()) - assertEquals( - EmbType.Performance.Default.attributeValue, - attributes[EmbType.Performance.Default.otelAttributeName()] - ) + assertIsTypePerformance() expectedAttributes.forEach { assertEquals(it.value, attributes[it.key]) } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt index 14318f3d78..310adc579e 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt @@ -1,6 +1,10 @@ package io.embrace.android.embracesdk.internal.spans -import io.embrace.android.embracesdk.arch.schema.EmbType +import io.embrace.android.embracesdk.arch.assertError +import io.embrace.android.embracesdk.arch.assertIsKeySpan +import io.embrace.android.embracesdk.arch.assertIsTypePerformance +import io.embrace.android.embracesdk.arch.assertNotKeySpan +import io.embrace.android.embracesdk.arch.assertSuccessful import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fixtures.TOO_LONG_SPAN_NAME @@ -66,7 +70,7 @@ internal class EmbraceTracerTest { assertNotNull(embraceSpan) assertTrue(embraceSpan.start()) assertTrue(embraceSpan.stop(errorCode)) - verifyPublicSpan("test-span") + verifyPublicSpan(name = "test-span", errorCode = errorCode) spanSink.flushSpans() } } @@ -315,17 +319,16 @@ internal class EmbraceTracerTest { assertEquals(1, currentSpans.size) val currentSpan = currentSpans[0] assertEquals(name, currentSpan.name) - assertEquals( - EmbType.Performance.Default.attributeValue, - currentSpan.attributes[EmbType.Performance.Default.otelAttributeName()] - ) - assertEquals(if (traceRoot) "true" else null, currentSpan.attributes["emb.key"]) - errorCode?.run { - val errorCodeAttribute = fromErrorCode() - assertEquals( - errorCodeAttribute.attributeValue, - currentSpan.attributes[errorCodeAttribute.otelAttributeName()] - ) + currentSpan.assertIsTypePerformance() + if (traceRoot) { + currentSpan.assertIsKeySpan() + } else { + currentSpan.assertNotKeySpan() + } + if (errorCode == null) { + currentSpan.assertSuccessful() + } else { + currentSpan.assertError(errorCode) } assertFalse(currentSpan.isPrivate()) return currentSpan diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt index 97331f5103..64dea62eb4 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt @@ -1,6 +1,10 @@ package io.embrace.android.embracesdk.internal.spans -import io.embrace.android.embracesdk.arch.schema.EmbType +import io.embrace.android.embracesdk.arch.assertError +import io.embrace.android.embracesdk.arch.assertIsKeySpan +import io.embrace.android.embracesdk.arch.assertIsTypePerformance +import io.embrace.android.embracesdk.arch.assertNotKeySpan +import io.embrace.android.embracesdk.arch.assertSuccessful import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.internal.clock.millisToNanos @@ -210,7 +214,6 @@ internal class InternalTracerTest { val expectedName = "test-span" val expectedStartTimeMs = clock.now() val expectedEndTimeMs = expectedStartTimeMs + 100L - val expectedType = EmbType.Performance.Default val expectedAttributes = mapOf( Pair("attribute1", "value1"), Pair("attribute2", "value2") @@ -234,11 +237,8 @@ internal class InternalTracerTest { with(verifyPublicSpan(expectedName)) { assertEquals(expectedStartTimeMs, startTimeNanos.nanosToMillis()) assertEquals(expectedEndTimeMs, endTimeNanos.nanosToMillis()) - assertEquals( - expectedType.attributeValue, - attributes[expectedType.otelAttributeName()] - ) - assertEquals("true", attributes["emb.key"]) + assertIsTypePerformance() + assertIsKeySpan() expectedAttributes.forEach { assertEquals(it.value, attributes[it.key]) } @@ -277,17 +277,16 @@ internal class InternalTracerTest { assertEquals(1, currentSpans.size) val currentSpan = currentSpans[0] assertEquals(name, currentSpan.name) - assertEquals( - EmbType.Performance.Default.attributeValue, - currentSpan.attributes[EmbType.Performance.Default.otelAttributeName()] - ) - assertEquals(if (traceRoot) "true" else null, currentSpan.attributes["emb.key"]) - errorCode?.run { - val errorCodeAttribute = fromErrorCode() - assertEquals( - errorCodeAttribute.attributeValue, - currentSpan.attributes[errorCodeAttribute.otelAttributeName()] - ) + currentSpan.assertIsTypePerformance() + if (traceRoot) { + currentSpan.assertIsKeySpan() + } else { + currentSpan.assertNotKeySpan() + } + if (errorCode == null) { + currentSpan.assertSuccessful() + } else { + currentSpan.assertError(errorCode) } assertFalse(currentSpan.isPrivate()) return currentSpan diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt index 0199843614..ba1b5a48ea 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt @@ -1,9 +1,13 @@ package io.embrace.android.embracesdk.internal.spans import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.embrace.android.embracesdk.arch.assertError +import io.embrace.android.embracesdk.arch.assertIsKeySpan +import io.embrace.android.embracesdk.arch.assertIsType +import io.embrace.android.embracesdk.arch.assertIsTypePerformance +import io.embrace.android.embracesdk.arch.assertNotKeySpan import io.embrace.android.embracesdk.arch.schema.AppTerminationCause import io.embrace.android.embracesdk.arch.schema.EmbType -import io.embrace.android.embracesdk.arch.schema.ErrorCodeAttribute import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fixtures.MAX_LENGTH_SPAN_NAME @@ -56,11 +60,8 @@ internal class SpanServiceImplTest { assertTrue(embraceSpan.stop()) with(verifyAndReturnSoleCompletedSpan("emb-test-span")) { assertEquals(SpanId.getInvalid(), parentSpanId) - assertEquals( - EmbType.Performance.Default.attributeValue, - attributes[EmbType.Performance.Default.otelAttributeName()] - ) - assertTrue(isKey()) + assertIsTypePerformance() + assertIsKeySpan() } } @@ -85,11 +86,8 @@ internal class SpanServiceImplTest { assertTrue(embraceSpan.stop()) with(verifyAndReturnSoleCompletedSpan("emb-test-span")) { assertEquals(SpanId.getInvalid(), parentSpanId) - assertEquals( - EmbType.Performance.Default.attributeValue, - attributes[EmbType.Performance.Default.otelAttributeName()] - ) - assertTrue(isKey()) + assertIsTypePerformance() + assertIsKeySpan() } } @@ -112,7 +110,7 @@ internal class SpanServiceImplTest { assertEquals("emb-child-span", name) assertEquals(childSpan.spanId, spanId) assertEquals(childSpan.traceId, traceId) - assertFalse(isKey()) + assertNotKeySpan() assertTrue(isPrivate()) } @@ -121,7 +119,7 @@ internal class SpanServiceImplTest { assertEquals(SpanId.getInvalid(), parentSpanId) assertEquals(parentSpan.spanId, spanId) assertEquals(parentSpan.traceId, traceId) - assertTrue(isKey()) + assertIsKeySpan() assertTrue(isPrivate()) } } @@ -181,11 +179,8 @@ internal class SpanServiceImplTest { assertEquals(1, completedSpans.size) with(completedSpans[0]) { assertTrue(isPrivate()) - assertFalse(isKey()) - assertEquals( - EmbType.Ux.View.attributeValue, - attributes[EmbType.Ux.View.otelAttributeName()] - ) + assertNotKeySpan() + assertIsType(EmbType.Ux.View) assertEquals(childStartTimeMs, startTimeNanos.nanosToMillis()) assertEquals(childSpanEndTimeMs, endTimeNanos.nanosToMillis()) } @@ -218,12 +213,9 @@ internal class SpanServiceImplTest { with(verifyAndReturnSoleCompletedSpan("emb-$expectedName")) { assertEquals(expectedStartTimeMs, startTimeNanos.nanosToMillis()) assertEquals(expectedEndTimeMs, endTimeNanos.nanosToMillis()) - assertEquals( - EmbType.Performance.Default.attributeValue, - attributes[EmbType.Performance.Default.otelAttributeName()] - ) + assertIsTypePerformance() assertEquals(SpanId.getInvalid(), parentSpanId) - assertTrue(isKey()) + assertIsKeySpan() assertTrue(isPrivate()) expectedAttributes.forEach { assertEquals(it.value, attributes[it.key]) @@ -251,7 +243,7 @@ internal class SpanServiceImplTest { with(verifyAndReturnSoleCompletedSpan("emb-$expectedName")) { assertEquals(expectedStartTimeMs, startTimeNanos.nanosToMillis()) assertEquals(expectedEndTimeMs, endTimeNanos.nanosToMillis()) - assertFalse(isKey()) + assertNotKeySpan() assertTrue(isPrivate()) } assertTrue(parentSpan.stop()) @@ -307,11 +299,7 @@ internal class SpanServiceImplTest { ) ) with(verifyAndReturnSoleCompletedSpan("emb-test${errorCode.name}")) { - val errorCodeAttribute = checkNotNull(errorCode.fromErrorCode()) - assertEquals( - errorCodeAttribute.attributeValue, - attributes[errorCodeAttribute.otelAttributeName()] - ) + assertError(errorCode) } spanSink.flushSpans() } @@ -352,11 +340,8 @@ internal class SpanServiceImplTest { assertEquals(returnThis, lambdaReturn) with(verifyAndReturnSoleCompletedSpan("emb-test-span")) { assertEquals(SpanId.getInvalid(), parentSpanId) - assertEquals( - EmbType.Performance.Default.attributeValue, - attributes[EmbType.Performance.Default.otelAttributeName()] - ) - assertTrue(isKey()) + assertIsTypePerformance() + assertIsKeySpan() assertTrue(isPrivate()) } } @@ -378,7 +363,7 @@ internal class SpanServiceImplTest { with(currentSpans[0]) { assertEquals("emb-child-span", name) - assertFalse(isKey()) + assertNotKeySpan() assertTrue(isPrivate()) } } @@ -418,10 +403,7 @@ internal class SpanServiceImplTest { } with(verifyAndReturnSoleCompletedSpan("emb-test-span")) { - assertEquals( - ErrorCodeAttribute.Failure.attributeValue, - attributes[ErrorCodeAttribute.Failure.otelAttributeName()] - ) + assertError(ErrorCode.FAILURE) } } From 7a636efef79ca7fc183157e872f7832cc7d253d1 Mon Sep 17 00:00:00 2001 From: bidetofevil Date: Fri, 8 Mar 2024 23:56:08 -0800 Subject: [PATCH 12/14] Convert private span attribute to EmbraceAttributes --- .../embracesdk/assertions/SpanAssertions.kt | 9 +++++++-- .../embracesdk/arch/schema/PrivateSpan.kt | 10 ++++++++++ .../internal/spans/EmbraceExtensions.kt | 20 ++++--------------- .../arch/EmbraceAttributeExtensions.kt | 5 +++++ .../capture/startup/StartupServiceImplTest.kt | 5 ++--- .../internal/spans/EmbraceTracerTest.kt | 4 ++-- .../internal/spans/InternalTracerTest.kt | 3 ++- .../internal/spans/SpanServiceImplTest.kt | 15 +++++++------- 8 files changed, 40 insertions(+), 31 deletions(-) create mode 100644 embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/PrivateSpan.kt diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/assertions/SpanAssertions.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/assertions/SpanAssertions.kt index b5acb048af..a1d734bd32 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/assertions/SpanAssertions.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/assertions/SpanAssertions.kt @@ -2,11 +2,12 @@ package io.embrace.android.embracesdk.assertions import io.embrace.android.embracesdk.arch.assertError import io.embrace.android.embracesdk.arch.assertIsKeySpan +import io.embrace.android.embracesdk.arch.assertIsPrivateSpan import io.embrace.android.embracesdk.arch.assertNotKeySpan +import io.embrace.android.embracesdk.arch.assertNotPrivateSpan import io.embrace.android.embracesdk.arch.assertSuccessful import io.embrace.android.embracesdk.internal.clock.nanosToMillis import io.embrace.android.embracesdk.internal.spans.EmbraceSpanData -import io.embrace.android.embracesdk.internal.spans.isPrivate import io.embrace.android.embracesdk.spans.EmbraceSpanEvent import io.embrace.android.embracesdk.spans.ErrorCode import io.opentelemetry.api.trace.StatusCode @@ -48,7 +49,11 @@ internal fun assertEmbraceSpanData( assertEquals(entry.value, attributes[entry.key]) } assertEquals(expectedEvents, events) - assertEquals(private, isPrivate()) + if (private) { + assertIsPrivateSpan() + } else { + assertNotPrivateSpan() + } if (key) { assertIsKeySpan() } else { diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/PrivateSpan.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/PrivateSpan.kt new file mode 100644 index 0000000000..c6158ce982 --- /dev/null +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/arch/schema/PrivateSpan.kt @@ -0,0 +1,10 @@ +package io.embrace.android.embracesdk.arch.schema + +/** + * Denotes a private span recorded by Embrace for diagnostic or internal usage purposes that is not meant to be consumed directly by + * users of the SDK, nor is considered part of the public API. + */ +internal object PrivateSpan : EmbraceAttribute { + override val attributeName: String = "private" + override val attributeValue: String = "true" +} diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt index 487bb03556..ee5f3d773b 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/spans/EmbraceExtensions.kt @@ -2,6 +2,7 @@ package io.embrace.android.embracesdk.internal.spans import io.embrace.android.embracesdk.arch.schema.EmbraceAttribute import io.embrace.android.embracesdk.arch.schema.KeySpan +import io.embrace.android.embracesdk.arch.schema.PrivateSpan import io.embrace.android.embracesdk.arch.schema.TelemetryType import io.embrace.android.embracesdk.internal.utils.Provider import io.embrace.android.embracesdk.spans.EmbraceSpan @@ -42,12 +43,6 @@ private const val EMBRACE_USAGE_ATTRIBUTE_NAME_PREFIX = "emb.usage." */ private const val SEQUENCE_ID_ATTRIBUTE_NAME = EMBRACE_ATTRIBUTE_NAME_PREFIX + "sequence_id" -/** - * Denotes a private span logged by Embrace for diagnostic purposes and should not be displayed to customers in the dashboard, but - * should be shown to Embrace employees. - */ -private const val PRIVATE_SPAN_ATTRIBUTE_NAME = EMBRACE_ATTRIBUTE_NAME_PREFIX + "private" - /** * Creates a new [SpanBuilder] with the correctly prefixed name, to be used for recording Spans in the SDK internally */ @@ -88,8 +83,7 @@ internal fun SpanBuilder.setEmbraceAttribute(embraceAttribute: EmbraceAttribute) } /** - * Mark the span generated by this builder as an important one, to be listed in the Spans listing page in the UI. It is currently used - * for any spans that are the root of a trace. + * Mark the span generated by this builder as a [KeySpan] */ internal fun SpanBuilder.makeKey(): SpanBuilder { setEmbraceAttribute(KeySpan) @@ -97,11 +91,10 @@ internal fun SpanBuilder.makeKey(): SpanBuilder { } /** - * Mark the span generated by this builder as a private span logged by Embrace for diagnostic purposes and that should not be displayed - * to customers in the dashboard, but should be shown to Embrace employees. + * Mark the span generated by this builder as [PrivateSpan] */ internal fun SpanBuilder.makePrivate(): SpanBuilder { - setAttribute(PRIVATE_SPAN_ATTRIBUTE_NAME, true) + setEmbraceAttribute(PrivateSpan) return this } @@ -204,11 +197,6 @@ internal fun AttributesBuilder.fromMap(attributes: Map): Attribu return this } -/** - * Returns true if the Span is private - */ -internal fun EmbraceSpanData.isPrivate(): Boolean = attributes[PRIVATE_SPAN_ATTRIBUTE_NAME] == true.toString() - /** * Return the appropriate internal Embrace Span name given the current value */ diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/arch/EmbraceAttributeExtensions.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/arch/EmbraceAttributeExtensions.kt index 88c002de80..71309e604a 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/arch/EmbraceAttributeExtensions.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/arch/EmbraceAttributeExtensions.kt @@ -6,6 +6,7 @@ import io.embrace.android.embracesdk.arch.schema.EmbType import io.embrace.android.embracesdk.arch.schema.EmbraceAttribute import io.embrace.android.embracesdk.arch.schema.ErrorCodeAttribute import io.embrace.android.embracesdk.arch.schema.KeySpan +import io.embrace.android.embracesdk.arch.schema.PrivateSpan import io.embrace.android.embracesdk.arch.schema.TelemetryType import io.embrace.android.embracesdk.internal.spans.EmbraceSpanData import io.embrace.android.embracesdk.spans.ErrorCode @@ -28,6 +29,10 @@ internal fun EmbraceSpanData.assertIsKeySpan() = assertHasEmbraceAttribute(KeySp internal fun EmbraceSpanData.assertNotKeySpan() = assertDoesNotHaveEmbraceAttribute(KeySpan) +internal fun EmbraceSpanData.assertIsPrivateSpan() = assertHasEmbraceAttribute(PrivateSpan) + +internal fun EmbraceSpanData.assertNotPrivateSpan() = assertDoesNotHaveEmbraceAttribute(PrivateSpan) + /** * Assert [EmbraceSpanData] has the [EmbraceAttribute] defined by [embraceAttribute] */ diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/startup/StartupServiceImplTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/startup/StartupServiceImplTest.kt index acff21a74d..be1d8777e7 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/startup/StartupServiceImplTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/startup/StartupServiceImplTest.kt @@ -1,5 +1,6 @@ package io.embrace.android.embracesdk.capture.startup +import io.embrace.android.embracesdk.arch.assertIsPrivateSpan import io.embrace.android.embracesdk.arch.assertIsTypePerformance import io.embrace.android.embracesdk.concurrency.BlockableExecutorService import io.embrace.android.embracesdk.fakes.FakeClock @@ -7,13 +8,11 @@ import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.internal.clock.nanosToMillis import io.embrace.android.embracesdk.internal.spans.SpanService import io.embrace.android.embracesdk.internal.spans.SpanSink -import io.embrace.android.embracesdk.internal.spans.isPrivate import io.embrace.android.embracesdk.worker.BackgroundWorker import io.opentelemetry.api.trace.SpanId import io.opentelemetry.api.trace.StatusCode import org.junit.Assert.assertEquals import org.junit.Assert.assertNull -import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test @@ -51,7 +50,7 @@ internal class StartupServiceImplTest { assertEquals(startTimeMillis, startTimeNanos.nanosToMillis()) assertEquals(endTimeMillis, endTimeNanos.nanosToMillis()) assertIsTypePerformance() - assertTrue(isPrivate()) + assertIsPrivateSpan() assertEquals(StatusCode.OK, status) } } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt index 310adc579e..59281a3809 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/EmbraceTracerTest.kt @@ -4,6 +4,7 @@ import io.embrace.android.embracesdk.arch.assertError import io.embrace.android.embracesdk.arch.assertIsKeySpan import io.embrace.android.embracesdk.arch.assertIsTypePerformance import io.embrace.android.embracesdk.arch.assertNotKeySpan +import io.embrace.android.embracesdk.arch.assertNotPrivateSpan import io.embrace.android.embracesdk.arch.assertSuccessful import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.injection.FakeInitModule @@ -14,7 +15,6 @@ import io.embrace.android.embracesdk.spans.EmbraceSpanEvent import io.embrace.android.embracesdk.spans.ErrorCode import io.opentelemetry.api.trace.StatusCode import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Assert.assertSame @@ -330,7 +330,7 @@ internal class EmbraceTracerTest { } else { currentSpan.assertError(errorCode) } - assertFalse(currentSpan.isPrivate()) + currentSpan.assertNotPrivateSpan() return currentSpan } } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt index 64dea62eb4..88914de396 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/InternalTracerTest.kt @@ -4,6 +4,7 @@ import io.embrace.android.embracesdk.arch.assertError import io.embrace.android.embracesdk.arch.assertIsKeySpan import io.embrace.android.embracesdk.arch.assertIsTypePerformance import io.embrace.android.embracesdk.arch.assertNotKeySpan +import io.embrace.android.embracesdk.arch.assertNotPrivateSpan import io.embrace.android.embracesdk.arch.assertSuccessful import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.injection.FakeInitModule @@ -288,7 +289,7 @@ internal class InternalTracerTest { } else { currentSpan.assertError(errorCode) } - assertFalse(currentSpan.isPrivate()) + currentSpan.assertNotPrivateSpan() return currentSpan } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt index ba1b5a48ea..accd1fa4fa 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/spans/SpanServiceImplTest.kt @@ -3,6 +3,7 @@ package io.embrace.android.embracesdk.internal.spans import androidx.test.ext.junit.runners.AndroidJUnit4 import io.embrace.android.embracesdk.arch.assertError import io.embrace.android.embracesdk.arch.assertIsKeySpan +import io.embrace.android.embracesdk.arch.assertIsPrivateSpan import io.embrace.android.embracesdk.arch.assertIsType import io.embrace.android.embracesdk.arch.assertIsTypePerformance import io.embrace.android.embracesdk.arch.assertNotKeySpan @@ -111,7 +112,7 @@ internal class SpanServiceImplTest { assertEquals(childSpan.spanId, spanId) assertEquals(childSpan.traceId, traceId) assertNotKeySpan() - assertTrue(isPrivate()) + assertIsPrivateSpan() } with(currentSpans[1]) { @@ -120,7 +121,7 @@ internal class SpanServiceImplTest { assertEquals(parentSpan.spanId, spanId) assertEquals(parentSpan.traceId, traceId) assertIsKeySpan() - assertTrue(isPrivate()) + assertIsPrivateSpan() } } @@ -178,7 +179,7 @@ internal class SpanServiceImplTest { val completedSpans = spanSink.flushSpans() assertEquals(1, completedSpans.size) with(completedSpans[0]) { - assertTrue(isPrivate()) + assertIsPrivateSpan() assertNotKeySpan() assertIsType(EmbType.Ux.View) assertEquals(childStartTimeMs, startTimeNanos.nanosToMillis()) @@ -216,7 +217,7 @@ internal class SpanServiceImplTest { assertIsTypePerformance() assertEquals(SpanId.getInvalid(), parentSpanId) assertIsKeySpan() - assertTrue(isPrivate()) + assertIsPrivateSpan() expectedAttributes.forEach { assertEquals(it.value, attributes[it.key]) } @@ -244,7 +245,7 @@ internal class SpanServiceImplTest { assertEquals(expectedStartTimeMs, startTimeNanos.nanosToMillis()) assertEquals(expectedEndTimeMs, endTimeNanos.nanosToMillis()) assertNotKeySpan() - assertTrue(isPrivate()) + assertIsPrivateSpan() } assertTrue(parentSpan.stop()) @@ -342,7 +343,7 @@ internal class SpanServiceImplTest { assertEquals(SpanId.getInvalid(), parentSpanId) assertIsTypePerformance() assertIsKeySpan() - assertTrue(isPrivate()) + assertIsPrivateSpan() } } @@ -364,7 +365,7 @@ internal class SpanServiceImplTest { with(currentSpans[0]) { assertEquals("emb-child-span", name) assertNotKeySpan() - assertTrue(isPrivate()) + assertIsPrivateSpan() } } From b7672fd09b84cb1e2df0b1b60f8364565cf6a023 Mon Sep 17 00:00:00 2001 From: bidetofevil Date: Mon, 11 Mar 2024 09:19:02 -0700 Subject: [PATCH 13/14] Fix bad merge thanks to..., well, me --- .../embracesdk/comms/api/EmbraceApiService.kt | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/api/EmbraceApiService.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/api/EmbraceApiService.kt index 46ebe5c231..9a1072c7e0 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/api/EmbraceApiService.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/comms/api/EmbraceApiService.kt @@ -62,8 +62,8 @@ internal class EmbraceApiService( @Throws(IllegalStateException::class) @Suppress("UseCheckOrError") override fun getConfig(): RemoteConfig? { - var request = prepareConfigRequest(configUrl.value) - val cachedResponse = cachedConfigProvider(configUrl.value, request) + var request = prepareConfigRequest(configUrl) + val cachedResponse = cachedConfigProvider(configUrl, request) if (cachedResponse.isValid()) { // only bother if we have a useful response. request = request.copy(eTag = cachedResponse.eTag) } @@ -105,8 +105,8 @@ internal class EmbraceApiService( } override fun getCachedConfig(): CachedConfig { - val request = prepareConfigRequest(configUrl.value) - return cachedConfigProvider(configUrl.value, request) + val request = prepareConfigRequest(configUrl) + return cachedConfigProvider(configUrl, request) } private fun prepareConfigRequest(url: String) = ApiRequest( @@ -122,37 +122,37 @@ internal class EmbraceApiService( } override fun sendLog(eventMessage: EventMessage) { - post(eventMessage, mapper.value::logRequest) + post(eventMessage, mapper::logRequest) } override fun sendLogsEnvelope(logsEnvelope: Envelope) { val parameterizedType = Types.newParameterizedType(Envelope::class.java, LogPayload::class.java) - post(logsEnvelope, mapper.value::logsEnvelopeRequest, parameterizedType) + post(logsEnvelope, mapper::logsEnvelopeRequest, parameterizedType) } override fun sendSessionEnvelope(sessionEnvelope: Envelope) { val parameterizedType = Types.newParameterizedType(Envelope::class.java, SessionPayload::class.java) - post(sessionEnvelope, mapper.value::sessionEnvelopeRequest, parameterizedType) + post(sessionEnvelope, mapper::sessionEnvelopeRequest, parameterizedType) } override fun sendAEIBlob(blobMessage: BlobMessage) { - post(blobMessage, mapper.value::aeiBlobRequest) + post(blobMessage, mapper::aeiBlobRequest) } override fun sendNetworkCall(networkEvent: NetworkEvent) { - post(networkEvent, mapper.value::networkEventRequest) + post(networkEvent, mapper::networkEventRequest) } override fun sendEvent(eventMessage: EventMessage) { - post(eventMessage, mapper.value::eventMessageRequest) + post(eventMessage, mapper::eventMessageRequest) } override fun sendCrash(crash: EventMessage): Future<*> { - return post(crash, mapper.value::eventMessageRequest) { cacheManager.deleteCrash() } + return post(crash, mapper::eventMessageRequest) { cacheManager.deleteCrash() } } override fun sendSession(action: SerializationAction, onFinish: (() -> Unit)?): Future<*> { - return postOnWorker(action, mapper.value.sessionRequest(), onFinish) + return postOnWorker(action, mapper.sessionRequest(), onFinish) } private inline fun post( From 044080ceff4d397cebdff7ab9a4ef076ea8a8c7a Mon Sep 17 00:00:00 2001 From: Jamie Lynch Date: Mon, 11 Mar 2024 13:16:15 +0000 Subject: [PATCH 14/14] populate spans in new payload --- .../session/SessionPayloadSourceImpl.kt | 30 +++++++++++++++---- .../embracesdk/internal/payload/SpanMapper.kt | 23 +++++++------- .../envelope/SessionEnvelopeSourceTest.kt | 6 +++- .../session/SessionPayloadSourceImplTest.kt | 25 +++++++++++++++- .../fakes/FakeCurrentSessionSpan.kt | 3 +- .../internal/payload/SpanMapperTest.kt | 20 ++++++------- 6 files changed, 76 insertions(+), 31 deletions(-) diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/envelope/session/SessionPayloadSourceImpl.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/envelope/session/SessionPayloadSourceImpl.kt index 10be94e20b..a775a83aa6 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/envelope/session/SessionPayloadSourceImpl.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/capture/envelope/session/SessionPayloadSourceImpl.kt @@ -1,7 +1,13 @@ package io.embrace.android.embracesdk.capture.envelope.session import io.embrace.android.embracesdk.anr.ndk.NativeThreadSamplerService +import io.embrace.android.embracesdk.arch.schema.AppTerminationCause import io.embrace.android.embracesdk.internal.payload.SessionPayload +import io.embrace.android.embracesdk.internal.payload.Span +import io.embrace.android.embracesdk.internal.payload.toNewPayload +import io.embrace.android.embracesdk.internal.spans.CurrentSessionSpan +import io.embrace.android.embracesdk.internal.spans.EmbraceSpanData +import io.embrace.android.embracesdk.internal.spans.SpanSink import io.embrace.android.embracesdk.logging.InternalErrorService import io.embrace.android.embracesdk.session.captureDataSafely import io.embrace.android.embracesdk.session.orchestrator.SessionSnapshotType @@ -9,12 +15,24 @@ import io.embrace.android.embracesdk.session.orchestrator.SessionSnapshotType internal class SessionPayloadSourceImpl( private val internalErrorService: InternalErrorService, private val nativeThreadSamplerService: NativeThreadSamplerService?, + private val spanSink: SpanSink, + private val currentSessionSpan: CurrentSessionSpan ) : SessionPayloadSource { - override fun getSessionPayload(endType: SessionSnapshotType) = SessionPayload( - spans = null, - spanSnapshots = null, - internalError = captureDataSafely { internalErrorService.currentExceptionError?.toNewPayload() }, - sharedLibSymbolMapping = captureDataSafely { nativeThreadSamplerService?.getNativeSymbols() } - ) + override fun getSessionPayload(endType: SessionSnapshotType): SessionPayload { + return SessionPayload( + spans = retrieveSpanData(endType), + spanSnapshots = null, + internalError = captureDataSafely { internalErrorService.currentExceptionError?.toNewPayload() }, + sharedLibSymbolMapping = captureDataSafely { nativeThreadSamplerService?.getNativeSymbols() } + ) + } + + private fun retrieveSpanData(endType: SessionSnapshotType): List? = captureDataSafely { + when (endType) { + SessionSnapshotType.NORMAL_END -> currentSessionSpan.endSession(null) + SessionSnapshotType.PERIODIC_CACHE -> spanSink.completedSpans() + SessionSnapshotType.JVM_CRASH -> currentSessionSpan.endSession(AppTerminationCause.Crash) + }.map(EmbraceSpanData::toNewPayload) + } } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/payload/SpanMapper.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/payload/SpanMapper.kt index 42f05b7203..144e841c27 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/payload/SpanMapper.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/payload/SpanMapper.kt @@ -1,32 +1,31 @@ package io.embrace.android.embracesdk.internal.payload -import io.opentelemetry.api.common.Attributes +import io.embrace.android.embracesdk.internal.spans.EmbraceSpanData +import io.embrace.android.embracesdk.spans.EmbraceSpanEvent import io.opentelemetry.api.trace.StatusCode -import io.opentelemetry.sdk.trace.data.EventData -import io.opentelemetry.sdk.trace.data.SpanData -internal fun SpanData.toNewPayload() = Span( +internal fun EmbraceSpanData.toNewPayload() = Span( traceId = traceId, spanId = spanId, parentSpanId = parentSpanId, name = name, - startTimeUnixNano = startEpochNanos, - endTimeUnixNano = endEpochNanos, - status = when (status.statusCode) { + startTimeUnixNano = startTimeNanos, + endTimeUnixNano = endTimeNanos, + status = when (status) { StatusCode.UNSET -> Span.Status.UNSET StatusCode.OK -> Span.Status.OK StatusCode.ERROR -> Span.Status.ERROR else -> Span.Status.UNSET }, - events = events.map(EventData::toNewPayload), + events = events.map(EmbraceSpanEvent::toNewPayload), attributes = attributes.toNewPayload() ) -internal fun EventData.toNewPayload() = SpanEvent( +internal fun EmbraceSpanEvent.toNewPayload() = SpanEvent( name = name, - timeUnixNano = epochNanos, + timeUnixNano = timestampNanos, attributes = attributes.toNewPayload() ) -internal fun Attributes.toNewPayload(): List = - asMap().map { (key, value) -> Attribute(key.key, value.toString()) } +internal fun Map.toNewPayload(): List = + map { (key, value) -> Attribute(key, value) } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/envelope/SessionEnvelopeSourceTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/envelope/SessionEnvelopeSourceTest.kt index 930d6466e3..995788b271 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/envelope/SessionEnvelopeSourceTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/envelope/SessionEnvelopeSourceTest.kt @@ -1,8 +1,10 @@ package io.embrace.android.embracesdk.capture.envelope import io.embrace.android.embracesdk.capture.envelope.session.SessionPayloadSourceImpl +import io.embrace.android.embracesdk.fakes.FakeCurrentSessionSpan import io.embrace.android.embracesdk.fakes.FakeInternalErrorService import io.embrace.android.embracesdk.fakes.FakeNativeThreadSamplerService +import io.embrace.android.embracesdk.internal.spans.SpanSinkImpl import io.embrace.android.embracesdk.session.orchestrator.SessionSnapshotType import org.junit.Test @@ -13,7 +15,9 @@ internal class SessionEnvelopeSourceTest { SessionEnvelopeSource( SessionPayloadSourceImpl( FakeInternalErrorService(), - FakeNativeThreadSamplerService() + FakeNativeThreadSamplerService(), + SpanSinkImpl(), + FakeCurrentSessionSpan() ) ).getEnvelope(SessionSnapshotType.NORMAL_END) } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/envelope/session/SessionPayloadSourceImplTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/envelope/session/SessionPayloadSourceImplTest.kt index c3ad2c53b7..71422798e8 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/envelope/session/SessionPayloadSourceImplTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/capture/envelope/session/SessionPayloadSourceImplTest.kt @@ -1,18 +1,26 @@ package io.embrace.android.embracesdk.capture.envelope.session import io.embrace.android.embracesdk.fakes.FakeClock +import io.embrace.android.embracesdk.fakes.FakeCurrentSessionSpan import io.embrace.android.embracesdk.fakes.FakeInternalErrorService import io.embrace.android.embracesdk.fakes.FakeNativeThreadSamplerService +import io.embrace.android.embracesdk.fakes.FakeSpanData import io.embrace.android.embracesdk.internal.payload.SessionPayload +import io.embrace.android.embracesdk.internal.spans.EmbraceSpanData +import io.embrace.android.embracesdk.internal.spans.SpanSinkImpl import io.embrace.android.embracesdk.payload.LegacyExceptionError import io.embrace.android.embracesdk.session.orchestrator.SessionSnapshotType import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull import org.junit.Before import org.junit.Test internal class SessionPayloadSourceImplTest { private lateinit var impl: SessionPayloadSourceImpl + private lateinit var sink: SpanSinkImpl + private lateinit var currentSessionSpan: FakeCurrentSessionSpan + private val cacheSpan = FakeSpanData(name = "cache-span") @Before fun setUp() { @@ -21,9 +29,17 @@ internal class SessionPayloadSourceImplTest { addException(RuntimeException(), "test", FakeClock()) } } + sink = SpanSinkImpl().apply { + storeCompletedSpans(listOf(cacheSpan)) + } + currentSessionSpan = FakeCurrentSessionSpan().apply { + spanData = listOf(EmbraceSpanData(FakeSpanData("my-span"))) + } impl = SessionPayloadSourceImpl( errorService, - FakeNativeThreadSamplerService() + FakeNativeThreadSamplerService(), + sink, + currentSessionSpan ) } @@ -31,23 +47,30 @@ internal class SessionPayloadSourceImplTest { fun `session crash`() { val payload = impl.getSessionPayload(SessionSnapshotType.JVM_CRASH) assertPayloadPopulated(payload) + val span = checkNotNull(payload.spans?.single()) + assertEquals("my-span", span.name) } @Test fun `session cache`() { val payload = impl.getSessionPayload(SessionSnapshotType.PERIODIC_CACHE) assertPayloadPopulated(payload) + val span = checkNotNull(payload.spans?.single()) + assertEquals("cache-span", span.name) } @Test fun `session lifecycle change`() { val payload = impl.getSessionPayload(SessionSnapshotType.NORMAL_END) assertPayloadPopulated(payload) + val span = checkNotNull(payload.spans?.single()) + assertEquals("my-span", span.name) } private fun assertPayloadPopulated(payload: SessionPayload) { val err = checkNotNull(payload.internalError) assertEquals(1, err.count) assertEquals(mapOf("armeabi-v7a" to "my-symbols"), payload.sharedLibSymbolMapping) + assertNull(payload.spanSnapshots) } } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeCurrentSessionSpan.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeCurrentSessionSpan.kt index 98739a5fcc..a87ef46a5a 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeCurrentSessionSpan.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/FakeCurrentSessionSpan.kt @@ -12,6 +12,7 @@ internal class FakeCurrentSessionSpan : CurrentSessionSpan { var initializedCallCount = 0 var addedEvents = mutableListOf() var addedAttributes = mutableListOf() + var spanData = listOf() override fun initializeService(sdkInitStartTimeMs: Long) { } @@ -32,7 +33,7 @@ internal class FakeCurrentSessionSpan : CurrentSessionSpan { } override fun endSession(appTerminationCause: AppTerminationCause?): List { - return emptyList() + return spanData } override fun canStartNewSpan(parent: EmbraceSpan?, internal: Boolean): Boolean { diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/payload/SpanMapperTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/payload/SpanMapperTest.kt index b709b2aa11..ad46f09581 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/payload/SpanMapperTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/internal/payload/SpanMapperTest.kt @@ -1,7 +1,7 @@ package io.embrace.android.embracesdk.internal.payload import io.embrace.android.embracesdk.fakes.FakeSpanData -import io.opentelemetry.sdk.trace.data.SpanData +import io.embrace.android.embracesdk.internal.spans.EmbraceSpanData import org.junit.Assert.assertEquals import org.junit.Test @@ -9,33 +9,33 @@ internal class SpanMapperTest { @Test fun toSpan() { - val input: SpanData = FakeSpanData() + val input = EmbraceSpanData(FakeSpanData()) val output = input.toNewPayload() assertEquals(input.traceId, output.traceId) assertEquals(input.spanId, output.spanId) assertEquals(input.parentSpanId, output.parentSpanId) assertEquals(input.name, output.name) - assertEquals(input.startEpochNanos, output.startTimeUnixNano) - assertEquals(input.endEpochNanos, output.endTimeUnixNano) - assertEquals(input.status.statusCode.name, checkNotNull(output.status).name) + assertEquals(input.startTimeNanos, output.startTimeUnixNano) + assertEquals(input.endTimeNanos, output.endTimeUnixNano) + assertEquals(input.status.name, checkNotNull(output.status).name) // validate event copied val inputEvent = input.events.single() val outputEvent = checkNotNull(output.events).single() assertEquals(inputEvent.name, outputEvent.name) - assertEquals(inputEvent.epochNanos, outputEvent.timeUnixNano) + assertEquals(inputEvent.timestampNanos, outputEvent.timeUnixNano) // test event attributes - val inputEventAttrs = checkNotNull(inputEvent.attributes?.asMap()) + val inputEventAttrs = checkNotNull(inputEvent.attributes) val outputEventAttrs = checkNotNull(outputEvent.attributes?.single()) - assertEquals(inputEventAttrs.keys.single().key, outputEventAttrs.key) + assertEquals(inputEventAttrs.keys.single(), outputEventAttrs.key) assertEquals(inputEventAttrs.values.single(), outputEventAttrs.data) // test attributes - val inputAttrs = checkNotNull(input.attributes?.asMap()) + val inputAttrs = checkNotNull(input.attributes) val outputAttrs = checkNotNull(output.attributes?.single()) - assertEquals(inputAttrs.keys.single().key, outputAttrs.key) + assertEquals(inputAttrs.keys.single(), outputAttrs.key) assertEquals(inputAttrs.values.single(), outputAttrs.data) } }