Manually instrument Flutter applications

Manually instrument Flutter applications to collect additional telemetry, add global attributes, and more.

You can manually instrument Flutter applications using the Splunk RUM Flutter agent to collect additional telemetry, add global attributes, and more.

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 global attributes:

  • To add metadata during agent initialization:

    DART
    SplunkRum.instance.install(
      agentConfiguration: AgentConfiguration(
        globalAttributes: MutableAttributes(
          attributes: {
            "boolKey": MutableAttributeBool(value: true),
            "stringKey": MutableAttributeString(value: "testVal"),
            "intKey": MutableAttributeInt(value: 1),
            "doubleKey": MutableAttributeDouble(value: 1.3),
          },
        ),
      ),
    );
  • To add metadata after agent initialization:

    DART
    SplunkRum.instance.globalAttributes.setInt(
      key: "user_id",
      value: 123,
    );
    
    SplunkRum.instance.globalAttributes.setString(
      key: "user_name",
      value: "John",
    );

Get agent and session state

You can inspect the current SDK state and the active session:

DART
// Get session ID
final sessionId = await SplunkRum.instance.session.state.getId();

// Get session sampling rate
final samplingRate = await SplunkRum.instance.session.state.getSamplingRate();

Track users

By default, the Splunk RUM Flutter agent uses anonymous user tracking (ANONYMOUS_TRACKING). To disable it, set the tracking mode to NO_TRACKING:

DART
await SplunkRum.instance.user.preferences.setTrackingMode(
  userTrackingMode: UserTrackingMode.noTracking,
);

You can also get the current tracking mode:

DART
final trackingMode = await SplunkRum.instance.user.state.getTrackingMode();

Report custom events and workflows

You can report custom events and workflows happening in your Flutter application using the trackCustomEvent and startWorkflow APIs. Additionally, you have the option to set custom attributes. See example below:

DART
SplunkRum.instance.customTracking.trackCustomEvent(
  name: "Test event",
  attributes: MutableAttributes(
    attributes: {
      "intKeyTrackEvent": MutableAttributeInt(value: 5),
      "stringKeyTrackEvent": MutableAttributeString(value: "val"),
    },
  ),
);

The following example shows how to start a workflow for which metrics are recorded by Splunk RUM:

DART
final workflow = await SplunkRum.instance.customTracking.startWorkflow(
  name: 'checkout',
);
// Perform checkout steps.
await workflow.end(
  attributes: MutableAttributes(
    attributes: {
      'checkout.outcome': MutableAttributeString(value: 'success'),
      'checkout.items': MutableAttributeInt(value: 3),
    },
  ),
);

Pass optional attributes to workflow.end() to record details known when the workflow finishes, such as its outcome or item count. These attributes are attached to the workflow span and appear in its All Tags details in Splunk RUM. The workflow duration measures the time between startWorkflow() and end(). Calling workflow.end() without attributes continues to work.

SDK-managed attribute keys are ignored: component, workflow.name, session.id, session.previous_id, user.anonymous_id, app.installation.id, screen.name, and last.screen.name. Flutter additionally ignores workflow.start.time and workflow.end.time.

Automatic navigation detection

Automatic navigation detection is deactivated by default. You can activate it by configuring the Navigation detection module module.

Manually track navigation events

When automatic navigation tracking is enabled, the agent monitors native navigation components:

  • Android: Activity and Fragment lifecycle events
  • iOS: UIKit view-controller lifecycle callbacks

For Flutter apps, the agent can automatically track routes when you register SplunkNavigatorObserver. If automatic tracking does not support your navigation pattern, you can manually report navigation events. For more information, see Track Flutter Routes Automatically.

Use manual tracking for scenarios such as:

  • Custom routers
  • Unnamed routes
  • Tab changes
  • Unsupported navigation flows

To manually track a screen, call SplunkRum.instance.navigation.track():

CODE
SplunkRum.instance.navigation.track(screenName: "Screen name");

This sends a navigation span to Splunk RUM and associates the specified screen name with subsequent spans.

You can also include optional attributes when tracking a screen:

JSON
await SplunkRum.instance.navigation.track( screenName: 'Checkout', attributes: MutableAttributes( attributes: { 'checkout.flow': MutableAttributeString(value: 'express'), }, ), );

Manual tracking reports a navigation event and updates screen.name for subsequent RUM telemetry.

Add server trace context from Splunk APM

The Splunk RUM Flutter agent collects server trace context using back-end data provided by Splunk 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:

CODE
Server-Timing: traceparent;desc="00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"

The example resolves to a context containing the following data:

CODE
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:

CODE
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.

Report errors manually

Report errors that your application catches, such as errors in a catch block, by calling trackError. Configure automatic crash and error capture separately through module configuration.

Each reported error creates a Real User Monitoring (RUM) span with component=error. The span includes the error type, message, stack trace, source, and handled state. You can also attach custom attributes.

Report a caught error

Pass the stack trace from the catch clause to preserve the original throw site, as shown in the following example:

DART
import 'package:splunk_otel_flutter/splunk_otel_flutter.dart';

try {
  await riskyOperation();
} catch (e, st) {
  await SplunkRum.instance.customTracking.trackError(e, stackTrace: st);
}

The agent derives the error type from the object's runtime type and the message from toString(). For a string error, the error type is String. The agent records the provided stack trace without modification.

If you omit stackTrace, the agent records the stack trace at the trackError call site. You can pass any Dart object as the error, including a plain String.

DART
await SplunkRum.instance.customTracking.trackError(
  StateError('cart is empty'),
);

The trackError method completes without throwing. If reporting fails, the agent logs the failure and completes the call. The method doesn't consume or modify the error.

Add attributes to an error

Attach custom attributes to add application context to the error span:

DART
try {
  await checkout(cart);
} catch (e, st) {
  await SplunkRum.instance.customTracking.trackError(
    e,
    stackTrace: st,
    attributes: MutableAttributes(
      attributes: {
        'checkout.step': MutableAttributeString(value: 'payment'),
        'cart.items': MutableAttributeInt(value: 3),
      },
    ),
  );
}

Mark an error as unhandled

By default, the agent reports an error as handled and nonfatal. Set handled: false when an error escapes your code. The error span records exception.escaped=true.

DART
await SplunkRum.instance.customTracking.trackError(
  e,
  stackTrace: st,
  handled: false,
);