Skip to content

Commit

Permalink
feat(next): add sentry to payments-next
Browse files Browse the repository at this point in the history
Because:

- Be able to use Sentry in Next.js on both the client and the server

This commit:

- Add Sentry to payments-next using the installation wizard

Closes #FXA-9998
  • Loading branch information
StaberindeZA committed Sep 9, 2024
1 parent 3ac75ba commit 54fefd2
Show file tree
Hide file tree
Showing 24 changed files with 1,473 additions and 10 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,6 @@ tmp
# shared-cms
libs/shared/cms/src/__generated__/graphql.d.ts
libs/shared/cms/src/__generated__/graphql.js

# Sentry Config File
.env.sentry-build-plugin
13 changes: 13 additions & 0 deletions apps/payments/next/.env.development
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,18 @@ STATS_D_CONFIG__PREFIX=
CSP__ACCOUNTS_STATIC_CDN=https://accounts-static.cdn.mozilla.net
CSP__PAYPAL_API='https://www.sandbox.paypal.com'

# Sentry Config
SENTRY__SERVER_NAME=fxa-payments-next-server
SENTRY__AUTH_TOKEN=

# Other
CONTENT_SERVER_URL=http://localhost:3030

# Nextjs Public Environment Variables

# Sentry Config
NEXT_PUBLIC_SENTRY_DSN=
NEXT_PUBLIC_SENTRY_ENV=local
NEXT_PUBLIC_SENTRY_CLIENT_NAME=fxa-payments-next-client
NEXT_PUBLIC_SENTRY_SAMPLE_RATE=1
NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE=1
13 changes: 13 additions & 0 deletions apps/payments/next/.env.production
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,18 @@ STATS_D_CONFIG__PREFIX=
CSP__ACCOUNTS_STATIC_CDN=https://accounts-static.cdn.mozilla.net
CSP__PAYPAL_API='https://www.paypal.com'

# Sentry Config
SENTRY__SERVER_NAME=fxa-payments-next-server
SENTRY__AUTH_TOKEN=

# Other
CONTENT_SERVER_URL=https://accounts.firefox.com

# Nextjs Public Environment Variables

# Sentry Config
NEXT_PUBLIC_SENTRY_DSN=
NEXT_PUBLIC_SENTRY_ENV=prod
NEXT_PUBLIC_SENTRY_CLIENT_NAME=fxa-payments-next-client
NEXT_PUBLIC_SENTRY_SAMPLE_RATE=1
NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE=1
31 changes: 31 additions & 0 deletions apps/payments/next/app/global-error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use client';

import * as Sentry from '@sentry/nextjs';
import NextError from 'next/error';
import { useEffect } from 'react';

export default function GlobalError({
error,
}: {
error: Error & { digest?: string };
}) {
useEffect(() => {
Sentry.captureException(error);
}, [error]);

return (
<html>
<body>
{/* `NextError` is the default Next.js error page component. Its type
definition requires a `statusCode` prop. However, since the App Router
does not expose status codes for errors, we simply pass 0 to render a
generic error message. */}
<NextError statusCode={0} />
</body>
</html>
);
}
49 changes: 48 additions & 1 deletion apps/payments/next/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import 'reflect-metadata';
import 'server-only';
import { Type } from 'class-transformer';
import { IsString, ValidateNested, IsDefined, IsUrl } from 'class-validator';
import {
IsString,
ValidateNested,
IsDefined,
IsUrl,
IsNumber,
IsOptional,
} from 'class-validator';
import {
RootConfig as NestAppRootConfig,
validate,
Expand All @@ -20,6 +31,14 @@ class PaypalConfig {
clientId!: string;
}

class SentryServerConfig {
@IsString()
serverName!: string;

@IsString()
authToken!: string;
}

class AuthJSConfig {
@IsUrl({ require_tld: false })
issuerUrl!: string;
Expand Down Expand Up @@ -47,6 +66,11 @@ export class PaymentsNextConfig extends NestAppRootConfig {
@IsDefined()
csp!: CspConfig;

@Type(() => SentryServerConfig)
@ValidateNested()
@IsDefined()
sentry!: SentryServerConfig;

@IsString()
authSecret!: string;

Expand All @@ -55,6 +79,29 @@ export class PaymentsNextConfig extends NestAppRootConfig {

@IsUrl({ require_tld: false })
contentServerUrl!: string;

/**
* Nextjs Public Environment Variables
*/

/**
* Sentry Config
*/
@IsOptional()
@IsString()
nextPublicSentryDsn?: string;

@IsString()
nextPublicSentryEnv!: string;

@IsString()
nextPublicSentryClientName!: string;

@IsNumber()
nextPublicSentrySampleRate!: number;

@IsNumber()
nextPublicSentryTracesSampleRate!: number;
}

export const config = validate(process.env, PaymentsNextConfig);
5 changes: 5 additions & 0 deletions apps/payments/next/instrumentation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
await import('./sentry.server.config');
const { getApp } = await import('@fxa/payments/ui/server');

await getApp().initialize();
Expand Down
48 changes: 48 additions & 0 deletions apps/payments/next/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

// eslint-disable-next-line @typescript-eslint/no-var-requires
const { composePlugins, withNx } = require('@nx/next');
const { withSentryConfig } = require('@sentry/nextjs');

/**
* @type {import('@nx/next/plugins/with-nx').WithNxOptions}
Expand All @@ -22,6 +23,8 @@ const nextConfig = {
'@nestjs/core',
'@nestjs/common',
'@nestjs/websockets',
'@nestjs/graphql',
'@nestjs/mapped-types',
'class-transformer',
'class-validator',
'hot-shots',
Expand Down Expand Up @@ -50,9 +53,54 @@ const nextConfig = {
},
};

/**
* @type {import('@sentry/nextjs').SentryBuildOptions}
**/
const sentryOptions = {
// For all available options, see:
// https://github.com/getsentry/sentry-webpack-plugin#options

org: "mozilla",
project: "fxa-payments-next",

// Enable source maps
authToken: process.env.SENTRY_AUTH_TOKEN,

// Only print logs for uploading source maps in CI
silent: !process.env.CI,

// For all available options, see:
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/

// Upload a larger set of source maps for prettier stack traces (increases build time)
widenClientFileUpload: true,

// Automatically annotate React components to show their full name in breadcrumbs and session replay
reactComponentAnnotation: {
enabled: true,
},

// Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
// This can increase your server load as well as your hosting bill.
// Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
// side errors will fail.
tunnelRoute: "/monitoring",

// Hides source maps from generated client bundles
hideSourceMaps: true,

// Automatically tree-shake Sentry logger statements to reduce bundle size
disableLogger: true,
}

// Use withSentryConfig to wrap the next config
const sentryEnhancedConfig = (passedConfig) =>
withSentryConfig(passedConfig, sentryOptions);

const plugins = [
// Add more Next.js plugins to this list if needed.
withNx,
sentryEnhancedConfig,
];

module.exports = composePlugins(...plugins)(nextConfig);
2 changes: 1 addition & 1 deletion apps/payments/next/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "payments-next",
"version": "0.0.1",
"version": "0.0.0",
"scripts": {
"start": "next start"
}
Expand Down
30 changes: 30 additions & 0 deletions apps/payments/next/sentry.client.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// This file configures the initialization of Sentry on the client.
// The config you add here will be used whenever a users loads a page in their browser.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
import { initSentryForNextjsClient } from '@fxa/shared/sentry/client';
import { version } from './package.json';

const DEFAULT_SAMPLE_RATE = '1';
const DEFAULT_TRACES_SAMPLE_RATE = '1';

const sentryConfig = {
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
env: process.env.NEXT_PUBLIC_SENTRY_ENV,
clientName: process.env.NEXT_PUBLIC_SENTRY_CLIENT_NAME,
sampleRate: parseInt(
process.env.NEXT_PUBLIC_SENTRY_SAMPLE_RATE || DEFAULT_SAMPLE_RATE
),
tracesSampleRate: parseInt(
process.env.NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE ||
DEFAULT_TRACES_SAMPLE_RATE
),
};

initSentryForNextjsClient({
release: version,
sentry: sentryConfig,
});
26 changes: 26 additions & 0 deletions apps/payments/next/sentry.server.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// This file configures the initialization of Sentry on the server.
// The config you add here will be used whenever the server handles a request.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
import { initSentryForNextjsServer } from '@fxa/shared/sentry';
import { config } from './config';
import { version } from './package.json';

const sentryConfig = {
dsn: config.nextPublicSentryDsn,
env: config.nextPublicSentryEnv,
serverName: config.sentry.serverName,
sampleRate: config.nextPublicSentrySampleRate,
tracesSampleRate: config.nextPublicSentryTracesSampleRate,
};

initSentryForNextjsServer(
{
release: version,
sentry: sentryConfig,
},
console
);
4 changes: 4 additions & 0 deletions libs/payments/ui/src/lib/config.utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import set from 'set-value';
import { plainToInstance, ClassConstructor } from 'class-transformer';
import { validateSync } from 'class-validator';
Expand Down
5 changes: 5 additions & 0 deletions libs/shared/sentry/src/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

export * from './lib/next/client';
1 change: 1 addition & 0 deletions libs/shared/sentry/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from './lib/nest/sentry.constants';
export * from './lib/reporting';
export * from './lib/node';
export * from './lib/browser';
export * from './lib/next/server';
54 changes: 54 additions & 0 deletions libs/shared/sentry/src/lib/next/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// This file configures the initialization of Sentry on the client.
// The config you add here will be used whenever a users loads a page in their browser.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/

import * as Sentry from '@sentry/nextjs';
import { SentryConfigOpts } from '../models/SentryConfigOpts';
import { buildSentryConfig } from '../config-builder';
import { Logger } from '../sentry.types';
import { beforeSend } from '../utils/beforeSend.client';

/**
* @@todo - To be worked on in FXA-10398
*/
const sentryEnabled = true;

export function initSentryForNextjsClient(
config: SentryConfigOpts,
log?: Logger
) {
if (!log) {
log = console;
}

if (!config?.sentry?.dsn) {
log.error('No Sentry dsn provided');
return;
}

// We want sentry to be disabled by default... This is because we only emit data
// for users that 'have opted in'. A subsequent call to 'enable' is needed to ensure
// that sentry events only flow under the proper circumstances.
//disable();

const opts = buildSentryConfig(config, log);
try {
Sentry.init({
...opts,
integrations: [
Sentry.browserTracingIntegration({
enableInp: true,
}),
],
beforeSend: function (event: Sentry.ErrorEvent) {
return beforeSend(sentryEnabled, opts, event);
},
});
} catch (e) {
log.error(e);
}
}
Loading

0 comments on commit 54fefd2

Please sign in to comment.