Skip to content

Commit 9b0478b

Browse files
committed
feat: add top level fields
Also refactored crash handle classes into their own namespace. Refactored IOneSignalOpenTelemetry properties into suspend functions, as some values we want to add to all Otel requests were suspend.
1 parent 08dbcac commit 9b0478b

File tree

10 files changed

+135
-68
lines changed

10 files changed

+135
-68
lines changed

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,16 @@ import com.onesignal.core.internal.time.ITime
3535
import com.onesignal.core.internal.time.impl.Time
3636
import com.onesignal.debug.internal.crash.IOneSignalCrashReporter
3737
import com.onesignal.debug.internal.crash.OneSignalCrashHandler
38-
import com.onesignal.debug.internal.logging.otel.IOneSignalCrashConfigProvider
38+
import com.onesignal.debug.internal.logging.otel.crash.IOneSignalCrashConfigProvider
3939
import com.onesignal.debug.internal.logging.otel.IOneSignalOpenTelemetry
4040
import com.onesignal.debug.internal.logging.otel.IOneSignalOpenTelemetryCrash
4141
import com.onesignal.debug.internal.logging.otel.IOneSignalOpenTelemetryRemote
42-
import com.onesignal.debug.internal.logging.otel.OneSignalCrashConfigProvider
43-
import com.onesignal.debug.internal.logging.otel.OneSignalCrashReporterOtel
44-
import com.onesignal.debug.internal.logging.otel.OneSignalCrashUploader
42+
import com.onesignal.debug.internal.logging.otel.crash.OneSignalCrashConfigProvider
43+
import com.onesignal.debug.internal.logging.otel.crash.OneSignalCrashReporterOtel
44+
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
4748
import com.onesignal.inAppMessages.IInAppMessagesManager
4849
import com.onesignal.inAppMessages.internal.MisconfiguredIAMManager
4950
import com.onesignal.location.ILocationManager
@@ -92,7 +93,7 @@ internal class CoreModule : IModule {
9293
// Purchase Tracking
9394
builder.register<TrackGooglePurchase>().provides<IStartableService>()
9495

95-
// TODO: Should be a startable service instead (but we need to wait for the app id...)
96+
// Remote Crash and error logging
9697
builder.register<OneSignalOpenTelemetryRemote>().provides<IOneSignalOpenTelemetry>()
9798
builder.register<OneSignalCrashReporterOtel>().provides<IOneSignalCrashReporter>()
9899
builder.register<OneSignalOpenTelemetryRemote>().provides<IOneSignalOpenTelemetryRemote>()
@@ -103,6 +104,8 @@ internal class CoreModule : IModule {
103104
builder.register<OneSignalCrashHandler>().provides<IStartableService>()
104105
builder.register<OneSignalCrashUploader>().provides<IStartableService>()
105106

107+
builder.register<OneSignalOtelTopLevelFields>().provides<OneSignalOtelTopLevelFields>()
108+
106109
// Register dummy services in the event they are not configured. These dummy services
107110
// will throw an error message if the associated functionality is attempted to be used.
108111
builder.register<MisconfiguredNotificationsManager>().provides<INotificationsManager>()

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import io.opentelemetry.sdk.common.CompletableResultCode
55
import io.opentelemetry.sdk.logs.export.LogRecordExporter
66

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

1010
suspend fun forceFlush(): CompletableResultCode
1111
}

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

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,58 @@ 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
67
import com.onesignal.debug.internal.logging.otel.config.OtelConfigCrashFile
78
import com.onesignal.debug.internal.logging.otel.config.OtelConfigRemoteOneSignal
89
import com.onesignal.debug.internal.logging.otel.config.OtelConfigShared
10+
import com.onesignal.debug.internal.logging.otel.crash.IOneSignalCrashConfigProvider
911
import io.opentelemetry.api.logs.Logger
1012
import io.opentelemetry.sdk.OpenTelemetrySdk
1113
import io.opentelemetry.sdk.common.CompletableResultCode
1214
import java.util.concurrent.TimeUnit
1315
import kotlin.coroutines.resume
1416
import kotlin.coroutines.suspendCoroutine
1517

18+
internal abstract class OneSignalOpenTelemetryBase(
19+
private val _osFields: OneSignalOtelTopLevelFields
20+
) : IOneSignalOpenTelemetry {
21+
private val lock = Any()
22+
private var sdk: OpenTelemetrySdk? = null
23+
protected suspend fun getSdk(): OpenTelemetrySdk {
24+
val attributes = _osFields.getAttributes()
25+
synchronized(lock) {
26+
var localSdk = sdk
27+
if (localSdk != null) {
28+
return localSdk
29+
}
30+
31+
localSdk = getSdkInstance(attributes)
32+
sdk = localSdk
33+
return localSdk
34+
}
35+
}
36+
37+
protected abstract fun getSdkInstance(attributes: Map<String, String>): OpenTelemetrySdk
38+
39+
override suspend fun forceFlush(): CompletableResultCode {
40+
val sdkLoggerProvider = getSdk().sdkLoggerProvider
41+
return suspendCoroutine {
42+
it.resume(
43+
sdkLoggerProvider.forceFlush().join(10, TimeUnit.SECONDS)
44+
)
45+
}
46+
}
47+
48+
override suspend fun getLogger(): Logger =
49+
getSdk().sdkLoggerProvider.loggerBuilder("loggerBuilder").build()
50+
}
51+
1652
@RequiresApi(Build.VERSION_CODES.O)
1753
internal class OneSignalOpenTelemetryRemote(
1854
private val _configModelStore: ConfigModelStore,
19-
) : IOneSignalOpenTelemetryRemote {
55+
_osFields: OneSignalOtelTopLevelFields,
56+
) : OneSignalOpenTelemetryBase(_osFields),
57+
IOneSignalOpenTelemetryRemote {
2058
val extraHttpHeaders by lazy {
2159
mapOf(
2260
"OS-App-Id" to _configModelStore.model.appId,
@@ -28,53 +66,33 @@ internal class OneSignalOpenTelemetryRemote(
2866
OtelConfigRemoteOneSignal.HttpRecordBatchExporter.create(extraHttpHeaders)
2967
}
3068

31-
private val sdk: OpenTelemetrySdk by lazy {
69+
override fun getSdkInstance(attributes: Map<String, String>): OpenTelemetrySdk =
3270
OpenTelemetrySdk
3371
.builder()
3472
.setLoggerProvider(
3573
OtelConfigRemoteOneSignal.SdkLoggerProviderConfig.create(
36-
OtelConfigShared.ResourceConfig.create(_configModelStore.model),
74+
OtelConfigShared.ResourceConfig.create(attributes),
3775
extraHttpHeaders
3876
)
3977
).build()
40-
}
41-
42-
override val logger: Logger
43-
get() = sdk.sdkLoggerProvider.loggerBuilder("loggerBuilder").build()
44-
45-
override suspend fun forceFlush(): CompletableResultCode =
46-
suspendCoroutine {
47-
it.resume(
48-
sdk.sdkLoggerProvider.forceFlush().join(10, TimeUnit.SECONDS)
49-
)
50-
}
5178
}
5279

53-
@RequiresApi(Build.VERSION_CODES.O)
5480
internal class OneSignalOpenTelemetryCrashLocal(
55-
private val _configModelStore: ConfigModelStore,
5681
private val _crashPathProvider: IOneSignalCrashConfigProvider,
57-
) : IOneSignalOpenTelemetryCrash {
58-
private val sdk: OpenTelemetrySdk by lazy {
82+
_osFields: OneSignalOtelTopLevelFields,
83+
) : OneSignalOpenTelemetryBase(_osFields),
84+
IOneSignalOpenTelemetryCrash {
85+
override fun getSdkInstance(attributes: Map<String, String>): OpenTelemetrySdk =
5986
OpenTelemetrySdk
6087
.builder()
6188
.setLoggerProvider(
6289
OtelConfigCrashFile.SdkLoggerProviderConfig.create(
63-
OtelConfigShared.ResourceConfig.create(_configModelStore.model),
90+
OtelConfigShared.ResourceConfig.create(
91+
attributes
92+
),
6493
_crashPathProvider.path,
6594
_crashPathProvider.minFileAgeForReadMillis,
6695
)
6796
)
6897
.build()
69-
}
70-
71-
override val logger: Logger
72-
get() = sdk.sdkLoggerProvider.loggerBuilder("loggerBuilder").build()
73-
74-
override suspend fun forceFlush(): CompletableResultCode =
75-
suspendCoroutine {
76-
it.resume(
77-
sdk.sdkLoggerProvider.forceFlush().join(10, TimeUnit.SECONDS)
78-
)
79-
}
8098
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.onesignal.debug.internal.logging.otel.attributes
2+
3+
import android.os.Build
4+
import com.onesignal.common.AndroidUtils
5+
import com.onesignal.common.OneSignalUtils
6+
import com.onesignal.common.OneSignalWrapper
7+
import com.onesignal.core.internal.application.IApplicationService
8+
import com.onesignal.core.internal.config.ConfigModelStore
9+
import com.onesignal.core.internal.device.IInstallIdService
10+
import com.squareup.wire.internal.toUnmodifiableMap
11+
12+
/**
13+
* Purpose: Fields to be included in every Otel request that goes out.
14+
* Requirements: Only include fields that can NOT change during runtime,
15+
* as these are only fetched once. (Calculated fields are ok)
16+
*/
17+
class OneSignalOtelTopLevelFields(
18+
private val _applicationService: IApplicationService,
19+
private val _configModelStore: ConfigModelStore,
20+
private val _installIdService: IInstallIdService,
21+
) {
22+
suspend fun getAttributes(): Map<String, String> {
23+
val attributes: MutableMap<String, String> =
24+
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
30+
_applicationService.appContext.packageName,
31+
"ossdk.app_version" to
32+
(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,
38+
)
39+
40+
attributes
41+
.putIfValueNotNull(
42+
"ossdk.sdk_wrapper",
43+
OneSignalWrapper.sdkType
44+
)
45+
.putIfValueNotNull(
46+
"ossdk.sdk_wrapper_version",
47+
OneSignalWrapper.sdkVersion
48+
)
49+
50+
return attributes.toUnmodifiableMap()
51+
}
52+
}
53+
54+
internal fun <K, V> MutableMap<K, V>.putIfValueNotNull(key: K, value: V?): MutableMap<K, V> {
55+
if (value != null)
56+
this[key] = value
57+
return this
58+
}

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package com.onesignal.debug.internal.logging.otel.config
22

3-
import android.os.Build
4-
import androidx.annotation.RequiresApi
53
import com.onesignal.debug.internal.logging.otel.config.OtelConfigShared.LogLimitsConfig
6-
import com.onesignal.debug.internal.logging.otel.config.OtelConfigShared.LogLimitsConfig.logLimits
74
import io.opentelemetry.contrib.disk.buffering.exporters.LogRecordToDiskExporter
85
import io.opentelemetry.contrib.disk.buffering.storage.impl.FileLogRecordStorage
96
import io.opentelemetry.contrib.disk.buffering.storage.impl.FileStorageConfiguration
@@ -25,7 +22,7 @@ class OtelConfigCrashFile {
2522
.builder()
2623
// NOTE: Only use such as small maxFileAgeForWrite for
2724
// crashes, as we want to send them as soon as possible
28-
// without have to wait too long for buffers.
25+
// without having to wait too long for buffers.
2926
.setMaxFileAgeForWriteMillis(2_000)
3027
.setMinFileAgeForReadMillis(minFileAgeForReadMillis)
3128
.setMaxFileAgeForReadMillis(72.hours.inWholeMilliseconds)

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,29 @@ package com.onesignal.debug.internal.logging.otel.config
22

33
import android.os.Build
44
import androidx.annotation.RequiresApi
5-
import com.onesignal.core.internal.config.ConfigModel
65
import io.opentelemetry.sdk.logs.LogLimits
76
import io.opentelemetry.sdk.logs.LogRecordProcessor
87
import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor
98
import io.opentelemetry.sdk.logs.export.LogRecordExporter
109
import io.opentelemetry.sdk.resources.Resource
10+
import io.opentelemetry.sdk.resources.ResourceBuilder
1111
import io.opentelemetry.semconv.ServiceAttributes
1212
import java.time.Duration
1313

14+
internal fun ResourceBuilder.putAll(attributes: Map<String, String>): ResourceBuilder {
15+
attributes.forEach { this.put(it.key, it.value) }
16+
return this
17+
}
18+
1419
internal class OtelConfigShared {
1520
object ResourceConfig {
16-
fun create(configModel: ConfigModel): Resource =
21+
fun create(attributes: Map<String, String>): Resource =
1722
Resource
1823
.getDefault()
1924
.toBuilder()
20-
// .put(ServiceAttributes.SERVICE_NAME, "OneSignalDeviceSDK")
25+
// .put(ServiceAttributes.SERVICE_NAME, "OneSignalDeviceSDK")
2126
.put(ServiceAttributes.SERVICE_NAME, "OS-Android-SDK-Test")
22-
.put("ossdk.app_id", configModel.appId)
23-
// TODO: other fields
24-
// TODO: Why not set all top level fields here? Use a top level provider
27+
.putAll(attributes)
2528
.build()
2629
}
2730

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/debug/internal/logging/otel/IOneSignalCrashConfigProvider.kt renamed to OneSignalSDK/onesignal/core/src/main/java/com/onesignal/debug/internal/logging/otel/crash/IOneSignalCrashConfigProvider.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.onesignal.debug.internal.logging.otel
1+
package com.onesignal.debug.internal.logging.otel.crash
22

33
interface IOneSignalCrashConfigProvider {
44
val path: String

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/debug/internal/logging/otel/OneSignalCrashConfigProvider.kt renamed to OneSignalSDK/onesignal/core/src/main/java/com/onesignal/debug/internal/logging/otel/crash/OneSignalCrashConfigProvider.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.onesignal.debug.internal.logging.otel
1+
package com.onesignal.debug.internal.logging.otel.crash
22

33
import com.onesignal.core.internal.application.IApplicationService
44
import java.io.File

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/debug/internal/logging/otel/OneSignalCrashReporterOtel.kt renamed to OneSignalSDK/onesignal/core/src/main/java/com/onesignal/debug/internal/logging/otel/crash/OneSignalCrashReporterOtel.kt

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
package com.onesignal.debug.internal.logging.otel
1+
package com.onesignal.debug.internal.logging.otel.crash
22

3-
import android.os.Build
43
import android.util.Log
5-
import androidx.annotation.RequiresApi
64
import com.onesignal.debug.internal.crash.IOneSignalCrashReporter
5+
import com.onesignal.debug.internal.logging.otel.IOneSignalOpenTelemetryCrash
76
import io.opentelemetry.api.common.Attributes
8-
import java.io.PrintWriter
9-
import java.io.StringWriter
107

11-
@RequiresApi(Build.VERSION_CODES.O)
128
internal class OneSignalCrashReporterOtel(
139
val _openTelemetry: IOneSignalOpenTelemetryCrash
1410
) : IOneSignalCrashReporter {
@@ -18,11 +14,12 @@ internal class OneSignalCrashReporterOtel(
1814
private const val EXCEPTION_STACKTRACE = "exception.stacktrace"
1915
}
2016

21-
override suspend fun sendCrash(therad: Thread, throwable: Throwable) {
17+
override suspend fun sendCrash(thread: Thread, throwable: Throwable) {
2218
Log.e("OSCrashHandling", "sendCrash TOP")
2319
val attributesBuilder =
2420
Attributes
2521
.builder()
22+
.put(EXCEPTION_MESSAGE, throwable.message)
2623
.put(EXCEPTION_STACKTRACE, throwable.stackTraceToString())
2724
.put(EXCEPTION_TYPE, throwable.javaClass.name)
2825
.build()
@@ -33,22 +30,12 @@ internal class OneSignalCrashReporterOtel(
3330
// message.append("Process: ").append(processName).append(", ");
3431
// }
3532

36-
_openTelemetry.logger
33+
_openTelemetry.getLogger()
3734
.logRecordBuilder()
3835
.setAllAttributes(attributesBuilder)
3936
.emit()
4037

4138
_openTelemetry.forceFlush()
4239
Log.e("OSCrashHandling", "sendCrash BOTTOM")
4340
}
44-
45-
private fun stackTraceToString(throwable: Throwable): String {
46-
val stringWriter = StringWriter(256)
47-
val printWriter = PrintWriter(stringWriter)
48-
49-
throwable.printStackTrace(printWriter)
50-
printWriter.flush()
51-
52-
return stringWriter.toString()
53-
}
5441
}

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/debug/internal/logging/otel/OneSignalCrashUploader.kt renamed to OneSignalSDK/onesignal/core/src/main/java/com/onesignal/debug/internal/logging/otel/crash/OneSignalCrashUploader.kt

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

33
import com.onesignal.core.internal.startup.IStartableService
4+
import com.onesignal.debug.internal.logging.otel.crash.IOneSignalCrashConfigProvider
5+
import com.onesignal.debug.internal.logging.otel.IOneSignalOpenTelemetryRemote
46
import com.onesignal.debug.internal.logging.otel.config.OtelConfigCrashFile
57
import io.opentelemetry.sdk.logs.data.LogRecordData
68
import kotlinx.coroutines.delay
79
import kotlinx.coroutines.runBlocking
810
import java.util.concurrent.TimeUnit
911

10-
1112
/**
1213
* Purpose: This reads a local crash report files created by OneSignal's
1314
* crash handler and sends them to OneSignal on the app's next start.

0 commit comments

Comments
 (0)