Skip to content

Commit

Permalink
refactor: move init module to core
Browse files Browse the repository at this point in the history
  • Loading branch information
fractalwrench committed Jul 26, 2024
1 parent f08cdc7 commit fc86af6
Show file tree
Hide file tree
Showing 60 changed files with 224 additions and 271 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ import io.opentelemetry.api.trace.SpanId
import io.opentelemetry.api.trace.TraceId
import kotlin.random.Random

internal class IdGenerator(
public class IdGenerator(
private val random: Random = Random.Default
) {
/**
* Generate a valid W3C-compliant traceparent. See the format here: https://www.w3.org/TR/trace-context/#traceparent-header-field-values
*
* Note: because Embrace may be recording a span on our side for the given traceparent, we have set the "sampled" flag to indicate that.
*/
fun generateTraceparent(): String =
public fun generateTraceparent(): String =
"00-" + TraceId.fromLongs(validRandomLong(), validRandomLong()) + "-" + SpanId.fromLong(validRandomLong()) + "-01"

fun generateUUID(): String = SpanId.fromLong(validRandomLong())
public fun generateUUID(): String = SpanId.fromLong(validRandomLong())

private fun validRandomLong(): Long {
var value: Long
Expand All @@ -25,12 +25,12 @@ internal class IdGenerator(
return value
}

companion object {
public companion object {
private val INSTANCE = IdGenerator()

@JvmStatic
fun generateW3CTraceparent() = INSTANCE.generateTraceparent()
public fun generateW3CTraceparent(): String = INSTANCE.generateTraceparent()

fun generateLaunchInstanceId() = INSTANCE.generateUUID()
public fun generateLaunchInstanceId(): String = INSTANCE.generateUUID()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import android.os.Build
/**
* Information about the the device or OS that can be retrieved without disk or platform API access
*/
internal data class SystemInfo(
public data class SystemInfo(
/**
* Name of the operating system of the device. To use the Android SDK, this has to be Android, so this is always "android"
*/
Expand Down Expand Up @@ -81,3 +81,21 @@ internal fun getDeviceModel(): String {
""
}
}

/**
* Tries to determine whether the device is an emulator by looking for known models and
* manufacturers which correspond to emulators.
*
* @return true if the device is detected to be an emulator, false otherwise
*/
public fun SystemInfo.isEmulator(): Boolean =
Build.FINGERPRINT.startsWith("generic") ||
Build.FINGERPRINT.startsWith("unknown") ||
Build.FINGERPRINT.contains("emulator") ||
deviceModel.contains("google_sdk") ||
deviceModel.contains("sdk_gphone64") ||
deviceModel.contains("Emulator") ||
deviceModel.contains("Android SDK built for") ||
deviceManufacturer.contains("Genymotion") ||
Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic") ||
Build.PRODUCT.equals("google_sdk")
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,48 @@ package io.embrace.android.embracesdk.internal.injection

import io.embrace.android.embracesdk.internal.SystemInfo
import io.embrace.android.embracesdk.internal.logging.EmbLogger
import io.embrace.android.embracesdk.internal.serialization.EmbraceSerializer
import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer
import io.embrace.android.embracesdk.internal.telemetry.TelemetryService
import io.embrace.android.embracesdk.internal.telemetry.errors.InternalErrorService

/**
* A module of components and services required at [EmbraceImpl] instantiation time, i.e. before the SDK evens starts
*/
internal interface InitModule {
public interface InitModule {
/**
* Clock instance locked to the time of creation used by the SDK throughout its lifetime
*/
val clock: io.embrace.android.embracesdk.internal.clock.Clock

/**
* OpenTelemetry SDK compatible clock based on [clock]
*/
val openTelemetryClock: io.opentelemetry.sdk.common.Clock
public val clock: io.embrace.android.embracesdk.internal.clock.Clock

/**
* Service to track usage of public APIs and other internal metrics
*/
val telemetryService: TelemetryService
public val telemetryService: TelemetryService

/**
* Logger used by the SDK
*/
val logger: EmbLogger
public val logger: EmbLogger

/**
* Info about the system available at startup time without expensive disk or API calls
*/
val systemInfo: SystemInfo
public val systemInfo: SystemInfo

/**
* Unique ID generated for an instance of the app process and not related to the actual process ID assigned by the OS.
* This allows us to explicitly relate all the sessions associated with a particular app launch rather than having the backend figure
* this out by proximity for stitched sessions.
*/
val processIdentifier: String
public val processIdentifier: String

/**
* Tracks internal errors
*/
val internalErrorService: InternalErrorService
public val internalErrorService: InternalErrorService

/**
* Returns the serializer used to serialize data to JSON
*/
val jsonSerializer: EmbraceSerializer
public val jsonSerializer: PlatformSerializer
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
package io.embrace.android.embracesdk.internal.injection

import io.embrace.android.embracesdk.internal.IdGenerator
import io.embrace.android.embracesdk.internal.OpenTelemetryClock
import io.embrace.android.embracesdk.internal.SystemInfo
import io.embrace.android.embracesdk.internal.clock.NormalizedIntervalClock
import io.embrace.android.embracesdk.internal.clock.SystemClock
import io.embrace.android.embracesdk.internal.logging.EmbLogger
import io.embrace.android.embracesdk.internal.logging.EmbLoggerImpl
import io.embrace.android.embracesdk.internal.serialization.EmbraceSerializer
import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer
import io.embrace.android.embracesdk.internal.telemetry.EmbraceTelemetryService
import io.embrace.android.embracesdk.internal.telemetry.TelemetryService
import io.embrace.android.embracesdk.internal.telemetry.errors.EmbraceInternalErrorService
import io.embrace.android.embracesdk.internal.telemetry.errors.InternalErrorService

internal class InitModuleImpl(
public class InitModuleImpl(
override val clock: io.embrace.android.embracesdk.internal.clock.Clock =
NormalizedIntervalClock(systemClock = SystemClock()),
override val openTelemetryClock: io.opentelemetry.sdk.common.Clock = OpenTelemetryClock(
embraceClock = clock
),
override val logger: EmbLogger = EmbLoggerImpl(),
override val systemInfo: SystemInfo = SystemInfo()
) : InitModule {
Expand All @@ -37,7 +34,7 @@ internal class InitModuleImpl(

override val processIdentifier: String = IdGenerator.generateLaunchInstanceId()

override val jsonSerializer: EmbraceSerializer by singleton {
override val jsonSerializer: PlatformSerializer by singleton {
EmbraceSerializer()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ internal const val EMBRACE_TAG = "[Embrace]"
* Implementation of [EmbLogger] that logs to Android logcat & also allows tracking of internal
* errors for our telemetry.
*/
internal class EmbLoggerImpl : EmbLogger {
public class EmbLoggerImpl : EmbLogger {

override var internalErrorService: InternalErrorHandler? = null

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
package io.embrace.android.embracesdk.internal.spans

import io.opentelemetry.api.logs.Severity

/**
* Prefix added to OTel signal object names recorded by the SDK
*/
private const val EMBRACE_OBJECT_NAME_PREFIX = "emb-"

/**
* Prefix added to all attribute keys for all usage attributes added by the SDK
*/
private const val EMBRACE_USAGE_ATTRIBUTE_NAME_PREFIX = "emb.usage."

/**
* Return the appropriate name used for telemetry created by Embrace given the current value
*/
public fun String.toEmbraceObjectName(): String = EMBRACE_OBJECT_NAME_PREFIX + this

public fun io.embrace.android.embracesdk.Severity.toOtelSeverity(): Severity = when (this) {
io.embrace.android.embracesdk.Severity.INFO -> Severity.INFO
io.embrace.android.embracesdk.Severity.WARNING -> Severity.WARN
io.embrace.android.embracesdk.Severity.ERROR -> Severity.ERROR
}

/**
* Return the appropriate internal Embrace attribute usage name given the current string
*/
internal fun String.toEmbraceUsageAttributeName(): String = EMBRACE_USAGE_ATTRIBUTE_NAME_PREFIX + this
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ package io.embrace.android.embracesdk.internal.telemetry

import io.embrace.android.embracesdk.internal.SystemInfo
import io.embrace.android.embracesdk.internal.arch.schema.toEmbraceAttributeName
import io.embrace.android.embracesdk.internal.capture.envelope.resource.isEmulator
import io.embrace.android.embracesdk.internal.isEmulator
import io.embrace.android.embracesdk.internal.spans.toEmbraceUsageAttributeName
import java.util.concurrent.ConcurrentHashMap

/**
* Service for tracking usage of public APIs, and different internal metrics about the app.
*/
internal class EmbraceTelemetryService(
public class EmbraceTelemetryService(
private val systemInfo: SystemInfo
) : TelemetryService {

Expand Down Expand Up @@ -67,7 +67,7 @@ internal class EmbraceTelemetryService(
runCatching { KotlinVersion.CURRENT.toString() }.getOrDefault("unknown")

appAttributesMap["is_emulator".toEmbraceAttributeName()] =
runCatching { isEmulator(systemInfo).toString() }.getOrDefault("unknown")
runCatching { systemInfo.isEmulator().toString() }.getOrDefault("unknown")

return appAttributesMap
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
package io.embrace.android.embracesdk.internal.telemetry

internal interface TelemetryService {
public interface TelemetryService {

/**
* Tracks the usage of a public API by name. We only track public APIs that are called when the SDK is initialized.
* Name should be snake_case, e.g. "start_session".
*/
fun onPublicApiCalled(name: String)
public fun onPublicApiCalled(name: String)

/**
* Tracks the storage being used, in bytes. storageTelemetry is a map of storage telemetry names and their values in bytes,
* such as: emb.storage.usage -> "1234"
*/
fun logStorageTelemetry(storageTelemetry: Map<String, String>)
public fun logStorageTelemetry(storageTelemetry: Map<String, String>)

/**
* Returns a map with every telemetry value. This is called when the session ends.
* We clear the usage count map so we don't count the same usages in the next session.
*/
fun getAndClearTelemetryAttributes(): Map<String, String>
public fun getAndClearTelemetryAttributes(): Map<String, String>
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ package io.embrace.android.embracesdk.internal.telemetry.errors
import io.embrace.android.embracesdk.internal.arch.datasource.LogDataSource
import io.embrace.android.embracesdk.internal.logging.InternalErrorHandler

internal interface InternalErrorDataSource : LogDataSource, InternalErrorHandler
public interface InternalErrorDataSource : LogDataSource, InternalErrorHandler
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import io.embrace.android.embracesdk.internal.spans.toOtelSeverity
/**
* Tracks internal errors & sends them as OTel logs.
*/
internal class InternalErrorDataSourceImpl(
public class InternalErrorDataSourceImpl(
logWriter: LogWriter,
logger: EmbLogger,
) : InternalErrorDataSource,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ import io.embrace.android.embracesdk.internal.utils.Provider
* Reports an internal error to Embrace. An internal error is defined as an exception that was
* caught within Embrace code & logged to [EmbLogger].
*/
internal interface InternalErrorService : InternalErrorHandler {
var internalErrorDataSource: Provider<InternalErrorDataSource?>
public interface InternalErrorService : InternalErrorHandler {
public var internalErrorDataSource: Provider<InternalErrorDataSource?>
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.embrace.android.embracesdk.internal.serialization

import com.squareup.moshi.Moshi
import io.embrace.android.embracesdk.internal.utils.threadLocal
import okio.buffer
import okio.sink
import okio.source
Expand All @@ -12,15 +11,16 @@ import java.lang.reflect.Type
/**
* A wrapper around the JSON library to allow for thread-safe serialization.
*/
internal class EmbraceSerializer : PlatformSerializer {
public class EmbraceSerializer : PlatformSerializer {

private val impl by threadLocal {
Moshi.Builder()
.add(EmbraceUrlAdapter())
private val ref = object : ThreadLocal<Moshi>() {
override fun initialValue(): Moshi = Moshi.Builder()
.add(AppFrameworkAdapter())
.build()
}

private val impl by lazy { checkNotNull(ref.get()) }

override fun <T> toJson(src: T): String {
val clz = checkNotNull(src)::class.java
val adapter = impl.adapter<T>(clz)
Expand Down Expand Up @@ -74,9 +74,4 @@ internal class EmbraceSerializer : PlatformSerializer {
adapter.fromJson(it) ?: error("JSON conversion failed.")
}
}

inline fun <reified T> fromJsonWithTypeToken(json: String): T {
val adapter = impl.adapter(T::class.java)
return adapter.fromJson(json) ?: error("JSON conversion failed.")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.embrace.android.embracesdk.internal.serialization

import java.io.InputStream
import java.io.OutputStream
import java.lang.reflect.Type

/**
* Interface for JSON serializer wrapper than can then be wrapped for testing purposes
*/
public interface PlatformSerializer {
public fun <T> toJson(src: T): String
public fun <T> toJson(src: T, clz: Class<T>): String
public fun <T> toJson(src: T, type: Type): String
public fun <T> toJson(any: T, clazz: Class<T>, outputStream: OutputStream)
public fun <T> toJson(any: T, type: Type, outputStream: OutputStream)
public fun <T> fromJson(json: String, clz: Class<T>): T
public fun <T> fromJson(json: String, type: Type): T
public fun <T> fromJson(inputStream: InputStream, clz: Class<T>): T
public fun <T> fromJson(inputStream: InputStream, type: Type): T
}

/**
* Return the first 200 elements of [elements] as a JSON-encoded string
*/
public fun PlatformSerializer.truncatedStacktrace(
elements: Array<StackTraceElement>
): String = toJson(elements.take(200).map(StackTraceElement::toString).toList(), List::class.java)
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import io.embrace.android.embracesdk.internal.payload.NativeThreadAnrInterval
import io.embrace.android.embracesdk.internal.payload.NativeThreadAnrSample
import io.embrace.android.embracesdk.internal.payload.Span
import io.embrace.android.embracesdk.internal.payload.SpanEvent
import io.embrace.android.embracesdk.internal.serialization.EmbraceSerializer
import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer
import io.opentelemetry.api.trace.SpanId
import io.opentelemetry.sdk.trace.IdGenerator
import io.opentelemetry.semconv.ExceptionAttributes
Expand All @@ -20,7 +20,7 @@ import io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes

internal class NativeAnrOtelMapper(
private val nativeThreadSamplerService: NativeThreadSamplerService?,
private val serializer: EmbraceSerializer,
private val serializer: PlatformSerializer,
private val clock: Clock
) : DataCaptureServiceOtelConverter {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import io.embrace.android.embracesdk.internal.payload.JsException
import io.embrace.android.embracesdk.internal.payload.LegacyExceptionInfo
import io.embrace.android.embracesdk.internal.payload.ThreadInfo
import io.embrace.android.embracesdk.internal.prefs.PreferencesService
import io.embrace.android.embracesdk.internal.serialization.EmbraceSerializer
import io.embrace.android.embracesdk.internal.serialization.PlatformSerializer
import io.embrace.android.embracesdk.internal.session.orchestrator.SessionOrchestrator
import io.embrace.android.embracesdk.internal.session.properties.EmbraceSessionProperties
import io.embrace.android.embracesdk.internal.spans.toOtelSeverity
Expand All @@ -42,7 +42,7 @@ internal class CrashDataSourceImpl(
private val crashMarker: CrashFileMarker,
private val logWriter: LogWriter,
private val configService: ConfigService,
private val serializer: EmbraceSerializer,
private val serializer: PlatformSerializer,
private val logger: EmbLogger,
) : CrashDataSource,
LogDataSourceImpl(
Expand Down
Loading

0 comments on commit fc86af6

Please sign in to comment.