@dreamhorizonorg/pulse-react-native 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/PulseReactNativeOtel.podspec +21 -0
- package/README.md +900 -0
- package/android/build.gradle +97 -0
- package/android/gradle.properties +5 -0
- package/android/proguard-rules.pro +108 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/pulsereactnativeotel/PulseOtelConstants.kt +20 -0
- package/android/src/main/java/com/pulsereactnativeotel/PulseReactNativeOtelLogger.kt +73 -0
- package/android/src/main/java/com/pulsereactnativeotel/PulseReactNativeOtelModule.kt +85 -0
- package/android/src/main/java/com/pulsereactnativeotel/PulseReactNativeOtelPackage.kt +33 -0
- package/android/src/main/java/com/pulsereactnativeotel/PulseReactNativeOtelTracer.kt +127 -0
- package/ios/PulseReactNativeOtel.h +5 -0
- package/ios/PulseReactNativeOtel.mm +100 -0
- package/lib/module/NativePulseReactNativeOtel.js +5 -0
- package/lib/module/NativePulseReactNativeOtel.js.map +1 -0
- package/lib/module/config.js +55 -0
- package/lib/module/config.js.map +1 -0
- package/lib/module/errorBoundary.js +62 -0
- package/lib/module/errorBoundary.js.map +1 -0
- package/lib/module/errorHandler.js +112 -0
- package/lib/module/errorHandler.js.map +1 -0
- package/lib/module/events.js +14 -0
- package/lib/module/events.js.map +1 -0
- package/lib/module/global.d.js +4 -0
- package/lib/module/global.d.js.map +1 -0
- package/lib/module/globalAttributes.js +13 -0
- package/lib/module/globalAttributes.js.map +1 -0
- package/lib/module/index.js +27 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/initialization.js +18 -0
- package/lib/module/initialization.js.map +1 -0
- package/lib/module/network-interceptor/initialization.js +25 -0
- package/lib/module/network-interceptor/initialization.js.map +1 -0
- package/lib/module/network-interceptor/network.interface.js +2 -0
- package/lib/module/network-interceptor/network.interface.js.map +1 -0
- package/lib/module/network-interceptor/request-tracker-fetch.js +72 -0
- package/lib/module/network-interceptor/request-tracker-fetch.js.map +1 -0
- package/lib/module/network-interceptor/request-tracker-xhr.js +75 -0
- package/lib/module/network-interceptor/request-tracker-xhr.js.map +1 -0
- package/lib/module/network-interceptor/request-tracker.js +25 -0
- package/lib/module/network-interceptor/request-tracker.js.map +1 -0
- package/lib/module/network-interceptor/span-helpers.js +62 -0
- package/lib/module/network-interceptor/span-helpers.js.map +1 -0
- package/lib/module/network-interceptor/url-helper.js +127 -0
- package/lib/module/network-interceptor/url-helper.js.map +1 -0
- package/lib/module/pulse.interface.js +2 -0
- package/lib/module/pulse.interface.js.map +1 -0
- package/lib/module/reactNavigation.js +100 -0
- package/lib/module/reactNavigation.js.map +1 -0
- package/lib/module/trace.js +75 -0
- package/lib/module/trace.js.map +1 -0
- package/lib/module/user.js +23 -0
- package/lib/module/user.js.map +1 -0
- package/lib/module/utility.js +34 -0
- package/lib/module/utility.js.map +1 -0
- package/lib/typescript/src/NativePulseReactNativeOtel.d.ts +31 -0
- package/lib/typescript/src/NativePulseReactNativeOtel.d.ts.map +1 -0
- package/lib/typescript/src/config.d.ts +14 -0
- package/lib/typescript/src/config.d.ts.map +1 -0
- package/lib/typescript/src/errorBoundary.d.ts +26 -0
- package/lib/typescript/src/errorBoundary.d.ts.map +1 -0
- package/lib/typescript/src/errorHandler.d.ts +4 -0
- package/lib/typescript/src/errorHandler.d.ts.map +1 -0
- package/lib/typescript/src/events.d.ts +3 -0
- package/lib/typescript/src/events.d.ts.map +1 -0
- package/lib/typescript/src/globalAttributes.d.ts +4 -0
- package/lib/typescript/src/globalAttributes.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +30 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/initialization.d.ts +3 -0
- package/lib/typescript/src/initialization.d.ts.map +1 -0
- package/lib/typescript/src/network-interceptor/initialization.d.ts +3 -0
- package/lib/typescript/src/network-interceptor/initialization.d.ts.map +1 -0
- package/lib/typescript/src/network-interceptor/network.interface.d.ts +29 -0
- package/lib/typescript/src/network-interceptor/network.interface.d.ts.map +1 -0
- package/lib/typescript/src/network-interceptor/request-tracker-fetch.d.ts +7 -0
- package/lib/typescript/src/network-interceptor/request-tracker-fetch.d.ts.map +1 -0
- package/lib/typescript/src/network-interceptor/request-tracker-xhr.d.ts +4 -0
- package/lib/typescript/src/network-interceptor/request-tracker-xhr.d.ts.map +1 -0
- package/lib/typescript/src/network-interceptor/request-tracker.d.ts +9 -0
- package/lib/typescript/src/network-interceptor/request-tracker.d.ts.map +1 -0
- package/lib/typescript/src/network-interceptor/span-helpers.d.ts +7 -0
- package/lib/typescript/src/network-interceptor/span-helpers.d.ts.map +1 -0
- package/lib/typescript/src/network-interceptor/url-helper.d.ts +38 -0
- package/lib/typescript/src/network-interceptor/url-helper.d.ts.map +1 -0
- package/lib/typescript/src/pulse.interface.d.ts +8 -0
- package/lib/typescript/src/pulse.interface.d.ts.map +1 -0
- package/lib/typescript/src/reactNavigation.d.ts +10 -0
- package/lib/typescript/src/reactNavigation.d.ts.map +1 -0
- package/lib/typescript/src/trace.d.ts +19 -0
- package/lib/typescript/src/trace.d.ts.map +1 -0
- package/lib/typescript/src/user.d.ts +5 -0
- package/lib/typescript/src/user.d.ts.map +1 -0
- package/lib/typescript/src/utility.d.ts +8 -0
- package/lib/typescript/src/utility.d.ts.map +1 -0
- package/package.json +165 -0
- package/src/NativePulseReactNativeOtel.ts +57 -0
- package/src/config.ts +75 -0
- package/src/errorBoundary.tsx +92 -0
- package/src/errorHandler.ts +169 -0
- package/src/events.ts +15 -0
- package/src/global.d.ts +9 -0
- package/src/globalAttributes.ts +16 -0
- package/src/index.tsx +35 -0
- package/src/initialization.ts +18 -0
- package/src/network-interceptor/initialization.ts +28 -0
- package/src/network-interceptor/network.interface.ts +36 -0
- package/src/network-interceptor/request-tracker-fetch.ts +96 -0
- package/src/network-interceptor/request-tracker-xhr.ts +108 -0
- package/src/network-interceptor/request-tracker.ts +31 -0
- package/src/network-interceptor/span-helpers.ts +84 -0
- package/src/network-interceptor/url-helper.ts +155 -0
- package/src/pulse.interface.ts +14 -0
- package/src/reactNavigation.tsx +146 -0
- package/src/trace.ts +108 -0
- package/src/user.ts +24 -0
- package/src/utility.ts +36 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
ext.getExtOrDefault = {name ->
|
|
3
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['PulseReactNativeOtel_' + name]
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
repositories {
|
|
7
|
+
google()
|
|
8
|
+
mavenCentral()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
dependencies {
|
|
12
|
+
classpath "com.android.tools.build:gradle:8.7.2"
|
|
13
|
+
// noinspection DifferentKotlinGradleVersion
|
|
14
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
apply plugin: "com.android.library"
|
|
20
|
+
apply plugin: "kotlin-android"
|
|
21
|
+
|
|
22
|
+
apply plugin: "com.facebook.react"
|
|
23
|
+
|
|
24
|
+
def getExtOrIntegerDefault(name) {
|
|
25
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["PulseReactNativeOtel_" + name]).toInteger()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
android {
|
|
29
|
+
namespace "com.pulsereactnativeotel"
|
|
30
|
+
|
|
31
|
+
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
32
|
+
|
|
33
|
+
defaultConfig {
|
|
34
|
+
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
35
|
+
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
buildFeatures {
|
|
39
|
+
buildConfig true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
buildTypes {
|
|
43
|
+
release {
|
|
44
|
+
minifyEnabled false
|
|
45
|
+
consumerProguardFiles 'proguard-rules.pro'
|
|
46
|
+
}
|
|
47
|
+
debug {
|
|
48
|
+
consumerProguardFiles 'proguard-rules.pro'
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
lintOptions {
|
|
53
|
+
disable "GradleCompatible"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
compileOptions {
|
|
57
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
58
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
sourceSets {
|
|
62
|
+
main {
|
|
63
|
+
java.srcDirs += [
|
|
64
|
+
"generated/java",
|
|
65
|
+
"generated/jni"
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
repositories {
|
|
72
|
+
mavenCentral()
|
|
73
|
+
google()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
77
|
+
|
|
78
|
+
dependencies {
|
|
79
|
+
implementation("com.facebook.react:react-android")
|
|
80
|
+
implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version")
|
|
81
|
+
|
|
82
|
+
api("org.dreamhorizon:pulse-android-sdk:0.0.1-alpha") {
|
|
83
|
+
exclude group: "com.squareup.okhttp3", module: "okhttp-jvm"
|
|
84
|
+
}
|
|
85
|
+
api("com.squareup.okhttp3:okhttp:5.0.0-alpha.14")
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
configurations.all {
|
|
89
|
+
resolutionStrategy {
|
|
90
|
+
eachDependency {
|
|
91
|
+
if (requested.group == "com.squareup.okhttp3" && requested.name == "okhttp-jvm") {
|
|
92
|
+
useTarget("com.squareup.okhttp3:okhttp:${requested.version}")
|
|
93
|
+
because("Resolving okhttp-jvm to okhttp for Android compatibility")
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Pulse React Native OpenTelemetry - Consumer ProGuard Rules
|
|
2
|
+
# These rules are automatically applied to consuming applications
|
|
3
|
+
|
|
4
|
+
# ===== OpenTelemetry ProGuard Rules =====
|
|
5
|
+
# OpenTelemetry is heavily reflection-based and needs these classes preserved
|
|
6
|
+
|
|
7
|
+
# Keep all OpenTelemetry classes
|
|
8
|
+
-keep class io.opentelemetry.** { *; }
|
|
9
|
+
-dontwarn io.opentelemetry.**
|
|
10
|
+
|
|
11
|
+
# Keep OpenTelemetry API
|
|
12
|
+
-keep class io.opentelemetry.api.** { *; }
|
|
13
|
+
-keep interface io.opentelemetry.api.** { *; }
|
|
14
|
+
|
|
15
|
+
# Keep OpenTelemetry SDK
|
|
16
|
+
-keep class io.opentelemetry.sdk.** { *; }
|
|
17
|
+
|
|
18
|
+
# Keep OpenTelemetry Context (critical for span propagation)
|
|
19
|
+
-keep class io.opentelemetry.context.** { *; }
|
|
20
|
+
-keep class io.opentelemetry.context.Context { *; }
|
|
21
|
+
-keep class io.opentelemetry.context.Scope { *; }
|
|
22
|
+
-keep class io.opentelemetry.context.ImplicitContextKeyed { *; }
|
|
23
|
+
-keep interface io.opentelemetry.context.** { *; }
|
|
24
|
+
|
|
25
|
+
# ===== Missing Compile-Time Annotations =====
|
|
26
|
+
# These annotations are compile-time only and not needed at runtime
|
|
27
|
+
# R8/ProGuard should ignore them when missing
|
|
28
|
+
|
|
29
|
+
-dontwarn com.google.errorprone.annotations.**
|
|
30
|
+
-dontwarn javax.annotation.**
|
|
31
|
+
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
|
|
32
|
+
-dontwarn edu.umd.cs.findbugs.annotations.**
|
|
33
|
+
-dontwarn org.checkerframework.checker.**
|
|
34
|
+
-dontwarn com.google.j2objc.annotations.**
|
|
35
|
+
|
|
36
|
+
# Specifically ignore the MustBeClosed annotation that R8 complains about
|
|
37
|
+
-dontwarn com.google.errorprone.annotations.MustBeClosed
|
|
38
|
+
-dontwarn com.google.errorprone.annotations.CanIgnoreReturnValue
|
|
39
|
+
-dontwarn com.google.errorprone.annotations.CheckReturnValue
|
|
40
|
+
-dontwarn com.google.errorprone.annotations.concurrent.**
|
|
41
|
+
|
|
42
|
+
# ===== Missing Java SE Classes (not available in Android) =====
|
|
43
|
+
# java.beans package is part of Java SE but not Android
|
|
44
|
+
# These are referenced by libraries like SnakeYAML and Jackson but not used at runtime
|
|
45
|
+
|
|
46
|
+
-dontwarn java.beans.**
|
|
47
|
+
-dontwarn java.beans.BeanInfo
|
|
48
|
+
-dontwarn java.beans.ConstructorProperties
|
|
49
|
+
-dontwarn java.beans.FeatureDescriptor
|
|
50
|
+
-dontwarn java.beans.IntrospectionException
|
|
51
|
+
-dontwarn java.beans.Introspector
|
|
52
|
+
-dontwarn java.beans.PropertyDescriptor
|
|
53
|
+
-dontwarn java.beans.Transient
|
|
54
|
+
|
|
55
|
+
# SnakeYAML uses java.beans for reflection but has fallbacks
|
|
56
|
+
-dontwarn org.yaml.snakeyaml.introspector.MethodProperty
|
|
57
|
+
|
|
58
|
+
# Jackson uses java.beans for annotations but has fallbacks
|
|
59
|
+
-dontwarn com.fasterxml.jackson.databind.ext.Java7SupportImpl
|
|
60
|
+
|
|
61
|
+
# ===== Pulse React Native OTEL =====
|
|
62
|
+
# Keep our library classes
|
|
63
|
+
-keep class com.pulsereactnativeotel.** { *; }
|
|
64
|
+
-keepclassmembers class com.pulsereactnativeotel.** { *; }
|
|
65
|
+
|
|
66
|
+
# Keep Kotlin metadata
|
|
67
|
+
-keep class kotlin.Metadata { *; }
|
|
68
|
+
|
|
69
|
+
# ===== General Rules =====
|
|
70
|
+
# Keep attributes needed for proper functioning
|
|
71
|
+
-keepattributes Signature
|
|
72
|
+
-keepattributes *Annotation*
|
|
73
|
+
-keepattributes InnerClasses
|
|
74
|
+
-keepattributes EnclosingMethod
|
|
75
|
+
-keepattributes Exceptions
|
|
76
|
+
-keepattributes SourceFile
|
|
77
|
+
-keepattributes LineNumberTable
|
|
78
|
+
|
|
79
|
+
# Keep generic signatures for proper type checking
|
|
80
|
+
-keepattributes Signature
|
|
81
|
+
|
|
82
|
+
# ===== React Native Specific =====
|
|
83
|
+
# Keep native method names
|
|
84
|
+
-keepclasseswithmembernames class * {
|
|
85
|
+
native <methods>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# Keep React Native annotations
|
|
89
|
+
-keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
|
|
90
|
+
-keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
|
|
91
|
+
-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
|
|
92
|
+
-keep,allowobfuscation @interface com.facebook.jni.annotations.DoNotStrip
|
|
93
|
+
|
|
94
|
+
-keep @com.facebook.proguard.annotations.DoNotStrip class *
|
|
95
|
+
-keep @com.facebook.common.internal.DoNotStrip class *
|
|
96
|
+
-keep @com.facebook.jni.annotations.DoNotStrip class *
|
|
97
|
+
|
|
98
|
+
-keepclassmembers class * {
|
|
99
|
+
@com.facebook.proguard.annotations.DoNotStrip *;
|
|
100
|
+
@com.facebook.common.internal.DoNotStrip *;
|
|
101
|
+
@com.facebook.jni.annotations.DoNotStrip *;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
-keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
|
|
105
|
+
void set*(***);
|
|
106
|
+
*** get*();
|
|
107
|
+
}
|
|
108
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
package com.pulsereactnativeotel
|
|
2
|
+
|
|
3
|
+
object PulseOtelConstants {
|
|
4
|
+
const val INSTRUMENTATION_SCOPE = "com.pulsereactnativeotel"
|
|
5
|
+
|
|
6
|
+
const val ATTR_ERROR_FATAL = "error.fatal"
|
|
7
|
+
const val ATTR_ERROR_TYPE = "exception.type"
|
|
8
|
+
const val ATTR_ERROR_MESSAGE = "exception.message"
|
|
9
|
+
const val ATTR_ERROR_STACK = "exception.stacktrace"
|
|
10
|
+
const val ATTR_ERROR_SOURCE = "error.source"
|
|
11
|
+
|
|
12
|
+
const val ATTR_THREAD_ID = "thread.id"
|
|
13
|
+
const val ATTR_THREAD_NAME = "thread.name"
|
|
14
|
+
|
|
15
|
+
const val PLATFORM_REACT_NATIVE = "react-native"
|
|
16
|
+
const val DEFAULT_ERROR_TYPE = "javascript"
|
|
17
|
+
const val ERROR_SOURCE_JS = "js"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
package com.pulsereactnativeotel
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReadableArray
|
|
4
|
+
import com.facebook.react.bridge.ReadableMap
|
|
5
|
+
import com.facebook.react.bridge.ReadableType
|
|
6
|
+
import com.pulse.android.sdk.PulseSDK
|
|
7
|
+
|
|
8
|
+
object PulseReactNativeOtelLogger {
|
|
9
|
+
|
|
10
|
+
fun trackEvent(
|
|
11
|
+
event: String,
|
|
12
|
+
observedTimeMs: Long,
|
|
13
|
+
properties: ReadableMap?
|
|
14
|
+
) {
|
|
15
|
+
val params = buildMap {
|
|
16
|
+
properties?.let { putAll(it.toMap()) }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
PulseSDK.INSTANCE.trackEvent(event, observedTimeMs, params)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
fun reportException(
|
|
23
|
+
errorMessage: String,
|
|
24
|
+
observedTimeMs: Long,
|
|
25
|
+
stackTrace: String,
|
|
26
|
+
isFatal: Boolean,
|
|
27
|
+
errorType: String,
|
|
28
|
+
attributes: ReadableMap?
|
|
29
|
+
) {
|
|
30
|
+
val params = buildMap {
|
|
31
|
+
put(PulseOtelConstants.ATTR_ERROR_TYPE, errorType.ifEmpty { PulseOtelConstants.DEFAULT_ERROR_TYPE })
|
|
32
|
+
put(PulseOtelConstants.ATTR_ERROR_FATAL, isFatal)
|
|
33
|
+
put(PulseOtelConstants.ATTR_ERROR_MESSAGE, errorMessage)
|
|
34
|
+
put(PulseOtelConstants.ATTR_ERROR_STACK, stackTrace)
|
|
35
|
+
put(PulseOtelConstants.ATTR_THREAD_ID, getCurrentThreadId())
|
|
36
|
+
put(PulseOtelConstants.ATTR_THREAD_NAME, Thread.currentThread().name)
|
|
37
|
+
put(PulseOtelConstants.ATTR_ERROR_SOURCE, PulseOtelConstants.ERROR_SOURCE_JS)
|
|
38
|
+
attributes?.let { putAll(it.toMap()) }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
PulseSDK.INSTANCE.trackNonFatal(errorMessage, observedTimeMs, params)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private fun getCurrentThreadId(): String {
|
|
45
|
+
@Suppress("DEPRECATION")
|
|
46
|
+
return Thread.currentThread().id.toString()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private fun ReadableMap.toMap(): Map<String, Any?> = buildMap {
|
|
50
|
+
entryIterator.forEach { (key, value) ->
|
|
51
|
+
put(key, value?.toAny())
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private fun Any.toAny(): Any = when (this) {
|
|
56
|
+
is String -> this
|
|
57
|
+
is Number -> this
|
|
58
|
+
is Boolean -> this
|
|
59
|
+
is ReadableArray -> this.toList()
|
|
60
|
+
else -> this.toString()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private fun ReadableArray.toList(): List<Any?> {
|
|
64
|
+
if (size() == 0) return emptyList()
|
|
65
|
+
|
|
66
|
+
return when (getType(0)) {
|
|
67
|
+
ReadableType.String -> toArrayList()
|
|
68
|
+
ReadableType.Number -> toArrayList()
|
|
69
|
+
ReadableType.Boolean -> toArrayList()
|
|
70
|
+
else -> emptyList()
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
package com.pulsereactnativeotel
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
4
|
+
import com.facebook.react.bridge.ReadableMap
|
|
5
|
+
import com.facebook.react.module.annotations.ReactModule
|
|
6
|
+
import com.pulse.android.sdk.PulseSDK
|
|
7
|
+
import android.os.Looper
|
|
8
|
+
import android.util.Log
|
|
9
|
+
import android.os.Handler
|
|
10
|
+
|
|
11
|
+
@ReactModule(name = PulseReactNativeOtelModule.NAME)
|
|
12
|
+
class PulseReactNativeOtelModule(reactContext: ReactApplicationContext) :
|
|
13
|
+
NativePulseReactNativeOtelSpec(reactContext) {
|
|
14
|
+
|
|
15
|
+
override fun getName(): String {
|
|
16
|
+
return NAME
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
override fun isInitialized(): Boolean {
|
|
20
|
+
return PulseSDK.INSTANCE.isInitialized()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
override fun trackEvent(event: String, observedTimeMs: Double, properties: ReadableMap?): Boolean {
|
|
24
|
+
PulseReactNativeOtelLogger.trackEvent(event, observedTimeMs.toLong(), properties)
|
|
25
|
+
return true
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
override fun reportException(errorMessage: String, observedTimeMs: Double, stackTrace: String, isFatal: Boolean, errorType: String, attributes: ReadableMap?): Boolean {
|
|
29
|
+
PulseReactNativeOtelLogger.reportException(errorMessage, observedTimeMs.toLong(), stackTrace, isFatal, errorType, attributes)
|
|
30
|
+
return true
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
override fun startSpan(name: String, attributes: ReadableMap?): String {
|
|
34
|
+
return PulseReactNativeOtelTracer.startSpan(name, attributes)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
override fun endSpan(spanId: String, statusCode: String?): Boolean {
|
|
38
|
+
PulseReactNativeOtelTracer.endSpan(spanId, statusCode)
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
override fun addSpanEvent(spanId: String, name: String, attributes: ReadableMap?): Boolean {
|
|
43
|
+
PulseReactNativeOtelTracer.addEvent(spanId, name, attributes)
|
|
44
|
+
return true
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
override fun setSpanAttributes(spanId: String, attributes: ReadableMap?): Boolean {
|
|
48
|
+
PulseReactNativeOtelTracer.setAttributes(spanId, attributes)
|
|
49
|
+
return true
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
override fun recordSpanException(spanId: String, errorMessage: String, stackTrace: String?): Boolean {
|
|
53
|
+
PulseReactNativeOtelTracer.recordException(spanId, errorMessage, stackTrace)
|
|
54
|
+
return true
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
override fun setUserId(id: String?) {
|
|
58
|
+
PulseSDK.INSTANCE.setUserId(id)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
override fun setUserProperty(name: String, value: String?) {
|
|
62
|
+
PulseSDK.INSTANCE.setUserProperty(name, value)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
override fun setUserProperties(properties: ReadableMap?) {
|
|
66
|
+
properties?.let { props ->
|
|
67
|
+
PulseSDK.INSTANCE.setUserProperties {
|
|
68
|
+
props.entryIterator.forEach { (key, value) ->
|
|
69
|
+
put(key, value)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
override fun triggerAnr() {
|
|
76
|
+
Handler(Looper.getMainLooper()).postAtFrontOfQueue {
|
|
77
|
+
Log.d("[Pulse]", "Now running PostAtFrontQueue: ${Thread.currentThread().name}")
|
|
78
|
+
Thread.sleep(10_000)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
companion object {
|
|
83
|
+
const val NAME = "PulseReactNativeOtel"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
package com.pulsereactnativeotel
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.BaseReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.module.model.ReactModuleInfo
|
|
7
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
8
|
+
import java.util.HashMap
|
|
9
|
+
|
|
10
|
+
class PulseReactNativeOtelPackage : BaseReactPackage() {
|
|
11
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
12
|
+
return if (name == PulseReactNativeOtelModule.NAME) {
|
|
13
|
+
PulseReactNativeOtelModule(reactContext)
|
|
14
|
+
} else {
|
|
15
|
+
null
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
|
|
20
|
+
return ReactModuleInfoProvider {
|
|
21
|
+
val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
|
|
22
|
+
moduleInfos[PulseReactNativeOtelModule.NAME] = ReactModuleInfo(
|
|
23
|
+
PulseReactNativeOtelModule.NAME,
|
|
24
|
+
PulseReactNativeOtelModule.NAME,
|
|
25
|
+
false, // canOverrideExistingModule
|
|
26
|
+
false, // needsEagerInit
|
|
27
|
+
false, // isCxxModule
|
|
28
|
+
true // isTurboModule
|
|
29
|
+
)
|
|
30
|
+
moduleInfos
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
package com.pulsereactnativeotel
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReadableMap
|
|
4
|
+
import com.facebook.react.bridge.ReadableArray
|
|
5
|
+
import com.facebook.react.bridge.ReadableType
|
|
6
|
+
import com.pulse.android.sdk.PulseSDK
|
|
7
|
+
import io.opentelemetry.api.common.AttributeKey
|
|
8
|
+
import io.opentelemetry.api.trace.Span
|
|
9
|
+
import io.opentelemetry.api.trace.SpanKind
|
|
10
|
+
import io.opentelemetry.api.trace.StatusCode
|
|
11
|
+
import io.opentelemetry.api.trace.Tracer
|
|
12
|
+
import io.opentelemetry.context.Scope
|
|
13
|
+
import java.util.UUID
|
|
14
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
15
|
+
|
|
16
|
+
object PulseReactNativeOtelTracer {
|
|
17
|
+
|
|
18
|
+
private val tracer: Tracer by lazy {
|
|
19
|
+
PulseSDK.INSTANCE.getOtelOrThrow()
|
|
20
|
+
.getOpenTelemetry()
|
|
21
|
+
.tracerProvider
|
|
22
|
+
.tracerBuilder(PulseOtelConstants.INSTRUMENTATION_SCOPE)
|
|
23
|
+
.build()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private val idToSpan = ConcurrentHashMap<String, Span>()
|
|
27
|
+
private val idToScope = ConcurrentHashMap<String, Scope>()
|
|
28
|
+
|
|
29
|
+
fun startSpan(name: String, attributes: ReadableMap?): String {
|
|
30
|
+
val span = tracer.spanBuilder(name)
|
|
31
|
+
.setSpanKind(SpanKind.INTERNAL)
|
|
32
|
+
.startSpan()
|
|
33
|
+
|
|
34
|
+
val scope = span.makeCurrent()
|
|
35
|
+
|
|
36
|
+
attributes?.applyTo(span)
|
|
37
|
+
|
|
38
|
+
val id = UUID.randomUUID().toString()
|
|
39
|
+
idToSpan[id] = span
|
|
40
|
+
idToScope[id] = scope
|
|
41
|
+
|
|
42
|
+
return id
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fun addEvent(spanId: String, name: String, attributes: ReadableMap?) {
|
|
46
|
+
idToSpan[spanId]?.let { span ->
|
|
47
|
+
attributes?.applyTo(span)
|
|
48
|
+
span.addEvent(name)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fun setAttributes(spanId: String, attributes: ReadableMap?) {
|
|
53
|
+
idToSpan[spanId]?.let { span ->
|
|
54
|
+
attributes?.applyTo(span)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fun recordException(spanId: String, errorMessage: String, stackTrace: String?) {
|
|
59
|
+
idToSpan[spanId]?.let { span ->
|
|
60
|
+
val throwable = RuntimeException(errorMessage)
|
|
61
|
+
span.recordException(throwable)
|
|
62
|
+
span.setAttribute(
|
|
63
|
+
AttributeKey.stringKey(PulseOtelConstants.ATTR_ERROR_MESSAGE),
|
|
64
|
+
errorMessage
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
stackTrace?.takeIf { it.isNotEmpty() }?.let {
|
|
68
|
+
span.setAttribute(
|
|
69
|
+
AttributeKey.stringKey(PulseOtelConstants.ATTR_ERROR_STACK),
|
|
70
|
+
it
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fun endSpan(spanId: String, statusCode: String?) {
|
|
77
|
+
idToSpan.remove(spanId)?.let { span ->
|
|
78
|
+
when (statusCode?.uppercase()) {
|
|
79
|
+
"OK" -> span.setStatus(StatusCode.OK)
|
|
80
|
+
"ERROR" -> span.setStatus(StatusCode.ERROR)
|
|
81
|
+
else -> span.setStatus(StatusCode.UNSET)
|
|
82
|
+
}
|
|
83
|
+
span.end()
|
|
84
|
+
}
|
|
85
|
+
idToScope.remove(spanId)?.close()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private fun ReadableMap.applyTo(span: Span) {
|
|
89
|
+
entryIterator.forEach { (key, value) ->
|
|
90
|
+
when (value) {
|
|
91
|
+
is String -> span.setAttribute(AttributeKey.stringKey(key), value)
|
|
92
|
+
is Boolean -> span.setAttribute(AttributeKey.booleanKey(key), value)
|
|
93
|
+
is Number -> span.setAttribute(AttributeKey.doubleKey(key), value.toDouble())
|
|
94
|
+
is ReadableArray -> {
|
|
95
|
+
applyArrayAttribute(span, key, value)
|
|
96
|
+
}
|
|
97
|
+
else -> {
|
|
98
|
+
span.setAttribute(AttributeKey.stringKey(key), value.toString())
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private fun applyArrayAttribute(span: Span, key: String, array: ReadableArray) {
|
|
105
|
+
if (array.size() == 0) {
|
|
106
|
+
span.setAttribute(AttributeKey.stringArrayKey(key), emptyList())
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
when (array.getType(0)) {
|
|
111
|
+
ReadableType.String -> {
|
|
112
|
+
span.setAttribute(AttributeKey.stringArrayKey(key), array.toArrayList() as List<String>)
|
|
113
|
+
}
|
|
114
|
+
ReadableType.Number -> {
|
|
115
|
+
span.setAttribute(AttributeKey.doubleArrayKey(key), array.toArrayList() as List<Double>)
|
|
116
|
+
}
|
|
117
|
+
ReadableType.Boolean -> {
|
|
118
|
+
span.setAttribute(AttributeKey.booleanArrayKey(key), array.toArrayList() as List<Boolean>)
|
|
119
|
+
}
|
|
120
|
+
else -> {
|
|
121
|
+
span.setAttribute(AttributeKey.stringKey(key), array.toString())
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#import "PulseReactNativeOtel.h"
|
|
2
|
+
|
|
3
|
+
@implementation PulseReactNativeOtel
|
|
4
|
+
|
|
5
|
+
RCT_EXPORT_MODULE()
|
|
6
|
+
|
|
7
|
+
+ (BOOL)requiresMainQueueSetup
|
|
8
|
+
{
|
|
9
|
+
return NO;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
- (instancetype)init
|
|
13
|
+
{
|
|
14
|
+
self = [super init];
|
|
15
|
+
if (self) {
|
|
16
|
+
static dispatch_once_t onceToken;
|
|
17
|
+
dispatch_once(&onceToken, ^{
|
|
18
|
+
NSLog(@"[Pulse] iOS support coming soon. All operations will be no-ops.");
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return self;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Check if native SDK is initialized (always false on iOS)
|
|
25
|
+
- (NSNumber *)isInitialized
|
|
26
|
+
{
|
|
27
|
+
return @NO;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Track event (no-op)
|
|
31
|
+
- (NSNumber *)trackEvent:(NSString *)event
|
|
32
|
+
observedTimeMs:(double)observedTimeMs
|
|
33
|
+
properties:(NSDictionary *)properties
|
|
34
|
+
{
|
|
35
|
+
return @NO;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Start span (returns no-op span ID)
|
|
39
|
+
- (NSString *)startSpan:(NSString *)name
|
|
40
|
+
attributes:(NSDictionary *)attributes
|
|
41
|
+
{
|
|
42
|
+
return [NSString stringWithFormat:@"noop-ios-%@", [[NSUUID UUID] UUIDString]];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// End span (no-op)
|
|
46
|
+
- (NSNumber *)endSpan:(NSString *)spanId
|
|
47
|
+
{
|
|
48
|
+
return @NO;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Add span event (no-op)
|
|
52
|
+
- (NSNumber *)addSpanEvent:(NSString *)spanId
|
|
53
|
+
name:(NSString *)name
|
|
54
|
+
attributes:(NSDictionary *)attributes
|
|
55
|
+
{
|
|
56
|
+
return @NO;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Set span attributes (no-op)
|
|
60
|
+
- (NSNumber *)setSpanAttributes:(NSString *)spanId
|
|
61
|
+
attributes:(NSDictionary *)attributes
|
|
62
|
+
{
|
|
63
|
+
return @NO;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Record span exception (no-op)
|
|
67
|
+
- (NSNumber *)recordSpanException:(NSString *)spanId
|
|
68
|
+
errorMessage:(NSString *)errorMessage
|
|
69
|
+
stackTrace:(NSString *)stackTrace
|
|
70
|
+
{
|
|
71
|
+
return @NO;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
- (nonnull NSNumber *)reportException:(nonnull NSString *)errorMessage observedTimeMs:(double)observedTimeMs stackTrace:(nonnull NSString *)stackTrace isFatal:(BOOL)isFatal errorType:(nonnull NSString *)errorType attributes:(nonnull NSDictionary *)attributes {
|
|
75
|
+
return @NO;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
- (void)setUserId:(NSString * _Nullable)id {
|
|
80
|
+
// No op
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
- (void)setUserProperties:(nonnull NSDictionary *)properties {
|
|
85
|
+
// No op
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
- (void)setUserProperty:(nonnull NSString *)name value:(NSString * _Nullable)value {
|
|
90
|
+
// No op
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
95
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
96
|
+
{
|
|
97
|
+
return std::make_shared<facebook::react::NativePulseReactNativeOtelSpecJSI>(params);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativePulseReactNativeOtel.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAwDpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,sBAAsB,CAAC","ignoreList":[]}
|