From 7ce2108621500466eeb2b08fe713a35793336900 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 14 Oct 2024 16:14:59 +0200 Subject: [PATCH 01/13] Add `stackFrameExcludes` to `SentryOptions` --- dart/lib/src/sentry_options.dart | 29 +++++++- dart/lib/src/sentry_stack_trace_factory.dart | 5 ++ dart/test/stack_trace_test.dart | 77 +++++++++++++++++++- 3 files changed, 103 insertions(+), 8 deletions(-) diff --git a/dart/lib/src/sentry_options.dart b/dart/lib/src/sentry_options.dart index 1d8cd5bb7d..6a01722abb 100644 --- a/dart/lib/src/sentry_options.dart +++ b/dart/lib/src/sentry_options.dart @@ -198,6 +198,17 @@ class SentryOptions { /// To use regex add the `^` and the `$` to the string. List ignoreTransactions = []; + final List _stackFrameExcludes = [ + 'sentry', + 'sentry_flutter', + ]; + + /// A list of packages names that will be removed from stack traces. + /// Per default, `sentry` and `sentry_flutter` package stack frames will be removed. + /// + /// example : `['sentry']` will remove stack frames with package `package:sentry/sentry.dart` origin + List get stackFrameExcludes => List.unmodifiable(_stackFrameExcludes); + final List _inAppExcludes = []; /// A list of string prefixes of packages names that do not belong to the app, but rather third-party @@ -564,13 +575,23 @@ class SentryOptions { } /// Adds an inAppExclude - void addInAppExclude(String inApp) { - _inAppExcludes.add(inApp); + void addInAppExclude(String inAppInclude) { + _inAppExcludes.add(inAppInclude); } /// Adds an inAppIncludes - void addInAppInclude(String inApp) { - _inAppIncludes.add(inApp); + void addInAppInclude(String inAppExclude) { + _inAppIncludes.add(inAppExclude); + } + + /// Adds an stackFrameExclude + void addStackFrameExclude(String stackFrameExclude) { + _stackFrameExcludes.add(stackFrameExclude); + } + + /// Removes an stackFrameExclude + void removeStackFrameExclude(String stackFrameExclude) { + _stackFrameExcludes.remove(stackFrameExclude); } /// Returns if tracing should be enabled. If tracing is disabled, starting transactions returns diff --git a/dart/lib/src/sentry_stack_trace_factory.dart b/dart/lib/src/sentry_stack_trace_factory.dart index efcca49c70..ce9076a3ec 100644 --- a/dart/lib/src/sentry_stack_trace_factory.dart +++ b/dart/lib/src/sentry_stack_trace_factory.dart @@ -97,6 +97,11 @@ class SentryStackTraceFactory { /// converts [Frame] to [SentryStackFrame] @visibleForTesting SentryStackFrame? encodeStackTraceFrame(Frame frame) { + final package = frame.package; + if (package != null && _options.stackFrameExcludes.contains(package)) { + return null; + } + final member = frame.member; if (frame is UnparsedFrame && member != null) { diff --git a/dart/test/stack_trace_test.dart b/dart/test/stack_trace_test.dart index 3b250ddc81..75a9285e39 100644 --- a/dart/test/stack_trace_test.dart +++ b/dart/test/stack_trace_test.dart @@ -284,12 +284,14 @@ isolate_instructions: 10fa27070, vm_instructions: 10fa21e20 final fixture = Fixture(); // Test for web platform - final webSut = fixture.getSut(isWeb: true); + fixture.options.platformChecker = MockPlatformChecker(isWebValue: true); + final webSut = fixture.getSut(); var webFrame = webSut.encodeStackTraceFrame(frame)!; expect(webFrame.platform, 'javascript'); // Test for non-web platform - final nativeFrameBeforeSut = fixture.getSut(isWeb: false); + fixture.options.platformChecker = MockPlatformChecker(isWebValue: false); + final nativeFrameBeforeSut = fixture.getSut(); var nativeFrameBefore = nativeFrameBeforeSut.encodeStackTraceFrame(frame)!; expect(nativeFrameBefore.platform, 'dart'); @@ -301,17 +303,84 @@ isolate_instructions: 10fa27070, vm_instructions: 10fa21e20 .copyWith(platform: 'native'); expect(frameWithPlatform.platform, 'native'); }); + + group('stackFrameExcludes', () { + final stackTraceString = ''' + #0 getCurrentStackTrace (package:sentry/src/utils/stacktrace_utils.dart:10:49) +#1 OnErrorIntegration.call. (package:sentry_flutter/src/integrations/on_error_integration.dart:82:22) +#2 MainScaffold.build. (package:sentry_flutter_example/main.dart:349:23) +#3 _InkResponseState.handleTap (package:flutter/src/material/ink_well.dart:1170:21) +#4 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:351:24) +#5 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:656:11) +#6 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:313:5) +#7 BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:283:7) +#8 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:169:27) +#9 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:505:20) +#10 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:481:22) +#11 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:450:11) +#12 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:426:7) +#13 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:389:5) +#14 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:336:7) +#15 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:305:9) +#16 _invoke1 (dart:ui/hooks.dart:328:13) +#17 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:442:7) +#18 _dispatchPointerDataPacket (dart:ui/hooks.dart:262:31) + '''; + + test('excludes sentry and sentry_flutter stack frames per default', () { + final fixture = Fixture(); + + expect( + fixture.options.stackFrameExcludes, ['sentry', 'sentry_flutter']); + + final sut = fixture.getSut(); + final sentryStackTrace = + sut.parse(StackTrace.fromString(stackTraceString)); + + expect(sentryStackTrace.frames.length, 17); + expect(sentryStackTrace.frames[16].package, 'sentry_flutter_example'); + expect(sentryStackTrace.frames[15].package, 'flutter'); + }); + + test('excludes added stack frame exclude', () { + final fixture = Fixture(); + + final sut = fixture.getSut(); + fixture.options.addStackFrameExclude('sentry_flutter_example'); + + final sentryStackTrace = + sut.parse(StackTrace.fromString(stackTraceString)); + + expect(sentryStackTrace.frames.length, 16); + expect(sentryStackTrace.frames[15].package, 'flutter'); + }); + + test('ignores removed stack frame excludes', () { + final fixture = Fixture(); + + final sut = fixture.getSut(); + fixture.options.removeStackFrameExclude('sentry'); + fixture.options.removeStackFrameExclude('sentry_flutter'); + + final sentryStackTrace = + sut.parse(StackTrace.fromString(stackTraceString)); + + expect(sentryStackTrace.frames.length, 19); + expect(sentryStackTrace.frames[18].package, 'sentry'); + expect(sentryStackTrace.frames[17].package, 'sentry_flutter'); + }); + }); }); } class Fixture { + final options = defaultTestOptions(MockPlatformChecker(isWebValue: false)); + SentryStackTraceFactory getSut({ List inAppIncludes = const [], List inAppExcludes = const [], bool considerInAppFramesByDefault = true, - bool isWeb = false, }) { - final options = defaultTestOptions(MockPlatformChecker(isWebValue: isWeb)); inAppIncludes.forEach(options.addInAppInclude); inAppExcludes.forEach(options.addInAppExclude); options.considerInAppFramesByDefault = considerInAppFramesByDefault; From 45a99cbe73eb34549b1bb295d7261b2111770baa Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 14 Oct 2024 16:22:03 +0200 Subject: [PATCH 02/13] update cl --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6f4103d25..d0cda4dc64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,8 @@ ); ``` +- Add `stackFrameExcludes` to `SentryOptions` ([#2351](https://github.com/getsentry/sentry-dart/pull/2351)) + ### Enhancements - Use native spotlight integrations on Flutter Android, iOS, macOS ([#2285](https://github.com/getsentry/sentry-dart/pull/2285)) From be807efe66b0d18f7dff8e83e16e57140e1ef4a3 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 14 Oct 2024 16:49:31 +0200 Subject: [PATCH 03/13] fix failing test expectations --- dart/test/sentry_client_test.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dart/test/sentry_client_test.dart b/dart/test/sentry_client_test.dart index 7a2b5e52f8..59d4d60d34 100644 --- a/dart/test/sentry_client_test.dart +++ b/dart/test/sentry_client_test.dart @@ -377,6 +377,7 @@ void main() { }); test('should capture sentry frames exception', () async { + fixture.options.removeStackFrameExclude('sentry'); fixture.options.addExceptionCauseExtractor( ExceptionWithCauseExtractor(), ); @@ -497,6 +498,8 @@ void main() { }); test('should capture sentry frames exception', () async { + fixture.options.removeStackFrameExclude('sentry'); + try { throw Exception('Error'); } catch (err) { From 7690750d737b56f22e2e977a0c2c2e05ae04a7a5 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Thu, 17 Oct 2024 13:12:01 +0200 Subject: [PATCH 04/13] Remove `sentry` frames if SDK falls back to `currentStackTrace` --- dart/lib/src/sentry_client.dart | 24 ++++-- dart/lib/src/sentry_exception_factory.dart | 7 +- dart/lib/src/sentry_options.dart | 21 ----- dart/lib/src/sentry_stack_trace_factory.dart | 18 ++-- dart/lib/src/type_check_hint.dart | 3 + dart/test/sentry_client_test.dart | 84 +++++++++++++++++-- dart/test/sentry_exception_factory_test.dart | 59 ++++++++++++- .../flutter_error_integration.dart | 3 + .../integrations/on_error_integration.dart | 5 +- .../on_error_integration_test.dart | 30 ++++++- 10 files changed, 204 insertions(+), 50 deletions(-) diff --git a/dart/lib/src/sentry_client.dart b/dart/lib/src/sentry_client.dart index 43e5e0be9d..546da209c8 100644 --- a/dart/lib/src/sentry_client.dart +++ b/dart/lib/src/sentry_client.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:math'; import 'package:meta/meta.dart'; +import 'package:sentry/src/type_check_hint.dart'; import 'client_reports/client_report_recorder.dart'; import 'client_reports/discard_reason.dart'; @@ -123,10 +124,10 @@ class SentryClient { return _emptySentryId; } - SentryEvent? preparedEvent = _prepareEvent(event, stackTrace: stackTrace); - hint ??= Hint(); + SentryEvent? preparedEvent = _prepareEvent(event, hint, stackTrace: stackTrace); + if (scope != null) { preparedEvent = await scope.applyToEvent(preparedEvent, hint); } else { @@ -208,7 +209,7 @@ class SentryClient { return isMatchingRegexPattern(message, _options.ignoreErrors); } - SentryEvent _prepareEvent(SentryEvent event, {dynamic stackTrace}) { + SentryEvent _prepareEvent(SentryEvent event, Hint hint, {dynamic stackTrace}) { event = event.copyWith( serverName: event.serverName ?? _options.serverName, dist: event.dist ?? _options.dist, @@ -245,6 +246,7 @@ class SentryClient { var sentryException = _exceptionFactory.getSentryException( extractedException.exception, stackTrace: extractedException.stackTrace, + removeSentryFrames: hint.get(TypeCheckHint.currentStackTrace), ); SentryThread? sentryThread; @@ -280,8 +282,14 @@ class SentryClient { // therefore add it to the threads. // https://develop.sentry.dev/sdk/event-payloads/stacktrace/ if (stackTrace != null || _options.attachStacktrace) { - stackTrace ??= getCurrentStackTrace(); - final sentryStackTrace = _stackTraceFactory.parse(stackTrace); + if (stackTrace == null || stackTrace == StackTrace.empty) { + stackTrace = getCurrentStackTrace(); + hint.addAll({TypeCheckHint.currentStackTrace: true}); + } + final sentryStackTrace = _stackTraceFactory.parse( + stackTrace, + removeSentryFrames: hint.get(TypeCheckHint.currentStackTrace), + ); if (sentryStackTrace.frames.isNotEmpty) { event = event.copyWith(threads: [ ...?event.threads, @@ -353,11 +361,11 @@ class SentryClient { Scope? scope, SentryTraceContextHeader? traceContext, }) async { - SentryTransaction? preparedTransaction = - _prepareEvent(transaction) as SentryTransaction; - final hint = Hint(); + SentryTransaction? preparedTransaction = + _prepareEvent(transaction, hint) as SentryTransaction; + if (scope != null) { preparedTransaction = await scope.applyToEvent(preparedTransaction, hint) as SentryTransaction?; diff --git a/dart/lib/src/sentry_exception_factory.dart b/dart/lib/src/sentry_exception_factory.dart index 6f766014ac..3a2fad9279 100644 --- a/dart/lib/src/sentry_exception_factory.dart +++ b/dart/lib/src/sentry_exception_factory.dart @@ -18,6 +18,7 @@ class SentryExceptionFactory { SentryException getSentryException( dynamic exception, { dynamic stackTrace, + bool? removeSentryFrames, }) { var throwable = exception; Mechanism? mechanism; @@ -38,17 +39,21 @@ class SentryExceptionFactory { // throwable.stackTrace is null if its an exception that was never thrown // hence we check again if stackTrace is null and if not, read the current stack trace // but only if attachStacktrace is enabled + if (_options.attachStacktrace) { if (stackTrace == null || stackTrace == StackTrace.empty) { snapshot = true; stackTrace = getCurrentStackTrace(); + removeSentryFrames = true; } } SentryStackTrace? sentryStackTrace; if (stackTrace != null) { sentryStackTrace = - _stacktraceFactory.parse(stackTrace).copyWith(snapshot: snapshot); + _stacktraceFactory + .parse(stackTrace, removeSentryFrames: removeSentryFrames) + .copyWith(snapshot: snapshot); if (sentryStackTrace.frames.isEmpty) { sentryStackTrace = null; } diff --git a/dart/lib/src/sentry_options.dart b/dart/lib/src/sentry_options.dart index 6a01722abb..17e2dc4b3a 100644 --- a/dart/lib/src/sentry_options.dart +++ b/dart/lib/src/sentry_options.dart @@ -198,17 +198,6 @@ class SentryOptions { /// To use regex add the `^` and the `$` to the string. List ignoreTransactions = []; - final List _stackFrameExcludes = [ - 'sentry', - 'sentry_flutter', - ]; - - /// A list of packages names that will be removed from stack traces. - /// Per default, `sentry` and `sentry_flutter` package stack frames will be removed. - /// - /// example : `['sentry']` will remove stack frames with package `package:sentry/sentry.dart` origin - List get stackFrameExcludes => List.unmodifiable(_stackFrameExcludes); - final List _inAppExcludes = []; /// A list of string prefixes of packages names that do not belong to the app, but rather third-party @@ -584,16 +573,6 @@ class SentryOptions { _inAppIncludes.add(inAppExclude); } - /// Adds an stackFrameExclude - void addStackFrameExclude(String stackFrameExclude) { - _stackFrameExcludes.add(stackFrameExclude); - } - - /// Removes an stackFrameExclude - void removeStackFrameExclude(String stackFrameExclude) { - _stackFrameExcludes.remove(stackFrameExclude); - } - /// Returns if tracing should be enabled. If tracing is disabled, starting transactions returns /// [NoOpSentrySpan]. bool isTracingEnabled() { diff --git a/dart/lib/src/sentry_stack_trace_factory.dart b/dart/lib/src/sentry_stack_trace_factory.dart index ce9076a3ec..c26fb10840 100644 --- a/dart/lib/src/sentry_stack_trace_factory.dart +++ b/dart/lib/src/sentry_stack_trace_factory.dart @@ -24,7 +24,7 @@ class SentryStackTraceFactory { return parse(stackTrace).frames; } - SentryStackTrace parse(dynamic stackTrace) { + SentryStackTrace parse(dynamic stackTrace, {bool? removeSentryFrames}) { final parsed = _parseStackTrace(stackTrace); final frames = []; var onlyAsyncGap = true; @@ -32,11 +32,16 @@ class SentryStackTraceFactory { for (var t = 0; t < parsed.traces.length; t += 1) { final trace = parsed.traces[t]; - // NOTE: We want to keep the Sentry frames for crash detection + // NOTE: We want to keep the Sentry frames for SDK crash detection // this does not affect grouping since they're not marked as inApp + // only exception if there was no stack trace, we remove them for (final frame in trace.frames) { - final stackTraceFrame = encodeStackTraceFrame(frame); + var stackTraceFrame = encodeStackTraceFrame(frame); + if (stackTraceFrame != null) { + if (removeSentryFrames == true && (stackTraceFrame.package == 'sentry' || stackTraceFrame.package == 'sentry_flutter')) { + continue; + } frames.add(stackTraceFrame); onlyAsyncGap = false; } @@ -97,15 +102,10 @@ class SentryStackTraceFactory { /// converts [Frame] to [SentryStackFrame] @visibleForTesting SentryStackFrame? encodeStackTraceFrame(Frame frame) { - final package = frame.package; - if (package != null && _options.stackFrameExcludes.contains(package)) { - return null; - } - final member = frame.member; if (frame is UnparsedFrame && member != null) { - // if --split-debug-info is enabled, thats what we see: + // if --split-debug-info is enabled, that's what we see: // #00 abs 000000723d6346d7 _kDartIsolateSnapshotInstructions+0x1e26d7 // we are only interested on the #01, 02... items which contains the 'abs' addresses. diff --git a/dart/lib/src/type_check_hint.dart b/dart/lib/src/type_check_hint.dart index 6497577ccc..7ba6c78d0c 100644 --- a/dart/lib/src/type_check_hint.dart +++ b/dart/lib/src/type_check_hint.dart @@ -16,4 +16,7 @@ class TypeCheckHint { /// Widget that was tapped in `sentry_flutter/SentryUserInteractionWidget` static const widget = 'widget'; + + /// Used to indicate that the SDK added a synthetic current stack trace. + static const currentStackTrace = 'currentStackTrace'; } diff --git a/dart/test/sentry_client_test.dart b/dart/test/sentry_client_test.dart index 59d4d60d34..8e908d8800 100644 --- a/dart/test/sentry_client_test.dart +++ b/dart/test/sentry_client_test.dart @@ -377,7 +377,6 @@ void main() { }); test('should capture sentry frames exception', () async { - fixture.options.removeStackFrameExclude('sentry'); fixture.options.addExceptionCauseExtractor( ExceptionWithCauseExtractor(), ); @@ -498,8 +497,6 @@ void main() { }); test('should capture sentry frames exception', () async { - fixture.options.removeStackFrameExclude('sentry'); - try { throw Exception('Error'); } catch (err) { @@ -525,6 +522,38 @@ void main() { true, ); }); + + test('should remove sentry frames if null stackStrace', () async { + final throwable = Object(); + + final client = fixture.getSut(attachStacktrace: true); + await client.captureException(throwable, stackTrace: null); + + final capturedEnvelope = (fixture.transport).envelopes.first; + final capturedEvent = await eventFromEnvelope(capturedEnvelope); + + final sentryFramesCount = capturedEvent.exceptions?[0].stackTrace!.frames + .where((frame) => frame.package == 'sentry') + .length; + + expect(sentryFramesCount, 0); + }); + + test('should remove sentry frames if empty stackStrace', () async { + final throwable = Object(); + + final client = fixture.getSut(attachStacktrace: true); + await client.captureException(throwable, stackTrace: StackTrace.empty); + + final capturedEnvelope = (fixture.transport).envelopes.first; + final capturedEvent = await eventFromEnvelope(capturedEnvelope); + + final sentryFramesCount = capturedEvent.exceptions?[0].stackTrace!.frames + .where((frame) => frame.package == 'sentry') + .length; + + expect(sentryFramesCount, 0); + }); }); group('SentryClient captures transaction', () { @@ -2172,10 +2201,55 @@ void main() { final capturedEnvelope = (fixture.transport).envelopes.first; final attachmentItem = capturedEnvelope.items.firstWhereOrNull( - (element) => element.header.type == SentryItemType.attachment); - + (element) => element.header.type == SentryItemType.attachment, + ); expect(attachmentItem, isNull); }); + + test('null stack trace marked in hint & sentry frames removed from thread stackTrace', () async { + final beforeSendCallback = (SentryEvent event, Hint hint) { + expect(hint.get(TypeCheckHint.currentStackTrace), isTrue); + return event; + }; + final client = fixture.getSut(beforeSend: beforeSendCallback, attachStacktrace: true); + await client.captureEvent(fakeEvent); + + final capturedEnvelope = (fixture.transport).envelopes.first; + final capturedEvent = await eventFromEnvelope(capturedEnvelope); + + final sentryFramesCount = capturedEvent.threads?[0].stacktrace!.frames + .where((frame) => frame.package == 'sentry') + .length; + + expect(sentryFramesCount, 0); + }); + + test('empty stack trace marked in hint & sentry frames removed from thread stackTrace', () async { + final beforeSendCallback = (SentryEvent event, Hint hint) { + expect(hint.get(TypeCheckHint.currentStackTrace), isTrue); + return event; + }; + final client = fixture.getSut(beforeSend: beforeSendCallback, attachStacktrace: true); + await client.captureEvent(fakeEvent, stackTrace: StackTrace.empty); + + final capturedEnvelope = (fixture.transport).envelopes.first; + final capturedEvent = await eventFromEnvelope(capturedEnvelope); + + final sentryFramesCount = capturedEvent.threads?[0].stacktrace!.frames + .where((frame) => frame.package == 'sentry') + .length; + + expect(sentryFramesCount, 0); + }); + + test('non-null stack trace not marked in hint', () async { + final beforeSendCallback = (SentryEvent event, Hint hint) { + expect(hint.get(TypeCheckHint.currentStackTrace), isNull); + return event; + }; + final client = fixture.getSut(beforeSend: beforeSendCallback, attachStacktrace: true); + await client.captureEvent(fakeEvent, stackTrace: StackTrace.current); + }); }); group('Capture metrics', () { diff --git a/dart/test/sentry_exception_factory_test.dart b/dart/test/sentry_exception_factory_test.dart index f2e12fce10..5157350bd6 100644 --- a/dart/test/sentry_exception_factory_test.dart +++ b/dart/test/sentry_exception_factory_test.dart @@ -230,6 +230,23 @@ void main() { expect(sentryStackTrace.baseAddr, isNull); expect(sentryStackTrace.buildId, isNull); }); + + test('remove sentry frames', () { + final sentryException = fixture + .getSut(attachStacktrace: false) + .getSentryException( + SentryStackTraceError(), + stackTrace: SentryStackTrace(), + removeSentryFrames: true, + ); + + final sentryStackTrace = sentryException.stackTrace!; + expect(sentryStackTrace.baseAddr, isNull); + + expect(sentryStackTrace.frames.length, 17); + expect(sentryStackTrace.frames[16].package, 'sentry_flutter_example'); + expect(sentryStackTrace.frames[15].package, 'flutter'); + }); } class CustomError extends Error {} @@ -288,11 +305,51 @@ isolate_instructions: 7526344980, vm_instructions: 752633f000 } } +class SentryStackTraceError extends Error { + var prefix = + "Unknown error without own stacktrace"; + + @override + String toString() { + return ''' +$prefix + +${SentryStackTrace()}'''; + } +} + +class SentryStackTrace extends StackTrace { + @override + String toString() { + return ''' + #0 getCurrentStackTrace (package:sentry/src/utils/stacktrace_utils.dart:10:49) +#1 OnErrorIntegration.call. (package:sentry_flutter/src/integrations/on_error_integration.dart:82:22) +#2 MainScaffold.build. (package:sentry_flutter_example/main.dart:349:23) +#3 _InkResponseState.handleTap (package:flutter/src/material/ink_well.dart:1170:21) +#4 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:351:24) +#5 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:656:11) +#6 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:313:5) +#7 BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:283:7) +#8 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:169:27) +#9 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:505:20) +#10 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:481:22) +#11 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:450:11) +#12 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:426:7) +#13 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:389:5) +#14 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:336:7) +#15 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:305:9) +#16 _invoke1 (dart:ui/hooks.dart:328:13) +#17 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:442:7) +#18 _dispatchPointerDataPacket (dart:ui/hooks.dart:262:31) + '''; + } +} + class Fixture { final options = defaultTestOptions(); SentryExceptionFactory getSut({bool attachStacktrace = true}) { - options.attachStacktrace = true; + options.attachStacktrace = attachStacktrace; return SentryExceptionFactory(options); } } diff --git a/flutter/lib/src/integrations/flutter_error_integration.dart b/flutter/lib/src/integrations/flutter_error_integration.dart index 7a3945906e..e16aa0d8ac 100644 --- a/flutter/lib/src/integrations/flutter_error_integration.dart +++ b/flutter/lib/src/integrations/flutter_error_integration.dart @@ -79,6 +79,9 @@ class FlutterErrorIntegration implements Integration { (scope) => scope.span?.status ??= const SpanStatus.internalError(), ); + final hint = Hint(); + hint.addAll({TypeCheckHint.syntheticException: errorDetails}); + await hub.captureEvent(event, // ignore: invalid_use_of_internal_member stackTrace: errorDetails.stack ?? getCurrentStackTrace(), diff --git a/flutter/lib/src/integrations/on_error_integration.dart b/flutter/lib/src/integrations/on_error_integration.dart index 365a3067ab..f01d1a03ee 100644 --- a/flutter/lib/src/integrations/on_error_integration.dart +++ b/flutter/lib/src/integrations/on_error_integration.dart @@ -77,13 +77,14 @@ class OnErrorIntegration implements Integration { (scope) => scope.span?.status ??= const SpanStatus.internalError(), ); + Hint? hint; if (stackTrace == StackTrace.empty) { // ignore: invalid_use_of_internal_member stackTrace = getCurrentStackTrace(); + hint = Hint.withMap({TypeCheckHint.currentStackTrace: true}); } - // unawaited future - hub.captureEvent(event, stackTrace: stackTrace); + hub.captureEvent(event, stackTrace: stackTrace, hint: hint); return handled; }; diff --git a/flutter/test/integrations/on_error_integration_test.dart b/flutter/test/integrations/on_error_integration_test.dart index f367916612..8d90905fde 100644 --- a/flutter/test/integrations/on_error_integration_test.dart +++ b/flutter/test/integrations/on_error_integration_test.dart @@ -28,9 +28,16 @@ void main() { return onErrorReturnValue; }; - when(fixture.hub.captureEvent(captureAny, - stackTrace: captureAnyNamed('stackTrace'))) - .thenAnswer((_) => Future.value(SentryId.empty())); + when(fixture.hub.captureEvent( + captureAny, + stackTrace: captureAnyNamed('stackTrace'), + )).thenAnswer((_) => Future.value(SentryId.empty())); + + when(fixture.hub.captureEvent( + captureAny, + stackTrace: captureAnyNamed('stackTrace'), + hint: captureAnyNamed('hint'), + )).thenAnswer((_) => Future.value(SentryId.empty())); when(fixture.hub.options).thenReturn(fixture.options); final tracer = MockSentryTracer(); @@ -189,6 +196,23 @@ void main() { await span?.finish(); }); + + test('adds current stack trace hint if error has empty stack trace', + () async { + final exception = StateError('error'); + + _reportError(exception: exception, stackTrace: StackTrace.empty); + + final hint = verify( + await fixture.hub.captureEvent( + captureAny, + stackTrace: captureAnyNamed('stackTrace'), + hint: captureAnyNamed('hint'), + ), + ).captured[2] as Hint; + + expect(hint.get(TypeCheckHint.currentStackTrace), isTrue); + }); }); } From 3e0b272e2ce056d8fc1c1a94714b9f04556fa672 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Thu, 17 Oct 2024 13:14:36 +0200 Subject: [PATCH 05/13] update cl --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90329004fd..fe2993c6b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,7 +55,7 @@ ); ``` -- Add `stackFrameExcludes` to `SentryOptions` ([#2351](https://github.com/getsentry/sentry-dart/pull/2351)) +- Remove `sentry` frames if SDK falls back to `currentStackTrace` ([#2351](https://github.com/getsentry/sentry-dart/pull/2351)) ### Enhancements From 0054c9d17f5c17bb2555571f51d641a2cdbae905 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Thu, 17 Oct 2024 13:26:09 +0200 Subject: [PATCH 06/13] fix cl --- CHANGELOG.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe2993c6b9..646dd1955b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Enhancements + +- Remove `sentry` frames if SDK falls back to current stack trace ([#2351](https://github.com/getsentry/sentry-dart/pull/2351)) + ## 8.10.0-beta.2 ### Fixes @@ -55,8 +61,6 @@ ); ``` -- Remove `sentry` frames if SDK falls back to `currentStackTrace` ([#2351](https://github.com/getsentry/sentry-dart/pull/2351)) - ### Enhancements - Use native spotlight integrations on Flutter Android, iOS, macOS ([#2285](https://github.com/getsentry/sentry-dart/pull/2285)) From 6a2fb35c8a6b0b085859e1c4a5b15e86978560f2 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Thu, 17 Oct 2024 13:39:18 +0200 Subject: [PATCH 07/13] Add hint in flutter error integration --- .../flutter_error_integration.dart | 14 +++--- .../flutter_error_integration_test.dart | 50 +++++++++++++++++++ 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/flutter/lib/src/integrations/flutter_error_integration.dart b/flutter/lib/src/integrations/flutter_error_integration.dart index e16aa0d8ac..cae17cc24b 100644 --- a/flutter/lib/src/integrations/flutter_error_integration.dart +++ b/flutter/lib/src/integrations/flutter_error_integration.dart @@ -81,12 +81,14 @@ class FlutterErrorIntegration implements Integration { final hint = Hint(); hint.addAll({TypeCheckHint.syntheticException: errorDetails}); - - await hub.captureEvent(event, - // ignore: invalid_use_of_internal_member - stackTrace: errorDetails.stack ?? getCurrentStackTrace(), - hint: - Hint.withMap({TypeCheckHint.syntheticException: errorDetails})); + var stackTrace = errorDetails.stack; + if (errorDetails.stack == null || + errorDetails.stack == StackTrace.empty) { + // ignore: invalid_use_of_internal_member + stackTrace = getCurrentStackTrace(); + hint.addAll({TypeCheckHint.currentStackTrace: true}); + } + await hub.captureEvent(event, stackTrace: stackTrace, hint: hint); // we don't call Zone.current.handleUncaughtError because we'd like // to set a specific mechanism for FlutterError.onError. } else { diff --git a/flutter/test/integrations/flutter_error_integration_test.dart b/flutter/test/integrations/flutter_error_integration_test.dart index 4ec10025cb..a4ee7c8e9c 100644 --- a/flutter/test/integrations/flutter_error_integration_test.dart +++ b/flutter/test/integrations/flutter_error_integration_test.dart @@ -36,6 +36,7 @@ void main() { FlutterExceptionHandler? handler, dynamic exception, FlutterErrorDetails? optionalDetails, + StackTrace? stackTrace, }) { _mockValues(); @@ -53,6 +54,7 @@ void main() { context: DiagnosticsNode.message('while handling a gesture'), library: 'sentry', informationCollector: () => [DiagnosticsNode.message('foo bar')], + stack: stackTrace, ); FlutterError.reportError(optionalDetails ?? details); @@ -310,6 +312,54 @@ void main() { expect(event.level, SentryLevel.error); }); + + test('adds current stack trace hint on null stack trace', () async { + final exception = StateError('error'); + + _reportError(exception: exception, stackTrace: null); + + final hint = verify( + await fixture.hub.captureEvent( + captureAny, + stackTrace: captureAnyNamed('stackTrace'), + hint: captureAnyNamed('hint'), + ), + ).captured[2] as Hint; + + expect(hint.get(TypeCheckHint.currentStackTrace), isTrue); + }); + + test('adds current stack trace hint on empty stack trace', () async { + final exception = StateError('error'); + + _reportError(exception: exception, stackTrace: StackTrace.empty); + + final hint = verify( + await fixture.hub.captureEvent( + captureAny, + stackTrace: captureAnyNamed('stackTrace'), + hint: captureAnyNamed('hint'), + ), + ).captured[2] as Hint; + + expect(hint.get(TypeCheckHint.currentStackTrace), isTrue); + }); + + test('does not add current stack trace hint with stack trace', () async { + final exception = StateError('error'); + + _reportError(exception: exception, stackTrace: StackTrace.current); + + final hint = verify( + await fixture.hub.captureEvent( + captureAny, + stackTrace: captureAnyNamed('stackTrace'), + hint: captureAnyNamed('hint'), + ), + ).captured[2] as Hint; + + expect(hint.get(TypeCheckHint.currentStackTrace), isNull); + }); }); } From b74ef0c5eca3e9a19e4c83743dcd4e0bcc4143ce Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 21 Oct 2024 11:37:31 +0200 Subject: [PATCH 08/13] update test expectations --- dart/lib/src/sentry_client.dart | 6 +++-- dart/lib/src/sentry_exception_factory.dart | 7 +++--- dart/lib/src/sentry_stack_trace_factory.dart | 4 ++- dart/test/sentry_client_test.dart | 19 +++++++++----- dart/test/sentry_exception_factory_test.dart | 26 ++++++++++---------- 5 files changed, 36 insertions(+), 26 deletions(-) diff --git a/dart/lib/src/sentry_client.dart b/dart/lib/src/sentry_client.dart index 546da209c8..90036cda3d 100644 --- a/dart/lib/src/sentry_client.dart +++ b/dart/lib/src/sentry_client.dart @@ -126,7 +126,8 @@ class SentryClient { hint ??= Hint(); - SentryEvent? preparedEvent = _prepareEvent(event, hint, stackTrace: stackTrace); + SentryEvent? preparedEvent = + _prepareEvent(event, hint, stackTrace: stackTrace); if (scope != null) { preparedEvent = await scope.applyToEvent(preparedEvent, hint); @@ -209,7 +210,8 @@ class SentryClient { return isMatchingRegexPattern(message, _options.ignoreErrors); } - SentryEvent _prepareEvent(SentryEvent event, Hint hint, {dynamic stackTrace}) { + SentryEvent _prepareEvent(SentryEvent event, Hint hint, + {dynamic stackTrace}) { event = event.copyWith( serverName: event.serverName ?? _options.serverName, dist: event.dist ?? _options.dist, diff --git a/dart/lib/src/sentry_exception_factory.dart b/dart/lib/src/sentry_exception_factory.dart index 3a2fad9279..9263f07bf6 100644 --- a/dart/lib/src/sentry_exception_factory.dart +++ b/dart/lib/src/sentry_exception_factory.dart @@ -50,10 +50,9 @@ class SentryExceptionFactory { SentryStackTrace? sentryStackTrace; if (stackTrace != null) { - sentryStackTrace = - _stacktraceFactory - .parse(stackTrace, removeSentryFrames: removeSentryFrames) - .copyWith(snapshot: snapshot); + sentryStackTrace = _stacktraceFactory + .parse(stackTrace, removeSentryFrames: removeSentryFrames) + .copyWith(snapshot: snapshot); if (sentryStackTrace.frames.isEmpty) { sentryStackTrace = null; } diff --git a/dart/lib/src/sentry_stack_trace_factory.dart b/dart/lib/src/sentry_stack_trace_factory.dart index c26fb10840..36c93e5fc0 100644 --- a/dart/lib/src/sentry_stack_trace_factory.dart +++ b/dart/lib/src/sentry_stack_trace_factory.dart @@ -39,7 +39,9 @@ class SentryStackTraceFactory { var stackTraceFrame = encodeStackTraceFrame(frame); if (stackTraceFrame != null) { - if (removeSentryFrames == true && (stackTraceFrame.package == 'sentry' || stackTraceFrame.package == 'sentry_flutter')) { + if (removeSentryFrames == true && + (stackTraceFrame.package == 'sentry' || + stackTraceFrame.package == 'sentry_flutter')) { continue; } frames.add(stackTraceFrame); diff --git a/dart/test/sentry_client_test.dart b/dart/test/sentry_client_test.dart index 8e908d8800..38b8e24300 100644 --- a/dart/test/sentry_client_test.dart +++ b/dart/test/sentry_client_test.dart @@ -2201,17 +2201,20 @@ void main() { final capturedEnvelope = (fixture.transport).envelopes.first; final attachmentItem = capturedEnvelope.items.firstWhereOrNull( - (element) => element.header.type == SentryItemType.attachment, + (element) => element.header.type == SentryItemType.attachment, ); expect(attachmentItem, isNull); }); - test('null stack trace marked in hint & sentry frames removed from thread stackTrace', () async { + test( + 'null stack trace marked in hint & sentry frames removed from thread stackTrace', + () async { final beforeSendCallback = (SentryEvent event, Hint hint) { expect(hint.get(TypeCheckHint.currentStackTrace), isTrue); return event; }; - final client = fixture.getSut(beforeSend: beforeSendCallback, attachStacktrace: true); + final client = fixture.getSut( + beforeSend: beforeSendCallback, attachStacktrace: true); await client.captureEvent(fakeEvent); final capturedEnvelope = (fixture.transport).envelopes.first; @@ -2224,12 +2227,15 @@ void main() { expect(sentryFramesCount, 0); }); - test('empty stack trace marked in hint & sentry frames removed from thread stackTrace', () async { + test( + 'empty stack trace marked in hint & sentry frames removed from thread stackTrace', + () async { final beforeSendCallback = (SentryEvent event, Hint hint) { expect(hint.get(TypeCheckHint.currentStackTrace), isTrue); return event; }; - final client = fixture.getSut(beforeSend: beforeSendCallback, attachStacktrace: true); + final client = fixture.getSut( + beforeSend: beforeSendCallback, attachStacktrace: true); await client.captureEvent(fakeEvent, stackTrace: StackTrace.empty); final capturedEnvelope = (fixture.transport).envelopes.first; @@ -2247,7 +2253,8 @@ void main() { expect(hint.get(TypeCheckHint.currentStackTrace), isNull); return event; }; - final client = fixture.getSut(beforeSend: beforeSendCallback, attachStacktrace: true); + final client = fixture.getSut( + beforeSend: beforeSendCallback, attachStacktrace: true); await client.captureEvent(fakeEvent, stackTrace: StackTrace.current); }); }); diff --git a/dart/test/sentry_exception_factory_test.dart b/dart/test/sentry_exception_factory_test.dart index 5157350bd6..643414ab67 100644 --- a/dart/test/sentry_exception_factory_test.dart +++ b/dart/test/sentry_exception_factory_test.dart @@ -208,7 +208,8 @@ void main() { final sentryException = fixture.getSut(attachStacktrace: false).getSentryException(Object()); - expect(sentryException.stackTrace!.snapshot, true); + // stackTrace is null anyway when not present and attachStacktrace false + expect(sentryException.stackTrace?.snapshot, isNull); }); test('sets stacktrace build id and image address', () { @@ -226,19 +227,19 @@ void main() { .getSut(attachStacktrace: false) .getSentryException(Object(), stackTrace: null); - final sentryStackTrace = sentryException.stackTrace!; - expect(sentryStackTrace.baseAddr, isNull); - expect(sentryStackTrace.buildId, isNull); + // stackTrace is null anyway with null stack trace and attachStacktrace false + final sentryStackTrace = sentryException.stackTrace; + expect(sentryStackTrace?.baseAddr, isNull); + expect(sentryStackTrace?.buildId, isNull); }); test('remove sentry frames', () { - final sentryException = fixture - .getSut(attachStacktrace: false) - .getSentryException( - SentryStackTraceError(), - stackTrace: SentryStackTrace(), - removeSentryFrames: true, - ); + final sentryException = + fixture.getSut(attachStacktrace: false).getSentryException( + SentryStackTraceError(), + stackTrace: SentryStackTrace(), + removeSentryFrames: true, + ); final sentryStackTrace = sentryException.stackTrace!; expect(sentryStackTrace.baseAddr, isNull); @@ -306,8 +307,7 @@ isolate_instructions: 7526344980, vm_instructions: 752633f000 } class SentryStackTraceError extends Error { - var prefix = - "Unknown error without own stacktrace"; + var prefix = "Unknown error without own stacktrace"; @override String toString() { From 89ff369cde4636c0b01906169f4e01586fac204c Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 21 Oct 2024 13:43:55 +0200 Subject: [PATCH 09/13] cleanup --- dart/test/stack_trace_test.dart | 67 --------------------------------- 1 file changed, 67 deletions(-) diff --git a/dart/test/stack_trace_test.dart b/dart/test/stack_trace_test.dart index 75a9285e39..9e38eef032 100644 --- a/dart/test/stack_trace_test.dart +++ b/dart/test/stack_trace_test.dart @@ -303,73 +303,6 @@ isolate_instructions: 10fa27070, vm_instructions: 10fa21e20 .copyWith(platform: 'native'); expect(frameWithPlatform.platform, 'native'); }); - - group('stackFrameExcludes', () { - final stackTraceString = ''' - #0 getCurrentStackTrace (package:sentry/src/utils/stacktrace_utils.dart:10:49) -#1 OnErrorIntegration.call. (package:sentry_flutter/src/integrations/on_error_integration.dart:82:22) -#2 MainScaffold.build. (package:sentry_flutter_example/main.dart:349:23) -#3 _InkResponseState.handleTap (package:flutter/src/material/ink_well.dart:1170:21) -#4 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:351:24) -#5 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:656:11) -#6 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:313:5) -#7 BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:283:7) -#8 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:169:27) -#9 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:505:20) -#10 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:481:22) -#11 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:450:11) -#12 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:426:7) -#13 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:389:5) -#14 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:336:7) -#15 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:305:9) -#16 _invoke1 (dart:ui/hooks.dart:328:13) -#17 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:442:7) -#18 _dispatchPointerDataPacket (dart:ui/hooks.dart:262:31) - '''; - - test('excludes sentry and sentry_flutter stack frames per default', () { - final fixture = Fixture(); - - expect( - fixture.options.stackFrameExcludes, ['sentry', 'sentry_flutter']); - - final sut = fixture.getSut(); - final sentryStackTrace = - sut.parse(StackTrace.fromString(stackTraceString)); - - expect(sentryStackTrace.frames.length, 17); - expect(sentryStackTrace.frames[16].package, 'sentry_flutter_example'); - expect(sentryStackTrace.frames[15].package, 'flutter'); - }); - - test('excludes added stack frame exclude', () { - final fixture = Fixture(); - - final sut = fixture.getSut(); - fixture.options.addStackFrameExclude('sentry_flutter_example'); - - final sentryStackTrace = - sut.parse(StackTrace.fromString(stackTraceString)); - - expect(sentryStackTrace.frames.length, 16); - expect(sentryStackTrace.frames[15].package, 'flutter'); - }); - - test('ignores removed stack frame excludes', () { - final fixture = Fixture(); - - final sut = fixture.getSut(); - fixture.options.removeStackFrameExclude('sentry'); - fixture.options.removeStackFrameExclude('sentry_flutter'); - - final sentryStackTrace = - sut.parse(StackTrace.fromString(stackTraceString)); - - expect(sentryStackTrace.frames.length, 19); - expect(sentryStackTrace.frames[18].package, 'sentry'); - expect(sentryStackTrace.frames[17].package, 'sentry_flutter'); - }); - }); }); } From 48c36f9a1dd0b04cdfa20c05451a7e7511ba4bb4 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Tue, 22 Oct 2024 10:31:34 +0200 Subject: [PATCH 10/13] fix import --- dart/lib/src/sentry_client.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dart/lib/src/sentry_client.dart b/dart/lib/src/sentry_client.dart index 90036cda3d..5597708030 100644 --- a/dart/lib/src/sentry_client.dart +++ b/dart/lib/src/sentry_client.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'dart:math'; import 'package:meta/meta.dart'; -import 'package:sentry/src/type_check_hint.dart'; +import 'type_check_hint.dart'; import 'client_reports/client_report_recorder.dart'; import 'client_reports/discard_reason.dart'; From d72452a0c14015da7d7522365e60494966b93a7a Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Mon, 11 Nov 2024 13:56:40 +0100 Subject: [PATCH 11/13] Update CHANGELOG.md --- CHANGELOG.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f79c58cba..6bd670792e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,12 @@ ### Enhancements -- Remove `sentry` frames if SDK falls back to current stack trace ([#2351](https://github.com/getsentry/sentry-dart/pull/2351)) - +- Remove `sentry` frames if SDK falls back to current stack trace ([#2351](https://github.com/getsentry/sentry-dart/pull/2351)) + - Flutter doesn't always provide stack traces for unhandled errors - this is normal Flutter behavior + - When no stack trace is provided (in Flutter errors, `captureException`, or `captureMessage`): + - SDK creates a synthetic trace using `StackTrace.current` + - Internal SDK frames are removed to reduce noise + - Original stack traces (when provided) are left unchanged ## 8.10.1 ### Fixes From 47e1472787e3718f0ef75a7648fadabe048d0a18 Mon Sep 17 00:00:00 2001 From: Giancarlo Buenaflor Date: Mon, 11 Nov 2024 13:56:53 +0100 Subject: [PATCH 12/13] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bd670792e..ff29dfeeb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - SDK creates a synthetic trace using `StackTrace.current` - Internal SDK frames are removed to reduce noise - Original stack traces (when provided) are left unchanged + ## 8.10.1 ### Fixes From 5ddd2af1b1844a65fb15b62cc7496c57d88e1400 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 11 Nov 2024 16:43:45 +0100 Subject: [PATCH 13/13] update cl --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f08a4073a..0c58d41c28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - SDK creates a synthetic trace using `StackTrace.current` - Internal SDK frames are removed to reduce noise - Original stack traces (when provided) are left unchanged + ### Features - Improve frame tracking accuracy ([#2372](https://github.com/getsentry/sentry-dart/pull/2372))