Skip to content

Commit 9733bc8

Browse files
authored
Merge branch 'main' into enh/app-start-refresh-rate-jni-ffi
2 parents 1ffba6f + 2b5e090 commit 9733bc8

20 files changed

+965
-154
lines changed

CHANGELOG.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
11
# Changelog
22

3-
## 9.7.0
3+
## Unreleased
4+
5+
### Features
6+
7+
- Mark file sync spans run in the main isolate with `blocked_main_thread` ([#3270](https://github.com/getsentry/sentry-dart/pull/3270))
8+
- This allows Sentry to create issues automatically out of file spans running a certain time on the main thread: https://docs.sentry.io/product/issues/issue-details/performance-issues/file-main-thread-io/
49

10+
### Enhancements
11+
12+
- Refactor `AndroidReplayRecorder` to use the new worker isolate api [#3296](https://github.com/getsentry/sentry-dart/pull/3296/)
13+
- Offload `captureEnvelope` to background isolate for Cocoa and Android [#3232](https://github.com/getsentry/sentry-dart/pull/3232)
14+
15+
## 9.7.0
516

617
### Features
718

packages/dart/lib/src/protocol/sentry_span.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@ class SentrySpan extends ISentrySpan {
7777
}
7878
}
7979

80+
// Dispatch OnSpanFinish lifecycle event
81+
final callback =
82+
_hub.options.lifecycleRegistry.dispatchCallback(OnSpanFinish(this));
83+
if (callback is Future) {
84+
await callback;
85+
}
86+
8087
// The finished flag depends on the _endTimestamp
8188
// If we set this earlier then finished is true and then we cannot use setData etc...
8289
_endTimestamp = endTimestamp;

packages/dart/lib/src/sdk_lifecycle_hooks.dart

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,17 @@ class SdkLifecycleRegistry {
3434
callbacks?.remove(callback);
3535
}
3636

37-
FutureOr<void> dispatchCallback<T extends SdkLifecycleEvent>(T event) async {
38-
final callbacks = _lifecycleCallbacks[event.runtimeType] ?? [];
37+
FutureOr<void> dispatchCallback<T extends SdkLifecycleEvent>(T event) {
38+
final callbacks = _lifecycleCallbacks[event.runtimeType];
39+
if (callbacks == null || callbacks.isEmpty) {
40+
// Return synchronously when there are no callbacks to avoid unnecessary async boundary
41+
return null;
42+
}
43+
return _dispatchCallbackAsync(event, callbacks);
44+
}
45+
46+
Future<void> _dispatchCallbackAsync<T extends SdkLifecycleEvent>(
47+
T event, List<Function> callbacks) async {
3948
for (final cb in callbacks) {
4049
try {
4150
final result = (cb as SdkLifecycleCallback<T>)(event);
@@ -79,3 +88,11 @@ class OnSpanStart extends SdkLifecycleEvent {
7988

8089
final ISentrySpan span;
8190
}
91+
92+
/// Dispatched when a sampled span is finished.
93+
@internal
94+
class OnSpanFinish extends SdkLifecycleEvent {
95+
OnSpanFinish(this.span);
96+
97+
final ISentrySpan span;
98+
}

packages/dart/lib/src/span_data_convention.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class SpanDataConvention {
1010
// https://develop.sentry.dev/sdk/telemetry/traces/span-data-conventions/#thread
1111
static const threadId = 'thread.id';
1212
static const threadName = 'thread.name';
13+
static const blockedMainThread = 'blocked_main_thread';
1314

1415
// TODO: eventually add other data keys here as well
1516
}

packages/drift/README.md

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@
55
<br />
66
</p>
77

8-
Sentry integration for `drift` package
9-
===========
8+
# Sentry integration for `drift` package
109

11-
| package | build | pub | likes | popularity | pub points |
12-
|-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------| ------- |
13-
| sentry_drift | [![build](https://github.com/getsentry/sentry-dart/actions/workflows/drift.yml/badge.svg?branch=main)](https://github.com/getsentry/sentry-dart/actions?query=workflow%3Asentry-drift) | [![pub package](https://img.shields.io/pub/v/sentry_drift.svg)](https://pub.dev/packages/sentry_drift) | [![likes](https://img.shields.io/pub/likes/sentry_drift)](https://pub.dev/packages/sentry_drift/score) | [![popularity](https://img.shields.io/pub/popularity/sentry_drift)](https://pub.dev/packages/sentry_drift/score) | [![pub points](https://img.shields.io/pub/points/sentry_drift)](https://pub.dev/packages/sentry_drift/score)
10+
| package | build | pub | likes | popularity | pub points |
11+
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
12+
| sentry_drift | [![build](https://github.com/getsentry/sentry-dart/actions/workflows/drift.yml/badge.svg?branch=main)](https://github.com/getsentry/sentry-dart/actions?query=workflow%3Asentry-drift) | [![pub package](https://img.shields.io/pub/v/sentry_drift.svg)](https://pub.dev/packages/sentry_drift) | [![likes](https://img.shields.io/pub/likes/sentry_drift)](https://pub.dev/packages/sentry_drift/score) | [![popularity](https://img.shields.io/pub/popularity/sentry_drift)](https://pub.dev/packages/sentry_drift/score) | [![pub points](https://img.shields.io/pub/points/sentry_drift)](https://pub.dev/packages/sentry_drift/score) |
1413

1514
Integration for the [`drift`](https://pub.dev/packages/drift) package.
1615

@@ -50,9 +49,8 @@ Future<void> main() async {
5049
Future<void> runApp() async {
5150
final tr =
5251
Sentry.startTransaction('drift', 'op', bindToScope: true);
53-
final executor = SentryQueryExecutor(
54-
() => NativeDatabase.memory(),
55-
databaseName: 'my_db_name',
52+
final executor = NativeDatabase.memory().interceptWith(
53+
SentryQueryInterceptor(databaseName: 'my_db_name'),
5654
);
5755
final db = AppDatabase(executor);
5856
@@ -74,9 +72,9 @@ Future<void> runApp() async {
7472

7573
#### Resources
7674

77-
* [![Flutter docs](https://img.shields.io/badge/documentation-sentry.io-green.svg?label=flutter%20docs)](https://docs.sentry.io/platforms/flutter/)
78-
* [![Dart docs](https://img.shields.io/badge/documentation-sentry.io-green.svg?label=dart%20docs)](https://docs.sentry.io/platforms/dart/)
79-
* [![Discussions](https://img.shields.io/github/discussions/getsentry/sentry-dart.svg)](https://github.com/getsentry/sentry-dart/discussions)
80-
* [![Discord Chat](https://img.shields.io/discord/621778831602221064?logo=discord&logoColor=ffffff&color=7389D8)](https://discord.gg/gB6ja9uZuN)
81-
* [![Stack Overflow](https://img.shields.io/badge/stack%20overflow-sentry-green.svg)](https://stackoverflow.com/questions/tagged/sentry)
82-
* [![Twitter Follow](https://img.shields.io/twitter/follow/getsentry?label=getsentry&style=social)](https://twitter.com/intent/follow?screen_name=getsentry)
75+
- [![Flutter docs](https://img.shields.io/badge/documentation-sentry.io-green.svg?label=flutter%20docs)](https://docs.sentry.io/platforms/flutter/)
76+
- [![Dart docs](https://img.shields.io/badge/documentation-sentry.io-green.svg?label=dart%20docs)](https://docs.sentry.io/platforms/dart/)
77+
- [![Discussions](https://img.shields.io/github/discussions/getsentry/sentry-dart.svg)](https://github.com/getsentry/sentry-dart/discussions)
78+
- [![Discord Chat](https://img.shields.io/discord/621778831602221064?logo=discord&logoColor=ffffff&color=7389D8)](https://discord.gg/gB6ja9uZuN)
79+
- [![Stack Overflow](https://img.shields.io/badge/stack%20overflow-sentry-green.svg)](https://stackoverflow.com/questions/tagged/sentry)
80+
- [![Twitter Follow](https://img.shields.io/twitter/follow/getsentry?label=getsentry&style=social)](https://twitter.com/intent/follow?screen_name=getsentry)

packages/file/lib/src/sentry_file.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,7 @@ class SentryFile implements File {
489489

490490
span?.origin = SentryTraceOrigins.autoFile;
491491
span?.setData('file.async', false);
492+
span?.setData('sync', true);
492493

493494
final Map<String, dynamic> breadcrumbData = {};
494495
breadcrumbData['file.async'] = false;

packages/file/test/sentry_file_test.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ void main() {
2828
expect(span.context.operation, 'file.copy');
2929
expect(span.data['file.size'], 7);
3030
expect(span.data['file.async'], async);
31+
if (!async) {
32+
expect(span.data['sync'], true);
33+
}
3134
expect(span.context.description, 'testfile.txt');
3235
expect(
3336
(span.data['file.path'] as String)
@@ -115,6 +118,9 @@ void main() {
115118
expect(span.context.operation, 'file.write');
116119
expect(span.data['file.size'], size);
117120
expect(span.data['file.async'], async);
121+
if (!async) {
122+
expect(span.data['sync'], true);
123+
}
118124
expect(span.context.description, 'testfile_create.txt');
119125
expect(
120126
(span.data['file.path'] as String)
@@ -200,6 +206,9 @@ void main() {
200206
expect(span.context.operation, 'file.delete');
201207
expect(span.data['file.size'], size);
202208
expect(span.data['file.async'], async);
209+
if (!async) {
210+
expect(span.data['sync'], true);
211+
}
203212
expect(span.context.description, 'testfile_delete.txt');
204213
expect(
205214
(span.data['file.path'] as String)
@@ -341,6 +350,9 @@ void main() {
341350
expect(span.context.operation, 'file.read');
342351
expect(span.data['file.size'], size);
343352
expect(span.data['file.async'], async);
353+
if (!async) {
354+
expect(span.data['sync'], true);
355+
}
344356
expect(span.context.description, fileName);
345357
expect(
346358
(span.data['file.path'] as String)
@@ -492,6 +504,9 @@ void main() {
492504
expect(span.context.operation, 'file.rename');
493505
expect(span.data['file.size'], 0);
494506
expect(span.data['file.async'], async);
507+
if (!async) {
508+
expect(span.data['sync'], true);
509+
}
495510
expect(span.context.description, name);
496511
expect(
497512
(span.data['file.path'] as String).endsWith('test_resources/$name'),
@@ -580,6 +595,9 @@ void main() {
580595
final span = call.transaction.spans.first;
581596

582597
expect(span.data['file.async'], async);
598+
if (!async) {
599+
expect(span.data['sync'], true);
600+
}
583601
expect(span.data['file.path'], null);
584602
expect(span.origin, SentryTraceOrigins.autoFile);
585603
}

packages/flutter/lib/src/integrations/thread_info_integration.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import 'package:meta/meta.dart';
44

55
import '../../sentry_flutter.dart';
6+
// ignore: implementation_imports
67
import '../isolate/isolate_helper.dart';
78

89
/// Integration for adding thread information to spans.
@@ -33,6 +34,8 @@ class ThreadInfoIntegration implements Integration<SentryFlutterOptions> {
3334

3435
options.lifecycleRegistry
3536
.registerCallback<OnSpanStart>(_addThreadInfoToSpan);
37+
options.lifecycleRegistry
38+
.registerCallback<OnSpanFinish>(_processSyncSpanOnFinish);
3639

3740
options.sdk.addIntegration(integrationName);
3841
}
@@ -41,6 +44,8 @@ class ThreadInfoIntegration implements Integration<SentryFlutterOptions> {
4144
void close() {
4245
_hub?.options.lifecycleRegistry
4346
.removeCallback<OnSpanStart>(_addThreadInfoToSpan);
47+
_hub?.options.lifecycleRegistry
48+
.removeCallback<OnSpanFinish>(_processSyncSpanOnFinish);
4449
}
4550

4651
Future<void> _addThreadInfoToSpan(OnSpanStart event) async {
@@ -65,4 +70,25 @@ class ThreadInfoIntegration implements Integration<SentryFlutterOptions> {
6570
span.setData(SpanDataConvention.threadName, threadName);
6671
}
6772
}
73+
74+
void _processSyncSpanOnFinish(OnSpanFinish event) {
75+
final span = event.span;
76+
if (span is! SentrySpan) {
77+
return;
78+
}
79+
80+
final data = span.data;
81+
82+
// Check if this is a sync operation
83+
if (data.containsKey('sync')) {
84+
// Check if we're on the main isolate by looking at thread name
85+
if (data['sync'] == true &&
86+
data[SpanDataConvention.threadName] == 'main') {
87+
span.setData(SpanDataConvention.blockedMainThread, true);
88+
}
89+
90+
// Always remove the sync flag
91+
span.removeData('sync');
92+
}
93+
}
6894
}

packages/flutter/lib/src/isolate/isolate_worker.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import 'dart:isolate';
44
import '../../sentry_flutter.dart';
55
import 'isolate_logger.dart';
66

7+
typedef SpawnWorkerFn = Future<Worker> Function(WorkerConfig, WorkerEntry);
8+
79
const _shutdownCommand = '_shutdown_';
810

911
// -------------------------------------------

packages/flutter/lib/src/native/java/android_envelope_sender.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import '../../isolate/isolate_worker.dart';
1010
import '../../isolate/isolate_logger.dart';
1111
import 'binding.dart' as native;
1212

13-
typedef SpawnWorkerFn = Future<Worker> Function(WorkerConfig, WorkerEntry);
14-
1513
class AndroidEnvelopeSender {
1614
final SentryFlutterOptions _options;
1715
final WorkerConfig _config;

0 commit comments

Comments
 (0)