Skip to content

Commit

Permalink
refactor: embrace impl property delegates
Browse files Browse the repository at this point in the history
  • Loading branch information
fractalwrench committed May 17, 2024
1 parent 248a719 commit dd4a0db
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 162 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,24 @@ import android.app.Application.ActivityLifecycleCallbacks
import android.content.Context
import io.embrace.android.embracesdk.Embrace.LastRunEndState
import io.embrace.android.embracesdk.EventType.Companion.fromSeverity
import io.embrace.android.embracesdk.anr.AnrService
import io.embrace.android.embracesdk.anr.ndk.NativeThreadSamplerInstaller
import io.embrace.android.embracesdk.anr.ndk.NativeThreadSamplerService
import io.embrace.android.embracesdk.capture.crumbs.BreadcrumbService
import io.embrace.android.embracesdk.capture.crumbs.PushNotificationCaptureService
import io.embrace.android.embracesdk.capture.metadata.MetadataService
import io.embrace.android.embracesdk.capture.user.UserService
import io.embrace.android.embracesdk.capture.webview.WebViewService
import io.embrace.android.embracesdk.config.ConfigService
import io.embrace.android.embracesdk.config.behavior.NetworkBehavior
import io.embrace.android.embracesdk.event.EmbraceEventService
import io.embrace.android.embracesdk.event.EventService
import io.embrace.android.embracesdk.event.LogMessageService
import io.embrace.android.embracesdk.injection.CoreModule
import io.embrace.android.embracesdk.injection.CrashModule
import io.embrace.android.embracesdk.injection.ModuleInitBootstrapper
import io.embrace.android.embracesdk.injection.embraceImplInject
import io.embrace.android.embracesdk.internal.ApkToolsConfig
import io.embrace.android.embracesdk.internal.EmbraceInternalInterface
import io.embrace.android.embracesdk.internal.IdGenerator.Companion.generateW3CTraceparent
import io.embrace.android.embracesdk.internal.Systrace.endSynchronous
import io.embrace.android.embracesdk.internal.Systrace.startSynchronous
import io.embrace.android.embracesdk.internal.crash.LastRunCrashVerifier
import io.embrace.android.embracesdk.internal.spans.EmbraceTracer
import io.embrace.android.embracesdk.internal.utils.getSafeStackTrace
import io.embrace.android.embracesdk.logging.EmbLogger
import io.embrace.android.embracesdk.logging.InternalErrorService
import io.embrace.android.embracesdk.ndk.NdkService
import io.embrace.android.embracesdk.network.EmbraceNetworkRequest
import io.embrace.android.embracesdk.network.logging.NetworkCaptureService
import io.embrace.android.embracesdk.network.logging.NetworkLoggingService
import io.embrace.android.embracesdk.payload.PushNotificationBreadcrumb
import io.embrace.android.embracesdk.payload.TapBreadcrumb.TapBreadcrumbType
import io.embrace.android.embracesdk.prefs.PreferencesService
import io.embrace.android.embracesdk.session.id.SessionIdTracker
import io.embrace.android.embracesdk.session.lifecycle.ActivityTracker
import io.embrace.android.embracesdk.session.lifecycle.ProcessStateService
import io.embrace.android.embracesdk.session.orchestrator.SessionOrchestrator
import io.embrace.android.embracesdk.session.properties.SessionPropertiesService
import io.embrace.android.embracesdk.telemetry.TelemetryService
import io.embrace.android.embracesdk.utils.PropertyUtils.sanitizeProperties
import io.embrace.android.embracesdk.worker.WorkerName
import io.embrace.android.embracesdk.worker.WorkerThreadModule
Expand All @@ -62,22 +41,22 @@ import java.util.regex.Pattern
*/
@SuppressLint("EmbracePublicApiPackageRule")
internal class EmbraceImpl @JvmOverloads constructor(
private val moduleInitBootstrapper: ModuleInitBootstrapper = ModuleInitBootstrapper()
private val bootstrapper: ModuleInitBootstrapper = ModuleInitBootstrapper()
) {

@JvmField
val tracer: EmbraceTracer = moduleInitBootstrapper.openTelemetryModule.embraceTracer
val tracer: EmbraceTracer = bootstrapper.openTelemetryModule.embraceTracer

private val uninitializedSdkInternalInterface by lazy<EmbraceInternalInterface> {
UninitializedSdkInternalInterfaceImpl(moduleInitBootstrapper.openTelemetryModule.internalTracer)
UninitializedSdkInternalInterfaceImpl(bootstrapper.openTelemetryModule.internalTracer)
}

/**
* Whether the Embrace SDK has been started yet.
*/
private val started = AtomicBoolean(false)
private val sdkClock = moduleInitBootstrapper.initModule.clock
private val logger = moduleInitBootstrapper.initModule.logger
private val sdkClock = bootstrapper.initModule.clock
private val logger = bootstrapper.initModule.logger

/**
* Custom app ID that overrides the one specified at build time
Expand All @@ -93,89 +72,47 @@ internal class EmbraceImpl @JvmOverloads constructor(
private set

/**
* The type of application being instrumented by this SDK instance, whether it's directly used by an Android app, or used via a hosted
* SDK like Flutter, React Native, or Unity.
* Variable pointing to the composeActivityListener instance obtained using reflection
*/
@Volatile
private var appFramework: Embrace.AppFramework? = null

@Volatile
private var breadcrumbService: BreadcrumbService? = null

@Volatile
private var sessionOrchestrator: SessionOrchestrator? = null

@Volatile
private var sessionPropertiesService: SessionPropertiesService? = null

@Volatile
var metadataService: MetadataService? = null
private set

@Volatile
private var sessionIdTracker: SessionIdTracker? = null

@Volatile
var activityService: ProcessStateService? = null
private set

@Volatile
var activityLifecycleTracker: ActivityTracker? = null
private set

@Volatile
private var networkLoggingService: NetworkLoggingService? = null

@Volatile
private var anrService: AnrService? = null

@Volatile
private var logMessageService: LogMessageService? = null

@Volatile
private var configService: ConfigService? = null

@Volatile
private var preferencesService: PreferencesService? = null

@Volatile
private var eventService: EventService? = null

@Volatile
private var userService: UserService? = null

@Volatile
var internalErrorService: InternalErrorService? = null
private set

@Volatile
private var ndkService: NdkService? = null

@Volatile
private var networkCaptureService: NetworkCaptureService? = null

@Volatile
private var webViewService: WebViewService? = null

@Volatile
private var telemetryService: TelemetryService? = null

private var nativeThreadSampler: NativeThreadSamplerService? = null

private var nativeThreadSamplerInstaller: NativeThreadSamplerInstaller? = null

private var composeActivityListenerInstance: Any? = null
private var embraceInternalInterface: EmbraceInternalInterface? = null

private var internalInterfaceModule: InternalInterfaceModule? = null

private var pushNotificationService: PushNotificationCaptureService? = null

private var crashVerifier: LastRunCrashVerifier? = null
val metadataService by embraceImplInject { bootstrapper.essentialServiceModule.metadataService }
val activityService by embraceImplInject { bootstrapper.essentialServiceModule.processStateService }
val activityLifecycleTracker by embraceImplInject { bootstrapper.essentialServiceModule.activityLifecycleTracker }
val internalErrorService by embraceImplInject {
bootstrapper.initModule.internalErrorService.also {
it.configService = bootstrapper.essentialServiceModule.configService
}
}

/**
* Variable pointing to the composeActivityListener instance obtained using reflection
* The type of application being instrumented by this SDK instance, whether it's directly used by an Android app, or used via a hosted
* SDK like Flutter, React Native, or Unity.
*/
private var composeActivityListenerInstance: Any? = null
private val appFramework by embraceImplInject { bootstrapper.coreModule.appFramework }
private val breadcrumbService by embraceImplInject { bootstrapper.dataCaptureServiceModule.breadcrumbService }
private val sessionOrchestrator by embraceImplInject { bootstrapper.sessionModule.sessionOrchestrator }
private val sessionPropertiesService by embraceImplInject { bootstrapper.sessionModule.sessionPropertiesService }
private val sessionIdTracker by embraceImplInject { bootstrapper.essentialServiceModule.sessionIdTracker }
private val networkLoggingService by embraceImplInject { bootstrapper.customerLogModule.networkLoggingService }
private val anrService by embraceImplInject { bootstrapper.anrModule.anrService }
private val logMessageService by embraceImplInject { bootstrapper.customerLogModule.logMessageService }
private val configService by embraceImplInject { bootstrapper.essentialServiceModule.configService }
private val preferencesService by embraceImplInject { bootstrapper.androidServicesModule.preferencesService }
private val eventService by embraceImplInject { bootstrapper.dataContainerModule.eventService }
private val userService by embraceImplInject { bootstrapper.essentialServiceModule.userService }
private val ndkService by embraceImplInject { bootstrapper.nativeModule.ndkService }
private val networkCaptureService by embraceImplInject { bootstrapper.customerLogModule.networkCaptureService }
private val webViewService by embraceImplInject { bootstrapper.dataCaptureServiceModule.webviewService }
private val telemetryService by embraceImplInject { bootstrapper.initModule.telemetryService }
private val nativeThreadSampler by embraceImplInject { bootstrapper.nativeModule.nativeThreadSamplerService }
private val nativeThreadSamplerInstaller by embraceImplInject { bootstrapper.nativeModule.nativeThreadSamplerInstaller }
private val pushNotificationService by embraceImplInject {
bootstrapper.dataCaptureServiceModule.pushNotificationService
}
private val crashVerifier by embraceImplInject { bootstrapper.crashModule.lastRunCrashVerifier }

/**
* Starts instrumentation of the Android application using the Embrace SDK. This should be
Expand Down Expand Up @@ -225,18 +162,13 @@ internal class EmbraceImpl @JvmOverloads constructor(

val startTimeMs = sdkClock.now()
logger.logInfo("Starting SDK for framework " + framework.name, null)
moduleInitBootstrapper.init(context, framework, startTimeMs, customAppId, configServiceProvider)
bootstrapper.init(context, framework, startTimeMs, customAppId, configServiceProvider)
startSynchronous("post-services-setup")
telemetryService = moduleInitBootstrapper.initModule.telemetryService

val coreModule = moduleInitBootstrapper.coreModule
val coreModule = bootstrapper.coreModule
application = coreModule.application
appFramework = coreModule.appFramework

val androidServicesModule = moduleInitBootstrapper.androidServicesModule
preferencesService = androidServicesModule.preferencesService

val essentialServiceModule = moduleInitBootstrapper.essentialServiceModule
val essentialServiceModule = bootstrapper.essentialServiceModule
if (essentialServiceModule.configService.isSdkDisabled()) {
logger.logInfo("Interrupting SDK start because it is disabled", null)
stop()
Expand All @@ -247,57 +179,22 @@ internal class EmbraceImpl @JvmOverloads constructor(
registerComposeActivityListener(coreModule)
}

activityService = essentialServiceModule.processStateService
metadataService = essentialServiceModule.metadataService
sessionIdTracker = essentialServiceModule.sessionIdTracker
configService = essentialServiceModule.configService
activityLifecycleTracker = essentialServiceModule.activityLifecycleTracker
userService = essentialServiceModule.userService

val dataCaptureServiceModule = moduleInitBootstrapper.dataCaptureServiceModule
webViewService = dataCaptureServiceModule.webviewService
breadcrumbService = dataCaptureServiceModule.breadcrumbService
pushNotificationService = dataCaptureServiceModule.pushNotificationService

val anrModule = moduleInitBootstrapper.anrModule
anrService = anrModule.anrService

internalErrorService = moduleInitBootstrapper.initModule.internalErrorService.apply {
configService = configService
}

val deliveryModule = moduleInitBootstrapper.deliveryModule

val customerLogModule = moduleInitBootstrapper.customerLogModule
logMessageService = customerLogModule.logMessageService
networkCaptureService = customerLogModule.networkCaptureService
networkLoggingService = customerLogModule.networkLoggingService

val nativeModule = moduleInitBootstrapper.nativeModule
ndkService = nativeModule.ndkService
nativeThreadSampler = nativeModule.nativeThreadSamplerService
nativeThreadSamplerInstaller = nativeModule.nativeThreadSamplerInstaller

val dataContainerModule = moduleInitBootstrapper.dataContainerModule
eventService = dataContainerModule.eventService

val sessionModule = moduleInitBootstrapper.sessionModule
sessionOrchestrator = sessionModule.sessionOrchestrator
sessionPropertiesService = sessionModule.sessionPropertiesService

val crashModule = moduleInitBootstrapper.crashModule
val dataCaptureServiceModule = bootstrapper.dataCaptureServiceModule
val deliveryModule = bootstrapper.deliveryModule
val dataContainerModule = bootstrapper.dataContainerModule
val crashModule = bootstrapper.crashModule

startSynchronous("send-cached-sessions")
// Send any sessions that were cached and not yet sent.
deliveryModule.deliveryService.sendCachedSessions(crashModule::nativeCrashService, essentialServiceModule.sessionIdTracker)
endSynchronous()

loadCrashVerifier(crashModule, moduleInitBootstrapper.workerThreadModule)
loadCrashVerifier(crashModule, bootstrapper.workerThreadModule)

val internalInterfaceModuleImpl =
InternalInterfaceModuleImpl(
moduleInitBootstrapper.initModule,
moduleInitBootstrapper.openTelemetryModule,
bootstrapper.initModule,
bootstrapper.openTelemetryModule,
coreModule,
essentialServiceModule,
this,
Expand Down Expand Up @@ -335,7 +232,7 @@ internal class EmbraceImpl @JvmOverloads constructor(

// This should return immediately given that EmbraceSpansService initialization should be finished at this point
// Put in emergency timeout just in case something unexpected happens so as to fail the SDK startup.
moduleInitBootstrapper.waitForAsyncInit()
bootstrapper.waitForAsyncInit()
}

/**
Expand All @@ -346,9 +243,7 @@ internal class EmbraceImpl @JvmOverloads constructor(
* @param workerThreadModule an instance of [WorkerThreadModule]
*/
private fun loadCrashVerifier(crashModule: CrashModule, workerThreadModule: WorkerThreadModule) {
crashVerifier = crashModule.lastRunCrashVerifier.apply {
readAndCleanMarkerAsync(workerThreadModule.backgroundWorker(WorkerName.BACKGROUND_REGISTRATION))
}
crashModule.lastRunCrashVerifier.readAndCleanMarkerAsync(workerThreadModule.backgroundWorker(WorkerName.BACKGROUND_REGISTRATION))
}

/**
Expand Down Expand Up @@ -427,7 +322,7 @@ internal class EmbraceImpl @JvmOverloads constructor(
}
}
application = null
moduleInitBootstrapper.stopServices()
bootstrapper.stopServices()
} catch (ex: Exception) {
logger.logError("Error while shutting down Embrace SDK", ex)
}
Expand Down Expand Up @@ -638,7 +533,8 @@ internal class EmbraceImpl @JvmOverloads constructor(

fun getTraceIdHeader(): String {
if (configService != null && checkSdkStarted("get_trace_id_header", false)) {
return configService?.networkBehavior?.getTraceIdHeader() ?: NetworkBehavior.CONFIG_TRACE_ID_HEADER_DEFAULT_VALUE
return configService?.networkBehavior?.getTraceIdHeader()
?: NetworkBehavior.CONFIG_TRACE_ID_HEADER_DEFAULT_VALUE

Check warning on line 537 in embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceImpl.kt

View check run for this annotation

Codecov / codecov/patch

embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceImpl.kt#L537

Added line #L537 was not covered by tests
}
return NetworkBehavior.CONFIG_TRACE_ID_HEADER_DEFAULT_VALUE
}
Expand Down Expand Up @@ -803,7 +699,10 @@ internal class EmbraceImpl @JvmOverloads constructor(
}

fun getDeviceId(): String = when {
checkSdkStartedAndLogPublicApiUsage("get_device_id") -> preferencesService?.deviceIdentifier ?: ""
checkSdkStartedAndLogPublicApiUsage("get_device_id") ->
preferencesService?.deviceIdentifier
?: ""

Check warning on line 704 in embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceImpl.kt

View check run for this annotation

Codecov / codecov/patch

embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/EmbraceImpl.kt#L704

Added line #L704 was not covered by tests

else -> ""
}

Expand Down Expand Up @@ -1055,15 +954,15 @@ internal class EmbraceImpl @JvmOverloads constructor(
logger.logError("A SpanExporter can only be added before the SDK is started.", null)
return
}
moduleInitBootstrapper.openTelemetryModule.openTelemetryConfiguration.addSpanExporter(spanExporter)
bootstrapper.openTelemetryModule.openTelemetryConfiguration.addSpanExporter(spanExporter)
}

fun addLogRecordExporter(logRecordExporter: LogRecordExporter) {
if (isStarted()) {
logger.logError("A LogRecordExporter can only be added before the SDK is started.", null)
return
}
moduleInitBootstrapper.openTelemetryModule.openTelemetryConfiguration.addLogExporter(logRecordExporter)
bootstrapper.openTelemetryModule.openTelemetryConfiguration.addLogExporter(logRecordExporter)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.embrace.android.embracesdk.injection

import io.embrace.android.embracesdk.EmbraceImpl
import io.embrace.android.embracesdk.internal.utils.Provider
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

/**
* Provides a lazy property delegate for fields in [EmbraceImpl]. If the SDK is not started, this will
* return null. If it is started, the value will return the value from the provider.
*
* This is convenient syntax to avoid a large number of lateinit vars in [EmbraceImpl].
*/
internal inline fun <reified T> EmbraceImpl.embraceImplInject(
noinline provider: Provider<T>
): ReadOnlyProperty<Any?, T?> = EmbraceImplFieldDelegate(::isStarted, provider)

internal class EmbraceImplFieldDelegate<T>(
private val startedCheck: () -> Boolean,
provider: Provider<T>
) : ReadOnlyProperty<Any?, T?> {

// optimization: use atomic checks rather than synchronized in lazy.
// Taking out a lock is overkill for the vast majority of objects on the graph
private val value: T by lazy(LazyThreadSafetyMode.PUBLICATION, provider)

override fun getValue(thisRef: Any?, property: KProperty<*>): T? = when {
startedCheck() -> value
else -> null // not started yet - don't resolve anything.
}
}

0 comments on commit dd4a0db

Please sign in to comment.