Tracing database operations
Drift provides the relatively simple logStatements
option to print the statements it
executes.
The QueryInterceptor
API can be used to extend this logging to provide more information,
which this example will show.
class LogInterceptor extends QueryInterceptor {
Future<T> _run<T>(
String description, FutureOr<T> Function() operation) async {
final stopwatch = Stopwatch()..start();
print('Running $description');
try {
final result = await operation();
print(' => succeeded after ${stopwatch.elapsedMilliseconds}ms');
return result;
} on Object catch (e) {
print(' => failed after ${stopwatch.elapsedMilliseconds}ms ($e)');
rethrow;
}
}
@override
TransactionExecutor beginTransaction(QueryExecutor parent) {
print('begin');
return super.beginTransaction(parent);
}
@override
Future<void> commitTransaction(TransactionExecutor inner) {
return _run('commit', () => inner.send());
}
@override
Future<void> rollbackTransaction(TransactionExecutor inner) {
return _run('rollback', () => inner.rollback());
}
@override
Future<void> runBatched(
QueryExecutor executor, BatchedStatements statements) {
return _run(
'batch with $statements', () => executor.runBatched(statements));
}
@override
Future<int> runInsert(
QueryExecutor executor, String statement, List<Object?> args) {
return _run(
'$statement with $args', () => executor.runInsert(statement, args));
}
@override
Future<int> runUpdate(
QueryExecutor executor, String statement, List<Object?> args) {
return _run(
'$statement with $args', () => executor.runUpdate(statement, args));
}
@override
Future<int> runDelete(
QueryExecutor executor, String statement, List<Object?> args) {
return _run(
'$statement with $args', () => executor.runDelete(statement, args));
}
@override
Future<void> runCustom(
QueryExecutor executor, String statement, List<Object?> args) {
return _run(
'$statement with $args', () => executor.runCustom(statement, args));
}
@override
Future<List<Map<String, Object?>>> runSelect(
QueryExecutor executor, String statement, List<Object?> args) {
return _run(
'$statement with $args', () => executor.runSelect(statement, args));
}
}
Interceptors can be applied with the interceptWith
extension on QueryExecutor
and
DatabaseConnection
:
NativeDatabase.createInBackground(
myDatabaseFile,
).interceptWith(LogInterceptor());
The QueryInterceptor
class is pretty powerful, as it allows you to fully control the underlying
database connection. You could also use it to retry some failing statements or to aggregate
statistics about query times to an external monitoring service.