getsentry / sentry-dart

Sentry SDK for Dart and Flutter
https://sentry.io/for/flutter/
MIT License
746 stars 233 forks source link

Improve support for Flutter Web #897

Open marandaneto opened 2 years ago

marandaneto commented 2 years ago

Likely more improvements can be made.

ueman commented 2 years ago

Breadcrumbs and spans for fetch/xmlhttprequest

This should not create duplicates when used together with the http or dio integrations.

bruno-garcia commented 2 years ago

https://trends.builtwith.com/websitelist/Flutter

image

https://trends.builtwith.com/websitelist/Dart

image

marandaneto commented 1 year ago

https://github.com/dart-lang/source_map_stack_trace/tree/master might be something to look at in case JS stack traces are not so user-friendly, we could do something similar on the Server, well, we already symbolicate it but not sure if this library does something different than us.

blaugold commented 1 year ago

https://github.com/dart-lang/source_map_stack_trace/tree/master might be something to look at in case JS stack traces are not so user-friendly, we could do something similar on the Server, well, we already symbolicate it but not sure if this library does something different than us.

Demo App Code ```dart import 'package:flutter/material.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; void main() { SentryFlutter.init( (options) { options.dsn = 'https://ba66c99d92d647c5bb41ca7dcd261e88@o61502.ingest.sentry.io/4504991898140672'; }, appRunner: () => runApp(const MainApp()), ); } class MainApp extends StatelessWidget { const MainApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Center( child: ElevatedButton( onPressed: () { throw Exception('Test exception'); }, child: const Text('Hello World!'), ), ), ), ); } } ```

This is how Sentry deobfuscates the stack trace for the error thrown in the demo app:

Screenshot 2023-05-07 at 15 59 23

Using source_map_stack_trace to deobfuscate the stack trace yields the following:

../../../lib/main.dart 24:15                                                                    MainApp.build.<anonymous function>
../../../../../../fvm/versions/stable/packages/flutter/lib/src/widgets/framework.dart 921:26    _InkResponseState.handleTap
org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_helper.dart 2105:9                          Closure.cspForwardCall
../../../../../../fvm/versions/stable/packages/flutter/lib/src/gestures/recognizer.dart 253:16  GestureRecognizer.invokeCallback
../../../../../../fvm/versions/stable/packages/flutter/lib/src/gestures/recognizer.dart 239:6   GestureRecognizer.invokeCallback[function-entry$2]
../../../../../../fvm/versions/stable/packages/flutter/lib/src/gestures/tap.dart 627:11         TapGestureRecognizer.handleTapUp
../../../../../../fvm/versions/stable/packages/flutter/lib/src/gestures/tap.dart 306:5          BaseTapGestureRecognizer._checkUp
../../../../../../fvm/versions/stable/packages/flutter/lib/src/gestures/tap.dart 239:7          BaseTapGestureRecognizer.handlePrimaryPointer
../../../../../../fvm/versions/stable/packages/flutter/lib/src/gestures/recognizer.dart 615:9   PrimaryPointerGestureRecognizer.handleEvent

source_map_stack_trace seems to be able to fully restore function names for each frame, whereas Sentry currently is not. I would love to see support for that and be happy to help with a pointer to where to start.

marandaneto commented 1 year ago

@blaugold can you provide a minimal reproducible example? (with build commands, upload source maps command, etc). Every time I test a sample app (Flutter web), symbolication works just fine. If that's indeed the case, I'd consider it a bug, and raise a new issue, I can do that if that's the case.

blaugold commented 1 year ago

Flutter version:

Flutter 3.7.12 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 4d9e56e694 (3 weeks ago) • 2023-04-17 21:47:46 -0400
Engine • revision 1a65d409c7
Tools • Dart 2.19.6 • DevTools 2.20.1
  1. flutter create sentry_flutter_web_stacktraces_repro

  2. cd sentry_flutter_web_stacktraces_repro

  3. flutter pub add sentry_flutter

  4. Replace lib/main.dart with the following:

    void main() {
     SentryFlutter.init(
       (options) {
         options.dsn = '...';
       },
       appRunner: () => runApp(const MainApp()),
     );
    }
    
    class MainApp extends StatelessWidget {
     const MainApp({super.key});
    
     @override
     Widget build(BuildContext context) {
       return MaterialApp(
         home: Scaffold(
           body: Center(
             child: ElevatedButton(
               onPressed: () {
                 throw Exception('Test exception');
               },
               child: const Text('Throw exception'),
             ),
           ),
         ),
       );
     }
    }
  5. Configure DSN in main.dart

  6. flutter build web --source-maps

  7. sentry-cli sourcemaps upload -o ... -p ... -r sentry_flutter_web_stacktraces_repro@1.0.0+1 -d 1 build/web

  8. cd build/web

  9. caddy file-server: Start a web server to serve the files.

  10. Open http://localhost:80: Open the web app in a browser, wherever it is served.

  11. Click on the "Throw exception" button

  12. This is what the raw but deobfuscated stack trace looks like in Sentry:

    minified:Gi: Exception: Test exception
      at A.c(org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_helper.dart:1128:37)
      at A.Tt.prototype.$0(../../../lib/main.dart:24:15)
      at A.v6.prototype.Em(../../../../../../fvm/versions/stable/packages/flutter/lib/src/widgets/framework.dart:921:26)
      at A.akv(org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_helper.dart:2105:9)
      at A.cN.prototype.EC(../../../../../../fvm/versions/stable/packages/flutter/lib/src/gestures/recognizer.dart:253:16)
      at A.cN.prototype.fG(../../../../../../fvm/versions/stable/packages/flutter/lib/src/gestures/recognizer.dart:239:6)
      at A.fA.prototype.Eo(../../../../../../fvm/versions/stable/packages/flutter/lib/src/gestures/tap.dart:627:11)
      at A.xl.prototype.zd(../../../../../../fvm/versions/stable/packages/flutter/lib/src/gestures/tap.dart:306:5)
      at A.xl.prototype.El(../../../../../../fvm/versions/stable/packages/flutter/lib/src/gestures/tap.dart:239:7)
      at A.nF.prototype.i4(../../../../../../fvm/versions/stable/packages/flutter/lib/src/gestures/recognizer.dart:615:9)
marandaneto commented 1 year ago

@blaugold can you try using the https://github.com/getsentry/sentry-dart-plugin plugin instead of calling sentry-cli manually? Just wanna be sure that all the configuration is correct. The plugin uses different commands than yours, maybe it's related to that. Also, link me to a sentry.io event so I can check myself as well. Btw feel free to raise a new issue about that, so we can avoid the back and forth there, which is a slightly different topic, it'd be a bug if there's something broken.

marandaneto commented 1 year ago

Flutter web has this option in the build cmd:

--build-number                                      An identifier used as an internal version number.
                                                    Each build must have a unique identifier to differentiate it from
                                                    previous builds.
                                                    It is used to determine whether one build is more recent than another,
                                                    with higher numbers indicating more recent build.
                                                    On Android it is used as "versionCode".
                                                    On Xcode builds it is used as "CFBundleVersion".
                                                    On Windows it is used as the build suffix for the product and file
                                                    versions.
--build-name=<x.y.z>                                A "x.y.z" string used as the version number shown to users.
                                                    For each new version of your app, you will provide a version number to
                                                    differentiate it from previous versions.
                                                    On Android it is used as "versionName".
                                                    On Xcode builds it is used as "CFBundleShortVersionString".
                                                    On Windows it is used as the major, minor, and patch parts of the
                                                    product and file versions.

If its set, we could use it as release/dist, instead of the pubspec file?

kahest commented 11 months ago

Duplicate of this with some context/rationale: https://github.com/getsentry/sentry-dart/issues/1661

alestiago commented 7 months ago

What's the status of this item so far?

buenaflor commented 7 months ago

@alestiago hi, anything specific you are looking for in the list?

mikeesouth commented 7 months ago

I'm also interested in updates here. My biggest issue is that the error information is minified, even if I upload debug symbols from Flutter using sentry_dart_plugin. I am uploading the symbols using sentry_dart_plugin and I see that the symbols are accepted and connected to my release when I check in sentry.io web portal.

When I run Flutter Web in release mode it presents alot of minified names, like this:

minified:am
ReferenceError: Can't find variable: a9

However, I can see relevant dart code in the stack trace even though the file paths are messy too.

If I switch to profile mode I get much nicer exceptions, the errors looks like this (with corret types, variable names etc)

_Exception
Exception: simulated locally (async)

Issue #1661 also describes this problem in a good way, but it's closed as a duplicate of this.

I guess that my main problem is the last checkbox, "demangle runtime types": https://github.com/getsentry/symbolic/issues/807 I'm not sure but I think that the second to last checkbox is relevant too: https://github.com/getsentry/team-mobile/issues/133 + https://github.com/dart-lang/sdk/issues/53027

buenaflor commented 7 months ago

thanks for the details. we'll discuss this and see if we can have some bandwidth soon to work on this.

Fraa-124 commented 5 months ago

I'm experiencing the same issue as @mikeesouth . Despite uploading source maps with the Sentry Dart plugin, my web stack traces remain minified. Is there a configuration step I'm missing, or is web source map support not yet implemented?

kahest commented 5 months ago

@Fraa-124 source maps are supported for Flutter Web, but with some limitations as discussed above

tazik561 commented 5 months ago

I also have same issue https://stackoverflow.com/questions/78290786/flutter-web-sentry-did-not-translate-correctly-issues/78291321#78291321

EmmanuelAdeiza commented 5 months ago

Is there a solution yet? @buenaflor. I keep getting this instead of dart source codes.

image
lisovyk commented 5 months ago

This ticket is 2 years old and it doesn't look promising in the thread. IMO it should be the top priority if you are to support Flutter web, I am surprised this takes so long. Right now we have around 50% of 'minified' issues and I am doubting my choice to use Sentry for our flutter product.

At least include some warning in the documentation about this limitation!

buenaflor commented 5 months ago

Hey, we totally understand the frustration and are sorry about the delay of this issue.

What should work:

What doesn't work:

So (in theory) the stacktrace should not be minified in the issues. We'll take a look at that @denrase

At least include some warning in the documentation about this limitation!

You're right, thanks for pointing that out, we will add more visibility on current flutter web limitations to our docs

EmmanuelAdeiza commented 5 months ago

@buenaflor Please just in case I am wrong. Could you give the steps required to get the "What should work:" to work?

We are trying to use sentry on Crx(Flutter Web) to record exceptions, It does that but returns minified stacktraces. To solve this, we uploaded sourcemaps after making build (with the --sourcemaps in the build command) using the sentry_dart_plugin.

It shows on sentry that sourcemaps were uploaded successfully but we still don't see dart code in exception stacktraces.

After this exceptions show minified:kP (minified:a_P at times) instead of the exception types but the stacktraces are still not readable.

Is there any work around to get dart code or are we missing any step?

PS: We also tried to use sentry cli and manually upload the source code after injecting debug-ids but got similar results

denrase commented 5 months ago

Minimal Setup

Below is a minimal setup + steps that show working stack traces + inline dart code for minified code from a flutter web application, build with the release flag.

The app only has one button, which throws an exception. This is then captured using Sentry.

We use the sentry dart plugin to upload all files needed. It will create a release from the name and version in pubspec.yaml. Since we also have a build number, it will take it as dist. The release name will be name@version, with dist parameter taken from the build 4 from the version:

calculator@1.0.3+4

pubspec.yaml

name: calculator
version: 1.0.3+4

dependencies:
  sentry_flutter: ^7.18.0

dev_dependencies:
  sentry_dart_plugin: ^1.7.1

sentry:
  upload_debug_symbols: true
  upload_source_maps: true
  project: ... # In this sample the project name is also `calculator`
  org: ...
  auth_token: ...
  wait_for_processing: true
  log_level: info
  commits: false
  ignore_missing: true

Setup & Throwing Code

I added another class in a separate file to see if source mapping works in this case.

main.js:

Future<void> main() async {
  await SentryFlutter.init(
    (options) {
      options.dsn = '... your dsn ...';
      options.tracesSampleRate = 1.0;
    },
    appRunner: () => runApp(const MyApp()),
  );
}

void _throwAnException() {
  try {
    Helper().aThrowingFunction("paramA", true);
  } catch (e, s) {
    Sentry.captureException(e, stackTrace: s);
  }
}

helper.js:

class Helper {
  void aThrowingFunction(String paramA, bool paramB) {
    throw Exception('Only throwing this to test Sentry with paramA $paramA and paramB $paramB.');
  }
}

Build & Upload

flutter build web --release --source-maps
dart run sentry_dart_plugin

This will then uplaod your source maps, and you see something like this output:

> Found 8 files

> Analyzing 8 sources

> Rewriting sources

> Adding source map references

> Bundled 8 files for upload
> Bundle ID: ec34b239-c576-57b3-a52a-c77151208c35

> Uploaded files to Sentry

> File processing complete
> Organization: denrase
> Project: calculator
> Release: calculator@1.0.3+4
> Dist: 4
> Upload type: artifact bundle

Source Map Upload Report
  Scripts
    ~/flutter_service_worker.js
  Minified Scripts
    ~/canvaskit/canvaskit.js (sourcemap at ../main.dart.js.map)
    ~/canvaskit/chromium/canvaskit.js (sourcemap at ../../main.dart.js.map)
    ~/canvaskit/skwasm.js (sourcemap at ../main.dart.js.map)
    ~/canvaskit/skwasm.worker.js (sourcemap at ../main.dart.js.map)
    ~/flutter.js (sourcemap at flutter.js.map)
    ~/main.dart.js (sourcemap at main.dart.js.map)
  Source Maps
    ~/main.dart.js.map

Run Web Server

I used python http.server webserver to run the flutter application.

cd build/web
python3 -m http.server 

Click the UI element that throws/captures the exception.

Results

Exception

It should the stack trace and also the dart code.

Bildschirmfoto 2024-04-16 um 16 07 37

Source Maps

We should see that the js files, minifier main.js & source map main.js.map are uploaded in the projects settings.

List Artifacts Artifacts 2
Bildschirmfoto 2024-04-16 um 16 07 56 Bildschirmfoto 2024-04-16 um 16 08 02 Bildschirmfoto 2024-04-16 um 16 08 09
EmmanuelAdeiza commented 5 months ago

Is this expected to behave differently if the flutter web app is loaded as a chrome extension? Because with the same steps you outlined I am getting this with a lot of missing source issues @denrase @buenaflor

image image image
tazik561 commented 5 months ago

Is this expected to behave differently if the flutter web app is loaded as a chrome extension? Because with the same steps you outlined I am getting this with a lot of missing source issues @denrase @buenaflor

image image image

I have same issue https://stackoverflow.com/questions/78290786/flutter-web-sentry-did-not-translate-correctly-issues/78291321#78291321

kahest commented 5 months ago

@EmmanuelAdeiza yes, it's expected to work differently when loaded as Chrome extension, and we didn't specifically add support for this - I added an item to the tasklist on top.