From 05b2855809ad63c38109dfc4f83a13a65de83e5d Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 13 Sep 2023 17:37:10 +0200 Subject: [PATCH] feat(sveltekit): Add telemetry collection --- src/sveltekit/sdk-setup.ts | 21 +++++-- src/sveltekit/sveltekit-wizard.ts | 94 +++++++++++++++++++++++-------- src/sveltekit/utils.ts | 34 +++++++++++ src/utils/clack-utils.ts | 16 ++++-- 4 files changed, 130 insertions(+), 35 deletions(-) create mode 100644 src/sveltekit/utils.ts diff --git a/src/sveltekit/sdk-setup.ts b/src/sveltekit/sdk-setup.ts index 09613040..5880baee 100644 --- a/src/sveltekit/sdk-setup.ts +++ b/src/sveltekit/sdk-setup.ts @@ -4,6 +4,8 @@ import * as path from 'path'; import * as url from 'url'; import chalk from 'chalk'; +import * as Sentry from '@sentry/node'; + // @ts-ignore - clack is ESM and TS complains about that. It works though import clack from '@clack/prompts'; // @ts-ignore - magicast is ESM and TS complains about that. It works though @@ -59,19 +61,25 @@ export async function createOrMergeSvelteKitFiles( const { dsn } = projectInfo; + Sentry.setTag( + 'server-hooks-file-strategy', + originalClientHooksFile ? 'merge' : 'create', + ); if (!originalClientHooksFile) { clack.log.info('No client hooks file found, creating a new one.'); await createNewHooksFile(`${clientHooksPath}.${fileEnding}`, 'client', dsn); + } else { + await mergeHooksFile(originalClientHooksFile, 'client', dsn); } + + Sentry.setTag( + 'client-hooks-file-strategy', + originalServerHooksFile ? 'merge' : 'create', + ); if (!originalServerHooksFile) { clack.log.info('No server hooks file found, creating a new one.'); await createNewHooksFile(`${serverHooksPath}.${fileEnding}`, 'server', dsn); - } - - if (originalClientHooksFile) { - await mergeHooksFile(originalClientHooksFile, 'client', dsn); - } - if (originalServerHooksFile) { + } else { await mergeHooksFile(originalServerHooksFile, 'server', dsn); } @@ -436,6 +444,7 @@ Skipping adding Sentry functionality to.`, viteConfigPath, getViteConfigCodeSnippet(org, project, selfHosted, url), ); + Sentry.captureException('Sveltekit Vite Config Modification Fail'); } } diff --git a/src/sveltekit/sveltekit-wizard.ts b/src/sveltekit/sveltekit-wizard.ts index e9f0832c..0749484f 100644 --- a/src/sveltekit/sveltekit-wizard.ts +++ b/src/sveltekit/sveltekit-wizard.ts @@ -1,7 +1,9 @@ // @ts-ignore - clack is ESM and TS complains about that. It works though -import clack from '@clack/prompts'; +import * as clack from '@clack/prompts'; import chalk from 'chalk'; +import * as Sentry from '@sentry/node'; + import { abort, addSentryCliConfig, @@ -12,46 +14,82 @@ import { installPackage, printWelcome, } from '../utils/clack-utils'; -import { hasPackageInstalled } from '../utils/package-json'; +import { getPackageVersion, hasPackageInstalled } from '../utils/package-json'; import { WizardOptions } from '../utils/types'; import { createExamplePage } from './sdk-example'; import { createOrMergeSvelteKitFiles, loadSvelteConfig } from './sdk-setup'; +import { traceStep, withTelemetry } from '../telemetry'; +import { getKitVersionBucket, getSvelteVersionBucket } from './utils'; export async function runSvelteKitWizard( options: WizardOptions, +): Promise { + return withTelemetry( + { + enabled: options.telemetryEnabled, + integration: 'sveltekit', + }, + () => runSvelteKitWizardWithTelemetry(options), + ); +} + +export async function runSvelteKitWizardWithTelemetry( + options: WizardOptions, ): Promise { printWelcome({ wizardName: 'Sentry SvelteKit Wizard', promoCode: options.promoCode, + telemetryEnabled: options.telemetryEnabled, }); - await confirmContinueEvenThoughNoGitRepo(); + await traceStep('detect-git', confirmContinueEvenThoughNoGitRepo); const packageJson = await getPackageDotJson(); - await ensurePackageIsInstalled(packageJson, '@sveltejs/kit', 'Sveltekit'); + await traceStep('detect-framework-version', () => + ensurePackageIsInstalled(packageJson, '@sveltejs/kit', 'Sveltekit'), + ); + + Sentry.setTag( + 'sveltekit-version', + getKitVersionBucket(getPackageVersion('@sveltejs/kit', packageJson)), + ); + Sentry.setTag( + 'svelte-version', + getSvelteVersionBucket(getPackageVersion('svelte', packageJson)), + ); const { selectedProject, selfHosted, sentryUrl, authToken } = await getOrAskForProjectData(options, 'javascript-sveltekit'); - await installPackage({ - packageName: '@sentry/sveltekit', - alreadyInstalled: hasPackageInstalled('@sentry/sveltekit', packageJson), - }); + const sdkAlreadyInstalled = hasPackageInstalled( + '@sentry/sveltekit', + packageJson, + ); + Sentry.setTag('sdk-already-installed', sdkAlreadyInstalled); + + await traceStep('install-sdk', () => + installPackage({ + packageName: '@sentry/sveltekit', + alreadyInstalled: sdkAlreadyInstalled, + }), + ); - await addSentryCliConfig(authToken); + await traceStep('add-cli-config', () => addSentryCliConfig(authToken)); - const svelteConfig = await loadSvelteConfig(); + const svelteConfig = await traceStep('load-svelte-config', loadSvelteConfig); try { - await createOrMergeSvelteKitFiles( - { - dsn: selectedProject.keys[0].dsn.public, - org: selectedProject.organization.slug, - project: selectedProject.slug, - selfHosted, - url: sentryUrl, - }, - svelteConfig, + await traceStep('configure-sdk', () => + createOrMergeSvelteKitFiles( + { + dsn: selectedProject.keys[0].dsn.public, + org: selectedProject.organization.slug, + project: selectedProject.slug, + selfHosted, + url: sentryUrl, + }, + svelteConfig, + ), ); } catch (e: unknown) { clack.log.error('Error while setting up the SvelteKit SDK:'); @@ -64,17 +102,20 @@ export async function runSvelteKitWizard( : 'Unknown error', ), ); + Sentry.captureException('Error while setting up the SvelteKit SDK'); await abort('Exiting Wizard'); return; } try { - await createExamplePage(svelteConfig, { - selfHosted, - url: sentryUrl, - orgSlug: selectedProject.organization.slug, - projectId: selectedProject.id, - }); + await traceStep('create-example-page', () => + createExamplePage(svelteConfig, { + selfHosted, + url: sentryUrl, + orgSlug: selectedProject.organization.slug, + projectId: selectedProject.id, + }), + ); } catch (e: unknown) { clack.log.error('Error while creating an example page to test Sentry:'); clack.log.info( @@ -86,6 +127,9 @@ export async function runSvelteKitWizard( : 'Unknown error', ), ); + Sentry.captureException( + 'Error while creating an example Svelte page to test Sentry', + ); await abort('Exiting Wizard'); return; } diff --git a/src/sveltekit/utils.ts b/src/sveltekit/utils.ts new file mode 100644 index 00000000..2c8a71b0 --- /dev/null +++ b/src/sveltekit/utils.ts @@ -0,0 +1,34 @@ +import { lt } from 'semver'; + +export function getKitVersionBucket(version: string | undefined): string { + if (!version) { + return 'none'; + } + if (lt(version, '1.0.0')) { + return '0.x'; + } else if (lt(version, '1.24.0')) { + return '>=1.0.0 <1.24.0'; + } else { + // This is the version when the client-side invalidation fix was released + // https://github.com/sveltejs/kit/releases/tag/%40sveltejs%2Fkit%401.24.0 + // https://github.com/sveltejs/kit/pull/10576 + return '>=1.24.0'; + } +} + +export function getSvelteVersionBucket(version: string | undefined): string { + if (!version) { + return 'none'; + } + if (lt(version, '3.0.0')) { + return '<3.0.0'; + } + if (lt(version, '4.0.0')) { + return '3.x'; + } + if (lt(version, '5.0.0')) { + return '4.x'; + } + // Svelte 5 isn't released yet but it's being worked on + return '>=5.0.0'; +} diff --git a/src/utils/clack-utils.ts b/src/utils/clack-utils.ts index aaad18cb..3c7e47b5 100644 --- a/src/utils/clack-utils.ts +++ b/src/utils/clack-utils.ts @@ -125,18 +125,23 @@ export function printWelcome(options: { let welcomeText = options.message || - 'This Wizard will help you set up Sentry for your application.\nThank you for using Sentry :)'; + `The ${options.wizardName} will help you set up Sentry for your application.\nThank you for using Sentry :)`; if (options.promoCode) { - welcomeText += `\n\nUsing promo-code: ${options.promoCode}`; + welcomeText = `${welcomeText}\n\nUsing promo-code: ${options.promoCode}`; } if (wizardPackage.version) { - welcomeText += `\n\nVersion: ${wizardPackage.version}`; + welcomeText = `${welcomeText}\n\nVersion: ${wizardPackage.version}`; } if (options.telemetryEnabled) { - welcomeText += `\n\nYou are using the Sentry Wizard with telemetry enabled. This helps us improve the Wizard.\nYou can disable it at any time by running \`sentry-wizard --disable-telemetry\`.`; + welcomeText = `${welcomeText} + +This wizard sends telemetry data and crash reports to Sentry. This helps us improve the Wizard. +You can turn this off at any time by running ${chalk.cyanBright( + 'sentry-wizard --disable-telemetry', + )}.`; } clack.note(welcomeText); @@ -419,6 +424,7 @@ export async function ensurePackageIsInstalled( packageName: string, ) { if (!hasPackageInstalled(packageId, packageJson)) { + Sentry.setTag('package-installed', false); const continueWithoutPackage = await abortIfCancelled( clack.confirm({ message: `${packageName} does not seem to be installed. Do you still want to continue?`, @@ -429,6 +435,8 @@ export async function ensurePackageIsInstalled( if (!continueWithoutPackage) { await abort(undefined, 0); } + } else { + Sentry.setTag('package-installed', true); } }