Skip to content

Commit

Permalink
feat(profiling): add support for enabling Profiling integration (#577)
Browse files Browse the repository at this point in the history
  • Loading branch information
rchl authored May 21, 2023
1 parent bde9750 commit 9a9aa85
Show file tree
Hide file tree
Showing 6 changed files with 436 additions and 23 deletions.
1 change: 1 addition & 0 deletions build.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default defineBuildConfig({
'@sentry/cli',
'@sentry/core',
'@sentry/node',
'@sentry/profiling-node',
'@sentry/types',
'@sentry/webpack-plugin',
'consola',
Expand Down
57 changes: 57 additions & 0 deletions docs/content/en/guide/profiling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
title: Profiling
description: Node profiling enhances tracing by providing profiles for individual transactions
position: 25
category: Guide
---

Node profiling can be enabled through an integration provided by the `@sentry/profiling-node` dependency that does not come with this module by default.

### Setup

Install required dependency:

<code-group>
<code-block label="Yarn" active>

```bash
yarn add @sentry/profiling-node
```

</code-block>
<code-block label="NPM">

```bash
npm install @sentry/profiling-node
```

</code-block>
</code-group>

Include the following options in the module's configuration:

```js [nuxt.config.js]
sentry: {
dsn: '...',
tracing: {
tracesSampleRate: 1.0,
},
serverIntegrations: {
ProfilingIntegration: {},
},
serverConfig: {
// Set sampling rate for profiling - this is relative to tracesSampleRate
profilesSampleRate: 1.0,
},
}
```

<alert type="info">

Note that the `tracesSampleRate` value can be between 0.0 and 1.0 (percentage of requests to capture) and Sentry documentation strongly recommends reducing the value from the default 1.0.

</alert>

### Documentation

See Sentry's [Profiling](https://docs.sentry.io/platforms/node/profiling/) pages for additional information.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
"@nuxtjs/eslint-config-typescript": "12.0.0",
"@nuxtjs/module-test-utils": "1.6.3",
"@release-it/conventional-changelog": "5.1.1",
"@sentry/profiling-node": "^0.3.0",
"@sentry/webpack-plugin": "1.20.1",
"@size-limit/file": "^8.2.4",
"@types/hash-sum": "1.0.0",
Expand Down
24 changes: 20 additions & 4 deletions src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { relative } from 'pathe'
import { Integrations as ServerIntegrations, autoDiscoverNodePerformanceMonitoringIntegrations } from '@sentry/node'
import type Sentry from '@sentry/node'
import * as PluggableIntegrations from '@sentry/integrations'
import type { Options } from '@sentry/types'
import type { Integration, Options } from '@sentry/types'
import type { Replay } from '@sentry/vue'
import type { AllIntegrations, ClientIntegrations, LazyConfiguration, TracingConfiguration } from './types/configuration'
import type { AllIntegrations, ClientIntegrations, LazyConfiguration, ProfilingIntegration, TracingConfiguration } from './types/configuration'
import type { ModuleConfiguration } from './types'
import { Nuxt, resolveAlias } from './kit-shim'
import { canInitialize } from './utils'
Expand All @@ -27,6 +27,8 @@ export const BROWSER_VUE_INTEGRATIONS = ['Replay']
const SERVER_INTEGRATIONS = ['Console', 'ContextLines', 'FunctionToString', 'Http', 'InboundFilters', 'LinkedErrors', 'LocalVariables', 'Modules', 'OnUncaughtException', 'OnUnhandledRejection', 'RequestData']
// Optional in Node.js - https://docs.sentry.io/platforms/node/configuration/integrations/pluggable-integrations/
const SERVER_PLUGGABLE_INTEGRATIONS = ['CaptureConsole', 'Debug', 'Dedupe', 'ExtraErrorData', 'RewriteFrames', 'Transaction']
// External and optional Node.js integration - https://docs.sentry.io/platforms/node/profiling/
export const SERVER_PROFILING_INTEGRATION: keyof ProfilingIntegration = 'ProfilingIntegration'

function filterDisabledIntegrations<T extends AllIntegrations> (integrations: T): (keyof T)[] {
return getIntegrationsKeys(integrations).filter(key => integrations[key])
Expand Down Expand Up @@ -246,13 +248,13 @@ export async function resolveServerOptions (nuxt: Nuxt, moduleOptions: Readonly<
options.config = defu(getServerRuntimeConfig(nuxt, options), options.serverConfig, options.config)

for (const name of getIntegrationsKeys(options.serverIntegrations)) {
if (!isServerDefaultIntegration(name) && !isServerPlugabbleIntegration(name)) {
if (!isServerDefaultIntegration(name) && !isServerPlugabbleIntegration(name) && name !== SERVER_PROFILING_INTEGRATION) {
logger.warn(`Sentry serverIntegration "${name}" is not recognized and will be ignored.`)
delete options.serverIntegrations[name]
}
}

let customIntegrations = []
let customIntegrations: Integration[] = []
if (options.customServerIntegrations) {
const resolvedPath = resolveAlias(options.customServerIntegrations)
try {
Expand All @@ -265,6 +267,20 @@ export async function resolveServerOptions (nuxt: Nuxt, moduleOptions: Readonly<
}
}

if (SERVER_PROFILING_INTEGRATION in options.serverIntegrations) {
const enabled = options.serverIntegrations[SERVER_PROFILING_INTEGRATION]
delete options.serverIntegrations[SERVER_PROFILING_INTEGRATION]
if (enabled) {
try {
const { ProfilingIntegration } = await (import('@sentry/profiling-node').then(m => m.default || m))
customIntegrations.push(new ProfilingIntegration())
} catch (error) {
logger.error(`To use the ${SERVER_PROFILING_INTEGRATION} integration you need to install the "@sentry/profiling-node" dependency.`)
throw new Error((error as Error).message)
}
}
}

options.config.integrations = [
// Automatically instrument Node.js libraries and frameworks
...(options.tracing ? autoDiscoverNodePerformanceMonitoringIntegrations() : []),
Expand Down
5 changes: 4 additions & 1 deletion src/types/configuration.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ type IntegrationsConfig<T extends Record<keyof T, IntegrationClass<unknown>>> =
[K in keyof T]: ConstructorParameters<T[K]>[0] | Record<string, never> | false
}>

// A replacement type since we don't want to depend on `@sentry/profiling-node`
type ProfilingIntegration = { ProfilingIntegration?: Record<string, never> | false }

type ClientIntegrations = IntegrationsConfig<typeof BrowserIntegrations & typeof PluggableIntegrations & { Replay: typeof Replay }>
type ServerIntegrations = IntegrationsConfig<typeof NodeIntegrations & typeof PluggableIntegrations>
type ServerIntegrations = IntegrationsConfig<typeof NodeIntegrations & typeof PluggableIntegrations> & ProfilingIntegration
type AllIntegrations = ClientIntegrations | ServerIntegrations

export interface LazyConfiguration {
Expand Down
Loading

0 comments on commit 9a9aa85

Please sign in to comment.