diff --git a/docs/platforms/dart/guides/flutter/migration.mdx b/docs/platforms/dart/guides/flutter/migration/index.mdx similarity index 94% rename from docs/platforms/dart/guides/flutter/migration.mdx rename to docs/platforms/dart/guides/flutter/migration/index.mdx index 9668157805786..15d2b007c9669 100644 --- a/docs/platforms/dart/guides/flutter/migration.mdx +++ b/docs/platforms/dart/guides/flutter/migration/index.mdx @@ -1,15 +1,20 @@ --- title: Migration Guide sidebar_order: 8000 +description: "Learn how to migrate your Flutter application between major versions of the Sentry SDK." --- +## Migrating From `sentry_flutter` `9.x` to `sentry_flutter` `10.x` + +See the [v9 to v10 migration guide](/platforms/dart/guides/flutter/migration/v9-to-v10/) for all changes, or use the [interactive migration checklist](/platforms/dart/guides/flutter/migration/v9-to-v10/interactive/) to track your progress. + ## Migrating from `sentry_flutter` `8.x` to `sentry_flutter` `9.x` ### General #### Dart and Flutter versions -The required minimum Dart and Flutter versions are now `3.5.0` and `3.24.0` respectively. +The required minimum Dart and Flutter versions are now `3.5.0` and `3.24.0` respectively. This change allows us to use safer APIs and better support for features such as Session Replay and WASM compilation. #### Android API version @@ -18,7 +23,7 @@ The required minimum Android API is now `21`. #### API Removals and Renames -- The `beforeScreenshot` method has been replaced by `beforeCaptureScreenshot`. +- The `beforeScreenshot` method has been replaced by `beforeCaptureScreenshot`. - Manual TTID implementation has been removed as the automatic TTID instrumentation has proven to be accurate enough so a manual one is not needed anymore. - `LoadImagesListIntegration` has been renamed to `LoadNativeDebugImagesIntegration`. - The `enableTracing` option has been removed. Use `options.traceSampleRate` or `options.tracesSampler` instead. @@ -31,7 +36,7 @@ The required minimum Android API is now `21`. #### Response Body Handling -Due to PII concerns, response bodies will no longer be added to Sentry events by the SDK automatically. +Due to PII concerns, response bodies will no longer be added to Sentry events by the SDK automatically. Responses are now attached to the `Hint` object, which can be read in `beforeSend`/`beforeSendTransaction` callbacks via `hint.response` so you can manually attach the response to your event. Response bodies with a size greater than 0.15MB are not added to the hint object. Currently as of version `9.0.0`, only the `dio` integration is supported. @@ -44,12 +49,12 @@ Read the guide [here](/platforms/dart/guides/flutter/enriching-events/screenshot #### Logging -The default log level is now `warning` when `debug = true`. +The default log level is now `warning` when `debug = true`. This can be adjusted by setting `options.diagnosticLevel = SentryLevel.info` in `SentryFlutter.init`. #### SDK Data Classes -SDK data classes are now mutable which makes it easier to manipulate them. +SDK data classes are now mutable which makes it easier to manipulate them. For backwards-compatibility, `copyWith` and `clone` can still be used but are officially deprecated. ```dart @@ -68,7 +73,7 @@ options.beforeSend = (event, hint) { ### Flutter Desktop -Native crash handling is now enabled by default for Flutter Desktop. +Native crash handling is now enabled by default for Flutter Desktop. Windows ARM64 uses [breakpad](https://chromium.googlesource.com/breakpad/breakpad) and all other desktop platforms use [crashpad](https://chromium.googlesource.com/crashpad/crashpad). You can disable this feature by setting the `SENTRY_NATIVE_BACKEND` environment variable to "none". e.g `export SENTRY_NATIVE_BACKEND=none` diff --git a/docs/platforms/dart/guides/flutter/migration/v9-to-v10/index.mdx b/docs/platforms/dart/guides/flutter/migration/v9-to-v10/index.mdx new file mode 100644 index 0000000000000..ae81374446d38 --- /dev/null +++ b/docs/platforms/dart/guides/flutter/migration/v9-to-v10/index.mdx @@ -0,0 +1,87 @@ +--- +title: Migrate from 9.x to 10.x +sidebar_order: 1 +description: "Learn how to migrate your Flutter application from Sentry SDK 9.x to 10.x." +--- + + + + + + + +The [Interactive v10 Migration Guide](./interactive/) lets you check off completed migration steps. + + + +## Minimum Supported Versions + + + +## Update Sentry Packages + + + +## Android Builds Use Flutter's Kotlin Support + + + +## Apple Builds Use Swift Package Manager + + + +## Logs and Metrics Are Always Enabled + + + +## Telemetry Callbacks Receive a Hint + + + +## Stream Mode Is the Default + + + +## App Start Is a Standalone Trace + + + +## Native Failed Requests Are Opt-In + + + +## Profiling and Deprecated APIs Are Removed + + + +## Feature Flags Use the Current Scope + + + +## Span Attribute Names Have Changed + + + +## Throwing Callbacks Drop Telemetry + + + +## Error Processing and Release Health + + + +## HTTP Error Capture and Grouping + + + +## Sensitive Content Masking + + + +## Desktop Crash Reporting Uses Breakpad + + + +## Flutter Web Uses JavaScript SDK v11 + + diff --git a/docs/platforms/dart/guides/flutter/migration/v9-to-v10/interactive.mdx b/docs/platforms/dart/guides/flutter/migration/v9-to-v10/interactive.mdx new file mode 100644 index 0000000000000..88abe2f96adb4 --- /dev/null +++ b/docs/platforms/dart/guides/flutter/migration/v9-to-v10/interactive.mdx @@ -0,0 +1,23 @@ +--- +title: Interactive v10 Migration Guide +sidebar_order: 1 +description: "Migrate from Sentry Flutter SDK 9.x to 10.x with an interactive checklist." +--- + + + + + +This checklist covers the Flutter v10 migration. Some steps only apply to specific target platforms, integrations, or configuration options. Check off a step after completing it or confirming that it doesn't apply to your app. Your progress is saved in this browser. + +For details about each change, see the [migration guide](../). + + + +The Dart analyzer won't catch every behavior change. Read through the steps before upgrading, including changes to telemetry defaults, error grouping, and release health. + + + +## Your Migration Steps + + diff --git a/includes/flutter-v10-agent-migration.mdx b/includes/flutter-v10-agent-migration.mdx new file mode 100644 index 0000000000000..a6be962bc9a27 --- /dev/null +++ b/includes/flutter-v10-agent-migration.mdx @@ -0,0 +1,5 @@ + diff --git a/includes/flutter-v10-migration-overview.mdx b/includes/flutter-v10-migration-overview.mdx new file mode 100644 index 0000000000000..e0ea723095877 --- /dev/null +++ b/includes/flutter-v10-migration-overview.mdx @@ -0,0 +1,11 @@ +Version 10 raises the minimum supported platform versions, removes deprecated APIs, and changes several telemetry defaults. The biggest changes are: + +- **Platform and build requirements:** Dart 3.12 and Flutter 3.44 are the new minimums, along with Android API 26, iOS 15, and macOS 12. Apple builds use Swift Package Manager instead of CocoaPods, and Android builds use Flutter's Kotlin support. +- **API removals:** Profiling and deprecated APIs, including `copyWith` and `SentryFeedbackWidget`, are removed. Logs and metrics no longer need enable flags. +- **Telemetry behavior:** The logging integration forwards Sentry logs by default, native failed-request capture becomes opt-in, and throwing filtering callbacks drop the affected telemetry. +- **Tracing:** Stream mode is now the default. Migrate custom transaction instrumentation, filtering callbacks, and samplers, or explicitly select `SentryTraceLifecycle.static` to retain transaction mode. +- **App-start tracing:** App start is a standalone `app.start` trace with its own sampling decision and a revised frame-span breakdown. Review custom sampling rules, dashboards, and alerts. +- **Desktop crash reporting:** Windows and Linux default to Breakpad, with native crash reports uploaded after an application restart. A build-time override retains Crashpad. +- **Flutter web:** The bundled JavaScript SDK moves to v11, and browser sessions report unhandled errors as `unhandled` rather than `crashed`. Flutter's `sendDefaultPii` option remains supported. + +The guide includes core Dart SDK changes that affect Flutter applications. diff --git a/includes/migration/flutter-v10/android-kotlin.mdx b/includes/migration/flutter-v10/android-kotlin.mdx new file mode 100644 index 0000000000000..baa18b9542db6 --- /dev/null +++ b/includes/migration/flutter-v10/android-kotlin.mdx @@ -0,0 +1,12 @@ +--- +id: android-kotlin +title: "Android Builds Use Flutter's Kotlin Support" +phase: prerequisites +category: version-support +severity: action-required +frameworks: all +platformCategory: all +order: 30 +--- + +The Sentry Flutter plugin no longer applies or bundles its own Kotlin Gradle Plugin. It relies on Flutter 3.44's Kotlin support. If you customize Android build configuration, update it for Flutter 3.44 rather than relying on Sentry to supply Kotlin. This change does not require upgrading your app to AGP 9. diff --git a/includes/migration/flutter-v10/app-start.mdx b/includes/migration/flutter-v10/app-start.mdx new file mode 100644 index 0000000000000..442f06eb20ce5 --- /dev/null +++ b/includes/migration/flutter-v10/app-start.mdx @@ -0,0 +1,28 @@ +--- +id: app-start +title: "App Start Is a Standalone Trace" +phase: code-changes +category: behavior +severity: action-required +frameworks: all +platformCategory: all +order: 70 +--- + +On Android and iOS, app start is now reported as its own `app.start` root when tracing is enabled. It is no longer attached to the initial `ui.load` transaction and has its own sampling decision. + +Remove `options.enableStandaloneAppStartTracing` assignments. The standalone path is now the default, and the option has been removed. Update custom sampling rules, dashboards, and alerts that assume app start belongs to the first navigation transaction. A sampler that only accepts `ui.load` roots may exclude app start. + +If startup work continues past the first frame, call `SentryFlutter.extendAppStart()` before the first frame renders and `await SentryFlutter.finishExtendedAppStart()` when that work finishes. An extension that reaches the 30-second app-start deadline is dropped, and the reported duration falls back to the first frame. + +The app-start breakdown also changes in v10. Update queries, dashboards, and `beforeSendSpan` callbacks that match `app.start.plugin_registration`, `app.start.sentry_setup`, or `app.start.first_frame_render`. These operations are no longer emitted. The SDK records measured startup work using: + +| Operation | Measured Work | +| ---------------------------------- | --------------------------------------------- | +| `app.start.root_widget_attachment` | Attachment of the root widget | +| `app.start.frame_build` | Flutter framework frame builds during startup | +| `app.start.frame_raster` | Rasterization of the first frame | + +These spans are not one-to-one replacements for the previous broad intervals. Frame-build spans include `flutter.frame.warm_up` and `flutter.frame.deferred` attributes. A frame-build span does not by itself mean that the frame was submitted to the engine or displayed. + +Startup frame builds that begin at or after the first frame's rasterization starts are also excluded. diff --git a/includes/migration/flutter-v10/apple-swift-package-manager.mdx b/includes/migration/flutter-v10/apple-swift-package-manager.mdx new file mode 100644 index 0000000000000..e3e89a22b33d8 --- /dev/null +++ b/includes/migration/flutter-v10/apple-swift-package-manager.mdx @@ -0,0 +1,23 @@ +--- +id: apple-swift-package-manager +title: "Apple Builds Use Swift Package Manager" +phase: prerequisites +category: package +severity: action-required +frameworks: all +platformCategory: all +order: 40 +--- + +The Sentry Flutter plugin no longer supports CocoaPods. On iOS and macOS, it uses Swift Package Manager to install the native Sentry Cocoa SDK, which has been upgraded to v9. If you initialize Sentry separately in native code or call its Swift/Objective-C APIs directly, also review the [Sentry Cocoa v8-to-v9 migration guide](/platforms/apple/migration/v8-to-v9/). + +Flutter 3.44 enables Swift Package Manager by default and migrates your project when you run the app. If you previously disabled it, re-enable it and remove any project-level `enable-swift-package-manager: false` setting: + +```bash +flutter config --enable-swift-package-manager +flutter pub get +``` + +Set the iOS and macOS deployment targets in Xcode to the minimum versions listed in [Minimum Supported Versions](/platforms/dart/guides/flutter/migration/v9-to-v10/#minimum-supported-versions). For custom targets, flavors, or projects that don't migrate automatically, follow [Flutter's Swift Package Manager migration instructions](https://docs.flutter.dev/packages-and-plugins/swift-package-manager/for-app-developers/). + +Other Flutter plugins may still require CocoaPods. Only remove your application's CocoaPods integration once all of its dependencies support Swift Package Manager. diff --git a/includes/migration/flutter-v10/callback-failures.mdx b/includes/migration/flutter-v10/callback-failures.mdx new file mode 100644 index 0000000000000..0e3ab4bf7ad4d --- /dev/null +++ b/includes/migration/flutter-v10/callback-failures.mdx @@ -0,0 +1,16 @@ +--- +id: callback-failures +title: "Throwing Callbacks Drop Telemetry" +phase: code-changes +category: behavior +severity: behavior-change +frameworks: all +platformCategory: all +order: 115 +--- + +In v10, an exception in an event processor or a filtering callback drops the affected telemetry item instead of sending the item through unchanged. + +This applies to event processors, `beforeSend`, `beforeSendTransaction`, `beforeSendFeedback`, `beforeBreadcrumb`, `beforeSendLog`, and `beforeSendMetric`. Review callbacks that can throw, especially custom filtering and scrubbing logic. Handle expected failures inside the callback and return the intended item or `null` explicitly. + +In transaction mode, a throwing `tracesSampler` uses the inherited sampling decision when available, then falls back to `tracesSampleRate`. In stream mode, it falls back to `tracesSampleRate`. Do not rely on throwing an exception to reject a trace; return `0.0` to reject it explicitly. diff --git a/includes/migration/flutter-v10/desktop-crash-reporting.mdx b/includes/migration/flutter-v10/desktop-crash-reporting.mdx new file mode 100644 index 0000000000000..3021a365b742d --- /dev/null +++ b/includes/migration/flutter-v10/desktop-crash-reporting.mdx @@ -0,0 +1,27 @@ +--- +id: desktop-crash-reporting +title: "Desktop Crash Reporting Uses Breakpad" +phase: verify +category: behavior +severity: behavior-change +frameworks: all +platformCategory: all +order: 150 +--- + +On Windows and Linux, the default native crash backend changes to Breakpad. Native crash reports are uploaded when the application starts again, rather than by a separate Crashpad handler at crash time. + +Breakpad handles crashes in the application process, without Crashpad's out-of-process isolation. It also lacks Crashpad's Windows Error Reporting (WER) and fast-fail coverage. Review crash-delivery expectations and test a crash followed by an application restart. + +To retain Crashpad, set the `SENTRY_NATIVE_BACKEND` environment variable to `crashpad` in your local build environment and CI before building the application. For example: + +```bash {tabTitle:Linux} {mdExpandTabs} +SENTRY_NATIVE_BACKEND=crashpad flutter build linux +``` + +```powershell {tabTitle:Windows} +$env:SENTRY_NATIVE_BACKEND = "crashpad" +flutter build windows +``` + +This is a build-time setting, not a Dart SDK initialization option. diff --git a/includes/migration/flutter-v10/error-processing.mdx b/includes/migration/flutter-v10/error-processing.mdx new file mode 100644 index 0000000000000..8cf78be9d8940 --- /dev/null +++ b/includes/migration/flutter-v10/error-processing.mdx @@ -0,0 +1,16 @@ +--- +id: error-processing +title: "Error Processing and Release Health" +phase: verify +category: behavior +severity: behavior-change +frameworks: all +platformCategory: all +order: 120 +--- + +Error sampling through `sampleRate` now happens after event processors and `beforeSend`. These callbacks run even for events that are subsequently sampled out, so review callbacks that perform expensive work or have side effects. An event dropped by `beforeSend` is reported as a `before_send` discard rather than a `sample_rate` discard. + +On Android, iOS, and macOS, unhandled Flutter errors now mark sessions as unhandled rather than crashed. Unhandled errors dropped by `sampleRate` also affect release health. Review release-health comparisons across the upgrade because session classification and sampling behavior have changed. + +For the web session-status change, see [Flutter Web Uses JavaScript SDK v11](/platforms/dart/guides/flutter/migration/v9-to-v10/#flutter-web-uses-javascript-sdk-v11). diff --git a/includes/migration/flutter-v10/feature-flags.mdx b/includes/migration/flutter-v10/feature-flags.mdx new file mode 100644 index 0000000000000..95185148d96bf --- /dev/null +++ b/includes/migration/flutter-v10/feature-flags.mdx @@ -0,0 +1,14 @@ +--- +id: feature-flags +title: "Feature Flags Use the Current Scope" +phase: code-changes +category: removed-api +severity: action-required +frameworks: all +platformCategory: all +order: 100 +--- + +Feature flag evaluations now belong to the current hub and scope, rather than `FeatureFlagsIntegration`. If you accessed that integration directly, replace those calls with `await Sentry.addFeatureFlag('flag-name', true)`. + +Existing calls to `Sentry.addFeatureFlag` continue to work. Custom `Hub` implementations must implement `addFeatureFlag(String flag, bool result)`. diff --git a/includes/migration/flutter-v10/flutter-web.mdx b/includes/migration/flutter-v10/flutter-web.mdx new file mode 100644 index 0000000000000..11fcb09c2b33e --- /dev/null +++ b/includes/migration/flutter-v10/flutter-web.mdx @@ -0,0 +1,20 @@ +--- +id: flutter-web +title: "Flutter Web Uses JavaScript SDK v11" +phase: verify +category: behavior +severity: behavior-change +frameworks: all +platformCategory: all +order: 160 +--- + +Flutter web now bundles Sentry JavaScript SDK `11.0.0`. Browser sessions affected by unhandled errors are reported as `unhandled` rather than `crashed`. On web, `unhandled` is a terminal session status; native sessions treat unhandled Flutter errors as non-terminating. + +For errors captured through the Dart SDK, web sessions are updated only after error sampling. Errors dropped by `sampleRate` do not update web release health. + +Review web release-health dashboards and alerts that depend on crashed-session counts. This applies to unhandled errors captured from both Dart and JavaScript. + +Keep using Flutter's `options.sendDefaultPii`. The Flutter SDK maps it to JavaScript v11's `dataCollection` settings and preserves the previous JavaScript privacy baseline when `sendDefaultPii` is `false`. You do not need to replace the Flutter option with a JavaScript configuration object. + +If you initialize Sentry separately in JavaScript or call its APIs directly, also review the [JavaScript v10-to-v11 migration guide](/platforms/javascript/migration/v10-to-v11/). diff --git a/includes/migration/flutter-v10/http-error-capture.mdx b/includes/migration/flutter-v10/http-error-capture.mdx new file mode 100644 index 0000000000000..3e5275810c74c --- /dev/null +++ b/includes/migration/flutter-v10/http-error-capture.mdx @@ -0,0 +1,20 @@ +--- +id: http-error-capture +title: "HTTP Error Capture and Grouping" +phase: verify +category: behavior +severity: behavior-change +frameworks: all +platformCategory: all +order: 130 +--- + +HTTP error types, messages, and capture rules have changed. Review HTTP error filters, grouping rules, and alert volumes after upgrading: + +- `SentryHttpClient` reports failed HTTP status responses with the stable exception type `SentryHttpClientError`, and its message no longer starts with `Exception:`. +- `sentry_dio` uses the stable exception type `DioException`, including in obfuscated builds. Default error messages become `HTTP Client Error with status code: ` or `HTTP Client Error: `. Custom Dio string builders are preserved. +- `sentry_dio` now captures connection failures without a status code, such as timeouts, DNS failures, and certificate errors, when failed-request capture is enabled and the target matches. Caller-initiated cancellations without a status code are excluded. +- `sentry_dio` matches `failedRequestTargets` against the resolved full URL instead of the relative request path. Update patterns that assumed a path-only value. +- `sentry_supabase` uses the stable exception type `SentrySupabaseClientError`, and its message no longer starts with `Exception:`. + +Dio events now include response status and metadata in `event.contexts.response`; response bodies remain on `hint.response`. Failed-request capture through `SentryHttpClient` and Dio excludes requests to the configured DSN host to prevent recursive error reporting. diff --git a/includes/migration/flutter-v10/logs-and-metrics.mdx b/includes/migration/flutter-v10/logs-and-metrics.mdx new file mode 100644 index 0000000000000..97cb8a501e573 --- /dev/null +++ b/includes/migration/flutter-v10/logs-and-metrics.mdx @@ -0,0 +1,26 @@ +--- +id: logs-and-metrics +title: "Logs and Metrics Are Always Enabled" +phase: code-changes +category: removed-api +severity: action-required +frameworks: all +platformCategory: all +order: 50 +--- + +Remove assignments to `options.enableLogs` and `options.enableMetrics`. These options no longer exist. Calls to `Sentry.logger` and `Sentry.metrics` send telemetry without an enable flag. To filter logs or metrics, return `null` from `beforeSendLog` or `beforeSendMetric`. + +If you use `sentry_logging`, adding `LoggingIntegration` now also forwards records as Sentry logs, with a default minimum level of `Level.INFO`. If you previously disabled logs and want to keep only breadcrumbs and error events, set `minSentryLogLevel` to `Level.OFF`: + +```dart +import 'package:logging/logging.dart'; +import 'package:sentry_logging/sentry_logging.dart'; + +// Inside your SentryFlutter.init options callback: +options.addIntegration(LoggingIntegration(minSentryLogLevel: Level.OFF)); +``` + +The `Sentry.logger` and `Sentry.logger.fmt` methods now return `void`. Remove `await` from log calls. Update custom implementations of `SentryLogger` and `SentryLoggerFormatter` to match these return types. Custom `Hub.captureLog` overrides now return `Future` instead of `FutureOr`. + +Replace `SentryLogAttribute` with `SentryAttribute`, using the same typed factories such as `SentryAttribute.string(...)`. If you construct `SentryLog` directly, its `traceId` is now a required, non-nullable `SentryId`. Prefer `Sentry.logger` for application logging so the SDK supplies trace context. diff --git a/includes/migration/flutter-v10/minimum-versions.mdx b/includes/migration/flutter-v10/minimum-versions.mdx new file mode 100644 index 0000000000000..4e428423dd1fe --- /dev/null +++ b/includes/migration/flutter-v10/minimum-versions.mdx @@ -0,0 +1,20 @@ +--- +id: minimum-versions +title: "Minimum Supported Versions" +phase: prerequisites +category: version-support +severity: action-required +frameworks: all +platformCategory: all +order: 10 +--- + +Update your local development environment and CI before upgrading: + +| Dependency | v9 Minimum | v10 Minimum | +| ----------------------- | ---------- | ----------- | +| Dart | `3.5.0` | `3.12.0` | +| Flutter | `3.24.0` | `3.44.0` | +| Android API (`minSdk`) | `21` | `26` | +| iOS deployment target | `12.0` | `15.0` | +| macOS deployment target | `10.14` | `12.0` | diff --git a/includes/migration/flutter-v10/native-failed-requests.mdx b/includes/migration/flutter-v10/native-failed-requests.mdx new file mode 100644 index 0000000000000..b8dcd6e66e555 --- /dev/null +++ b/includes/migration/flutter-v10/native-failed-requests.mdx @@ -0,0 +1,20 @@ +--- +id: native-failed-requests +title: "Native Failed Requests Are Opt-In" +phase: code-changes +category: behavior +severity: action-required +frameworks: all +platformCategory: all +order: 80 +--- + +On iOS and macOS, `captureNativeFailedRequests` now defaults to `false` and no longer accepts `null`. It no longer falls back to `captureFailedRequests`. + +To continue capturing failed HTTP requests made by the native SDK's network instrumentation, opt in explicitly: + +```dart +options.captureNativeFailedRequests = true; +``` + +Dart-side failed request capture through `SentryHttpClient` and `sentry_dio` is still controlled independently by `captureFailedRequests`, which defaults to `true`. diff --git a/includes/migration/flutter-v10/removed-apis.mdx b/includes/migration/flutter-v10/removed-apis.mdx new file mode 100644 index 0000000000000..f5e9272b5d1c4 --- /dev/null +++ b/includes/migration/flutter-v10/removed-apis.mdx @@ -0,0 +1,36 @@ +--- +id: removed-apis +title: "Profiling and Deprecated APIs Are Removed" +phase: code-changes +category: removed-api +severity: action-required +frameworks: all +platformCategory: all +order: 90 +--- + +Remove `options.profilesSampleRate`. The Flutter SDK no longer captures profiles, and there is no replacement profiling option in v10. + +Update code that uses the following APIs: + +| Removed or Internal API | Migration | +| --------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `SentryFeedbackWidget` | Use `SentryFeedbackForm`. | +| Deprecated `copyWith(...)` methods on SDK data classes | Assign mutable fields directly. | +| Deprecated protocol `clone()` methods | Stop calling them; remaining SDK clone helpers are internal. Construct a new object explicitly if you need an independent copy. | +| `PerformanceCollector`, `PerformanceContinuousCollector`, `options.performanceCollectors`, `options.addPerformanceCollector(...)` | Remove custom collector registration. These deprecated APIs have been removed. | +| `BindingWrapper`, `options.bindingUtils` | Remove custom binding-wrapper overrides. `SentryWidgetsFlutterBinding` remains public. | +| `options.log`, `SdkLogCallback` | Remove usage of these diagnostic logging APIs. Use `options.debug` and `options.diagnosticLevel` to configure SDK diagnostics, or `Sentry.logger` for application logs. | + +The `SentryEventLike` mixin has also been removed with `copyWith`. Use `SentryEvent` as the common type for events and transactions (`SentryTransaction` extends `SentryEvent`). + +`Sentry.clone()`, `Hub.clone()`, and `Scope.clone()` are now internal APIs. Remove direct calls to them. For capture-specific context, use the capture method's `withScope` callback. + +For example, mutate events in `beforeSend` instead of using `copyWith`: + +```dart +options.beforeSend = (event, hint) { + event.release = 'my-release'; + return event; +}; +``` diff --git a/includes/migration/flutter-v10/sensitive-content.mdx b/includes/migration/flutter-v10/sensitive-content.mdx new file mode 100644 index 0000000000000..7eb3541e57ee4 --- /dev/null +++ b/includes/migration/flutter-v10/sensitive-content.mdx @@ -0,0 +1,12 @@ +--- +id: sensitive-content +title: "Sensitive Content Masking" +phase: verify +category: behavior +severity: behavior-change +frameworks: all +platformCategory: all +order: 140 +--- + +The SDK now masks Flutter `SensitiveContent` widgets marked `sensitive` or `autoSensitive` by default in Session Replay and screenshots. A `notSensitive` widget still goes through the other masking rules; it does not automatically unmask its contents. Review captured output if your app uses these widgets with custom privacy rules. diff --git a/includes/migration/flutter-v10/sentry-packages.mdx b/includes/migration/flutter-v10/sentry-packages.mdx new file mode 100644 index 0000000000000..ab9fbb0bc3896 --- /dev/null +++ b/includes/migration/flutter-v10/sentry-packages.mdx @@ -0,0 +1,27 @@ +--- +id: sentry-packages +title: "Update Sentry Packages" +phase: packages +category: package +severity: action-required +frameworks: all +platformCategory: all +order: 20 +--- + +Update any directly declared Sentry integration packages, such as `sentry_dio` or `sentry_logging`, to the matching v10 release as well. Update `sentry_flutter` to v10: + +```yaml {filename:pubspec.yaml} +dependencies: + sentry_flutter: ^10.0.0 +``` + +The integration dependency constraints also change: + +| Package | Required Dependency | +| ---------------- | ------------------------------------------------------- | +| `sentry_flutter` | `jni >=1.0.0 <1.1.0` (previously `0.14.2`) | +| `sentry_dio` | `dio ^5.8.0` (previously `^5.2.0`) | +| `sentry_link` | `gql_link >=0.5.1 <2.0.0` (previously `>=0.5.0 <2.0.0`) | + +Update conflicting direct dependencies or dependency overrides before running `flutter pub get`. diff --git a/includes/migration/flutter-v10/span-attributes.mdx b/includes/migration/flutter-v10/span-attributes.mdx new file mode 100644 index 0000000000000..2f2b42561e34a --- /dev/null +++ b/includes/migration/flutter-v10/span-attributes.mdx @@ -0,0 +1,31 @@ +--- +id: span-attributes +title: "Span Attribute Names Have Changed" +phase: code-changes +category: rename +severity: action-required +frameworks: all +platformCategory: all +order: 110 +--- + +Update queries, dashboards, alerts, and callbacks that read the old attribute keys: + +| Previous Attribute | v10 Attribute | Affected Instrumentation | +| ------------------------------ | ------------------------- | ------------------------------ | +| `db.system` | `db.system.name` | Database integrations | +| `db.name` | `db.namespace` | Drift, Hive, Isar, and sqflite | +| `db.operation` | `db.operation.name` | Supabase | +| `db.table` | `db.collection.name` | Supabase | +| `db.collection` | `db.collection.name` | Isar | +| `url` | `url.full` | HTTP spans | +| `http.response_content_length` | `http.response.body.size` | HTTP spans | +| `app_start_type` | `app.vitals.start.type` | App start | + +Drift's database system value changes from `db.sqlite` to `sqlite`. Supabase replaces the raw filter list under `db.query` and the SQL under `db.sql.query` with a parameterized SQL statement under `db.query.text`, and adds `db.query.summary`. The SQL statement uses placeholders for values and is emitted regardless of `sendDefaultPii`. + +The `sentry_file` integration no longer emits `file.async`. `SentrySpanData` has been removed. If you referenced this internal class, use the attribute strings listed above in application code. `SemanticAttributesConstants` and `ProposedSemanticAttributes` are also internal APIs. + +For custom code that used internal constants, the old `appApp*`, `osBuild`, `deviceConnectionType`, `deviceLocale`, and `deviceTimezone` constants have been removed. Use the corresponding attribute strings: `app.build`, `app.identifier`, `app.name`, `app.start_time`, `app.version`, `os.build_id`, `network.connection.type`, `culture.locale`, and `culture.timezone`. The `appVitalsStartValue` constant moves to `ProposedSemanticAttributes`; its wire key remains `app.vitals.start.value`. + +Database breadcrumb data also uses `db.system.name` and `db.namespace` instead of `db.system` and `db.name` in Hive, Isar, and sqflite. Update `beforeBreadcrumb` callbacks that read these fields. diff --git a/includes/migration/flutter-v10/stream-default.mdx b/includes/migration/flutter-v10/stream-default.mdx new file mode 100644 index 0000000000000..acda121f9c388 --- /dev/null +++ b/includes/migration/flutter-v10/stream-default.mdx @@ -0,0 +1,18 @@ +--- +id: stream-default +title: "Stream Mode Is the Default" +phase: code-changes +category: behavior +severity: action-required +frameworks: all +platformCategory: all +order: 65 +--- + +In v10, `SentryTraceLifecycle.stream` is the default. The SDK sends spans in batches as they finish instead of waiting for a transaction to end. Tracing still requires `tracesSampleRate` or `tracesSampler` to be configured. + +Automatic instrumentation uses the streaming span APIs. If you use custom instrumentation, migrate `Sentry.startTransaction` and `ISentrySpan.startChild` to the streaming span APIs. Transaction APIs are ignored in stream mode. Move `beforeSendTransaction` and `ignoreTransactions` logic to `beforeSendSpan` and `ignoreSpans`, and update custom samplers to read `samplingContext.spanContext`. + +Follow the stream mode migration guide and use the two-parameter `beforeSendSpan` signature in [Telemetry Callbacks Receive a Hint](/platforms/dart/guides/flutter/migration/v9-to-v10/#telemetry-callbacks-receive-a-hint). + +To retain transaction mode while migrating, explicitly set `options.traceLifecycle = SentryTraceLifecycle.static` during SDK initialization. Removing this option uses stream mode in v10. diff --git a/includes/migration/flutter-v10/telemetry-callbacks.mdx b/includes/migration/flutter-v10/telemetry-callbacks.mdx new file mode 100644 index 0000000000000..4cb092474a136 --- /dev/null +++ b/includes/migration/flutter-v10/telemetry-callbacks.mdx @@ -0,0 +1,28 @@ +--- +id: telemetry-callbacks +title: "Telemetry Callbacks Receive a Hint" +phase: code-changes +category: type +severity: action-required +frameworks: all +platformCategory: all +order: 60 +--- + +Add a second `Hint` parameter to `beforeSendLog`, `beforeSendMetric`, and `beforeSendSpan`, even if you don't use it: + +```dart +options.beforeSendLog = (log, hint) { + return log; +}; + +options.beforeSendMetric = (metric, hint) { + return metric; +}; + +options.beforeSendSpan = (span, hint) { + span.removeAttribute('private.attribute'); +}; +``` + +`beforeSendSpan` modifies the span in place. It cannot drop spans by returning `null`; use `options.ignoreSpans` to filter spans in stream mode. diff --git a/includes/migration/javascript-v11/cloudflare-dedupe-across-requests.mdx b/includes/migration/javascript-v11/cloudflare-dedupe-across-requests.mdx index 99a9d4d8f215e..749b8e201f49d 100644 --- a/includes/migration/javascript-v11/cloudflare-dedupe-across-requests.mdx +++ b/includes/migration/javascript-v11/cloudflare-dedupe-across-requests.mdx @@ -9,7 +9,9 @@ platformCategory: serverless order: 130 --- -In v10, the SDK created a new client for every request, so the Dedupe integration never compared errors from different requests. The SDK now reuses one client for all requests and Durable Object calls in an isolate, so Dedupe works like in the other SDKs: it drops an error that has the same type, message, stack trace, and fingerprint as the error captured directly before it in that isolate, even when a different request threw it. An error that many requests throw in a row can have a lower event count in Sentry than before. +In v10, the SDK created a new client for every request, so the Dedupe integration never compared errors from different requests. + +The SDK now reuses one client for all requests and Durable Object calls in an isolate, so Dedupe works like in the other SDKs: it drops an error that has the same type, message, stack trace, and fingerprint as the error captured directly before it in that isolate, even when a different request threw it. An error that many requests throw in a row can have a lower event count in Sentry than before. To send every occurrence, remove the integration: diff --git a/scripts/validate-migration-items.mjs b/scripts/validate-migration-items.mjs index 93165c3e371bf..3cd861015f32e 100644 --- a/scripts/validate-migration-items.mjs +++ b/scripts/validate-migration-items.mjs @@ -1,15 +1,14 @@ /** - * Validates the v11 migration item collection in `includes/migration/javascript-v11/`. + * Validates migration item collections in `includes/migration/`. * * Each item is a standalone MDX file with tagged frontmatter, rendered by the - * interactive migration guide at - * `docs/platforms/javascript/common/migration/v10-to-v11/interactive.mdx`. + * corresponding interactive migration guide. * Frontmatter controls platform filtering, reading order and item anchors. * Validate it so mistakes cannot silently drop items from the guide. * - * `loadItems` is called from `migrationGuide.spec.ts`, so `pnpm test` fails on - * invalid frontmatter. Run `pnpm migration-items` for the breakdown by phase, - * severity and category as well. + * `loadItems` is called from `validate-migration-items.spec.ts`, so `pnpm test` + * fails on invalid frontmatter. Run `pnpm migration-items` (JavaScript) or + * `pnpm migration-items flutter-v10` for the phase, severity and category counts. */ import {readdirSync, readFileSync} from 'fs'; import path from 'path'; @@ -17,8 +16,10 @@ import {pathToFileURL} from 'url'; import matter from 'gray-matter'; -const ITEMS_DIR = 'includes/migration/javascript-v11'; -const GUIDES_DIR = 'docs/platforms/javascript/guides'; +const COLLECTIONS = { + 'javascript-v11': 'docs/platforms/javascript/guides', + 'flutter-v10': 'docs/platforms/dart/guides', +}; /** The change itself, stated once. Anything longer is an explanation. */ const MAX_FIRST_PARAGRAPH = 400; @@ -36,9 +37,12 @@ export const SEVERITIES = ['action-required', 'behavior-change', 'informational' export const PLATFORM_CATEGORIES = ['browser', 'server', 'serverless', 'all']; /** Reads and validates every item. Returns `{items, errors}`. */ -export function loadItems(root = process.cwd()) { - const dir = path.join(root, ITEMS_DIR); - const guides = new Set(readdirSync(path.join(root, GUIDES_DIR))); +export function loadItems(root = process.cwd(), collection = 'javascript-v11') { + if (!Object.hasOwn(COLLECTIONS, collection)) { + throw new Error(`Unknown migration collection "${collection}"`); + } + const dir = path.join(root, 'includes/migration', collection); + const guides = new Set(readdirSync(path.join(root, COLLECTIONS[collection]))); const files = readdirSync(dir).filter(f => f.endsWith('.mdx')); const errors = []; @@ -134,7 +138,7 @@ export function loadItems(root = process.cwd()) { } function main() { - const {items, errors} = loadItems(); + const {items, errors} = loadItems(process.cwd(), process.argv[2]); const count = (list, predicate) => list.filter(predicate).length; const pad = (value, width) => String(value).padEnd(width); diff --git a/scripts/validate-migration-items.spec.ts b/scripts/validate-migration-items.spec.ts index d5112a9c5905f..29a1b65699434 100644 --- a/scripts/validate-migration-items.spec.ts +++ b/scripts/validate-migration-items.spec.ts @@ -3,6 +3,7 @@ import { copyFileSync, mkdirSync, mkdtempSync, + readFileSync, rmSync, symlinkSync, writeFileSync, @@ -12,6 +13,28 @@ import path from 'path'; import {pathToFileURL} from 'url'; import {afterEach, beforeEach, describe, expect, it} from 'vitest'; +import {loadItems} from './validate-migration-items.mjs'; + +describe('migration collections', () => { + it.each(['javascript-v11', 'flutter-v10'])('validates %s', collection => { + const {items, errors} = loadItems(process.cwd(), collection); + expect(items.length).toBeGreaterThan(0); + expect(errors).toEqual([]); + }); + + it('includes every Flutter checklist item exactly once in the full guide', () => { + const {items} = loadItems(process.cwd(), 'flutter-v10'); + const guide = readFileSync( + 'docs/platforms/dart/guides/flutter/migration/v9-to-v10/index.mdx', + 'utf8' + ); + const included = [...guide.matchAll(/name="migration\/flutter-v10\/([^"]+)"/g)] + .map(match => match[1]) + .sort(); + expect(included).toEqual(items.map(item => item.file.replace(/\.mdx$/, '')).sort()); + }); +}); + describe('migration item validator CLI', () => { let root: string; diff --git a/src/components/migrationGuide/client.tsx b/src/components/migrationGuide/client.tsx index 52ac68b3aa2ae..b73e1a5a7d3b7 100644 --- a/src/components/migrationGuide/client.tsx +++ b/src/components/migrationGuide/client.tsx @@ -16,20 +16,19 @@ type Props = { * divergence instead of failing. */ bodies: ItemBody[]; - /** Guide slug, used to scope checklist storage. */ - framework: string; + /** Scopes saved progress to a migration and platform or guide. */ + storageKey: string; items: MigrationItem[]; }; -export function MigrationGuideClient({items, bodies: renderedBodies, framework}: Props) { +export function MigrationGuideClient({items, bodies: renderedBodies, storageKey}: Props) { const bodies = useMemo( () => new Map(renderedBodies.map(({id, body}) => [id, body])), [renderedBodies] ); - // Checklist progress is scoped per framework: someone migrating a monorepo - // has separate checklists for their Next.js app and their Node service. - const storageKey = `sentry-v11-migration:${framework}`; + // The parent keys this component by storageKey, so changing migrations or + // frameworks starts a fresh hydration before any progress is written. const [checked, setChecked] = useState>(new Set()); const [hydrated, setHydrated] = useState(false); diff --git a/src/components/migrationGuide/index.spec.tsx b/src/components/migrationGuide/index.spec.tsx new file mode 100644 index 0000000000000..9c0fc31d03443 --- /dev/null +++ b/src/components/migrationGuide/index.spec.tsx @@ -0,0 +1,69 @@ +import {readFileSync} from 'fs'; +import matter from 'gray-matter'; +import React from 'react'; +import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'; + +const {context} = vi.hoisted(() => ({ + context: { + platform: undefined as {type: string; name: string; categories: string[]} | undefined, + }, +})); + +vi.mock('sentry-docs/docTree', () => ({ + getCurrentPlatformOrGuide: () => context.platform, +})); +vi.mock('sentry-docs/serverContext', () => ({ + serverContext: () => ({rootNode: {}, path: ''}), +})); +vi.mock('sentry-docs/mdx', () => ({ + getFileBySlugWithCache: (slug: string) => { + const {data, content} = matter(readFileSync(`${slug}.mdx`, 'utf8')); + return {frontMatter: data, mdxSource: content}; + }, +})); +vi.mock('sentry-docs/getMDXComponent', () => ({getMDXComponent: vi.fn()})); +vi.mock('sentry-docs/mdxComponents', () => ({mdxComponents: () => ({})})); +vi.mock('./client', () => ({MigrationGuideClient: () => null})); + +import {MigrationGuide} from './index'; + +describe('migration guide collection selection', () => { + afterEach(() => vi.unstubAllGlobals()); + + beforeEach(() => { + vi.stubGlobal('React', React); + context.platform = undefined; + }); + + it('preserves the default JavaScript collection and existing saved progress key', async () => { + const result = await MigrationGuide({}); + expect(result.props.storageKey).toBe('sentry-v11-migration:javascript'); + expect(result.props.items.some(item => item.id === 'node-version')).toBe(true); + expect( + result.props.items.some(item => item.id === 'apple-swift-package-manager') + ).toBe(false); + }); + + it('loads all Flutter steps with independent saved progress', async () => { + context.platform = {type: 'guide', name: 'flutter', categories: []}; + const result = await MigrationGuide({migration: 'flutter-v10'}); + expect(result.props.storageKey).toBe('sentry-flutter-v10-migration:flutter'); + expect(result.key).toBe(result.props.storageKey); + expect(result.props.items).toHaveLength(18); + expect(result.props.items.some(item => item.id === 'stream-default')).toBe(true); + expect( + result.props.items.some(item => item.id === 'apple-swift-package-manager') + ).toBe(true); + expect(result.props.items.some(item => item.id === 'node-version')).toBe(false); + expect(result.props.bodies.map(item => item.id)).toEqual( + result.props.items.map(item => item.id) + ); + }); + + it('keeps JavaScript progress scoped to the selected framework', async () => { + context.platform = {type: 'guide', name: 'nextjs', categories: ['browser', 'server']}; + const result = await MigrationGuide({}); + expect(result.props.storageKey).toBe('sentry-v11-migration:nextjs'); + expect(result.key).toBe(result.props.storageKey); + }); +}); diff --git a/src/components/migrationGuide/index.tsx b/src/components/migrationGuide/index.tsx index 4a2c66bdfce8d..b620a26031439 100644 --- a/src/components/migrationGuide/index.tsx +++ b/src/components/migrationGuide/index.tsx @@ -10,7 +10,10 @@ import {PlatformCategory} from 'sentry-docs/types'; import {MigrationGuideClient} from './client'; import {compareItems, MigrationItem, PhaseId} from './constants'; -const ITEMS_DIR = 'includes/migration/javascript-v11'; +const MIGRATIONS = { + 'javascript-v11': {storagePrefix: 'sentry-v11-migration', platform: 'javascript'}, + 'flutter-v10': {storagePrefix: 'sentry-flutter-v10-migration', platform: 'dart'}, +} as const; // Share the MDX component map across item bodies and platform pages. const components = mdxComponents(); @@ -30,7 +33,13 @@ function ItemBody({mdxSource}: {mdxSource: string}) { } /** Load the migration checklist for the platform or guide selected in the URL. */ -export async function MigrationGuide() { +export async function MigrationGuide({ + migration = 'javascript-v11', +}: { + migration?: keyof typeof MIGRATIONS; +}) { + const {storagePrefix, platform} = MIGRATIONS[migration]; + const itemsDir = `includes/migration/${migration}`; const {rootNode, path: urlPath} = serverContext(); const platformOrGuide = getCurrentPlatformOrGuide(rootNode, urlPath); @@ -40,12 +49,12 @@ export async function MigrationGuide() { const framework = platformOrGuide?.type === 'guide' ? platformOrGuide.name : undefined; const categories: PlatformCategory[] = platformOrGuide?.categories ?? []; - const slugs = readdirSync(path.join(process.cwd(), ITEMS_DIR)) + const slugs = readdirSync(path.join(process.cwd(), itemsDir)) .filter(file => file.endsWith('.mdx')) .map(file => file.replace(/\.mdx$/, '')); const docs = await Promise.all( - slugs.map(slug => getFileBySlugWithCache(`${ITEMS_DIR}/${slug}`)) + slugs.map(slug => getFileBySlugWithCache(`${itemsDir}/${slug}`)) ); const rendered = docs @@ -89,14 +98,17 @@ export async function MigrationGuide() { }) => item ); + const storageKey = `${storagePrefix}:${framework ?? platform}`; + return ( ({ id: item.id, body: , }))} - framework={framework ?? 'javascript'} + storageKey={storageKey} /> ); }