-
-
Notifications
You must be signed in to change notification settings - Fork 281
feat(span-first): add span v2 support for frames tracking #3447
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
12aee4d
5c3b095
023c89e
e9f9b74
4133f4f
8ea21f8
1a33052
74dc1f9
49eae68
3c9ad4a
6d5613b
9aa55a4
e97ec07
8c954bd
3e9b6c4
b499167
1c14c1c
bb0fe39
4f088cb
4a6eac3
084fabd
7444114
1d5083e
2c27e38
62e81d8
1ea10a9
703a66f
1b084cd
f80fd0d
9413892
514e7b5
b66742e
6d6b381
ced147d
8741446
0cc7c26
c55cfa6
5799275
c6d4abe
a0fcebc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| // ignore_for_file: invalid_use_of_internal_member | ||
|
|
||
| import 'package:meta/meta.dart'; | ||
|
|
||
| import '../../sentry_flutter.dart'; | ||
| import '../utils/internal_logger.dart'; | ||
| import 'sentry_delayed_frames_tracker.dart'; | ||
|
|
||
| /// Collects frames from [SentryDelayedFramesTracker], calculates the metrics | ||
| /// and attaches them to spans. | ||
| @internal | ||
| class SpanFrameMetricsCollectorV2 implements PerformanceContinuousCollectorV2 { | ||
buenaflor marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| SpanFrameMetricsCollectorV2( | ||
| this._frameTracker, { | ||
| required void Function() resumeFrameTracking, | ||
| required void Function() pauseFrameTracking, | ||
| }) : _resumeFrameTracking = resumeFrameTracking, | ||
| _pauseFrameTracking = pauseFrameTracking; | ||
|
|
||
| final SentryDelayedFramesTracker _frameTracker; | ||
| final void Function() _resumeFrameTracking; | ||
| final void Function() _pauseFrameTracking; | ||
|
|
||
| /// Stores the spans that are actively being tracked. | ||
| /// After the frames are calculated and stored in the span the span is removed from this list. | ||
| @visibleForTesting | ||
| final List<SentrySpanV2> activeSpans = []; | ||
|
|
||
| @override | ||
| Future<void> onSpanStarted(SentrySpanV2 span) async { | ||
| return _tryCatch('onSpanStarted', () async { | ||
| if (span is NoOpSentrySpan) { | ||
cursor[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
buenaflor marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return; | ||
| } | ||
|
|
||
| activeSpans.add(span); | ||
| _resumeFrameTracking(); | ||
| }); | ||
| } | ||
|
|
||
| @override | ||
| Future<void> onSpanFinished(SentrySpanV2 span, DateTime endTimestamp) async { | ||
| return _tryCatch('onSpanFinished', () async { | ||
| if (span is NoOpSentrySpan) { | ||
buenaflor marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return; | ||
| } | ||
|
|
||
| final startTimestamp = span.startTimestamp; | ||
| final metrics = _frameTracker.getFrameMetrics( | ||
| spanStartTimestamp: startTimestamp, spanEndTimestamp: endTimestamp); | ||
|
|
||
| if (metrics != null) { | ||
| final attributes = Map<String, SentryAttribute>.from(span.attributes); | ||
| attributes.putIfAbsent(SemanticAttributesConstants.framesTotal, | ||
| () => SentryAttribute.int(metrics.totalFrameCount)); | ||
| attributes.putIfAbsent(SemanticAttributesConstants.framesSlow, | ||
| () => SentryAttribute.int(metrics.slowFrameCount)); | ||
| attributes.putIfAbsent(SemanticAttributesConstants.framesFrozen, | ||
| () => SentryAttribute.int(metrics.frozenFrameCount)); | ||
| attributes.putIfAbsent(SemanticAttributesConstants.framesDelay, | ||
| () => SentryAttribute.int(metrics.framesDelay)); | ||
| span.setAttributes(attributes); | ||
| } | ||
cursor[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| activeSpans.remove(span); | ||
| if (activeSpans.isEmpty) { | ||
| clear(); | ||
| } else { | ||
| _frameTracker.removeIrrelevantFrames(activeSpans.first.startTimestamp); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| Future<void> _tryCatch(String methodName, Future<void> Function() fn) async { | ||
| try { | ||
| return fn(); | ||
cursor[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } catch (exception, stackTrace) { | ||
| internalLogger.error( | ||
| 'SpanV2FrameMetricsCollector $methodName failed', | ||
| error: exception, | ||
| stackTrace: stackTrace, | ||
| ); | ||
| clear(); | ||
| } | ||
| } | ||
|
|
||
| @override | ||
| void clear() { | ||
| _pauseFrameTracking(); | ||
| _frameTracker.clear(); | ||
| activeSpans.clear(); | ||
| // we don't need to clear the expected frame duration as that realistically | ||
| // won't change throughout the application's lifecycle | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ import '../../sentry_flutter.dart'; | |
| import '../binding_wrapper.dart'; | ||
| import '../frames_tracking/sentry_delayed_frames_tracker.dart'; | ||
| import '../frames_tracking/span_frame_metrics_collector.dart'; | ||
| import '../frames_tracking/span_frame_metrics_collector_v2.dart'; | ||
| import '../native/sentry_native_binding.dart'; | ||
|
|
||
| class FramesTrackingIntegration implements Integration<SentryFlutterOptions> { | ||
|
|
@@ -50,11 +51,29 @@ class FramesTrackingIntegration implements Integration<SentryFlutterOptions> { | |
| SentryDelayedFramesTracker(options, expectedFrameDuration); | ||
| widgetsBinding.initializeFramesTracking( | ||
| framesTracker.addDelayedFrame, options, expectedFrameDuration); | ||
| final collector = SpanFrameMetricsCollector(options, framesTracker, | ||
| resumeFrameTracking: () => widgetsBinding.resumeTrackingFrames(), | ||
| pauseFrameTracking: () => widgetsBinding.pauseTrackingFrames()); | ||
| options.addPerformanceCollector(collector); | ||
| _collector = collector; | ||
| switch (options.traceLifecycle) { | ||
| case SentryTraceLifecycle.streaming: | ||
| final collector = SpanFrameMetricsCollectorV2(framesTracker, | ||
| resumeFrameTracking: () => widgetsBinding.resumeTrackingFrames(), | ||
| pauseFrameTracking: () => widgetsBinding.pauseTrackingFrames()); | ||
| _collector = collector; | ||
|
||
|
|
||
| options.lifecycleRegistry.registerCallback<OnSpanStartV2>((event) { | ||
| collector.onSpanStarted(event.span); | ||
| }); | ||
|
|
||
| options.lifecycleRegistry.registerCallback<OnProcessSpan>((event) { | ||
| if (event.span.endTimestamp != null) { | ||
| collector.onSpanFinished(event.span, event.span.endTimestamp!); | ||
| } | ||
| }); | ||
cursor[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
sentry[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| case SentryTraceLifecycle.static: | ||
| final collector = SpanFrameMetricsCollector(options, framesTracker, | ||
| resumeFrameTracking: () => widgetsBinding.resumeTrackingFrames(), | ||
| pauseFrameTracking: () => widgetsBinding.pauseTrackingFrames()); | ||
| options.addPerformanceCollector(collector); | ||
| _collector = collector; | ||
| } | ||
|
|
||
| options.sdk.addIntegration(integrationName); | ||
| options.log(SentryLevel.debug, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.