Skip to main content

Custom instrumentation

Custom spans​

Custom spans let you mark manual business flows and correlate them with Dart-side network/interaction/error instrumentation.

Prerequisite: initialize the SDK with traceParentInHeader.enable = true. Without this, CxFlutterPlugin.getCustomTracer() returns null.

import 'package:cx_flutter_plugin/cx_flutter_plugin.dart';
import 'package:cx_flutter_plugin/cx_http_client.dart';

Future<void> runCheckoutFlow() async {
final tracer = CxFlutterPlugin.getCustomTracer(
ignoredInstruments: const [
// Optional: disable linkage for specific Dart instruments.
CoralogixIgnoredInstrument.userInteractions,
],
);
if (tracer == null) return;

final global = await tracer.startGlobalSpan(
'checkout.flow',
labels: {'screen': 'checkout'},
);
if (global == null) return; // Native rejected (e.g., another global span is active).

try {
await global.withContext(() async {
final client = CxHttpClient();
try {
await client.get(Uri.parse('https://api.example.com/checkout'));
} finally {
client.close();
}
});

final child = await global.startCustomSpan('checkout.payment');
await child.endSpan();
} finally {
await global.endSpan();
}
}

withContext uses runZonedGuarded under the hood, so the active global span id is preserved across await chains in that flow and not leaked to unrelated async tasks.

Time measurement​

Time arbitrary spans of work with startTimeMeasure / endTimeMeasure. The duration is reported by the native SDK as a custom-measurement span (milliseconds) — useful when you need to measure something the SDK can't auto-instrument (checkout flows, custom render passes, asset loading, etc.).

await CxFlutterPlugin.startTimeMeasure('checkout', labels: {'cart_size': 3});
// ... do work ...
await CxFlutterPlugin.endTimeMeasure('checkout');
MethodArgumentBehavior
startTimeMeasurename: StringUnique identifier. Empty / whitespace-only names are ignored. Duplicate start for an in-flight name is ignored (first wins).
startTimeMeasurelabels: Map<String, dynamic>?Optional. Merged with SDK-level labels at end; start labels win on key collision.
endTimeMeasurename: StringMust match a prior start. No-op when never started, already ended, or the session has gone idle.

The bridge is a pure pass-through — no Dart-side state is kept, the native SDK owns the in-flight registry. You are responsible for pairing every start with exactly one end (leaked starts persist in memory until the session ends).

Last updated on
Was this page helpful?