Skip to content

feat(nuxt): Automatically add BrowserTracing #13005

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
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
tunnel: `http://localhost:3031/`, // proxy server
tracesSampleRate: 1.0,
integrations: [Sentry.browserTracingIntegration()],
});
27 changes: 24 additions & 3 deletions packages/nuxt/src/client/sdk.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { init as initBrowser } from '@sentry/browser';
import { applySdkMetadata } from '@sentry/core';
import type { Client } from '@sentry/types';
import {
type BrowserOptions,
browserTracingIntegration,
getDefaultIntegrations as getBrowserDefaultIntegrations,
init as initBrowser,
} from '@sentry/browser';
import { applySdkMetadata, hasTracingEnabled } from '@sentry/core';
import type { Client, Integration } from '@sentry/types';
import type { SentryNuxtOptions } from '../common/types';

/**
Expand All @@ -10,10 +15,26 @@ import type { SentryNuxtOptions } from '../common/types';
*/
export function init(options: SentryNuxtOptions): Client | undefined {
const sentryOptions = {
defaultIntegrations: getDefaultIntegrations(options),
...options,
};

applySdkMetadata(sentryOptions, 'nuxt', ['nuxt', 'vue']);

return initBrowser(sentryOptions);
}

// Tree-shakable guard to remove all code related to tracing
declare const __SENTRY_TRACING__: boolean;

function getDefaultIntegrations(options: BrowserOptions): Integration[] | undefined {
// This evaluates to true unless __SENTRY_TRACING__ is text-replaced with "false",
// in which case everything inside will get tree-shaken away
if (typeof __SENTRY_TRACING__ === 'undefined' || __SENTRY_TRACING__) {
if (hasTracingEnabled(options)) {
return [...getBrowserDefaultIntegrations(options), browserTracingIntegration()];
Copy link
Member

@Lms24 Lms24 Jul 22, 2024

Choose a reason for hiding this comment

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

So, I checked and this logic is identical to next, sveltekit and astro. So far so good but there is a flaw here that we probably were not aware of:

hasTracingEnabled returns false if neither tracesSampleRate, tracesSampler nor enableTracing are set. So in this case, we'd not add the integration here. However, the only way to enable "Tracing without Performance" in browser land is to add browserTracingIntegration but not set any of the three options.

Before we merge this PR, let's discuss if we change this behaviour in all SDKs or continue with it.

Copy link
Member

Choose a reason for hiding this comment

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

As discussed today, let's remove this guard and later, once we have a vite plugin setup, add an easy-to-use option to configure the __SENTRY_TRACING__ flag :)

}
}

return undefined;
}
58 changes: 53 additions & 5 deletions packages/nuxt/test/client/sdk.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as SentryBrowser from '@sentry/browser';
import { SDK_VERSION } from '@sentry/vue';
import { type BrowserClient, SDK_VERSION, getClient } from '@sentry/vue';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { init } from '../../src/client';

const vueInit = vi.spyOn(SentryBrowser, 'init');
const browserInit = vi.spyOn(SentryBrowser, 'init');

describe('Nuxt Client SDK', () => {
describe('init', () => {
Expand All @@ -12,7 +12,7 @@ describe('Nuxt Client SDK', () => {
});

it('Adds Nuxt metadata to the SDK options', () => {
expect(vueInit).not.toHaveBeenCalled();
expect(browserInit).not.toHaveBeenCalled();

init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
Expand All @@ -31,8 +31,56 @@ describe('Nuxt Client SDK', () => {
},
};

expect(vueInit).toHaveBeenCalledTimes(1);
expect(vueInit).toHaveBeenLastCalledWith(expect.objectContaining(expectedMetadata));
expect(browserInit).toHaveBeenCalledTimes(1);
expect(browserInit).toHaveBeenLastCalledWith(expect.objectContaining(expectedMetadata));
});

describe('Automatically adds BrowserTracing integration', () => {
it.each([
['tracesSampleRate', { tracesSampleRate: 0 }],
['tracesSampler', { tracesSampler: () => 1.0 }],
['enableTracing', { enableTracing: true }],
])('adds a browserTracingIntegration if tracing is enabled via %s', (_, tracingOptions) => {
init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
...tracingOptions,
});

const browserTracing = getClient<BrowserClient>()?.getIntegrationByName('BrowserTracing');
expect(browserTracing).toBeDefined();
});

it.each([
['enableTracing', { enableTracing: false }],
['no tracing option set', {}],
])("doesn't add a browserTracingIntegration integration if tracing is disabled via %s", (_, tracingOptions) => {
init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
...tracingOptions,
});

const browserTracing = getClient<BrowserClient>()?.getIntegrationByName('BrowserTracing');
expect(browserTracing).toBeUndefined();
});

it("doesn't add a browserTracingIntegration if `__SENTRY_TRACING__` is set to false", () => {
// This is the closest we can get to unit-testing the `__SENTRY_TRACING__` tree-shaking guard
// IRL, the code to add the integration would most likely be removed by the bundler.

// @ts-expect-error: Testing purposes
globalThis.__SENTRY_TRACING__ = false;

init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
tracesSampleRate: 0.3,
});

const browserTracing = getClient<BrowserClient>()?.getIntegrationByName('BrowserTracing');
expect(browserTracing).toBeUndefined();

// @ts-expect-error: Testing purposes
delete globalThis.__SENTRY_TRACING__;
});
});

it('returns client from init', () => {
Expand Down
Loading