Skip to content
Next Next commit
Add support for readTransaction
  • Loading branch information
denrase committed Jan 15, 2024
commit 4cc63be9e842e9589e878a7ed03175c97be165b4
70 changes: 67 additions & 3 deletions sqflite/lib/src/sentry_database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ class SentryDatabase extends SentryDatabaseExecutor implements Database {
// ignore: public_member_api_docs
static const dbSqlQueryOp = 'db.sql.query';

static const _dbSqlOp = 'db.sql.transaction';
static const _dbSqlTransactionOp = 'db.sql.transaction';

static const _dbSqlReadTransactionOp = 'db.sql.readTransaction';

@internal
// ignore: public_member_api_docs
static const dbSystemKey = 'db.system';
Expand Down Expand Up @@ -143,7 +146,7 @@ class SentryDatabase extends SentryDatabaseExecutor implements Database {
final currentSpan = _hub.getSpan();
final description = 'Transaction DB: ${_database.path}';
final span = currentSpan?.startChild(
_dbSqlOp,
_dbSqlTransactionOp,
description: description,
);
// ignore: invalid_use_of_internal_member
Expand All @@ -152,7 +155,7 @@ class SentryDatabase extends SentryDatabaseExecutor implements Database {

var breadcrumb = Breadcrumb(
message: description,
category: _dbSqlOp,
category: _dbSqlTransactionOp,
data: {},
type: 'query',
);
Expand Down Expand Up @@ -196,4 +199,65 @@ class SentryDatabase extends SentryDatabaseExecutor implements Database {
}
});
}

@override
// ignore: override_on_non_overriding_member
Future<T> readTransaction<T>(Future<T> Function(Transaction txn) action) {
return Future<T>(() async {
final currentSpan = _hub.getSpan();
final description = 'Transaction DB: ${_database.path}';
final span = currentSpan?.startChild(
_dbSqlReadTransactionOp,
description: description,
);
// ignore: invalid_use_of_internal_member
span?.origin = SentryTraceOrigins.autoDbSqfliteDatabase;
setDatabaseAttributeData(span, dbName);

var breadcrumb = Breadcrumb(
message: description,
category: _dbSqlReadTransactionOp,
data: {},
type: 'query',
);
setDatabaseAttributeOnBreadcrumb(breadcrumb, dbName);

Future<T> newAction(Transaction txn) async {
final executor = SentryDatabaseExecutor(
txn,
parentSpan: span,
hub: _hub,
dbName: dbName,
);
final sentrySqfliteTransaction =
SentrySqfliteTransaction(executor, hub: _hub, dbName: dbName);

return await action(sentrySqfliteTransaction);
}

try {
final result =
await _database.readTransaction(newAction);

span?.status = SpanStatus.ok();
breadcrumb.data?['status'] = 'ok';

return result;
} catch (exception) {
span?.throwable = exception;
span?.status = SpanStatus.internalError();
breadcrumb.data?['status'] = 'internal_error';
breadcrumb = breadcrumb.copyWith(
level: SentryLevel.warning,
);

rethrow;
} finally {
await span?.finish();

// ignore: invalid_use_of_internal_member
await _hub.scope.addBreadcrumb(breadcrumb);
}
});
}
}
42 changes: 42 additions & 0 deletions sqflite/test/sentry_database_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,27 @@ void main() {
await db.close();
});

test('creates readTransaction span', () async {
final db = await fixture.getSut();

await db.readTransaction((txn) async {
expect(txn is SentrySqfliteTransaction, true);
});
final span = fixture.tracer.children.last;
expect(span.context.operation, 'db.sql.readTransaction');
expect(span.context.description, 'Transaction DB: $inMemoryDatabasePath');
expect(span.status, SpanStatus.ok());
expect(span.data[SentryDatabase.dbSystemKey], SentryDatabase.dbSystem);
expect(span.data[SentryDatabase.dbNameKey], inMemoryDatabasePath);
expect(
span.origin,
// ignore: invalid_use_of_internal_member
SentryTraceOrigins.autoDbSqfliteDatabase,
);

await db.close();
});

test('creates transaction breadcrumb', () async {
final db = await fixture.getSut();

Expand All @@ -128,6 +149,27 @@ void main() {
await db.close();
});

test('creates readTransaction breadcrumb', () async {
final db = await fixture.getSut();

await db.readTransaction((txn) async {
expect(txn is SentrySqfliteTransaction, true);
});

final breadcrumb = fixture.hub.scope.breadcrumbs.first;
expect(breadcrumb.message, 'Transaction DB: $inMemoryDatabasePath');
expect(breadcrumb.category, 'db.sql.readTransaction');
expect(breadcrumb.data?['status'], 'ok');
expect(
breadcrumb.data?[SentryDatabase.dbSystemKey],
SentryDatabase.dbSystem,
);
expect(breadcrumb.data?[SentryDatabase.dbNameKey], inMemoryDatabasePath);
expect(breadcrumb.type, 'query');

await db.close();
});

test('creates transaction children run by the transaction', () async {
final db = await fixture.getSut();

Expand Down