Skip to content

feat(onboarding): Add dart onboarding and revamp flutter logs onboarding #97432

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions static/app/components/onboarding/productSelection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export const platformProductAvailability = {
ProductSolution.SESSION_REPLAY,
ProductSolution.LOGS,
],
dart: [ProductSolution.PERFORMANCE_MONITORING, ProductSolution.LOGS],
kotlin: [ProductSolution.PERFORMANCE_MONITORING],
go: [ProductSolution.PERFORMANCE_MONITORING, ProductSolution.LOGS],
'go-echo': [ProductSolution.PERFORMANCE_MONITORING, ProductSolution.LOGS],
Expand Down
1 change: 1 addition & 0 deletions static/app/components/platformPicker.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ describe('PlatformPicker', function () {
'Angular',
'Astro',
'Browser JavaScript',
'Dart',
'Ember',
'Flutter',
'Gatsby',
Expand Down
4 changes: 4 additions & 0 deletions static/app/data/platformPickerCategories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const popularPlatformCategories: Set<PlatformKey> = new Set([
]);

const browser: Set<PlatformKey> = new Set([
'dart',
'flutter',
'javascript',
'javascript-angular',
Expand All @@ -58,6 +59,7 @@ const browser: Set<PlatformKey> = new Set([

const server: Set<PlatformKey> = new Set([
'bun',
'dart',
'deno',
'dotnet',
'dotnet-aspnet',
Expand Down Expand Up @@ -122,6 +124,7 @@ const mobile: Set<PlatformKey> = new Set([
'cordova',
'dotnet-maui',
'dotnet-xamarin',
'dart',
'flutter',
'ionic',
'react-native',
Expand All @@ -136,6 +139,7 @@ const desktop: Set<PlatformKey> = new Set([
'dotnet-winforms',
'dotnet-wpf',
'electron',
'dart',
'flutter',
'godot',
'java',
Expand Down
148 changes: 102 additions & 46 deletions static/app/gettingStartedDocs/dart/dart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {
Docs,
DocsParams,
OnboardingConfig,
OnboardingStep,
} from 'sentry/components/onboarding/gettingStartedDoc/types';
import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/types';
import {
Expand All @@ -16,7 +17,7 @@ type Params = DocsParams;

const getInstallSnippet = (params: Params) => `
dependencies:
sentry: ^${getPackageVersion(params, 'sentry.dart', '7.8.0')}`;
sentry: ^${getPackageVersion(params, 'sentry.dart', '9.6.0')}`;

const getConfigureSnippet = (params: Params) => `
import 'package:sentry/sentry.dart';
Expand All @@ -26,30 +27,48 @@ Future<void> main() async {
options.dsn = '${params.dsn.public}';
// Adds request headers and IP for users,
// visit: https://docs.sentry.io/platforms/dart/data-management/data-collected/ for more info
options.sendDefaultPii = true;
options.sendDefaultPii = true;${
params.isLogsSelected
? `
// Enable logs to be sent to Sentry
options.enableLogs = true;`
: ''
}${
params.isPerformanceSelected
? `
// Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing.
// We recommend adjusting this value in production.
options.tracesSampleRate = 1.0;
options.tracesSampleRate = 1.0;`
: ''
}
});

// or define SENTRY_DSN via Dart environment variable (--dart-define)
}`;

const getVerifySnippet = () => `
const getVerifySnippet = (params: Params) => {
const logsCode = params.isLogsSelected
? `
// Send a log before throwing the error
Sentry.logger.info("User triggered test error", {
'action': SentryLogAttribute.string('test_error'),
});`
Comment on lines +53 to +55
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential bug: The Dart onboarding guide generates code using `SentryLogAttribute` but falls back to an incompatible SDK version (`7.8.0`), causing compilation errors when the package registry is down.
  • Description: When the 'logs' feature is selected in the Dart onboarding documentation, the generated code uses SentryLogAttribute. However, if the Sentry package registry is unavailable, the configuration falls back to using sentry.dart version 7.8.0. This version predates the introduction of SentryLogAttribute (added in v9.0.0), which will cause a compilation error for the user. This breaks the onboarding flow under network or API failure conditions, preventing successful SDK integration.
  • Suggested fix: Update the fallback version for the sentry.dart package to at least 9.0.0 to ensure compatibility with the logging features. Alternatively, the code generation for logging should be conditional on the resolved SDK version supporting the feature.
    severity: 0.75, confidence: 0.98

Did we get this right? 👍 / 👎 to inform future reviews.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yoo good work seer! you right!

: '';
return `
import 'package:sentry/sentry.dart';

try {
try {${logsCode}
aMethodThatMightFail();
} catch (exception, stackTrace) {
await Sentry.captureException(
exception,
stackTrace: stackTrace,
);
}`;
};

const getPerfomanceSnippet = () => `
import 'package:sentry/sentry.dart';
import { getPackageVersion } from 'sentry/utils/gettingStartedDocs/getPackageVersion';

final transaction = Sentry.startTransaction('processOrderBatch()', 'task');

Expand Down Expand Up @@ -115,46 +134,83 @@ const onboarding: OnboardingConfig = {
],
},
],
verify: () => [
{
type: StepType.VERIFY,
description: t(
'Create an intentional error, so you can test that everything is working:'
),
configurations: [
{
language: 'dart',
code: getVerifySnippet(),
additionalInfo: tct(
"If you're new to Sentry, use the email alert to access your account and complete a product tour.[break] If you're an existing user and have disabled alerts, you won't receive this email.",
{
break: <br />,
}
),
},
],
},
{
title: t('Tracing'),
description: t(
"You'll be able to monitor the performance of your app using the SDK. For example:"
),
configurations: [
{
language: 'dart',
code: getPerfomanceSnippet(),
additionalInfo: tct(
'To learn more about the API and automatic instrumentations, check out the [perfDocs: performance documentation].',
{
perfDocs: (
<ExternalLink href="https://docs.sentry.io/platforms/dart/tracing/instrumentation/" />
),
}
),
},
],
},
],
verify: params => {
const steps: OnboardingStep[] = [
{
type: StepType.VERIFY,
description: t(
'Create an intentional error, so you can test that everything is working:'
),
configurations: [
{
language: 'dart',
code: getVerifySnippet(params),
additionalInfo: tct(
"If you're new to Sentry, use the email alert to access your account and complete a product tour.[break] If you're an existing user and have disabled alerts, you won't receive this email.",
{
break: <br />,
}
),
},
],
},
];

if (params.isPerformanceSelected) {
steps.push({
title: t('Tracing'),
description: t(
"You'll be able to monitor the performance of your app using the SDK. For example:"
),
configurations: [
{
language: 'dart',
code: getPerfomanceSnippet(),
additionalInfo: tct(
'To learn more about the API and automatic instrumentations, check out the [perfDocs: performance documentation].',
{
perfDocs: (
<ExternalLink href="https://docs.sentry.io/platforms/dart/tracing/instrumentation/" />
),
}
),
},
],
});
}

return steps;
},
nextSteps: params => {
const steps = [
{
name: t('Upload Debug Symbols'),
description: t(
'We offer a range of methods to provide Sentry with debug symbols so that you can see symbolicated stack traces and find the cause of your errors faster.'
),
link: 'https://docs.sentry.io/platforms/flutter/upload-debug/',
},
{
name: t('Connect your Git Repo'),
description: t(
'Adding our Git integrations will allow us determine suspect commits, comment on PRs, and create links directly to your source code from Sentry issues.'
),
link: 'https://docs.sentry.io/organization/integrations/source-code-mgmt/',
},
];

if (params.isLogsSelected) {
steps.push({
name: t('Structured Logs'),
description: t(
'Learn how to send structured logs to Sentry and view them alongside your errors.'
),
link: 'https://docs.sentry.io/platforms/dart/logs/',
});
}

return steps;
},
};

export const feedbackOnboardingCrashApiDart: OnboardingConfig = {
Expand Down
19 changes: 17 additions & 2 deletions static/app/gettingStartedDocs/flutter/flutter.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ describe('flutter onboarding docs', function () {
expect(
await screen.findByText(textWithMarkupMatcher(/options.tracesSampleRate/))
).toBeInTheDocument();
expect(
await screen.findByText(textWithMarkupMatcher(/options\.tracesSampleRate = 1\.0/))
).toBeInTheDocument();
expect(
await screen.findByText(
textWithMarkupMatcher(
Expand Down Expand Up @@ -103,6 +106,9 @@ describe('flutter onboarding docs', function () {
expect(
await screen.findByText(textWithMarkupMatcher(/options.profilesSampleRate/))
).toBeInTheDocument();
expect(
await screen.findByText(textWithMarkupMatcher(/options\.profilesSampleRate = 1\.0/))
).toBeInTheDocument();
expect(
await screen.findByText(
textWithMarkupMatcher(/Flutter Profiling alpha is available/)
Expand All @@ -128,10 +134,14 @@ describe('flutter onboarding docs', function () {
});

expect(
await screen.findByText(textWithMarkupMatcher(/options.replay.sessionSampleRate/))
await screen.findByText(
textWithMarkupMatcher(/options\.replay\.sessionSampleRate = 0\.1/)
)
).toBeInTheDocument();
expect(
await screen.findByText(textWithMarkupMatcher(/options.replay.onErrorSampleRate/))
await screen.findByText(
textWithMarkupMatcher(/options\.replay\.onErrorSampleRate = 1\.0/)
)
).toBeInTheDocument();
});

Expand All @@ -153,6 +163,11 @@ describe('flutter onboarding docs', function () {
await screen.findByText(textWithMarkupMatcher(/options\.enableLogs = true/))
).toBeInTheDocument();

// Should include a log call in the verify snippet
expect(
await screen.findByText(textWithMarkupMatcher(/Sentry\.logger\.info/))
).toBeInTheDocument();

// Should include logging next step
expect(await screen.findByText('Structured Logs')).toBeInTheDocument();
});
Expand Down
45 changes: 28 additions & 17 deletions static/app/gettingStartedDocs/flutter/flutter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const isAutoInstall = (params: Params) =>
params.platformOptions?.installationMode === InstallationMode.AUTO;

const getManualInstallSnippet = (params: Params) => {
const version = getPackageVersion(params, 'sentry.dart.flutter', '8.13.2');
const version = getPackageVersion(params, 'sentry.dart.flutter', '9.6.0');
return `dependencies:
sentry_flutter: ^${version}`;
};
Expand All @@ -64,6 +64,19 @@ Future<void> main() async {
// Adds request headers and IP for users,
// visit: https://docs.sentry.io/platforms/dart/data-management/data-collected/ for more info
options.sendDefaultPii = true;${
params.isLogsSelected
? `
// Enable logs to be sent to Sentry
options.enableLogs = true;`
: ''
}${
params.isReplaySelected
? `
// Set sessionSampleRate to 0.1 to capture 10% of sessions and onErrorSampleRate to 1.0 to capture 100% of errors.
options.replay.sessionSampleRate = 0.1;
options.replay.onErrorSampleRate = 1.0;`
: ''
}${
params.isPerformanceSelected
? `
// Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing.
Expand All @@ -77,19 +90,6 @@ Future<void> main() async {
// Setting to 1.0 will profile 100% of sampled transactions:
options.profilesSampleRate = 1.0;`
: ''
}${
params.isReplaySelected
? `
// Set sessionSampleRate to 0.1 to capture 10% of sessions and onErrorSampleRate to 1.0 to capture 100% of errors.
options.replay.sessionSampleRate = 0.1;
options.replay.onErrorSampleRate = 1.0;`
: ''
}${
params.isLogsSelected
? `
// Enable logs to be sent to Sentry
options.enableLogs = true;`
: ''
}
},
appRunner: () => runApp(
Expand All @@ -109,14 +109,25 @@ const configureAdditionalInfo = tct(
}
);

const getVerifySnippet = () => `
const getVerifySnippet = (params: Params) => {
const logsCode = params.isLogsSelected
? `
// Send a log before throwing the error
Sentry.logger.info("User triggered test error button", {
'action': SentryLogAttribute.string('test_error_button_click'),
});`
: '';
return `
import 'package:sentry/sentry.dart';

child: ElevatedButton(
onPressed: () {
onPressed: () {${logsCode}
throw StateError('This is test exception');
},
child: const Text('Verify Sentry Setup'),
)
`;
};

const getFeedbackConfigureSnippet = () => `
// The example uses the NavigatorState to present the widget. Adapt as needed to your navigation stack.
Expand Down Expand Up @@ -342,7 +353,7 @@ const onboarding: OnboardingConfig<PlatformOptions> = {
label: 'Dart',
value: 'dart',
language: 'dart',
code: getVerifySnippet(),
code: getVerifySnippet(params),
},
],
},
Expand Down
Loading