diff --git a/CHANGELOG.md b/CHANGELOG.md index a553b7e7ec..5e349b0912 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +* Feat: Allow to set startTimestamp & endTimestamp manually to SentrySpan (#676) * Bump: Sentry-Cocoa to 7.10.0 (#777) * Feat: Additional Dart/Flutter context information (#778) * Bump: Kotlin plugin to 1.5.31 (#763) diff --git a/dart/lib/src/hub.dart b/dart/lib/src/hub.dart index 1b82ea4115..8411e155eb 100644 --- a/dart/lib/src/hub.dart +++ b/dart/lib/src/hub.dart @@ -342,6 +342,7 @@ class Hub { String name, String operation, { String? description, + DateTime? startTimestamp, bool? bindToScope, bool? waitForChildren, Duration? autoFinishAfter, @@ -354,6 +355,7 @@ class Hub { operation, description: description, ), + startTimestamp: startTimestamp, bindToScope: bindToScope, waitForChildren: waitForChildren, autoFinishAfter: autoFinishAfter, @@ -365,6 +367,7 @@ class Hub { ISentrySpan startTransactionWithContext( SentryTransactionContext transactionContext, { Map? customSamplingContext, + DateTime? startTimestamp, bool? bindToScope, bool? waitForChildren, Duration? autoFinishAfter, @@ -395,6 +398,7 @@ class Hub { final tracer = SentryTracer( transactionContext, this, + startTimestamp: startTimestamp, waitForChildren: waitForChildren ?? false, autoFinishAfter: autoFinishAfter, trimEnd: trimEnd ?? false, diff --git a/dart/lib/src/hub_adapter.dart b/dart/lib/src/hub_adapter.dart index fa2e957fb7..9f8bed7c59 100644 --- a/dart/lib/src/hub_adapter.dart +++ b/dart/lib/src/hub_adapter.dart @@ -108,6 +108,7 @@ class HubAdapter implements Hub { ISentrySpan startTransactionWithContext( SentryTransactionContext transactionContext, { Map? customSamplingContext, + DateTime? startTimestamp, bool? bindToScope, bool? waitForChildren, Duration? autoFinishAfter, @@ -116,6 +117,7 @@ class HubAdapter implements Hub { Sentry.startTransactionWithContext( transactionContext, customSamplingContext: customSamplingContext, + startTimestamp: startTimestamp, bindToScope: bindToScope, waitForChildren: waitForChildren, autoFinishAfter: autoFinishAfter, @@ -127,6 +129,7 @@ class HubAdapter implements Hub { String name, String operation, { String? description, + DateTime? startTimestamp, bool? bindToScope, bool? waitForChildren, Duration? autoFinishAfter, @@ -137,6 +140,7 @@ class HubAdapter implements Hub { name, operation, description: description, + startTimestamp: startTimestamp, bindToScope: bindToScope, waitForChildren: waitForChildren, autoFinishAfter: autoFinishAfter, diff --git a/dart/lib/src/noop_hub.dart b/dart/lib/src/noop_hub.dart index 0aab1bb306..49c8fc4404 100644 --- a/dart/lib/src/noop_hub.dart +++ b/dart/lib/src/noop_hub.dart @@ -87,6 +87,7 @@ class NoOpHub implements Hub { String name, String operation, { String? description, + DateTime? startTimestamp, bool? bindToScope, bool? waitForChildren, Duration? autoFinishAfter, @@ -99,6 +100,7 @@ class NoOpHub implements Hub { ISentrySpan startTransactionWithContext( SentryTransactionContext transactionContext, { Map? customSamplingContext, + DateTime? startTimestamp, bool? bindToScope, bool? waitForChildren, Duration? autoFinishAfter, diff --git a/dart/lib/src/noop_sentry_span.dart b/dart/lib/src/noop_sentry_span.dart index d1a98c4d87..89c7dc8bf9 100644 --- a/dart/lib/src/noop_sentry_span.dart +++ b/dart/lib/src/noop_sentry_span.dart @@ -26,7 +26,7 @@ class NoOpSentrySpan extends ISentrySpan { } @override - Future finish({SpanStatus? status}) async {} + Future finish({SpanStatus? status, DateTime? endTimestamp}) async {} @override void removeData(String key) {} @@ -41,7 +41,11 @@ class NoOpSentrySpan extends ISentrySpan { void setTag(String key, String value) {} @override - ISentrySpan startChild(String operation, {String? description}) => + ISentrySpan startChild( + String operation, { + String? description, + DateTime? startTimestamp, + }) => NoOpSentrySpan(); @override diff --git a/dart/lib/src/protocol/sentry_span.dart b/dart/lib/src/protocol/sentry_span.dart index ce856a00ab..8d1ab0b81a 100644 --- a/dart/lib/src/protocol/sentry_span.dart +++ b/dart/lib/src/protocol/sentry_span.dart @@ -9,8 +9,8 @@ import '../utils.dart'; class SentrySpan extends ISentrySpan { final SentrySpanContext _context; - DateTime? _timestamp; - final DateTime _startTimestamp = getUtcDateTime(); + DateTime? _endTimestamp; + late final DateTime _startTimestamp; final Hub _hub; final SentryTracer _tracer; @@ -19,7 +19,7 @@ class SentrySpan extends ISentrySpan { SpanStatus? _status; final Map _tags = {}; - void Function()? _finishedCallback; + void Function({DateTime? endTimestamp})? _finishedCallback; @override bool? sampled; @@ -28,9 +28,11 @@ class SentrySpan extends ISentrySpan { this._tracer, this._context, this._hub, { + DateTime? startTimestamp, bool? sampled, - Function()? finishedCallback, + Function({DateTime? endTimestamp})? finishedCallback, }) { + _startTimestamp = startTimestamp?.toUtc() ?? getUtcDateTime(); this.sampled = sampled; _finishedCallback = finishedCallback; } @@ -41,17 +43,28 @@ class SentrySpan extends ISentrySpan { return; } + final utcDateTime = getUtcDateTime(); + if (status != null) { _status = status; } - _timestamp = endTimestamp ?? getUtcDateTime(); + + if (endTimestamp?.isBefore(_startTimestamp) ?? false) { + _hub.options.logger( + SentryLevel.warning, + 'End timestamp ($endTimestamp) cannot be before start timestamp ($_startTimestamp)', + ); + _endTimestamp = utcDateTime; + } else { + _endTimestamp = endTimestamp?.toUtc() ?? utcDateTime; + } // associate error if (_throwable != null) { _hub.setSpanContext(_throwable, this, _tracer.name); } - _finishedCallback?.call(); - await super.finish(status: status); + _finishedCallback?.call(endTimestamp: _endTimestamp); + await super.finish(status: status, endTimestamp: _endTimestamp); } @override @@ -94,15 +107,25 @@ class SentrySpan extends ISentrySpan { ISentrySpan startChild( String operation, { String? description, + DateTime? startTimestamp, }) { if (finished) { return NoOpSentrySpan(); } + if (startTimestamp?.isBefore(_startTimestamp) ?? false) { + _hub.options.logger( + SentryLevel.warning, + "Start timestamp ($startTimestamp) cannot be before parent span's start timestamp ($_startTimestamp). Returning NoOpSpan.", + ); + return NoOpSentrySpan(); + } + return _tracer.startChildWithParentSpanId( _context.spanId, operation, description: description, + startTimestamp: startTimestamp, ); } @@ -116,7 +139,7 @@ class SentrySpan extends ISentrySpan { DateTime get startTimestamp => _startTimestamp; @override - DateTime? get endTimestamp => _timestamp; + DateTime? get endTimestamp => _endTimestamp; @override SentrySpanContext get context => _context; @@ -125,8 +148,9 @@ class SentrySpan extends ISentrySpan { final json = _context.toJson(); json['start_timestamp'] = formatDateAsIso8601WithMillisPrecision(_startTimestamp); - if (_timestamp != null) { - json['timestamp'] = formatDateAsIso8601WithMillisPrecision(_timestamp!); + if (_endTimestamp != null) { + json['timestamp'] = + formatDateAsIso8601WithMillisPrecision(_endTimestamp!); } if (_data.isNotEmpty) { json['data'] = _data; @@ -141,7 +165,7 @@ class SentrySpan extends ISentrySpan { } @override - bool get finished => _timestamp != null; + bool get finished => _endTimestamp != null; @override dynamic get throwable => _throwable; diff --git a/dart/lib/src/sentry.dart b/dart/lib/src/sentry.dart index e7252bf66c..aeac8eb85e 100644 --- a/dart/lib/src/sentry.dart +++ b/dart/lib/src/sentry.dart @@ -224,6 +224,7 @@ class Sentry { String name, String operation, { String? description, + DateTime? startTimestamp, bool? bindToScope, bool? waitForChildren, Duration? autoFinishAfter, @@ -234,6 +235,7 @@ class Sentry { name, operation, description: description, + startTimestamp: startTimestamp, bindToScope: bindToScope, waitForChildren: waitForChildren, autoFinishAfter: autoFinishAfter, @@ -245,6 +247,7 @@ class Sentry { static ISentrySpan startTransactionWithContext( SentryTransactionContext transactionContext, { Map? customSamplingContext, + DateTime? startTimestamp, bool? bindToScope, bool? waitForChildren, Duration? autoFinishAfter, @@ -253,6 +256,7 @@ class Sentry { _hub.startTransactionWithContext( transactionContext, customSamplingContext: customSamplingContext, + startTimestamp: startTimestamp, bindToScope: bindToScope, waitForChildren: waitForChildren, autoFinishAfter: autoFinishAfter, diff --git a/dart/lib/src/sentry_span_interface.dart b/dart/lib/src/sentry_span_interface.dart index d59de3c0ed..a49d687078 100644 --- a/dart/lib/src/sentry_span_interface.dart +++ b/dart/lib/src/sentry_span_interface.dart @@ -9,6 +9,7 @@ abstract class ISentrySpan { ISentrySpan startChild( String operation, { String? description, + DateTime? startTimestamp, }); /// Sets the tag on span or transaction. @@ -24,7 +25,7 @@ abstract class ISentrySpan { void removeData(String key); /// Sets span timestamp marking this span as finished. - Future finish({SpanStatus? status}) async {} + Future finish({SpanStatus? status, DateTime? endTimestamp}) async {} /// Gets span status. SpanStatus? get status; diff --git a/dart/lib/src/sentry_tracer.dart b/dart/lib/src/sentry_tracer.dart index 9ffb1cdaf5..e8c803ea5f 100644 --- a/dart/lib/src/sentry_tracer.dart +++ b/dart/lib/src/sentry_tracer.dart @@ -33,7 +33,8 @@ class SentryTracer extends ISentrySpan { /// transaction after a given "idle time" and we don't want this "idle time" /// to be part of the transaction. SentryTracer(SentryTransactionContext transactionContext, this._hub, - {bool waitForChildren = false, + {DateTime? startTimestamp, + bool waitForChildren = false, Duration? autoFinishAfter, bool trimEnd = false}) { _rootSpan = SentrySpan( @@ -41,6 +42,7 @@ class SentryTracer extends ISentrySpan { transactionContext, _hub, sampled: transactionContext.sampled, + startTimestamp: startTimestamp, ); _waitForChildren = waitForChildren; if (autoFinishAfter != null) { @@ -53,21 +55,27 @@ class SentryTracer extends ISentrySpan { } @override - Future finish({SpanStatus? status}) async { + Future finish({SpanStatus? status, DateTime? endTimestamp}) async { + final commonEndTimestamp = endTimestamp ?? getUtcDateTime(); _autoFinishAfterTimer?.cancel(); _finishStatus = SentryTracerFinishStatus.finishing(status); if (!_rootSpan.finished && (!_waitForChildren || _haveAllChildrenFinished())) { _rootSpan.status ??= status; - // finish unfinished spans otherwise transaction gets dropped - for (final span in _children) { - if (!span.finished) { - await span.finish(status: SpanStatus.deadlineExceeded()); - } - } + // remove span where its endTimestamp is before startTimestamp + _children.removeWhere( + (span) => !_hasSpanSuitableTimestamps(span, commonEndTimestamp)); - var _rootEndTimestamp = getUtcDateTime(); + // finish unfinished spans otherwise transaction gets dropped + final spansToBeFinished = _children.where((span) => !span.finished); + await Future.forEach( + spansToBeFinished, + (SentrySpan span) async => await span.finish( + status: SpanStatus.deadlineExceeded(), + endTimestamp: commonEndTimestamp)); + + var _rootEndTimestamp = commonEndTimestamp; if (_trimEnd && children.isNotEmpty) { final childEndTimestamps = children .where((child) => child.endTimestamp != null) @@ -136,6 +144,7 @@ class SentryTracer extends ISentrySpan { ISentrySpan startChild( String operation, { String? description, + DateTime? startTimestamp, }) { if (finished) { return NoOpSentrySpan(); @@ -152,6 +161,7 @@ class SentryTracer extends ISentrySpan { return _rootSpan.startChild( operation, description: description, + startTimestamp: startTimestamp, ); } @@ -159,6 +169,7 @@ class SentryTracer extends ISentrySpan { SpanId parentSpanId, String operation, { String? description, + DateTime? startTimestamp, }) { if (finished) { return NoOpSentrySpan(); @@ -178,11 +189,12 @@ class SentryTracer extends ISentrySpan { operation: operation, description: description); - final child = SentrySpan(this, context, _hub, sampled: _rootSpan.sampled, - finishedCallback: () { + final child = SentrySpan(this, context, _hub, + sampled: _rootSpan.sampled, startTimestamp: startTimestamp, + finishedCallback: ({DateTime? endTimestamp}) { final finishStatus = _finishStatus; if (finishStatus.finishing) { - finish(status: finishStatus.status); + finish(status: finishStatus.status, endTimestamp: endTimestamp); } }); @@ -235,4 +247,9 @@ class SentryTracer extends ISentrySpan { } return true; } + + bool _hasSpanSuitableTimestamps( + SentrySpan span, DateTime endTimestampCandidate) => + !span.startTimestamp + .isAfter((span.endTimestamp ?? endTimestampCandidate)); } diff --git a/dart/test/default_integrations_test.dart b/dart/test/default_integrations_test.dart index 7db47ea243..b8dc3054a0 100644 --- a/dart/test/default_integrations_test.dart +++ b/dart/test/default_integrations_test.dart @@ -234,6 +234,7 @@ class PrintRecursionMockHub extends MockHub { ISentrySpan startTransactionWithContext( SentryTransactionContext transactionContext, { Map? customSamplingContext, + DateTime? startTimestamp, bool? bindToScope, bool? waitForChildren, Duration? autoFinishAfter, diff --git a/dart/test/hub_test.dart b/dart/test/hub_test.dart index 7af9227d02..8a4cbeef51 100644 --- a/dart/test/hub_test.dart +++ b/dart/test/hub_test.dart @@ -139,17 +139,21 @@ void main() { fixture = Fixture(); }); - test('start transaction with given name, op and desc', () async { + test('start transaction with given name, op, desc and start time', + () async { final hub = fixture.getSut(); + final startTime = DateTime.now(); final tr = hub.startTransaction( 'name', 'op', + startTimestamp: startTime, description: 'desc', ); expect(tr.context.operation, 'op'); expect(tr.context.description, 'desc'); + expect(tr.startTimestamp.isAtSameMomentAs(startTime), true); expect((tr as SentryTracer).name, 'name'); }); diff --git a/dart/test/sentry_span_test.dart b/dart/test/sentry_span_test.dart index ef3f3f8f2c..c4311e6303 100644 --- a/dart/test/sentry_span_test.dart +++ b/dart/test/sentry_span_test.dart @@ -1,5 +1,6 @@ import 'package:sentry/sentry.dart'; import 'package:sentry/src/sentry_tracer.dart'; +import 'package:sentry/src/utils.dart'; import 'package:test/test.dart'; import 'mocks/mock_hub.dart'; @@ -7,6 +8,26 @@ import 'mocks/mock_hub.dart'; void main() { final fixture = Fixture(); + test('convert given startTimestamp to utc date time', () async { + final nonUtcStartTimestamp = DateTime.now().toLocal(); + + final sut = fixture.getSut(startTimestamp: nonUtcStartTimestamp); + + expect(nonUtcStartTimestamp.isUtc, false); + expect(sut.startTimestamp.isUtc, true); + }); + + test('convert given endTimestamp to utc date time', () async { + final nonUtcEndTimestamp = DateTime.now().toLocal(); + + final sut = fixture.getSut(startTimestamp: nonUtcEndTimestamp); + + await sut.finish(endTimestamp: nonUtcEndTimestamp); + + expect(nonUtcEndTimestamp.isUtc, false); + expect(sut.endTimestamp?.isUtc, true); + }); + test('finish sets status', () async { final sut = fixture.getSut(); @@ -23,6 +44,15 @@ void main() { expect(sut.endTimestamp, isNotNull); }); + test('finish uses given end timestamp', () async { + final sut = fixture.getSut(); + final endTimestamp = getUtcDateTime(); + + expect(sut.endTimestamp, isNull); + await sut.finish(endTimestamp: endTimestamp); + expect(sut.endTimestamp, endTimestamp); + }); + test('finish sets throwable', () { final sut = fixture.getSut(); sut.throwable = StateError('message'); @@ -32,6 +62,17 @@ void main() { expect(fixture.hub.spanContextCals, 1); }); + test( + 'finish does not set endTimestamp if given end timestamp is before start timestamp', + () async { + final sut = fixture.getSut(); + + final invalidEndTimestamp = sut.startTimestamp.add(-Duration(hours: 1)); + await sut.finish(endTimestamp: invalidEndTimestamp); + + expect(sut.endTimestamp, isNot(equals(invalidEndTimestamp))); + }); + test('span adds data', () { final sut = fixture.getSut(); @@ -171,9 +212,21 @@ void main() { expect(NoOpSentrySpan(), span); }); + test( + 'startChild isnt allowed to be called if childs startTimestamp is before parents', + () async { + final parentStartTimestamp = DateTime.now(); + final childStartTimestamp = parentStartTimestamp.add(-Duration(hours: 1)); + final sut = fixture.getSut(startTimestamp: parentStartTimestamp); + + final span = sut.startChild('op', startTimestamp: childStartTimestamp); + + expect(NoOpSentrySpan(), span); + }); + test('callback called on finish', () async { var numberOfCallbackCalls = 0; - final sut = fixture.getSut(finishedCallback: () { + final sut = fixture.getSut(finishedCallback: ({DateTime? endTimestamp}) { numberOfCallbackCalls += 1; }); @@ -185,7 +238,7 @@ void main() { test('optional endTimestamp set instead of current time', () async { final sut = fixture.getSut(); - final endTimestamp = DateTime.now().add(Duration(days: 1)); + final endTimestamp = getUtcDateTime().add(Duration(days: 1)); await sut.finish(endTimestamp: endTimestamp); @@ -201,13 +254,17 @@ class Fixture { late SentryTracer tracer; final hub = MockHub(); - SentrySpan getSut({bool? sampled = true, Function()? finishedCallback}) { + SentrySpan getSut( + {DateTime? startTimestamp, + bool? sampled = true, + Function({DateTime? endTimestamp})? finishedCallback}) { tracer = SentryTracer(context, hub); return SentrySpan( tracer, context, hub, + startTimestamp: startTimestamp, sampled: sampled, finishedCallback: finishedCallback, ); diff --git a/dart/test/sentry_tracer_test.dart b/dart/test/sentry_tracer_test.dart index d7ac94f0a6..5c9b3e7e49 100644 --- a/dart/test/sentry_tracer_test.dart +++ b/dart/test/sentry_tracer_test.dart @@ -1,5 +1,6 @@ import 'package:sentry/sentry.dart'; import 'package:sentry/src/sentry_tracer.dart'; +import 'package:sentry/src/utils.dart'; import 'package:test/test.dart'; import 'mocks/mock_hub.dart'; @@ -34,6 +35,32 @@ void main() { expect(trace?.status.toString(), 'aborted'); }); + test('tracer finishes with end timestamp', () async { + final sut = fixture.getSut(); + final endTimestamp = getUtcDateTime(); + + await sut.finish(endTimestamp: endTimestamp); + + expect(sut.endTimestamp, endTimestamp); + }); + + test( + 'tracer finish sets given end timestamp to all children while finishing them', + () async { + final sut = fixture.getSut(); + + final childA = sut.startChild('operation-a', description: 'description'); + final childB = sut.startChild('operation-b', description: 'description'); + final endTimestamp = getUtcDateTime(); + + await sut.finish(endTimestamp: endTimestamp); + await childA.finish(); + await childB.finish(); + + expect(childA.endTimestamp, endTimestamp); + expect(childB.endTimestamp, endTimestamp); + }); + test('tracer finishes unfinished spans', () async { final sut = fixture.getSut(); sut.startChild('child'); @@ -238,50 +265,41 @@ void main() { }); test('end trimmed to last child', () async { - final sut = fixture.getSut( - trimEnd: true, autoFinishAfter: Duration(milliseconds: 200)); + final sut = fixture.getSut(trimEnd: true); + final endTimestamp = getUtcDateTime().add(Duration(minutes: 1)); + final olderEndTimeStamp = endTimestamp.add(Duration(seconds: 1)); + final oldestEndTimeStamp = olderEndTimeStamp.add(Duration(seconds: 1)); final childA = sut.startChild('operation-a', description: 'description'); final childB = sut.startChild('operation-b', description: 'description'); - await childA.finish(); - await Future.delayed(Duration(milliseconds: 10)); - await childB.finish(); - await Future.delayed(Duration(milliseconds: 210)); + await childA.finish(endTimestamp: endTimestamp); + await childB.finish(endTimestamp: olderEndTimeStamp); + await sut.finish(endTimestamp: oldestEndTimeStamp); expect(sut.endTimestamp, childB.endTimestamp); }); test('end trimmed to child', () async { - final sut = fixture.getSut( - trimEnd: true, autoFinishAfter: Duration(milliseconds: 200)); + final sut = fixture.getSut(trimEnd: true); + final endTimestamp = getUtcDateTime().add(Duration(minutes: 1)); + final olderEndTimeStamp = endTimestamp.add(Duration(seconds: 1)); final childA = sut.startChild('operation-a', description: 'description'); - await childA.finish(); - await Future.delayed(Duration(milliseconds: 210)); + await childA.finish(endTimestamp: endTimestamp); + await sut.finish(endTimestamp: olderEndTimeStamp); expect(sut.endTimestamp, childA.endTimestamp); }); test('end not trimmed when no child', () async { - final sut = fixture.getSut( - trimEnd: true, autoFinishAfter: Duration(milliseconds: 200)); + final sut = fixture.getSut(trimEnd: true); + final endTimestamp = getUtcDateTime(); - await Future.delayed(Duration(milliseconds: 210)); - - expect(sut.endTimestamp, isNotNull); - }); - - test('end not trimmed when no finished child', () async { - final sut = fixture.getSut( - trimEnd: true, autoFinishAfter: Duration(milliseconds: 200)); - - sut.startChild('operation-a', description: 'description'); - - await Future.delayed(Duration(milliseconds: 210)); + await sut.finish(endTimestamp: endTimestamp); - expect(sut.endTimestamp, isNotNull); + expect(sut.endTimestamp, endTimestamp); }); test('does not add more spans than configured in options', () async { diff --git a/flutter/test/mocks.dart b/flutter/test/mocks.dart index 84e86e97ec..1a114053ae 100644 --- a/flutter/test/mocks.dart +++ b/flutter/test/mocks.dart @@ -14,6 +14,7 @@ ISentrySpan startTransactionShim( String? name, String? operation, { String? description, + DateTime? startTimestamp, bool? bindToScope, bool? waitForChildren, Duration? autoFinishAfter, diff --git a/flutter/test/mocks.mocks.dart b/flutter/test/mocks.mocks.dart index 227f78825f..8ecf2b3a34 100644 --- a/flutter/test/mocks.mocks.dart +++ b/flutter/test/mocks.mocks.dart @@ -86,8 +86,10 @@ class MockNoOpSentrySpan extends _i1.Mock implements _i2.NoOpSentrySpan { super.noSuchMethod(Invocation.setter(#status, status), returnValueForMissingStub: null); @override - _i7.Future finish({_i3.SpanStatus? status}) => - (super.noSuchMethod(Invocation.method(#finish, [], {#status: status}), + _i7.Future finish({_i3.SpanStatus? status, DateTime? endTimestamp}) => + (super.noSuchMethod( + Invocation.method( + #finish, [], {#status: status, #endTimestamp: endTimestamp}), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i7.Future); @override @@ -107,10 +109,11 @@ class MockNoOpSentrySpan extends _i1.Mock implements _i2.NoOpSentrySpan { super.noSuchMethod(Invocation.method(#setTag, [key, value]), returnValueForMissingStub: null); @override - _i2.ISentrySpan startChild(String? operation, {String? description}) => + _i2.ISentrySpan startChild(String? operation, + {String? description, DateTime? startTimestamp}) => (super.noSuchMethod( - Invocation.method( - #startChild, [operation], {#description: description}), + Invocation.method(#startChild, [operation], + {#description: description, #startTimestamp: startTimestamp}), returnValue: _FakeISentrySpan_2()) as _i2.ISentrySpan); @override _i3.SentryTraceHeader toSentryTrace() => @@ -211,6 +214,7 @@ class MockHub extends _i1.Mock implements _i5.Hub { @override _i2.ISentrySpan startTransaction(String? name, String? operation, {String? description, + DateTime? startTimestamp, bool? bindToScope, bool? waitForChildren, Duration? autoFinishAfter, @@ -222,6 +226,7 @@ class MockHub extends _i1.Mock implements _i5.Hub { operation ], { #description: description, + #startTimestamp: startTimestamp, #bindToScope: bindToScope, #waitForChildren: waitForChildren, #autoFinishAfter: autoFinishAfter, @@ -230,6 +235,7 @@ class MockHub extends _i1.Mock implements _i5.Hub { }), returnValue: _i11.startTransactionShim(name, operation, description: description, + startTimestamp: startTimestamp, bindToScope: bindToScope, waitForChildren: waitForChildren, autoFinishAfter: autoFinishAfter, @@ -240,6 +246,7 @@ class MockHub extends _i1.Mock implements _i5.Hub { _i2.ISentrySpan startTransactionWithContext( _i2.SentryTransactionContext? transactionContext, {Map? customSamplingContext, + DateTime? startTimestamp, bool? bindToScope, bool? waitForChildren, Duration? autoFinishAfter, @@ -249,6 +256,7 @@ class MockHub extends _i1.Mock implements _i5.Hub { transactionContext ], { #customSamplingContext: customSamplingContext, + #startTimestamp: startTimestamp, #bindToScope: bindToScope, #waitForChildren: waitForChildren, #autoFinishAfter: autoFinishAfter,