Skip to content

Commit 67851ba

Browse files
authored
fix(tracing): autoSessionTracking not working on the server-side (#466)
1 parent b81b2cf commit 67851ba

File tree

6 files changed

+69
-36
lines changed

6 files changed

+69
-36
lines changed

lib/core/hooks.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,11 @@ export async function webpackConfigHook (moduleContainer, webpackConfigs, option
133133
*
134134
* @param {ThisParameterType<import('@nuxt/types').Module>} moduleContainer
135135
* @param {import('../../types/sentry').ResolvedModuleConfiguration} moduleOptions
136+
* @param {import('../../types/sentry').SentryHandlerProxy} sentryHandlerProxy
136137
* @param {import('consola').Consola} logger
137138
* @return {Promise<void>}
138139
*/
139-
export async function initializeServerSentry (moduleContainer, moduleOptions, logger) {
140+
export async function initializeServerSentry (moduleContainer, moduleOptions, sentryHandlerProxy, logger) {
140141
if (process.sentry) {
141142
return
142143
}
@@ -154,6 +155,8 @@ export async function initializeServerSentry (moduleContainer, moduleOptions, lo
154155

155156
if (canInitialize(moduleOptions)) {
156157
Sentry.init(config)
158+
sentryHandlerProxy.errorHandler = Sentry.Handlers.errorHandler()
159+
sentryHandlerProxy.requestHandler = Sentry.Handlers.requestHandler(moduleOptions.requestHandlerConfig)
157160
}
158161

159162
process.sentry = Sentry

lib/core/utils.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,17 @@ export const clientSentryEnabled = options => !options.disabled && !options.disa
3737
* @return {boolean} True if node Sentry is enabled.
3838
*/
3939
export const serverSentryEnabled = options => !options.disabled && !options.disableServerSide
40+
41+
/**
42+
* @param {(...args: any[]) => any} fn
43+
* @return {(...args: any[]) => any}
44+
*/
45+
export function callOnce (fn) {
46+
let called = false
47+
return function callOnceWrapper () {
48+
if (!called) {
49+
called = true
50+
return fn(arguments)
51+
}
52+
}
53+
}

lib/module.js

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import consola from 'consola'
22
import merge from 'lodash.mergewith'
3-
import { Handlers as SentryHandlers, captureException, withScope } from '@sentry/node'
3+
import { captureException, withScope } from '@sentry/node'
44
import { buildHook, initializeServerSentry, shutdownServerSentry, webpackConfigHook } from './core/hooks'
5-
import { boolToText, canInitialize, clientSentryEnabled, envToBool, serverSentryEnabled } from './core/utils'
5+
import { boolToText, callOnce, canInitialize, clientSentryEnabled, envToBool, serverSentryEnabled } from './core/utils'
66

77
const logger = consola.withScope('nuxt:sentry')
88

@@ -73,21 +73,6 @@ export default function SentryModule (moduleOptions) {
7373
options.publishRelease = merged
7474
}
7575

76-
if (serverSentryEnabled(options)) {
77-
// @ts-ignore
78-
this.nuxt.hook('render:setupMiddleware', app => app.use(SentryHandlers.requestHandler(options.requestHandlerConfig)))
79-
// @ts-ignore
80-
this.nuxt.hook('render:errorMiddleware', app => app.use(SentryHandlers.errorHandler()))
81-
// @ts-ignore
82-
this.nuxt.hook('generate:routeFailed', ({ route, errors }) => {
83-
// @ts-ignore
84-
errors.forEach(({ error }) => withScope((scope) => {
85-
scope.setExtra('route', route)
86-
captureException(error)
87-
}))
88-
})
89-
}
90-
9176
if (canInitialize(options) && (clientSentryEnabled(options) || serverSentryEnabled(options))) {
9277
const status = `(client side: ${boolToText(clientSentryEnabled(options))}, server side: ${boolToText(serverSentryEnabled(options))})`
9378
logger.success(`Sentry reporting is enabled ${status}`)
@@ -105,21 +90,45 @@ export default function SentryModule (moduleOptions) {
10590
logger.info(`Sentry reporting is disabled (${why})`)
10691
}
10792

108-
this.nuxt.hook('build:before', () => buildHook(this, options, logger))
93+
this.nuxt.hook('build:before', callOnce(() => buildHook(this, options, logger)))
10994

110-
// This is messy but Nuxt provides many modes that it can be started with like:
111-
// - nuxt dev
112-
// - nuxt build
113-
// - nuxt start
114-
// - nuxt generate
115-
// but it doesn't really provide great way to differentiate those or enough hooks to
116-
// pick from. This should ensure that server Sentry will only be initialized **after**
117-
// the release version has been determined and the options template created but before
118-
// the build is started (if building).
119-
const initHook = this.options._build ? 'build:compile' : 'ready'
12095
if (serverSentryEnabled(options)) {
121-
this.nuxt.hook(initHook, () => initializeServerSentry(this, options, logger))
122-
this.nuxt.hook('generate:done', () => shutdownServerSentry())
96+
/**
97+
* Proxy that provides a dummy request handler before Sentry is initialized and gets replaced with Sentry's own
98+
* handler after initialization. Otherwise server-side request tracing would not work as it depends on Sentry being
99+
* initialized already during handler creation.
100+
* @type {import('../types/sentry').SentryHandlerProxy}
101+
*/
102+
const sentryHandlerProxy = {
103+
errorHandler: (error, req, res, next) => { next(error) },
104+
requestHandler: (req, res, next) => { next() },
105+
}
106+
// @ts-ignore
107+
this.nuxt.hook('render:setupMiddleware', app => app.use((req, res, next) => { sentryHandlerProxy.requestHandler(req, res, next) }))
108+
// @ts-ignore
109+
this.nuxt.hook('render:errorMiddleware', app => app.use((error, req, res, next) => { sentryHandlerProxy.errorHandler(error, req, res, next) }))
110+
// @ts-ignore
111+
this.nuxt.hook('generate:routeFailed', ({ route, errors }) => {
112+
// @ts-ignore
113+
errors.forEach(({ error }) => withScope((scope) => {
114+
scope.setExtra('route', route)
115+
captureException(error)
116+
}))
117+
})
118+
// This is messy but Nuxt provides many modes that it can be started with like:
119+
// - nuxt dev
120+
// - nuxt build
121+
// - nuxt start
122+
// - nuxt generate
123+
// but it doesn't really provide great way to differentiate those or enough hooks to
124+
// pick from. This should ensure that server Sentry will only be initialized **after**
125+
// the release version has been determined and the options template created but before
126+
// the build is started (if building).
127+
const initHook = this.options._build ? 'build:compile' : 'ready'
128+
this.nuxt.hook(initHook, callOnce(() => initializeServerSentry(this, options, sentryHandlerProxy, logger)))
129+
const shutdownServerSentryOnce = callOnce(() => shutdownServerSentry())
130+
this.nuxt.hook('generate:done', shutdownServerSentryOnce)
131+
this.nuxt.hook('close', shutdownServerSentryOnce)
123132
}
124133

125134
// Enable publishing of sourcemaps

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,6 @@
8080
"playwright-chromium": "^1.28.1",
8181
"release-it": "^15.5.1",
8282
"sentry-testkit": "^4.1.0",
83-
"typescript": "^4.9.4"
83+
"typescript": "4.8.4"
8484
}
8585
}

types/sentry.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
import { IncomingMessage, ServerResponse } from 'http'
13
import { Options as WebpackOptions } from 'webpack'
24
import { BrowserTracing } from '@sentry/tracing'
35
import { Options as SentryOptions } from '@sentry/types'
46
import { BrowserOptions } from '@sentry/browser'
57
import { SentryCliPluginOptions } from '@sentry/webpack-plugin'
68
import { Handlers } from '@sentry/node'
79

10+
export interface SentryHandlerProxy {
11+
errorHandler: (error: any, req: IncomingMessage, res: ServerResponse, next: (error: any) => void) => void
12+
requestHandler: (req: IncomingMessage, res: ServerResponse, next: (error?: any) => void) => void
13+
}
14+
815
export type IntegrationsConfiguration = Record<string, unknown>
916

1017
export interface LazyConfiguration {

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15075,10 +15075,10 @@ typedarray@^0.0.6:
1507515075
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
1507615076
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
1507715077

15078-
typescript@^4.9.4:
15079-
version "4.9.4"
15080-
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78"
15081-
integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==
15078+
typescript@4.8.4:
15079+
version "4.8.4"
15080+
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6"
15081+
integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==
1508215082

1508315083
ua-parser-js@^0.7.28:
1508415084
version "0.7.31"

0 commit comments

Comments
 (0)