diff --git a/CHANGELOG.md b/CHANGELOG.md index 3517cd150b..1f7ce06059 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Features + +- Add support for span level measurements. ([#2214](https://github.com/getsentry/sentry-dart/pull/2214)) + ## 8.6.0 ### Improvements diff --git a/dart/lib/src/protocol/sentry_span.dart b/dart/lib/src/protocol/sentry_span.dart index 6358b4802a..00f6ec8f5a 100644 --- a/dart/lib/src/protocol/sentry_span.dart +++ b/dart/lib/src/protocol/sentry_span.dart @@ -242,7 +242,12 @@ 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; + } + _tracer.setMeasurementFromChild(name, value, unit: unit); } @override diff --git a/dart/lib/src/sentry_span_interface.dart b/dart/lib/src/sentry_span_interface.dart index 1d142c45b9..979822c2ac 100644 --- a/dart/lib/src/sentry_span_interface.dart +++ b/dart/lib/src/sentry_span_interface.dart @@ -71,7 +71,7 @@ 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, { diff --git a/dart/lib/src/sentry_tracer.dart b/dart/lib/src/sentry_tracer.dart index 66c179e386..1507143d69 100644 --- a/dart/lib/src/sentry_tracer.dart +++ b/dart/lib/src/sentry_tracer.dart @@ -17,7 +17,9 @@ class SentryTracer extends ISentrySpan { late final SentrySpan _rootSpan; final List _children = []; final Map _extra = {}; + final Map _measurements = {}; + Map get measurements => _measurements; Timer? _autoFinishAfterTimer; Duration? _autoFinishAfter; @@ -320,10 +322,6 @@ class SentryTracer extends ISentrySpan { @override SentryTraceHeader toSentryTrace() => _rootSpan.toSentryTrace(); - @visibleForTesting - Map get measurements => - Map.unmodifiable(_measurements); - bool _haveAllChildrenFinished() { for (final child in children) { if (!child.finished) { @@ -341,10 +339,19 @@ class SentryTracer extends ISentrySpan { @override void setMeasurement(String name, num value, {SentryMeasurementUnit? unit}) { if (finished) { + _hub.options.logger(SentryLevel.debug, + "The tracer is already finished. Measurement $name cannot be set"); return; } - final measurement = SentryMeasurement(name, value, unit: unit); - _measurements[name] = measurement; + _measurements[name] = SentryMeasurement(name, value, unit: unit); + } + + void setMeasurementFromChild(String name, num value, + {SentryMeasurementUnit? unit}) { + // We don't want to overwrite span measurement, if it comes from a child. + if (!_measurements.containsKey(name)) { + setMeasurement(name, value, unit: unit); + } } @override diff --git a/dart/test/sentry_span_test.dart b/dart/test/sentry_span_test.dart index e161ceee2f..d063cf787e 100644 --- a/dart/test/sentry_span_test.dart +++ b/dart/test/sentry_span_test.dart @@ -310,6 +310,21 @@ 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.tracer.measurements.containsKey("test"), true); + expect(sut.tracer.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.tracer.measurements.isEmpty, true); + }); } class Fixture { diff --git a/dart/test/sentry_tracer_test.dart b/dart/test/sentry_tracer_test.dart index a1251c224b..ba57aeb405 100644 --- a/dart/test/sentry_tracer_test.dart +++ b/dart/test/sentry_tracer_test.dart @@ -467,6 +467,22 @@ 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('setMeasurementFromChild does not override existing measurements', + () async { + final sut = fixture.getSut(); + sut.setMeasurement("test", 1); + sut.setMeasurementFromChild("test", 5); + expect(sut.measurements.containsKey("test"), true); + expect(sut.measurements["test"]!.value, 1); + }); }); group('$SentryBaggageHeader', () {