Skip to content

Commit 58c6361

Browse files
committed
feat: integrate Sentry with adaptive sampling and error tracking
1 parent 016ddd5 commit 58c6361

File tree

12 files changed

+117
-85
lines changed

12 files changed

+117
-85
lines changed

examples/sentry/pages/+config.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,6 @@ import vikeReactSentry from 'vike-react-sentry/config'
88
const config = {
99
title: 'Vike + React + Sentry Example',
1010
extends: [vikeReact, vikePhoton, vikeReactSentry],
11-
sentry: {
12-
tracesSampleRate: 1.0,
13-
replaysSessionSampleRate: 0.1,
14-
replaysOnErrorSampleRate: 1.0,
15-
},
1611
// Photon configuration
1712
photon: {
1813
server: '../server/index.ts',

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,10 @@
3131
"playwright-chromium": "^1.57.0",
3232
"prettier": "^3.2.5"
3333
},
34-
"packageManager": "[email protected]"
34+
"packageManager": "[email protected]",
35+
"pnpm": {
36+
"overrides": {
37+
"vike": "0.4.252-commit-7f781cf"
38+
}
39+
}
3540
}

packages/vike-react-sentry/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
"./__internal/integration/onCreateGlobalContext.client": "./dist/integration/onCreateGlobalContext.client.js",
1313
"./__internal/integration/onHookCall.server": "./dist/integration/onHookCall.server.js",
1414
"./__internal/integration/onHookCall.client": "./dist/integration/onHookCall.client.js",
15-
"./__internal/integration/client": "./dist/integration/client.js",
1615
"./__internal/integration/onError": "./dist/integration/onError.js"
1716
},
1817
"scripts": {
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const TRACE_DEFAULT_SAMPLE_RATE = 0.2
2+
export const TRACE_DEFAULT_SAMPLE_RATE_ERROR = 1.0
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { SentryReactOptions } from '../types.js'
2+
import { resolveDsn } from '../utils/resolveDsn.js'
3+
import * as SentryReact from '@sentry/react'
4+
import { TRACE_DEFAULT_SAMPLE_RATE, TRACE_DEFAULT_SAMPLE_RATE_ERROR } from './constants.js'
5+
6+
export const DEFAULT_SENTRY_CLIENT_SETTINGS = (clientConfig: SentryReactOptions) =>
7+
({
8+
environment: clientConfig.environment || import.meta.env.MODE || 'production',
9+
replaysSessionSampleRate: 0.1,
10+
replaysOnErrorSampleRate: 1.0,
11+
integrations: [SentryReact.browserTracingIntegration(), SentryReact.replayIntegration()],
12+
dsn: resolveDsn(clientConfig.dsn),
13+
tracesSampler: (samplingContext) => {
14+
const { attributes, inheritOrSampleWith } = samplingContext
15+
if (attributes?.hasRecentErrors === true) {
16+
return TRACE_DEFAULT_SAMPLE_RATE_ERROR
17+
}
18+
return inheritOrSampleWith(clientConfig.tracesSampleRate || TRACE_DEFAULT_SAMPLE_RATE)
19+
},
20+
}) as SentryReactOptions
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { SentryNodeOptions } from '../types.js'
2+
import { resolveDsn } from '../utils/resolveDsn.js'
3+
import { TRACE_DEFAULT_SAMPLE_RATE, TRACE_DEFAULT_SAMPLE_RATE_ERROR } from './constants.js'
4+
5+
export const DEFAULT_SENTRY_SERVER_SETTINGS = (serverConfig: SentryNodeOptions) =>
6+
({
7+
environment: serverConfig.environment || import.meta.env.MODE || 'production',
8+
dsn: resolveDsn(serverConfig.dsn),
9+
tracesSampler: (samplingContext) => {
10+
const { attributes, inheritOrSampleWith } = samplingContext
11+
if (attributes?.hasRecentErrors === true) {
12+
return TRACE_DEFAULT_SAMPLE_RATE_ERROR
13+
}
14+
return inheritOrSampleWith(serverConfig.tracesSampleRate || TRACE_DEFAULT_SAMPLE_RATE)
15+
},
16+
}) as SentryNodeOptions

packages/vike-react-sentry/src/integration/onCreateGlobalContext.client.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,22 @@ export { onCreateGlobalContext }
22

33
import * as SentryReact from '@sentry/react'
44
import type { GlobalContextClient } from 'vike/types'
5-
import { resolveDsn } from '../utils/resolveDsn.js'
65
import { assignDeep } from '../utils/assignDeep.js'
76
import { SentryOptions } from '../types.js'
7+
import { DEFAULT_SENTRY_CLIENT_SETTINGS } from './defaults-client.js'
88

99
async function onCreateGlobalContext(globalContext: GlobalContextClient): Promise<void> {
10-
if (!globalContext.config.sentry?.length) return
11-
const clientConfig = globalContext.config.sentry.reverse().reduce((acc, curr) => {
10+
const clientConfig = (globalContext.config.sentry || []).reverse().reduce((acc, curr) => {
1211
if (typeof curr === 'function') {
1312
curr = curr(globalContext)
1413
}
1514
return assignDeep(acc, curr)
1615
}, {}) as SentryOptions
1716

18-
if (!clientConfig) return
1917
if (!SentryReact.getClient()) {
2018
SentryReact.init({
21-
integrations: [SentryReact.browserTracingIntegration(), SentryReact.replayIntegration()],
19+
...DEFAULT_SENTRY_CLIENT_SETTINGS(clientConfig),
2220
...clientConfig,
23-
dsn: resolveDsn(clientConfig.dsn),
2421
})
2522
}
2623
}

packages/vike-react-sentry/src/integration/onCreateGlobalContext.server.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,22 @@ export { onCreateGlobalContext }
22

33
import * as SentryNode from '@sentry/node'
44
import type { GlobalContextServer } from 'vike/types'
5-
import { resolveDsn } from '../utils/resolveDsn.js'
6-
import { assignDeep } from '../utils/assignDeep.js'
75
import { SentryOptions } from '../types.js'
6+
import { assignDeep } from '../utils/assignDeep.js'
7+
import { DEFAULT_SENTRY_SERVER_SETTINGS } from './defaults-server.js'
88

99
async function onCreateGlobalContext(globalContext: GlobalContextServer): Promise<void> {
10-
if (!globalContext.config.sentry?.length) return
11-
const serverConfig = globalContext.config.sentry.reverse().reduce((acc, curr) => {
10+
const serverConfig = (globalContext.config.sentry || []).reverse().reduce((acc, curr) => {
1211
if (typeof curr === 'function') {
1312
curr = curr(globalContext)
1413
}
1514
return assignDeep(acc, curr)
1615
}, {}) as SentryOptions
1716

18-
if (!serverConfig) return
1917
if (!SentryNode.getClient()) {
2018
SentryNode.init({
19+
...DEFAULT_SENTRY_SERVER_SETTINGS(serverConfig),
2120
...serverConfig,
22-
dsn: resolveDsn(serverConfig.dsn),
2321
})
2422
}
2523
}

packages/vike-react-sentry/src/integration/onHookCall.client.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Config } from 'vike/types'
22
import * as Sentry from '@sentry/react'
3-
import { markErrorAsSeen } from '../utils/error.js'
3+
import { markErrorAsSeen, recordError, hasRecentErrors } from '../utils/error.js'
44

55
/**
66
* Vike onHookCall configuration for Sentry integration (client-side).
@@ -14,7 +14,6 @@ export const onHookCall: Config['onHookCall'] = async (hook, pageContext) => {
1414
// Extract useful context for Sentry
1515
const url = pageContext?.urlOriginal ?? 'unknown'
1616
const pageId = pageContext?.pageId ?? 'unknown'
17-
const routeParams = pageContext?.routeParams ?? {}
1817

1918
// withScope ensures any error captured during hook execution has Vike context
2019
return Sentry.withScope((scope) => {
@@ -25,7 +24,6 @@ export const onHookCall: Config['onHookCall'] = async (hook, pageContext) => {
2524
filePath: hook.filePath,
2625
pageId,
2726
url,
28-
routeParams,
2927
})
3028

3129
return Sentry.startSpan(
@@ -37,20 +35,15 @@ export const onHookCall: Config['onHookCall'] = async (hook, pageContext) => {
3735
'vike.hook.file': hook.filePath,
3836
'vike.page.id': pageId,
3937
'vike.url': url,
40-
...Object.entries(routeParams).reduce(
41-
(acc, [key, value]) => ({
42-
...acc,
43-
[`vike.route.${key}`]: value,
44-
}),
45-
{},
46-
),
38+
hasRecentErrors: hasRecentErrors(),
4739
},
4840
},
4941
async (span) => {
5042
try {
5143
await hook.call()
5244
} catch (error) {
5345
markErrorAsSeen(error)
46+
recordError()
5447
span.setStatus({
5548
code: 2,
5649
})

packages/vike-react-sentry/src/integration/onHookCall.server.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Config } from 'vike/types'
22
import * as Sentry from '@sentry/node'
3-
import { markErrorAsSeen } from '../utils/error.js'
3+
import { hasRecentErrors, markErrorAsSeen } from '../utils/error.js'
44

55
/**
66
* Vike onHookCall configuration for Sentry integration.
@@ -14,7 +14,6 @@ export const onHookCall: Config['onHookCall'] = async (hook, pageContext) => {
1414
// Extract useful context for Sentry
1515
const url = pageContext?.urlOriginal ?? 'unknown'
1616
const pageId = pageContext?.pageId ?? 'unknown'
17-
const routeParams = pageContext?.routeParams ?? {}
1817

1918
// withScope ensures any error captured during hook execution has Vike context
2019
return Sentry.withScope((scope) => {
@@ -25,7 +24,6 @@ export const onHookCall: Config['onHookCall'] = async (hook, pageContext) => {
2524
filePath: hook.filePath,
2625
pageId,
2726
url,
28-
routeParams,
2927
})
3028

3129
return Sentry.startSpan(
@@ -37,13 +35,7 @@ export const onHookCall: Config['onHookCall'] = async (hook, pageContext) => {
3735
'vike.hook.file': hook.filePath,
3836
'vike.page.id': pageId,
3937
'vike.url': url,
40-
...Object.entries(routeParams).reduce(
41-
(acc, [key, value]) => ({
42-
...acc,
43-
[`vike.route.${key}`]: value,
44-
}),
45-
{},
46-
),
38+
hasRecentErrors: hasRecentErrors(),
4739
},
4840
},
4941
async (span) => {

0 commit comments

Comments
 (0)