Skip to content

Commit 17dcf00

Browse files
committed
feat: add Otel shared per event fields
Also made all otel types internal and a few other misc cleanup.
1 parent 04a620c commit 17dcf00

File tree

14 files changed

+153
-57
lines changed

14 files changed

+153
-57
lines changed

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/CoreModule.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ import com.onesignal.debug.internal.logging.otel.crash.OneSignalCrashReporterOte
4444
import com.onesignal.debug.internal.logging.otel.crash.OneSignalCrashUploader
4545
import com.onesignal.debug.internal.logging.otel.OneSignalOpenTelemetryCrashLocal
4646
import com.onesignal.debug.internal.logging.otel.OneSignalOpenTelemetryRemote
47-
import com.onesignal.debug.internal.logging.otel.attributes.OneSignalOtelTopLevelFields
47+
import com.onesignal.debug.internal.logging.otel.attributes.OneSignalOtelFieldsPerEvent
48+
import com.onesignal.debug.internal.logging.otel.attributes.OneSignalOtelFieldsTopLevel
4849
import com.onesignal.inAppMessages.IInAppMessagesManager
4950
import com.onesignal.inAppMessages.internal.MisconfiguredIAMManager
5051
import com.onesignal.location.ILocationManager
@@ -104,7 +105,8 @@ internal class CoreModule : IModule {
104105
builder.register<OneSignalCrashHandler>().provides<IStartableService>()
105106
builder.register<OneSignalCrashUploader>().provides<IStartableService>()
106107

107-
builder.register<OneSignalOtelTopLevelFields>().provides<OneSignalOtelTopLevelFields>()
108+
builder.register<OneSignalOtelFieldsTopLevel>().provides<OneSignalOtelFieldsTopLevel>()
109+
builder.register<OneSignalOtelFieldsPerEvent>().provides<OneSignalOtelFieldsPerEvent>()
108110

109111
// Register dummy services in the event they are not configured. These dummy services
110112
// will throw an error message if the associated functionality is attempted to be used.

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/time/ITime.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,9 @@ interface ITime {
1010
* current time and midnight, January 1, 1970 UTC).
1111
*/
1212
val currentTimeMillis: Long
13+
14+
/**
15+
* Returns how long the app has been running.
16+
*/
17+
val processUptimeMillis: Long
1318
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
package com.onesignal.core.internal.time.impl
22

3+
import android.os.Build
4+
import android.os.SystemClock
5+
import androidx.annotation.RequiresApi
36
import com.onesignal.core.internal.time.ITime
47

58
internal class Time : ITime {
69
override val currentTimeMillis: Long
710
get() = System.currentTimeMillis()
11+
override val processUptimeMillis: Long
12+
@RequiresApi(Build.VERSION_CODES.N)
13+
get() = SystemClock.uptimeMillis() - android.os.Process.getStartUptimeMillis()
814
}

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/debug/internal/crash/OneSignalCrashHandler.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.onesignal.debug.internal.crash
22

3-
import android.util.Log
43
import com.onesignal.core.internal.startup.IStartableService
54
import kotlinx.coroutines.runBlocking
65

@@ -24,7 +23,7 @@ internal class OneSignalCrashHandler(
2423
override fun uncaughtException(thread: Thread, throwable: Throwable) {
2524
// Ensure we never attempt to process the same throwable instance
2625
// more than once. This would only happen if there was another crash
27-
// handler faulty in a specific way.
26+
// handler and was faulty in a specific way.
2827
synchronized(seenThrowables) {
2928
if (seenThrowables.contains(throwable))
3029
return
@@ -36,7 +35,6 @@ internal class OneSignalCrashHandler(
3635
// give a bit of time to finish and then call existingHandler.
3736
// * This way the app doesn't have to open a 2nd time to get the
3837
// crash report and should help prevent duplicated reports.
39-
Log.e("OSCrashHandling", "uncaughtException TOP")
4038
if (!isOneSignalAtFault(throwable)) {
4139
existingHandler?.uncaughtException(thread, throwable)
4240
return

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/debug/internal/logging/otel/IOneSignalOpenTelemetry.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package com.onesignal.debug.internal.logging.otel
22

3-
import io.opentelemetry.api.logs.Logger
3+
import io.opentelemetry.api.logs.LogRecordBuilder
44
import io.opentelemetry.sdk.common.CompletableResultCode
55
import io.opentelemetry.sdk.logs.export.LogRecordExporter
66

77
internal interface IOneSignalOpenTelemetry {
8-
suspend fun getLogger(): Logger
8+
suspend fun getLogger(): LogRecordBuilder
99

1010
suspend fun forceFlush(): CompletableResultCode
1111
}

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/debug/internal/logging/otel/OneSignalOpenTelemetry.kt

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,32 @@ package com.onesignal.debug.internal.logging.otel
33
import android.os.Build
44
import androidx.annotation.RequiresApi
55
import com.onesignal.core.internal.config.ConfigModelStore
6-
import com.onesignal.debug.internal.logging.otel.attributes.OneSignalOtelTopLevelFields
6+
import com.onesignal.debug.internal.logging.otel.attributes.OneSignalOtelFieldsPerEvent
7+
import com.onesignal.debug.internal.logging.otel.attributes.OneSignalOtelFieldsTopLevel
78
import com.onesignal.debug.internal.logging.otel.config.OtelConfigCrashFile
89
import com.onesignal.debug.internal.logging.otel.config.OtelConfigRemoteOneSignal
910
import com.onesignal.debug.internal.logging.otel.config.OtelConfigShared
1011
import com.onesignal.debug.internal.logging.otel.crash.IOneSignalCrashConfigProvider
11-
import io.opentelemetry.api.logs.Logger
12+
import io.opentelemetry.api.logs.LogRecordBuilder
1213
import io.opentelemetry.sdk.OpenTelemetrySdk
1314
import io.opentelemetry.sdk.common.CompletableResultCode
1415
import java.util.concurrent.TimeUnit
1516
import kotlin.coroutines.resume
1617
import kotlin.coroutines.suspendCoroutine
1718

19+
internal fun LogRecordBuilder.setAllAttributes(attributes: Map<String, String>): LogRecordBuilder {
20+
attributes.forEach { this.setAttribute(it.key, it.value) }
21+
return this
22+
}
23+
1824
internal abstract class OneSignalOpenTelemetryBase(
19-
private val _osFields: OneSignalOtelTopLevelFields
25+
private val _osTopLevelFields: OneSignalOtelFieldsTopLevel,
26+
private val _osPerEventFields: OneSignalOtelFieldsPerEvent,
2027
) : IOneSignalOpenTelemetry {
2128
private val lock = Any()
2229
private var sdk: OpenTelemetrySdk? = null
2330
protected suspend fun getSdk(): OpenTelemetrySdk {
24-
val attributes = _osFields.getAttributes()
31+
val attributes = _osTopLevelFields.getAttributes()
2532
synchronized(lock) {
2633
var localSdk = sdk
2734
if (localSdk != null) {
@@ -45,15 +52,20 @@ internal abstract class OneSignalOpenTelemetryBase(
4552
}
4653
}
4754

48-
override suspend fun getLogger(): Logger =
49-
getSdk().sdkLoggerProvider.loggerBuilder("loggerBuilder").build()
55+
override suspend fun getLogger(): LogRecordBuilder =
56+
getSdk().sdkLoggerProvider
57+
.loggerBuilder("loggerBuilder")
58+
.build()
59+
.logRecordBuilder()
60+
.setAllAttributes(_osPerEventFields.getAttributes())
5061
}
5162

5263
@RequiresApi(Build.VERSION_CODES.O)
5364
internal class OneSignalOpenTelemetryRemote(
5465
private val _configModelStore: ConfigModelStore,
55-
_osFields: OneSignalOtelTopLevelFields,
56-
) : OneSignalOpenTelemetryBase(_osFields),
66+
_osTopLevelFields: OneSignalOtelFieldsTopLevel,
67+
_osPerEventFields: OneSignalOtelFieldsPerEvent,
68+
) : OneSignalOpenTelemetryBase(_osTopLevelFields, _osPerEventFields),
5769
IOneSignalOpenTelemetryRemote {
5870
val extraHttpHeaders by lazy {
5971
mapOf(
@@ -79,8 +91,9 @@ internal class OneSignalOpenTelemetryRemote(
7991

8092
internal class OneSignalOpenTelemetryCrashLocal(
8193
private val _crashPathProvider: IOneSignalCrashConfigProvider,
82-
_osFields: OneSignalOtelTopLevelFields,
83-
) : OneSignalOpenTelemetryBase(_osFields),
94+
_osTopLevelFields: OneSignalOtelFieldsTopLevel,
95+
_osPerEventFields: OneSignalOtelFieldsPerEvent,
96+
) : OneSignalOpenTelemetryBase(_osTopLevelFields, _osPerEventFields),
8497
IOneSignalOpenTelemetryCrash {
8598
override fun getSdkInstance(attributes: Map<String, String>): OpenTelemetrySdk =
8699
OpenTelemetrySdk
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.onesignal.debug.internal.logging.otel.attributes
2+
3+
import com.onesignal.common.IDManager
4+
import com.onesignal.core.internal.application.IApplicationService
5+
import com.onesignal.core.internal.config.ConfigModelStore
6+
import com.onesignal.core.internal.time.ITime
7+
import com.onesignal.user.internal.identity.IdentityModelStore
8+
import com.squareup.wire.internal.toUnmodifiableMap
9+
10+
internal class OneSignalOtelFieldsPerEvent(
11+
private val _applicationService: IApplicationService,
12+
private val _configModelStore: ConfigModelStore,
13+
private val _identityModelStore: IdentityModelStore,
14+
private val _time: ITime,
15+
) {
16+
fun getAttributes(): Map<String, String> {
17+
val attributes: MutableMap<String, String> = mutableMapOf()
18+
19+
attributes
20+
.putIfValueNotNull(
21+
"$OS_OTEL_NAMESPACE.onesignal_id",
22+
onesignalId
23+
)
24+
.putIfValueNotNull(
25+
"$OS_OTEL_NAMESPACE.push_subscription_id",
26+
subscriptionId
27+
)
28+
29+
attributes.put("android.app.state", appState)
30+
attributes.put("process.uptime", processUptime.toString())
31+
attributes.put("thread.name", currentThreadName)
32+
33+
return attributes.toUnmodifiableMap()
34+
}
35+
36+
private val onesignalId: String? get() {
37+
val onesignalId = _identityModelStore.model.onesignalId
38+
if (IDManager.isLocalId(onesignalId)) {
39+
return null
40+
}
41+
return onesignalId
42+
}
43+
44+
private val subscriptionId: String? get() {
45+
val pushSubscriptionId = _configModelStore.model.pushSubscriptionId
46+
if (pushSubscriptionId == null ||
47+
IDManager.isLocalId(pushSubscriptionId)) {
48+
return null
49+
}
50+
return pushSubscriptionId
51+
}
52+
53+
// https://opentelemetry.io/docs/specs/semconv/registry/attributes/android/
54+
private val appState: String get() =
55+
if (_applicationService.isInForeground) "foreground" else "background"
56+
57+
// https://opentelemetry.io/docs/specs/semconv/system/process-metrics/#metric-processuptime
58+
private val processUptime: Double get() =
59+
_time.processUptimeMillis / 1_000.toDouble()
60+
61+
// https://opentelemetry.io/docs/specs/semconv/general/attributes/#general-thread-attributes
62+
private val currentThreadName: String get() =
63+
Thread.currentThread().name
64+
}

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/debug/internal/logging/otel/attributes/OneSignalOtelTopLevelFields.kt renamed to OneSignalSDK/onesignal/core/src/main/java/com/onesignal/debug/internal/logging/otel/attributes/OneSignalOtelFieldsTopLevel.kt

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,41 +9,53 @@ import com.onesignal.core.internal.config.ConfigModelStore
99
import com.onesignal.core.internal.device.IInstallIdService
1010
import com.squareup.wire.internal.toUnmodifiableMap
1111

12+
// Used on all attributes / fields we add to Otel events that is NOT part of
13+
// their spec. We do this to make it clear where the source of this field is.
14+
internal const val OS_OTEL_NAMESPACE: String = "ossdk"
15+
1216
/**
1317
* Purpose: Fields to be included in every Otel request that goes out.
1418
* Requirements: Only include fields that can NOT change during runtime,
1519
* as these are only fetched once. (Calculated fields are ok)
1620
*/
17-
class OneSignalOtelTopLevelFields(
21+
internal class OneSignalOtelFieldsTopLevel(
1822
private val _applicationService: IApplicationService,
1923
private val _configModelStore: ConfigModelStore,
2024
private val _installIdService: IInstallIdService,
2125
) {
2226
suspend fun getAttributes(): Map<String, String> {
23-
val attributes: MutableMap<String, String> =
27+
val attributes: MutableMap<String, String> =
2428
mutableMapOf(
25-
"ossdk.app_id" to _configModelStore.model.appId,
26-
"ossdk.install_id" to _installIdService.getId().toString(),
27-
"ossdk.sdk_base" to "android",
28-
"ossdk.sdk_base_version" to OneSignalUtils.sdkVersion,
29-
"ossdk.app_package_id" to
29+
"$OS_OTEL_NAMESPACE.app_id" to
30+
_configModelStore.model.appId,
31+
"$OS_OTEL_NAMESPACE.install_id" to
32+
_installIdService.getId().toString(),
33+
"$OS_OTEL_NAMESPACE.sdk_base"
34+
to "android",
35+
"$OS_OTEL_NAMESPACE.sdk_base_version" to
36+
OneSignalUtils.sdkVersion,
37+
"$OS_OTEL_NAMESPACE.app_package_id" to
3038
_applicationService.appContext.packageName,
31-
"ossdk.app_version" to
39+
"$OS_OTEL_NAMESPACE.app_version" to
3240
(AndroidUtils.getAppVersion(_applicationService.appContext) ?: "unknown"),
33-
"device.manufacturer" to Build.MANUFACTURER,
34-
"device.model.identifier" to Build.MODEL,
35-
"os.name" to "Android",
36-
"os.version" to Build.VERSION.RELEASE,
37-
"os.build_id" to Build.ID,
41+
"device.manufacturer"
42+
to Build.MANUFACTURER,
43+
"device.model.identifier"
44+
to Build.MODEL,
45+
"os.name"
46+
to "Android",
47+
"os.version"
48+
to Build.VERSION.RELEASE,
49+
"os.build_id"
50+
to Build.ID,
3851
)
3952

4053
attributes
4154
.putIfValueNotNull(
42-
"ossdk.sdk_wrapper",
55+
"$OS_OTEL_NAMESPACE.sdk_wrapper",
4356
OneSignalWrapper.sdkType
44-
)
45-
.putIfValueNotNull(
46-
"ossdk.sdk_wrapper_version",
57+
).putIfValueNotNull(
58+
"$OS_OTEL_NAMESPACE.sdk_wrapper_version",
4759
OneSignalWrapper.sdkVersion
4860
)
4961

@@ -52,7 +64,8 @@ class OneSignalOtelTopLevelFields(
5264
}
5365

5466
internal fun <K, V> MutableMap<K, V>.putIfValueNotNull(key: K, value: V?): MutableMap<K, V> {
55-
if (value != null)
67+
if (value != null) {
5668
this[key] = value
69+
}
5770
return this
5871
}

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/debug/internal/logging/otel/config/OtelConfigCrashFile.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import io.opentelemetry.sdk.resources.Resource
1010
import java.io.File
1111
import kotlin.time.Duration.Companion.hours
1212

13-
class OtelConfigCrashFile {
13+
internal class OtelConfigCrashFile {
1414
internal object SdkLoggerProviderConfig {
1515
fun getFileLogRecordStorage(
1616
rootDir: String,

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/debug/internal/logging/otel/config/OtelConfigShared.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ internal class OtelConfigShared {
2222
Resource
2323
.getDefault()
2424
.toBuilder()
25-
// .put(ServiceAttributes.SERVICE_NAME, "OneSignalDeviceSDK")
26-
.put(ServiceAttributes.SERVICE_NAME, "OS-Android-SDK-Test")
25+
.put(ServiceAttributes.SERVICE_NAME, "OneSignalDeviceSDK")
2726
.putAll(attributes)
2827
.build()
2928
}

0 commit comments

Comments
 (0)