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');
| Method | Argument | Behavior |
|---|---|---|
startTimeMeasure | name: String | Unique identifier. Empty / whitespace-only names are ignored. Duplicate start for an in-flight name is ignored (first wins). |
startTimeMeasure | labels: Map<String, dynamic>? | Optional. Merged with SDK-level labels at end; start labels win on key collision. |
endTimeMeasure | name: String | Must 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).