Skip to content

Commit 7f36772

Browse files
authored
feat(browser): Add browserSessionIntegration (#14551)
1 parent 20c267e commit 7f36772

File tree

10 files changed

+77
-51
lines changed

10 files changed

+77
-51
lines changed

dev-packages/browser-integration-tests/suites/public-api/debug/test.ts

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ sentryTest('logs debug messages correctly', async ({ getLocalTestUrl, page }) =>
3434
'Sentry Logger [log]: Integration installed: Dedupe',
3535
'Sentry Logger [log]: Integration installed: HttpContext',
3636
'Sentry Logger [warn]: Discarded session because of missing or non-string release',
37+
'Sentry Logger [log]: Integration installed: BrowserSession',
3738
'test log',
3839
]
3940
: ['[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.', 'test log'],

dev-packages/browser-integration-tests/suites/replay/captureReplay/test.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ sentryTest('should capture replays (@sentry/browser export)', async ({ getLocalT
3434
event_id: expect.stringMatching(/\w{32}/),
3535
environment: 'production',
3636
sdk: {
37-
integrations: [
37+
integrations: expect.arrayContaining([
3838
'InboundFilters',
3939
'FunctionToString',
4040
'BrowserApiErrors',
@@ -43,8 +43,9 @@ sentryTest('should capture replays (@sentry/browser export)', async ({ getLocalT
4343
'LinkedErrors',
4444
'Dedupe',
4545
'HttpContext',
46+
'BrowserSession',
4647
'Replay',
47-
],
48+
]),
4849
version: SDK_VERSION,
4950
name: 'sentry.javascript.browser',
5051
},
@@ -71,7 +72,7 @@ sentryTest('should capture replays (@sentry/browser export)', async ({ getLocalT
7172
event_id: expect.stringMatching(/\w{32}/),
7273
environment: 'production',
7374
sdk: {
74-
integrations: [
75+
integrations: expect.arrayContaining([
7576
'InboundFilters',
7677
'FunctionToString',
7778
'BrowserApiErrors',
@@ -80,8 +81,9 @@ sentryTest('should capture replays (@sentry/browser export)', async ({ getLocalT
8081
'LinkedErrors',
8182
'Dedupe',
8283
'HttpContext',
84+
'BrowserSession',
8385
'Replay',
84-
],
86+
]),
8587
version: SDK_VERSION,
8688
name: 'sentry.javascript.browser',
8789
},

dev-packages/browser-integration-tests/suites/replay/captureReplayFromReplayPackage/test.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ sentryTest('should capture replays (@sentry-internal/replay export)', async ({ g
3434
event_id: expect.stringMatching(/\w{32}/),
3535
environment: 'production',
3636
sdk: {
37-
integrations: [
37+
integrations: expect.arrayContaining([
3838
'InboundFilters',
3939
'FunctionToString',
4040
'BrowserApiErrors',
@@ -43,8 +43,9 @@ sentryTest('should capture replays (@sentry-internal/replay export)', async ({ g
4343
'LinkedErrors',
4444
'Dedupe',
4545
'HttpContext',
46+
'BrowserSession',
4647
'Replay',
47-
],
48+
]),
4849
version: SDK_VERSION,
4950
name: 'sentry.javascript.browser',
5051
},
@@ -71,7 +72,7 @@ sentryTest('should capture replays (@sentry-internal/replay export)', async ({ g
7172
event_id: expect.stringMatching(/\w{32}/),
7273
environment: 'production',
7374
sdk: {
74-
integrations: [
75+
integrations: expect.arrayContaining([
7576
'InboundFilters',
7677
'FunctionToString',
7778
'BrowserApiErrors',
@@ -80,8 +81,9 @@ sentryTest('should capture replays (@sentry-internal/replay export)', async ({ g
8081
'LinkedErrors',
8182
'Dedupe',
8283
'HttpContext',
84+
'BrowserSession',
8385
'Replay',
84-
],
86+
]),
8587
version: SDK_VERSION,
8688
name: 'sentry.javascript.browser',
8789
},

dev-packages/browser-integration-tests/utils/replayEventTemplates.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const DEFAULT_REPLAY_EVENT = {
1616
event_id: expect.stringMatching(/\w{32}/),
1717
environment: 'production',
1818
sdk: {
19-
integrations: [
19+
integrations: expect.arrayContaining([
2020
'InboundFilters',
2121
'FunctionToString',
2222
'BrowserApiErrors',
@@ -25,8 +25,9 @@ const DEFAULT_REPLAY_EVENT = {
2525
'LinkedErrors',
2626
'Dedupe',
2727
'HttpContext',
28+
'BrowserSession',
2829
'Replay',
29-
],
30+
]),
3031
version: SDK_VERSION,
3132
name: 'sentry.javascript.browser',
3233
},

dev-packages/e2e-tests/test-applications/astro-4/tests/errors.client.test.ts

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ test.describe('client-side errors', () => {
5959
'LinkedErrors',
6060
'Dedupe',
6161
'HttpContext',
62+
'BrowserSession',
6263
'BrowserTracing',
6364
]),
6465
name: 'sentry.javascript.astro',

packages/angular/src/sdk.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { VERSION } from '@angular/core';
22
import type { BrowserOptions } from '@sentry/browser';
33
import {
44
breadcrumbsIntegration,
5+
browserSessionIntegration,
56
globalHandlersIntegration,
67
httpContextIntegration,
78
init as browserInit,
@@ -22,7 +23,7 @@ import { IS_DEBUG_BUILD } from './flags';
2223
/**
2324
* Get the default integrations for the Angular SDK.
2425
*/
25-
export function getDefaultIntegrations(): Integration[] {
26+
export function getDefaultIntegrations(options: BrowserOptions = {}): Integration[] {
2627
// Don't include the BrowserApiErrors integration as it interferes with the Angular SDK's `ErrorHandler`:
2728
// BrowserApiErrors would catch certain errors before they reach the `ErrorHandler` and
2829
// thus provide a lower fidelity error than what `SentryErrorHandler`
@@ -31,7 +32,7 @@ export function getDefaultIntegrations(): Integration[] {
3132
// see:
3233
// - https://github.com/getsentry/sentry-javascript/issues/5417#issuecomment-1453407097
3334
// - https://github.com/getsentry/sentry-javascript/issues/2744
34-
return [
35+
const integrations = [
3536
inboundFiltersIntegration(),
3637
functionToStringIntegration(),
3738
breadcrumbsIntegration(),
@@ -40,6 +41,12 @@ export function getDefaultIntegrations(): Integration[] {
4041
dedupeIntegration(),
4142
httpContextIntegration(),
4243
];
44+
45+
if (options.autoSessionTracking !== false) {
46+
integrations.push(browserSessionIntegration());
47+
}
48+
49+
return integrations;
4350
}
4451

4552
/**

packages/angular/test/sdk.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ describe('init', () => {
1515
});
1616

1717
it('does not include the BrowserApiErrors integration', () => {
18-
const browserDefaultIntegrationsWithoutBrowserApiErrors = SentryBrowser.getDefaultIntegrations()
18+
const browserDefaultIntegrationsWithoutBrowserApiErrors = SentryBrowser.getDefaultIntegrations({})
1919
.filter(i => i.name !== 'BrowserApiErrors')
2020
.map(i => i.name)
2121
.sort();

packages/browser/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,4 @@ export type { Span } from '@sentry/core';
7777
export { makeBrowserOfflineTransport } from './transports/offline';
7878
export { browserProfilingIntegration } from './profiling/integration';
7979
export { spotlightBrowserIntegration } from './integrations/spotlight';
80+
export { browserSessionIntegration } from './integrations/browsersession';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { addHistoryInstrumentationHandler } from '@sentry-internal/browser-utils';
2+
import { captureSession, defineIntegration, logger, startSession } from '@sentry/core';
3+
import { DEBUG_BUILD } from '../debug-build';
4+
import { WINDOW } from '../helpers';
5+
6+
/**
7+
* When added, automatically creates sessions which allow you to track adoption and crashes (crash free rate) in your Releases in Sentry.
8+
* More information: https://docs.sentry.io/product/releases/health/
9+
*
10+
* Note: In order for session tracking to work, you need to set up Releases: https://docs.sentry.io/product/releases/
11+
*/
12+
export const browserSessionIntegration = defineIntegration(() => {
13+
return {
14+
name: 'BrowserSession',
15+
setupOnce() {
16+
if (typeof WINDOW.document === 'undefined') {
17+
DEBUG_BUILD &&
18+
logger.warn('Using the `browserSessionIntegration` in non-browser environments is not supported.');
19+
return;
20+
}
21+
22+
// The session duration for browser sessions does not track a meaningful
23+
// concept that can be used as a metric.
24+
// Automatically captured sessions are akin to page views, and thus we
25+
// discard their duration.
26+
startSession({ ignoreDuration: true });
27+
captureSession();
28+
29+
// We want to create a session for every navigation as well
30+
addHistoryInstrumentationHandler(({ from, to }) => {
31+
// Don't create an additional session for the initial route or if the location did not change
32+
if (from !== undefined && from !== to) {
33+
startSession({ ignoreDuration: true });
34+
captureSession();
35+
}
36+
});
37+
},
38+
};
39+
});

packages/browser/src/sdk.ts

+10-38
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import { addHistoryInstrumentationHandler } from '@sentry-internal/browser-utils';
21
import {
3-
captureSession,
42
consoleSandbox,
53
dedupeIntegration,
64
functionToStringIntegration,
@@ -13,7 +11,6 @@ import {
1311
lastEventId,
1412
logger,
1513
stackParserFromStackParserOptions,
16-
startSession,
1714
supportsFetch,
1815
} from '@sentry/core';
1916
import type { Client, DsnLike, Integration, Options, UserFeedback } from '@sentry/core';
@@ -23,19 +20,20 @@ import { DEBUG_BUILD } from './debug-build';
2320
import { WINDOW } from './helpers';
2421
import { breadcrumbsIntegration } from './integrations/breadcrumbs';
2522
import { browserApiErrorsIntegration } from './integrations/browserapierrors';
23+
import { browserSessionIntegration } from './integrations/browsersession';
2624
import { globalHandlersIntegration } from './integrations/globalhandlers';
2725
import { httpContextIntegration } from './integrations/httpcontext';
2826
import { linkedErrorsIntegration } from './integrations/linkederrors';
2927
import { defaultStackParser } from './stack-parsers';
3028
import { makeFetchTransport } from './transports/fetch';
3129

3230
/** Get the default integrations for the browser SDK. */
33-
export function getDefaultIntegrations(_options: Options): Integration[] {
31+
export function getDefaultIntegrations(options: Options): Integration[] {
3432
/**
3533
* Note: Please make sure this stays in sync with Angular SDK, which re-exports
3634
* `getDefaultIntegrations` but with an adjusted set of integrations.
3735
*/
38-
return [
36+
const integrations = [
3937
inboundFiltersIntegration(),
4038
functionToStringIntegration(),
4139
browserApiErrorsIntegration(),
@@ -45,6 +43,12 @@ export function getDefaultIntegrations(_options: Options): Integration[] {
4543
dedupeIntegration(),
4644
httpContextIntegration(),
4745
];
46+
47+
if (options.autoSessionTracking !== false) {
48+
integrations.push(browserSessionIntegration());
49+
}
50+
51+
return integrations;
4852
}
4953

5054
function applyDefaultOptions(optionsArg: BrowserOptions = {}): BrowserOptions {
@@ -187,13 +191,7 @@ export function init(browserOptions: BrowserOptions = {}): Client | undefined {
187191
transport: options.transport || makeFetchTransport,
188192
};
189193

190-
const client = initAndBind(BrowserClient, clientOptions);
191-
192-
if (options.autoSessionTracking) {
193-
startSessionTracking();
194-
}
195-
196-
return client;
194+
return initAndBind(BrowserClient, clientOptions);
197195
}
198196

199197
/**
@@ -308,32 +306,6 @@ export function onLoad(callback: () => void): void {
308306
callback();
309307
}
310308

311-
/**
312-
* Enable automatic Session Tracking for the initial page load.
313-
*/
314-
function startSessionTracking(): void {
315-
if (typeof WINDOW.document === 'undefined') {
316-
DEBUG_BUILD && logger.warn('Session tracking in non-browser environment with @sentry/browser is not supported.');
317-
return;
318-
}
319-
320-
// The session duration for browser sessions does not track a meaningful
321-
// concept that can be used as a metric.
322-
// Automatically captured sessions are akin to page views, and thus we
323-
// discard their duration.
324-
startSession({ ignoreDuration: true });
325-
captureSession();
326-
327-
// We want to create a session for every navigation as well
328-
addHistoryInstrumentationHandler(({ from, to }) => {
329-
// Don't create an additional session for the initial route or if the location did not change
330-
if (from !== undefined && from !== to) {
331-
startSession({ ignoreDuration: true });
332-
captureSession();
333-
}
334-
});
335-
}
336-
337309
/**
338310
* Captures user feedback and sends it to Sentry.
339311
*

0 commit comments

Comments
 (0)