Instrumentation
The execution of certain operations can be traced through the tracing API. This is useful for debugging and performance profiling.
CBL Dart has builtin trace points at which flow control is given to the
currently installed TracingDelegate
.
Included in this package is the DevToolsTracing
delegate, which records
timeline events that can be later visualized through the Dart DevTools
Performance Page.
You can install a delegate by calling TracingDelegate.install
:
await TracingDelegate.install(DevToolsTracing());
Sentry
The Sentry integration provided by cbl_sentry
installs a
DevToolsTracing
to transparently record breadcrumbs and transaction spans.
Features
- Record log messages as Sentry breadcrumbs
- Record CBL Dart API usage as Sentry breadcrumbs
- Record CBL Dart operations as Sentry transaction spans
Limitations
Sentry currently does not support binding transaction spans to zones. This means there can only be one global transaction span that integrations can transparently access. To support more advanced use cases, this package provides a mechanism to bind transaction spans to zones. This mechanism will be removed if and when Sentry supports this natively.
Getting started
Make sure your app is setup for using Couchbase Lite.
Add the
cbl_sentry
package as a dependency:flutter pub add cbl_sentry
Add the
CouchbaseLiteIntegration
when configuring Sentry:import 'package:cbl_sentry/cbl_sentry.dart';
import 'package:sentry/sentry.dart';
void main() {
Sentry.init(
(options) {
options
..dsn = ...
// While testing your Sentry configuration, make sure that all traces are sampled.
..tracesSampleRate = 1
// Add the CBL Dart integration.
..addIntegration(CouchbaseLiteIntegration());
},
appRunner: () async {
runApp(MyApp());
}
);
}To find out about configurable options, see the documentation of
CouchbaseLiteIntegration
.cautionMake sure you don't install a
TracingDelegate
when using theCouchbaseLiteIntegration
. The integration has to be able to install aTracingDelegate
itself.
Performance tracing
This integration only records transaction spans when a transaction has been started and a child span of the transaction is available in the environment.
To find a span, the integration uses cblSentrySpan
. This is
a getter that returns either a span that has been bound to the current zone or
as a fallback the result of Sentry.getSpan
. To bind a
span to a zone use runWithCblSentrySpan
.
The following code snippet shows functions that are useful to trace the performance of operations in an app:
Future<T> runAppTransaction<T>(String name, Future<T> Function() fn) =>
_runAppSpan(Sentry.startTransaction(name, 'task'), fn);
Future<T> runAppOperation<T>(String name, Future<T> Function() fn) =>
_runAppSpan(cblSentrySpan!.startChild(name), fn);
Future<T> _runAppSpan<T>(ISentrySpan span,Future<T> Function() fn) async {
try {
return await runWithCblSentrySpan(span, fn);
// ignore: avoid_catches_without_on_clauses
} catch (e) {
span
..throwable = e
..status = const SpanStatus.internalError();
rethrow;
} finally {
span.status ??= const SpanStatus.ok();
await span.finish();
}
}
A app operation like the one below is traced as a transaction span, with CBL Dart operations as child spans:
Future<void> queryDatabase() => runAppOperation('queryDatabase', () async {
final query = await db.createQuery(
'SELECT * FROM example WHERE age >= 28 OR name LIKE "A%"',
);
final resultSet = await query.execute();
final results = await resultSet
.asStream()
.map((result) => result.toPlainMap())
.toList();
prettyPrintJson(results);
});