# Custom instrumentation

Copy as Markdown[Open in ChatGPT](https://chatgpt.com/?q=Read%20https%3A%2F%2Fcoralogix.com%2Fdocs%2Fuser-guides%2Frum%2Fsdk-installation%2Fflutter%2Fmobile%2Ffeatures%2Fcustom-instrumentation.md%20and%20help%20me%20with%20my%20question%20about%20this%20Coralogix%20documentation%20page.)[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fcoralogix.com%2Fdocs%2Fuser-guides%2Frum%2Fsdk-installation%2Fflutter%2Fmobile%2Ffeatures%2Fcustom-instrumentation.md%20and%20help%20me%20with%20my%20question%20about%20this%20Coralogix%20documentation%20page.)

### Custom Spans[​](#custom-spans "Direct link to 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-measurement "Direct link to 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).
