Skip to content
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

feat: add span level measurements #1855 #2214

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Changelog

Check failure on line 1 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / danger / danger

Please consider adding a changelog entry for the next release.

Check notice on line 1 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / danger / danger

### Instructions and example for changelog Please add an entry to `CHANGELOG.md` to the "Unreleased" section. Make sure the entry includes this PR's number. Example: ```markdown ## Unreleased - add span level measurements #1855 ([#2214](https://github.com/getsentry/sentry-dart/pull/2214)) ``` If none of the above apply, you can opt out of this check by adding `#skip-changelog` to the PR description.

## 8.6.0

### Improvements

- Add support for span level measurements. ([#1855](https://github.com/getsentry/sentry-dart/issues/1855))
martinhaintz marked this conversation as resolved.
Show resolved Hide resolved
- Add error type identifier to improve obfuscated Flutter issue titles ([#2170](https://github.com/getsentry/sentry-dart/pull/2170))
- Example: transforms issue titles from `GA` to `FlutterError` or `minified:nE` to `FlutterError`
- This is enabled automatically and will change grouping if you already have issues with obfuscated titles
Expand Down
4 changes: 4 additions & 0 deletions dart/lib/src/noop_sentry_span.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,8 @@ class NoOpSentrySpan extends ISentrySpan {

@override
LocalMetricsAggregator? get localMetricsAggregator => null;

@override
// TODO: implement measurements
Map<String, SentryMeasurement> get measurements => throw UnimplementedError();
martinhaintz marked this conversation as resolved.
Show resolved Hide resolved
}
15 changes: 14 additions & 1 deletion dart/lib/src/protocol/sentry_span.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class SentrySpan extends ISentrySpan {
final SentryTracer _tracer;

final Map<String, dynamic> _data = {};
final Map<String, SentryMeasurement> _measurements = {};
dynamic _throwable;

SpanStatus? _status;
Expand Down Expand Up @@ -228,6 +229,8 @@ class SentrySpan extends ISentrySpan {
Map<String, String> get tags => _tags;

Map<String, dynamic> get data => _data;
@override
Map<String, SentryMeasurement> get measurements => _measurements;

@override
SentryTraceHeader toSentryTrace() => SentryTraceHeader(
Expand All @@ -242,7 +245,17 @@ class SentrySpan extends ISentrySpan {
num value, {
SentryMeasurementUnit? unit,
}) {
_tracer.setMeasurement(name, value, unit: unit);
if (finished) {
_hub.options.logger(SentryLevel.debug,
"The span is already finished. Measurement $name cannot be set");
return;
}
_measurements[name] = SentryMeasurement(name, value, unit: unit);
// We set the measurement in the transaction, too, but we have to check if this is the root span
// of the transaction, to avoid an infinite recursion
if (!_isRootSpan) {
_tracer.setMeasurementFromChild(name, value, unit: unit);
}
}

@override
Expand Down
5 changes: 4 additions & 1 deletion dart/lib/src/sentry_span_interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,16 @@ abstract class ISentrySpan {
/// Returns the trace information that could be sent as a sentry-trace header.
SentryTraceHeader toSentryTrace();

/// Set observed measurement for this transaction.
/// Set observed measurement for this span or transaction.
void setMeasurement(
String name,
num value, {
SentryMeasurementUnit? unit,
});

/// Returns the measurements for a span or transaction.
Map<String, SentryMeasurement> get measurements;

/// Returns the baggage that can be sent as "baggage" header.
SentryBaggageHeader? toBaggageHeader();

Expand Down
20 changes: 11 additions & 9 deletions dart/lib/src/sentry_tracer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ class SentryTracer extends ISentrySpan {
late final SentrySpan _rootSpan;
final List<SentrySpan> _children = [];
final Map<String, dynamic> _extra = {};
final Map<String, SentryMeasurement> _measurements = {};
@override
Map<String, SentryMeasurement> get measurements => _rootSpan.measurements;

Timer? _autoFinishAfterTimer;
Duration? _autoFinishAfter;
Expand Down Expand Up @@ -149,7 +150,7 @@ class SentryTracer extends ISentrySpan {
}

final transaction = SentryTransaction(this);
transaction.measurements.addAll(_measurements);
transaction.measurements.addAll(_rootSpan.measurements);

profileInfo = (status == null || status == SpanStatus.ok())
? await profiler?.finishFor(transaction)
Expand Down Expand Up @@ -320,9 +321,6 @@ class SentryTracer extends ISentrySpan {
@override
SentryTraceHeader toSentryTrace() => _rootSpan.toSentryTrace();

@visibleForTesting
Map<String, SentryMeasurement> get measurements =>
Map.unmodifiable(_measurements);

bool _haveAllChildrenFinished() {
for (final child in children) {
Expand All @@ -340,11 +338,15 @@ class SentryTracer extends ISentrySpan {

@override
void setMeasurement(String name, num value, {SentryMeasurementUnit? unit}) {
if (finished) {
return;
_rootSpan.setMeasurement(name, value, unit: unit);
}

void setMeasurementFromChild(String name, num value,
{SentryMeasurementUnit? unit}) {
// We don't want to overwrite the root span measurement, if it comes from a child.
if (!_rootSpan.measurements.containsKey(name)) {
setMeasurement(name, value, unit: unit);
}
martinhaintz marked this conversation as resolved.
Show resolved Hide resolved
final measurement = SentryMeasurement(name, value, unit: unit);
_measurements[name] = measurement;
}

@override
Expand Down
26 changes: 26 additions & 0 deletions dart/test/sentry_span_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,32 @@ void main() {
expect(fixture.hub.options.enableSpanLocalMetricAggregation, false);
expect(sut.localMetricsAggregator, null);
});

test('setMeasurement sets a measurement', () async {
final sut = fixture.getSut();
sut.setMeasurement("test", 1);
expect(sut.measurements.containsKey("test"), true);
expect(sut.measurements["test"]!.value, 1);
});

test('setMeasurement does not set a measurement if a span is finished',
() async {
final sut = fixture.getSut();
await sut.finish();
sut.setMeasurement("test", 1);
expect(sut.measurements.isEmpty, true);
});

test('setMeasurement also set a measurement to the transaction root span',
() async {
final sut = fixture.getSut();
final child = sut.tracer.startChild("child");
child.setMeasurement("child", 2);
expect(child.measurements.containsKey("child"), true);
expect(child.measurements["child"]!.value, 2);
expect(sut.measurements.containsKey("child"), true);
expect(sut.measurements["child"]!.value, 2);
});
}

class Fixture {
Expand Down
Loading