Skip to main content

Custom Span Instrumentation

Generates: Spans

Create custom performance traces (spans) to measure the execution time of specific operations.

The trackSpan() function automatically manages the span lifecycle:

// Automatic span management (recommended)
PulseSDK.INSTANCE.trackSpan(
spanName = "database_query",
params = mapOf("table" to "users", "operation" to "select"),
) {
// Your operation here
val users = database.getUsers()
processUsers(users)
}

Manual Span Control

Use startSpan() when you need fine-grained control:

// Manual span control
val endSpan = PulseSDK.INSTANCE.startSpan(
spanName = "image_processing",
params = mapOf("imageId" to "img-123", "format" to "jpeg"),
)

try {
processImage()
} finally {
endSpan() // Always end the span
}

Access OpenTelemetry APIs

For advanced use cases, access the underlying OpenTelemetry APIs:

// Get OpenTelemetry RUM instance
val otelRum = PulseSDK.INSTANCE.getOtelOrNull()

// Or throw if not initialized
val otelRum = PulseSDK.INSTANCE.getOtelOrThrow()

// Use OpenTelemetry APIs directly
val tracer = otelRum.getOpenTelemetry()
.tracerProvider
.get("my-instrumentation")

val span = tracer.spanBuilder("custom_operation").startSpan()
try {
// Your operation
} finally {
span.end()
}

Generated Telemetry

Type: Span
Span Name: Custom (set by you)
Span Kind: INTERNAL
pulse.type: Not set by default (only set for specific instrumentations like network, screen_load, etc.)

Attributes

Custom Span Attributes

AttributeDescriptionExampleAlways Present
Custom attributesAny key-value pairs you definetable: "users", operation: "select"⚠️ Only if provided
screen.nameCurrent screen/activity name"MainActivity"⚠️ If available
session.idSession identifier"f40364c92b85ec0c19c35a65be42b97f"✅ Yes

Note: All custom spans include global attributes (service, device, OS, session, network carrier, etc.) in the resources object. See Global Attributes for complete list.

Sample Payload: Custom Span

{
"name": "database_query",
"kind": "INTERNAL",
"startTimeUnixNano": "1701000050000000000",
"endTimeUnixNano": "1701000050125000000",
"duration": "125ms",
"attributes": {
"table": "users",
"operation": "select",
"screen.name": "MainActivity",
"session.id": "f40364c92b85ec0c19c35a65be42b97f"
},
"resources": {
"android.os.api_level": "36",
"app.build_id": "1",
"app.build_name": "1.0_1",
"device.manufacturer": "Google",
"device.model.identifier": "sdk_gphone64_arm64",
"device.model.name": "sdk_gphone64_arm64",
"os.description": "BE2A.250530.026.D1",
"os.name": "Android",
"os.type": "linux",
"os.version": "16",
"rum.sdk.version": "0.16.0-alpha-SNAPSHOT",
"service.name": "PulseReactNativeOtelExample",
"service.version": "1.0_1",
"telemetry.sdk.language": "java",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.54.1"
}
}

Note: Custom spans created via trackSpan() or startSpan() do not have a pulse.type attribute by default. The pulse.type attribute is only automatically set for specific instrumentations like network requests (network), screen loads (screen_load), app starts (app_start), etc.

Best Practices

  1. Prefer automatic spans: Use trackSpan() unless you need fine-grained control
  2. Always end manual spans: Use try/finally to ensure spans are ended
  3. Add meaningful attributes: Include IDs, counts, or other context in params