-
-
Notifications
You must be signed in to change notification settings - Fork 280
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
base: feat/span-first
Are you sure you want to change the base?
Changes from all 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
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 |
|---|---|---|
|
|
@@ -14,6 +14,8 @@ class SentrySpan extends ISentrySpan { | |
| late final DateTime _startTimestamp; | ||
| final Hub _hub; | ||
|
|
||
| bool _isFinished = false; | ||
| bool _isFinishing = false; | ||
| bool _isRootSpan = false; | ||
|
|
||
| bool get isRootSpan => _isRootSpan; | ||
|
|
@@ -51,50 +53,59 @@ class SentrySpan extends ISentrySpan { | |
| @override | ||
| Future<void> finish( | ||
| {SpanStatus? status, DateTime? endTimestamp, Hint? hint}) async { | ||
| if (finished) { | ||
| // Prevent concurrent or duplicate finish() calls | ||
| if (_isFinished || _isFinishing) { | ||
| return; | ||
| } | ||
|
|
||
| if (status != null) { | ||
| _status = status; | ||
| } | ||
| _isFinishing = true; | ||
|
|
||
| if (endTimestamp == null) { | ||
| endTimestamp = _hub.options.clock(); | ||
| } else if (endTimestamp.isBefore(_startTimestamp)) { | ||
| _hub.options.log( | ||
| SentryLevel.warning, | ||
| 'End timestamp ($endTimestamp) cannot be before start timestamp ($_startTimestamp)', | ||
| ); | ||
| endTimestamp = _hub.options.clock(); | ||
| } else { | ||
| endTimestamp = endTimestamp.toUtc(); | ||
| } | ||
| try { | ||
| if (status != null) { | ||
| _status = status; | ||
| } | ||
|
|
||
| for (final collector in _hub.options.performanceCollectors) { | ||
| if (collector is PerformanceContinuousCollector) { | ||
| await collector.onSpanFinished(this, endTimestamp); | ||
| if (endTimestamp == null) { | ||
| endTimestamp = _hub.options.clock(); | ||
| } else if (endTimestamp.isBefore(_startTimestamp)) { | ||
| _hub.options.log( | ||
| SentryLevel.warning, | ||
| 'End timestamp ($endTimestamp) cannot be before start timestamp ($_startTimestamp)', | ||
| ); | ||
| endTimestamp = _hub.options.clock(); | ||
| } else { | ||
| endTimestamp = endTimestamp.toUtc(); | ||
| } | ||
| } | ||
|
|
||
| // Dispatch OnSpanFinish lifecycle event | ||
| final callback = | ||
| _hub.options.lifecycleRegistry.dispatchCallback(OnSpanFinish(this)); | ||
| if (callback is Future) { | ||
| await callback; | ||
| } | ||
| _endTimestamp = endTimestamp; | ||
|
|
||
| // ignore: deprecated_member_use_from_same_package | ||
| for (final collector in _hub.options.performanceCollectors) { | ||
| if (collector is PerformanceContinuousCollector) { | ||
| await collector.onSpanFinished(this, endTimestamp); | ||
| } | ||
| } | ||
|
|
||
| // Dispatch OnSpanFinish lifecycle event | ||
| final callback = | ||
| _hub.options.lifecycleRegistry.dispatchCallback(OnSpanFinish(this)); | ||
| if (callback is Future) { | ||
| await callback; | ||
| } | ||
|
|
||
| // associate error | ||
| if (_throwable != null) { | ||
| _hub.setSpanContext(_throwable, this, _tracer.name); | ||
| } | ||
|
|
||
| // The finished flag depends on the _endTimestamp | ||
| // If we set this earlier then finished is true and then we cannot use setData etc... | ||
| _endTimestamp = endTimestamp; | ||
| _isFinished = true; | ||
|
|
||
| // associate error | ||
| if (_throwable != null) { | ||
| _hub.setSpanContext(_throwable, this, _tracer.name); | ||
| await _finishedCallback?.call(endTimestamp: _endTimestamp, hint: hint); | ||
| return super | ||
| .finish(status: status, endTimestamp: _endTimestamp, hint: hint); | ||
| } finally { | ||
| _isFinishing = false; | ||
| } | ||
| await _finishedCallback?.call(endTimestamp: _endTimestamp, hint: hint); | ||
| return super | ||
| .finish(status: status, endTimestamp: _endTimestamp, hint: hint); | ||
| } | ||
|
|
||
| @override | ||
|
|
@@ -207,7 +218,7 @@ class SentrySpan extends ISentrySpan { | |
| } | ||
|
|
||
| @override | ||
| bool get finished => _endTimestamp != null; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this change is necessary for the lifecycle callbacks to function correctly. we were triggering the callbacks before the endTimestamp was set (which means we never had access to the endTimestamp) but we could not trigger it after because then the span would be immutable to change in |
||
| bool get finished => _isFinished && _endTimestamp != null; | ||
|
|
||
| @override | ||
| dynamic get throwable => _throwable; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,12 @@ abstract class InstrumentationSpan { | |
| Future<void> finish({SpanStatus? status, DateTime? endTimestamp}); | ||
| SentryTraceHeader toSentryTrace(); | ||
| SentryBaggageHeader? toBaggageHeader(); | ||
|
|
||
| /// Returns true if this span is a no-op span that doesn't record data. | ||
| bool get isNoop; | ||
|
|
||
| /// The start timestamp of this span. | ||
| DateTime get startTimestamp; | ||
| } | ||
|
|
||
| /// [InstrumentationSpan] implementation wrapping [ISentrySpan]. | ||
|
|
@@ -65,6 +71,22 @@ class LegacyInstrumentationSpan implements InstrumentationSpan { | |
|
|
||
| @override | ||
| SentryBaggageHeader? toBaggageHeader() => _span.toBaggageHeader(); | ||
|
|
||
| @override | ||
| bool get isNoop => _span is NoOpSentrySpan; | ||
|
|
||
| @override | ||
| DateTime get startTimestamp => _span.startTimestamp; | ||
|
|
||
| @override | ||
| bool operator ==(Object other) => | ||
| identical(this, other) || | ||
| other is LegacyInstrumentationSpan && | ||
| runtimeType == other.runtimeType && | ||
| identical(_span, other._span); | ||
|
|
||
| @override | ||
| int get hashCode => _span.hashCode; | ||
| } | ||
|
|
||
| @internal | ||
|
|
@@ -180,4 +202,20 @@ class StreamingInstrumentationSpan implements InstrumentationSpan { | |
| } | ||
| return SentrySpanStatusV2.error; | ||
| } | ||
|
|
||
| @override | ||
| bool get isNoop => _span is! RecordingSentrySpanV2; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this check suffices |
||
|
|
||
| @override | ||
| DateTime get startTimestamp => _span.startTimestamp; | ||
|
|
||
| @override | ||
| bool operator ==(Object other) => | ||
| identical(this, other) || | ||
| other is StreamingInstrumentationSpan && | ||
| runtimeType == other.runtimeType && | ||
| identical(_span, other._span); | ||
|
|
||
| @override | ||
| int get hashCode => _span.hashCode; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.