Skip to content
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 packages/gatsby/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module.exports = {
},
parserOptions: {
ecmaVersion: 2018,
jsx: true,
},
extends: ['@sentry-internal/sdk'],
ignorePatterns: ['build/**', 'dist/**', 'esm/**', 'examples/**', 'scripts/**'],
Expand Down
24 changes: 23 additions & 1 deletion packages/gatsby/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ To automatically capture the `release` value on Vercel you will need to register

## Sentry Performance

To enable Tracing support, supply the `tracesSampleRate` to the options and make sure you have installed the `@sentry/tracing` package.
To enable Tracing support, supply the `tracesSampleRate` to the options and make sure you have installed the `@sentry/tracing` package. This will also turn on the `BrowserTracing` integration for automatic instrumentation of the browser.

```javascript
{
Expand All @@ -58,6 +58,28 @@ To enable Tracing support, supply the `tracesSampleRate` to the options and make
}
```

If you want to supply options to the `BrowserTracing` integration, use the `browserTracingOptions` parameter.

```javascript
{
// ...
plugins: [
{
resolve: "@sentry/gatsby",
options: {
dsn: process.env.SENTRY_DSN, // this is the default
tracesSampleRate: 1, // this is just to test, you should lower this in production
browserTracingOptions: {
// disable creating spans for XHR requests
traceXHR: false,
}
}
},
// ...
]
}
```

## Links

- [Official SDK Docs](https://docs.sentry.io/quickstart/)
Expand Down
87 changes: 37 additions & 50 deletions packages/gatsby/gatsby-browser.js
Original file line number Diff line number Diff line change
@@ -1,58 +1,45 @@
const Sentry = require('@sentry/react');
const Tracing = require('@sentry/tracing');

exports.onClientEntry = function(_, pluginParams) {
require.ensure(['@sentry/react'], function(require) {
const Sentry = require('@sentry/react');
if (pluginParams === undefined) {
return;
}

let TracingIntegration = undefined;
let BrowserTracingIntegration = undefined;
try {
BrowserTracingIntegration = require('@sentry/tracing').Integrations.BrowserTracing;
} catch (_) {
/* no-empty */
}
try {
/** @deprecated Remove when @sentry/apm is no longer used */
TracingIntegration = require('@sentry/apm').Integrations.Tracing;
} catch (_) {
/* no-empty */
}
const tracesSampleRate = pluginParams.tracesSampleRate !== undefined ? pluginParams.tracesSampleRate : 0;
const integrations = [...(pluginParams.integrations || [])];

const tracesSampleRate = pluginParams.tracesSampleRate !== undefined ? pluginParams.tracesSampleRate : 0;
const integrations = [...(pluginParams.integrations || [])];
if (tracesSampleRate && !integrations.some(ele => ele.name === 'BrowserTracing')) {
integrations.push(new Tracing.Integrations.BrowserTracing(pluginParams.browserTracingOptions));
}

if (tracesSampleRate) {
if (BrowserTracingIntegration) {
integrations.push(new BrowserTracingIntegration());
} else if (TracingIntegration) {
integrations.push(new TracingIntegration());
}
}
Tracing.addExtensionMethods();

Sentry.init({
environment: process.env.NODE_ENV || 'development',
// eslint-disable-next-line no-undef
release: __SENTRY_RELEASE__,
// eslint-disable-next-line no-undef
dsn: __SENTRY_DSN__,
...pluginParams,
tracesSampleRate,
integrations,
});
Sentry.init({
environment: process.env.NODE_ENV || 'development',
// eslint-disable-next-line no-undef
release: __SENTRY_RELEASE__,
// eslint-disable-next-line no-undef
dsn: __SENTRY_DSN__,
...pluginParams,
tracesSampleRate,
integrations,
});

Sentry.addGlobalEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.gatsby',
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/gatsby',
version: Sentry.SDK_VERSION,
},
],
version: Sentry.SDK_VERSION,
};
return event;
});
window.Sentry = Sentry;
Sentry.addGlobalEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.gatsby',
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/gatsby',
version: Sentry.SDK_VERSION,
},
],
version: Sentry.SDK_VERSION,
};
return event;
});
window.Sentry = Sentry;
};
39 changes: 22 additions & 17 deletions packages/gatsby/gatsby-node.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
const sentryRelease = JSON.stringify(
// Always read first as Sentry takes this as precedence
process.env.SENTRY_RELEASE ||
// GitHub Actions - https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables
process.env.GITHUB_SHA ||
// Netlify - https://docs.netlify.com/configure-builds/environment-variables/#build-metadata
process.env.COMMIT_REF ||
// Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables
process.env.VERCEL_GITHUB_COMMIT_SHA ||
process.env.VERCEL_GITLAB_COMMIT_SHA ||
process.env.VERCEL_BITBUCKET_COMMIT_SHA ||
// Zeit (now known as Vercel)
process.env.ZEIT_GITHUB_COMMIT_SHA ||
process.env.ZEIT_GITLAB_COMMIT_SHA ||
process.env.ZEIT_BITBUCKET_COMMIT_SHA ||
'',
);

const sentryDsn = JSON.stringify(process.env.SENTRY_DSN || '');

exports.onCreateWebpackConfig = ({ plugins, actions }) => {
actions.setWebpackConfig({
plugins: [
plugins.define({
__SENTRY_RELEASE__: JSON.stringify(
process.env.SENTRY_RELEASE ||
// GitHub Actions - https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables
process.env.GITHUB_SHA ||
// Netlify - https://docs.netlify.com/configure-builds/environment-variables/#build-metadata
process.env.COMMIT_REF ||
// Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables
process.env.VERCEL_GITHUB_COMMIT_SHA ||
process.env.VERCEL_GITLAB_COMMIT_SHA ||
process.env.VERCEL_BITBUCKET_COMMIT_SHA ||
// Zeit (now known as Vercel)
process.env.ZEIT_GITHUB_COMMIT_SHA ||
process.env.ZEIT_GITLAB_COMMIT_SHA ||
process.env.ZEIT_BITBUCKET_COMMIT_SHA ||
'',
),
__SENTRY_DSN__: JSON.stringify(process.env.SENTRY_DSN || ''),
__SENTRY_RELEASE__: sentryRelease,
__SENTRY_DSN__: sentryDsn,
}),
],
});
Expand Down
4 changes: 3 additions & 1 deletion packages/gatsby/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,19 @@
},
"dependencies": {
"@sentry/react": "5.21.4",
"@sentry/types": "5.21.4"
"@sentry/tracing": "5.21.4"
},
"peerDependencies": {
"gatsby": "*"
},
"devDependencies": {
"@sentry-internal/eslint-config-sdk": "5.21.4",
"@testing-library/react": "^10.4.9",
"eslint": "7.6.0",
"jest": "^24.7.1",
"npm-run-all": "^4.1.2",
"prettier": "1.17.0",
"react": "^16.13.1",
"rimraf": "^2.6.3",
"typescript": "3.9.7"
},
Expand Down
132 changes: 132 additions & 0 deletions packages/gatsby/test/gatsby-browser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable @typescript-eslint/no-explicit-any */

const { onClientEntry } = require('../gatsby-browser');

(global as any).__SENTRY_RELEASE__ = '683f3a6ab819d47d23abfca9a914c81f0524d35b';
(global as any).__SENTRY_DSN__ = 'https://examplePublicKey@o0.ingest.sentry.io/0';

let sentryInit = jest.fn();
let sentryProcessEvent: <T>(event: T) => T;
jest.mock('@sentry/react', () => {
const original = jest.requireActual('@sentry/react');
return {
...original,
init: (...args: any[]) => {
sentryInit(...args);
},
addGlobalEventProcessor: (callback: any) => {
sentryProcessEvent = callback;
},
};
});

let tracingAddExtensionMethods = jest.fn();
jest.mock('@sentry/tracing', () => {
const original = jest.requireActual('@sentry/tracing');
return {
...original,
addExtensionMethods: (...args: any[]) => {
tracingAddExtensionMethods(...args);
},
};
});

describe('onClientEntry', () => {
beforeEach(() => {
sentryInit = jest.fn();
tracingAddExtensionMethods = jest.fn();

// @ts-ignore need to set as undefined
sentryProcessEvent = undefined;
});

afterEach(() => {
(window as any).Sentry = undefined;
});

it('inits Sentry by default', () => {
onClientEntry(undefined, {});
expect(sentryInit).toHaveBeenCalledTimes(1);
expect(sentryInit).toHaveBeenLastCalledWith({
dsn: (global as any).__SENTRY_DSN__,
environment: process.env.NODE_ENV,
integrations: [],
release: (global as any).__SENTRY_RELEASE__,
tracesSampleRate: 0,
});
});

it('sets window.Sentry', () => {
onClientEntry(undefined, {});
expect((window as any).Sentry).not.toBeUndefined();
});

it('adds a global event processor', () => {
onClientEntry(undefined, {});
if (sentryProcessEvent) {
const changedEvent = sentryProcessEvent({});

expect(changedEvent).toEqual({
sdk: {
name: 'sentry.javascript.gatsby',
packages: [
{
name: 'npm:@sentry/gatsby',
version: expect.any(String),
},
],
version: expect.any(String),
},
});
} else {
fail('process event not defined');
}
});

it('adds Tracing extension methods', () => {
onClientEntry(undefined, {});

expect(tracingAddExtensionMethods).toHaveBeenCalledTimes(1);
expect(tracingAddExtensionMethods).toHaveBeenLastCalledWith();
});

it('sets a tracesSampleRate if defined as option', () => {
onClientEntry(undefined, { tracesSampleRate: 0.5 });
expect(sentryInit).toHaveBeenLastCalledWith(
expect.objectContaining({
tracesSampleRate: 0.5,
}),
);
});

it('adds `BrowserTracing` integration if tracesSampleRate is defined', () => {
onClientEntry(undefined, { tracesSampleRate: 0.5 });
expect(sentryInit).toHaveBeenLastCalledWith(
expect.objectContaining({
integrations: [expect.objectContaining({ name: 'BrowserTracing' })],
}),
);
});

it('only defines a single `BrowserTracing` integration', () => {
const Tracing = jest.requireActual('@sentry/tracing');
const integrations = [new Tracing.Integrations.BrowserTracing()];
onClientEntry(undefined, { tracesSampleRate: 0.5, integrations });

expect(sentryInit).toHaveBeenLastCalledWith(
expect.objectContaining({
integrations: [expect.objectContaining({ name: 'BrowserTracing' })],
}),
);
});

// Run this last to check for any test side effects
it('does not run if plugin params are undefined', () => {
onClientEntry();
expect(sentryInit).toHaveBeenCalledTimes(0);
expect((window as any).Sentry).toBeUndefined();
expect(sentryProcessEvent).toBeUndefined();
expect(tracingAddExtensionMethods).toHaveBeenCalledTimes(0);
});
});
27 changes: 27 additions & 0 deletions packages/gatsby/test/gatsby-node.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable @typescript-eslint/no-explicit-any */

const { onCreateWebpackConfig } = require('../gatsby-node');

describe('onCreateWebpackConfig', () => {
it('sets a webpack config', () => {
const plugins = {
define: jest.fn(),
};

const actions = {
setWebpackConfig: jest.fn(),
};

onCreateWebpackConfig({ plugins, actions });

expect(plugins.define).toHaveBeenCalledTimes(1);
expect(plugins.define).toHaveBeenLastCalledWith({
__SENTRY_DSN__: expect.any(String),
__SENTRY_RELEASE__: expect.any(String),
});

expect(actions.setWebpackConfig).toHaveBeenCalledTimes(1);
expect(actions.setWebpackConfig).toHaveBeenLastCalledWith({ plugins: expect.any(Array) });
});
});
Loading