Manually instrument Android applications
Manually instrument Android applications to collect additional telemetry, sanitize Personal Identifiable Information (PII), add global attributes, and more.
You can manually instrument Android applications for Splunk RUM using the Android RUM agent to collect additional telemetry, sanitize Personal Identifiable Information (PII), add global attributes, and more.
Filter spans
You can modify or drop spans using the type (SpanData) -> SpanData
. It allows you to provide a lambda or function that can inspect, modify, or filter SpanData
objects before they are exported. For example, you can drop or redact spans that contain personally identifiable information (PII).
The following example shows how to remove a span:
- Kotlin
// Remove all spans with name "SensitiveOperation" val spanInterceptor: ((SpanData) -> SpanData?) = { spanData -> if (spanData.name == "SensitiveOperation") { null // Returning null signals that this span should be dropped } else { spanData } } val agent = SplunkRum.install( this, AgentConfiguration( ..., spanInterceptor = spanInterceptor ) )
- Java
// Remove all spans with name "SensitiveOperation" Function<SpanData, SpanData> spanInterceptor = spanData -> { if ("SensitiveOperation".equals(spanData.getName())) { return null; // Returning null signals that this span should be dropped } else { return spanData; } }; SplunkRum agent = SplunkRum.install( this, new AgentConfiguration( ..., spanInterceptor ) );
The following example shows how to redact the value of an attribute to remove sensitive data:
- Kotlin
// Redact "user.email" attribute to protect sensitive data val spanInterceptor: ((SpanData) -> SpanData?) = { spanData -> val mutableSpan = spanData.toMutableSpanData() val mutableAttributes = mutableSpan.attributes.toMutableAttributes() // Check if user.email is present, then redact it if ("user.email" in mutableAttributes) { mutableAttributes["user.email"] = "[REDACTED]" mutableSpan.attributes = mutableAttributes } mutableSpan } val agent = SplunkRum.install( this, AgentConfiguration( ..., spanInterceptor = spanInterceptor ) )
- Java
// Redact "user.email" attribute to protect sensitive data Function<SpanData, SpanData> spanInterceptor = spanData -> { MutableSpanData mutableSpan = spanData.toMutableSpanData(); MutableAttributes mutableAttributes = mutableSpan.getAttributes().toMutableAttributes(); // Check if user.email is present, then redact it if (mutableAttributes.containsKey("user.email")) { mutableAttributes.put("user.email", "[REDACTED]"); mutableSpan.setAttributes(mutableAttributes); } return mutableSpan; }; SplunkRum agent = SplunkRum.install( this, new AgentConfiguration( ..., spanInterceptor ) );
Manage global attributes
Global attributes are key-value pairs added to all reported data. Global attributes are useful for reporting application or user-specific values as tags.
The following examples show how to define a key-value pair as global attributes:
To add metadata during agent initialization:
- Kotlin
val globalAttributes = MutableAttributes().apply { this["name"] = "John Doe" this["email"] = "john.doe@example.com" } val agent = SplunkRum.install( this, agentConfiguration = AgentConfiguration( endpoint = EndpointConfiguration( realm = "your-splunk-realm", rumAccessToken = "your_splunk_rum_access_token" ), appName = "your_app_name", deploymentEnvironment = "your_deployment_environment" globalAttributes = globalAttributes ) )
- Java
MutableAttributes globalAttributes = new MutableAttributes(); globalAttributes.set("name", "John Doe"); globalAttributes.set("email", "john.doe@example.com"); SplunkRum agent = SplunkRum.install( this, new AgentConfiguration( new EndpointConfiguration( "your_splunk_realm", "your_splunk_rum_access_token" ), "your_app_name", "your_deployment_environment", globalAttributes ) );
To add metadata after agent initialization:
- Kotlin
// Using map syntax SplunkRum.instance.globalAttributes["enduser.id"] = "user-id-123456" // Using a set method SplunkRum.instance.globalAttributes.set("enduser.role", "premium")
- Java
SplunkRum.getInstance().getGlobalAttributes().set("enduser.role", "premium")
Report custom events and workflows
You can report custom events and workflows happening in your Android application using the trackCustomEvent
and trackWorkflow
APIs. Additionally, you have the option to set custom attributes. See example below
- Kotlin
val testAttributes = MutableAttributes() testAttributes["attribute.one"] = "value1" testAttributes["attribute.two"] = "12345" SplunkRum.instance.customTracking.trackCustomEvent( "TestEvent", testAttributes )
- Java
MutableAttributes testAttributes = new MutableAttributes(); testAttributes.set("attribute.one", "value1"); testAttributes.set("attribute.two", "12345"); CustomTracking.getInstance().trackCustomEvent( "TestEvent", testAttributes );
The following example shows how to start a workflow for which metrics are recorded by Splunk RUM. To record the workflow you must end the OpenTelemetry span instance:
- Kotlin
binding.buttonWork.setOnClickListener { val hardWorkerSpan = SplunkRum.instance.customTracking.trackWorkflow("Main thread working hard") try { val random = Random() val startTime = System.currentTimeMillis() while (true) { random.nextDouble() if (System.currentTimeMillis() - startTime > 20_000) { break } } } finally { hardWorkerSpan.end() } }
- Java
binding.buttonWork.setOnClickListener(v -> { Span hardWorker = CustomTracking.getInstance().trackWorkflow("Main thread working hard"); try { Random random = new Random(); long startTime = System.currentTimeMillis(); while (true) { random.nextDouble(); if (System.currentTimeMillis() - startTime > 20_000) { break; } } } finally { hardWorker.end(); } });
Configure error reporting
You can report handled errors, exceptions, and messages using the trackException(Throwable, Attributes?)
method. You can set custom attributes for your reported exception. Exceptions appear as errors in the Splunk RUM UI, and error metrics are recorded.
The following example shows how to report the Unimplemented Feature: Settings
error in a sample application:
- Kotlin
private val SETTINGS_FEATURE_ATTRIBUTES = MutableAttributes().also { attributes -> attributes["feature.name"] = "Settings" attributes["feature.flag.enabled"] = true attributes["feature.used.count"] = 20 } override fun onOptionsItemSelected(item: MenuItem): Boolean { val id = item.itemId if (id == R.id.action_settings) { SplunkRum.instance.customTracking.trackException( UnsupportedOperationException("Unimplemented Feature: Settings"), SETTINGS_FEATURE_ATTRIBUTES ) return true } return super.onOptionsItemSelected(item) }
- Java
private static final Attributes SETTINGS_FEATURE_ATTRIBUTES = Attributes.of( AttributeKey.stringKey("feature.name"), "Settings", AttributeKey.stringKey("feature.flag.enabled"), "is_Settings_enabled", AttributeKey.stringKey("feature.usage"), "clicked" ); public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.action_settings) { CustomTracking.getInstance() .trackException( new UnsupportedOperationException("Unimplemented Feature: Settings"), SETTINGS_FEATURE_ATTRIBUTES ); return true; } return super.onOptionsItemSelected(item); }
Add server trace context from Splunk APM
The Android RUM agent collects server trace context using back-end data provided by APM instrumentation through the Server-Timing
header. In some cases, you might want to generate the header manually.
To create the Server-Timing
header manually, provide a Server-Timing
header with the name traceparent
, where the desc
field holds the version, the trace ID, the parent ID, and the trace flag.
Consider the following HTTP header:
Server-Timing: traceparent;desc="00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
The example resolves to a context containing the following data:
version=00 trace-id=4bf92f3577b34da6a3ce929d0e0e4736parent-id=00f067aa0ba902b7 trace-flags=01
When generating a value for the traceparent
header, make sure that it matches the following regular expression:
00-([0-9a-f]{32})-([0-9a-f]{16})-01
Server timing headers with values that don't match the pattern are automatically discarded. For more information, see the Server-Timing
and traceparent
documentation on the W3C website. If multiple valid server-timing headers are found, the last valid one is used.