Skip to content

Commit 2620907

Browse files
initialization report (#1178)
* initialization report Signed-off-by: Tomas Chladek <[email protected]> * Test fix Signed-off-by: Tomas Chladek <[email protected]> * Fix startup timing Signed-off-by: Tomas Chladek <[email protected]> --------- Signed-off-by: Tomas Chladek <[email protected]>
1 parent a8869d4 commit 2620907

File tree

41 files changed

+297
-98
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+297
-98
lines changed

.run/Format code.run.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
1919
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
2020
<DebugAllEnabled>false</DebugAllEnabled>
21+
<RunAsTest>false</RunAsTest>
2122
<method v="2" />
2223
</configuration>
2324
</component>

app/src/main/java/com/splunk/app/App.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import com.splunk.rum.integration.agent.api.AgentConfiguration
2222
import com.splunk.rum.integration.agent.api.SplunkRUMAgent
2323
import com.splunk.rum.integration.interactions.InteractionsModuleConfiguration
2424
import com.splunk.rum.integration.navigation.NavigationModuleConfiguration
25-
import com.splunk.rum.integration.sessionreplay.api.sessionReplay
25+
import com.splunk.rum.integration.sessionreplay.extension.sessionReplay
2626
import java.net.URL
2727

2828
class App : Application() {

common/otel/src/main/java/com/splunk/sdk/common/otel/logRecord/AndroidLogRecordExporter.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.splunk.sdk.common.otel.logRecord
1818

1919
import com.splunk.sdk.common.otel.OpenTelemetry
20+
import com.splunk.sdk.common.otel.internal.RumConstants
2021
import io.opentelemetry.api.common.AttributeKey
2122
import io.opentelemetry.api.trace.Span
2223
import io.opentelemetry.api.trace.SpanKind
@@ -37,7 +38,7 @@ internal class AndroidLogRecordExporter : LogRecordExporter {
3738
val activeSpan = Span.fromContextOrNull(parentContext)
3839

3940
// traceId and spanId should be inside the context already from global OTel instance
40-
val spanBuilder = OpenTelemetry.instance!!.sdkTracerProvider.get("SplunkRum")
41+
val spanBuilder = OpenTelemetry.instance!!.sdkTracerProvider.get(RumConstants.RUM_TRACER_NAME)
4142
.spanBuilder(log.attributes[AttributeKey.stringKey("event.name")] ?: "")
4243
.setSpanKind(SpanKind.INTERNAL)
4344
.setParent(parentContext)

instrumentation/runtime/startup/src/main/java/com/splunk/rum/startup/ApplicationStartupTimekeeper.kt

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import android.app.Application
2121
import android.os.Bundle
2222
import android.os.Handler
2323
import android.os.Looper
24+
import android.os.SystemClock
2425
import com.cisco.android.common.utils.adapters.ActivityLifecycleCallbacksAdapter
2526
import com.cisco.android.common.utils.extensions.forEachFast
2627

@@ -29,6 +30,8 @@ object ApplicationStartupTimekeeper {
2930
private val handler = Handler(Looper.getMainLooper())
3031

3132
private var firstTimestamp = 0L
33+
private var firstElapsed = 0L
34+
3235
private var isColdStartCompleted = false
3336

3437
var isEnabled = true
@@ -37,16 +40,17 @@ object ApplicationStartupTimekeeper {
3740

3841
internal fun onInit() {
3942
firstTimestamp = System.currentTimeMillis()
43+
firstElapsed = SystemClock.elapsedRealtime()
4044
}
4145

4246
internal fun onCreate(application: Application) {
4347
handler.twoConsecutivePosts {
4448
isColdStartCompleted = true
4549

4650
if (isEnabled) {
47-
val endTimestamp = System.currentTimeMillis()
48-
val duration = endTimestamp - firstTimestamp
49-
listeners.forEachFast { it.onColdStarted(firstTimestamp, endTimestamp, duration) }
51+
val endElapsed = SystemClock.elapsedRealtime()
52+
val duration = endElapsed - firstElapsed
53+
listeners.forEachFast { it.onColdStarted(firstTimestamp, firstTimestamp + duration, duration) }
5054
}
5155
}
5256

@@ -60,16 +64,19 @@ object ApplicationStartupTimekeeper {
6064
private var resumedActivityCount = 0
6165

6266
private var firstActivityCreateTimestamp = 0L
67+
private var firstActivityCreateElapsed = 0L
6368
private var isWarmStartPending = false
6469

6570
private var firstActivityStartTimestamp = 0L
71+
private var firstActivityStartElapsed = 0L
6672
private var isHotStartPending = false
6773

6874
override fun onActivityPreCreated(activity: Activity, savedInstanceState: Bundle?) {
6975
createdActivityCount++
7076

7177
if (isColdStartCompleted && createdActivityCount == 1) {
7278
firstActivityCreateTimestamp = System.currentTimeMillis()
79+
firstActivityCreateElapsed = SystemClock.elapsedRealtime()
7380
isWarmStartPending = true
7481
}
7582
}
@@ -79,6 +86,7 @@ object ApplicationStartupTimekeeper {
7986

8087
if (isColdStartCompleted && !isWarmStartPending && !isHotStartPending) {
8188
firstActivityStartTimestamp = System.currentTimeMillis()
89+
firstActivityStartElapsed = SystemClock.elapsedRealtime()
8290
isHotStartPending = true
8391
}
8492
}
@@ -88,21 +96,21 @@ object ApplicationStartupTimekeeper {
8896

8997
if (resumedActivityCount == 1 && (isHotStartPending || isWarmStartPending))
9098
handler.twoConsecutivePosts {
91-
val endTimestamp = System.currentTimeMillis()
99+
val endTimestamp = SystemClock.elapsedRealtime()
92100

93101
if (isHotStartPending) {
94102
if (isEnabled) {
95-
val duration = endTimestamp - firstActivityStartTimestamp
96-
listeners.forEachFast { it.onHotStarted(firstActivityStartTimestamp, endTimestamp, duration) }
103+
val duration = endTimestamp - firstActivityStartElapsed
104+
listeners.forEachFast { it.onHotStarted(firstActivityStartTimestamp, firstActivityStartTimestamp + duration, duration) }
97105
}
98106

99107
isHotStartPending = false
100108
}
101109

102110
if (isWarmStartPending) {
103111
if (isEnabled) {
104-
val duration = endTimestamp - firstActivityCreateTimestamp
105-
listeners.forEachFast { it.onWarmStarted(firstActivityCreateTimestamp, endTimestamp, duration) }
112+
val duration = endTimestamp - firstActivityCreateElapsed
113+
listeners.forEachFast { it.onWarmStarted(firstActivityCreateTimestamp, firstActivityCreateTimestamp + duration, duration) }
106114
}
107115

108116
isWarmStartPending = false

integration/agent/api/src/test/java/com/splunk/rum/integration/agent/api/JavaIntegration.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,14 @@
2020

2121
import com.splunk.rum.integration.agent.module.ModuleConfiguration;
2222

23+
import org.jetbrains.annotations.NotNull;
24+
2325
import java.net.MalformedURLException;
2426
import java.net.URL;
27+
import java.util.ArrayList;
28+
import java.util.List;
29+
30+
import kotlin.Pair;
2531

2632
public class JavaIntegration extends Application {
2733

@@ -45,5 +51,19 @@ public void onCreate() {
4551
}
4652

4753
private static class CustomModuleConfiguration implements ModuleConfiguration {
54+
55+
@NotNull
56+
@Override
57+
public String getName() {
58+
return "test";
59+
}
60+
61+
@NotNull
62+
@Override
63+
public List<Pair<String, String>> getAttributes() {
64+
ArrayList<Pair<String, String>> attributes = new ArrayList<>();
65+
attributes.add(new Pair<>("enabled", "true"));
66+
return attributes;
67+
}
4868
}
4969
}

integration/agent/internal/src/main/java/com/splunk/rum/integration/agent/internal/AgentIntegration.kt

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,36 @@
1717
package com.splunk.rum.integration.agent.internal
1818

1919
import android.content.Context
20+
import android.os.SystemClock
2021
import com.cisco.android.common.logger.Logger
2122
import com.cisco.android.common.utils.extensions.forEachFast
2223
import com.splunk.rum.integration.agent.internal.config.ModuleConfigurationManager
2324
import com.splunk.rum.integration.agent.internal.session.SessionManager
2425
import com.splunk.rum.integration.agent.module.ModuleConfiguration
26+
import com.splunk.rum.integration.agent.module.extension.toSplunkString
27+
import com.splunk.sdk.common.otel.OpenTelemetry
28+
import com.splunk.sdk.common.otel.internal.RumConstants
2529
import com.splunk.sdk.common.storage.AgentStorage
30+
import java.util.concurrent.TimeUnit
2631

2732
class AgentIntegration private constructor(
2833
context: Context
2934
) {
3035
private var appName: String? = null
3136
private var agentVersion: String? = null
3237

38+
private var startTimestamp = 0L
39+
private var startElapsed = 0L
40+
3341
val sessionManager: SessionManager
3442
val moduleConfigurationManager: ModuleConfigurationManager
3543
val listeners: MutableSet<Listener> = HashSet()
3644

3745
init {
38-
registerModule(MODULE_NAME)
46+
startTimestamp = System.currentTimeMillis()
47+
startElapsed = SystemClock.elapsedRealtime()
48+
49+
registerModuleInitializationStart(MODULE_NAME)
3950

4051
val storage = AgentStorage.attach(context)
4152

@@ -51,6 +62,11 @@ class AgentIntegration private constructor(
5162
this.appName = appName
5263
this.agentVersion = agentVersion
5364

65+
for (config in moduleConfigurations) {
66+
val module = modules[config.name] ?: Module(config.name)
67+
modules[config.name] = module.copy(configuration = config)
68+
}
69+
5470
moduleConfigurationManager.setup(moduleConfigurations)
5571

5672
return this
@@ -59,6 +75,35 @@ class AgentIntegration private constructor(
5975
fun install(context: Context) {
6076
sessionManager.install(context)
6177
listeners.forEachFast { it.onInstall(context) }
78+
79+
registerModuleInitializationEnd(MODULE_NAME)
80+
reportInitialization()
81+
}
82+
83+
private fun reportInitialization() {
84+
val provider = OpenTelemetry.instance?.sdkTracerProvider ?: throw IllegalStateException("unable to report initialization")
85+
val modules = modules.values
86+
87+
val span = provider.get(RumConstants.RUM_TRACER_NAME)
88+
.spanBuilder("SplunkRum.initialize")
89+
.setStartTimestamp(startTimestamp + SystemClock.elapsedRealtime() - startElapsed, TimeUnit.MILLISECONDS)
90+
.startSpan()
91+
92+
val resources = modules.joinToString(",", "[", "]") { it.configuration?.toSplunkString() ?: "${it.name}.enabled:true" }
93+
94+
span.setAttribute("config_settings", resources)
95+
96+
for (module in modules) {
97+
if (module.initialization == null)
98+
throw IllegalStateException("Module '${module.name}' initialization has not been started")
99+
100+
if (module.initialization.endElapsed == null)
101+
throw IllegalStateException("Module '${module.name}' is not initialized")
102+
103+
span.addEvent("${module.name}_initialized", module.initialization.run { endElapsed!! - startElapsed }, TimeUnit.MILLISECONDS)
104+
}
105+
106+
span.end()
62107
}
63108

64109
private inner class SessionManagerListener : SessionManager.SessionListener {
@@ -72,14 +117,26 @@ class AgentIntegration private constructor(
72117
fun onInstall(context: Context)
73118
}
74119

120+
private data class Module(
121+
val name: String,
122+
val configuration: ModuleConfiguration? = null,
123+
val initialization: Initialization? = null
124+
) {
125+
data class Initialization(
126+
val startTimestamp: Long,
127+
val startElapsed: Long,
128+
val endElapsed: Long?
129+
)
130+
}
131+
75132
companion object {
76133

77134
private const val TAG = "AgentIntegration"
78135

79136
private const val MODULE_NAME = "agent"
80137

81138
private var instanceInternal: AgentIntegration? = null
82-
private val moduleNames = HashSet<String>()
139+
private val modules = HashMap<String, Module>()
83140

84141
val instance: AgentIntegration
85142
get() = instanceInternal ?: throw IllegalStateException("Instance is not created, call createInstance() first")
@@ -91,8 +148,32 @@ class AgentIntegration private constructor(
91148
return instanceInternal!!
92149
}
93150

94-
fun registerModule(name: String) {
95-
moduleNames += name
151+
fun registerModuleInitializationStart(name: String) {
152+
val module = modules[name] ?: Module(name)
153+
154+
if (module.initialization != null)
155+
throw IllegalStateException()
156+
157+
modules[name] = module.copy(
158+
initialization = Module.Initialization(
159+
startTimestamp = System.currentTimeMillis(),
160+
startElapsed = SystemClock.elapsedRealtime(),
161+
endElapsed = null
162+
)
163+
)
164+
}
165+
166+
fun registerModuleInitializationEnd(name: String) {
167+
val module = modules[name] ?: throw IllegalStateException("Initialization start for module '$name' was not called")
168+
169+
if (module.initialization == null)
170+
throw IllegalStateException("Function registerModuleInitializationStart() for module '$name' was not called")
171+
172+
modules[name] = module.copy(
173+
initialization = module.initialization.copy(
174+
endElapsed = SystemClock.elapsedRealtime()
175+
)
176+
)
96177
}
97178
}
98179
}

integration/agent/module/src/main/java/com/splunk/rum/integration/agent/module/ModuleConfiguration.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,8 @@
1616

1717
package com.splunk.rum.integration.agent.module
1818

19-
interface ModuleConfiguration
19+
interface ModuleConfiguration {
20+
21+
val name: String
22+
val attributes: List<Pair<String, String>>
23+
}

integration/anr/src/main/java/com/splunk/rum/integration/anr/api/ANRModuleConfiguration.kt renamed to integration/agent/module/src/main/java/com/splunk/rum/integration/agent/module/extension/ModuleConfigurationExt.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
* limitations under the License.
1515
*/
1616

17-
package com.splunk.rum.integration.anr.api
17+
package com.splunk.rum.integration.agent.module.extension
1818

1919
import com.splunk.rum.integration.agent.module.ModuleConfiguration
2020

21-
class ANRModuleConfiguration : ModuleConfiguration
21+
fun ModuleConfiguration.toSplunkString(): String {
22+
return attributes.joinToString(",") { "$name.${it.first}:${it.second}" }
23+
}

integration/anr/consumer-rules.pro

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
-keep public class com.splunk.rum.integration.anr.installer.ANRInstaller
2-
-keep public class com.splunk.rum.integration.anr.configurer.ANRConfigurer
3-
-keepclassmembers class com.splunk.rum.integration.anr.configurer.ANRConfigurer {
1+
-keep public class com.splunk.rum.integration.anr.ANRInstaller
2+
-keep public class com.splunk.rum.integration.anr.ANRConfigurer
3+
-keepclassmembers class com.splunk.rum.integration.anr.ANRConfigurer {
44
public static boolean isANRReportingEnabled;
55
}

integration/anr/proguard-rules.pro

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
-keep public class com.splunk.rum.integration.anr.installer.ANRInstaller
2-
-keep public class com.splunk.rum.integration.anr.configurer.ANRConfigurer
3-
-keepclassmembers class com.splunk.rum.integration.anr.configurer.ANRConfigurer {
1+
-keep public class com.splunk.rum.integration.anr.ANRInstaller
2+
-keep public class com.splunk.rum.integration.anr.ANRConfigurer
3+
-keepclassmembers class com.splunk.rum.integration.anr.ANRConfigurer {
44
public static boolean isANRReportingEnabled;
55
}
66
-repackageclasses 'com.splunk.rum.integration.anr'

0 commit comments

Comments
 (0)