-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(node): pino
integration
#17584
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
feat(node): pino
integration
#17584
Changes from 33 commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
bf70c17
feat: Tidy existing loader hook
timfish 9ee96ab
Oh linty
timfish 9a57643
fix detection
timfish 9fb250e
Use `NODE_MAJOR` and `NODE_MINOR`
timfish 94a22de
Merge branch 'develop' into timfish/feat/tidy-loader-hooks
timfish 594286c
feat: `pino` integration
timfish 1699704
Merge remote-tracking branch 'upstream/develop' into timfish/feat/pin…
timfish 630a8c7
Update deps
timfish fd32e77
Merge remote-tracking branch 'upstream/develop' into timfish/feat/pin…
timfish c2f81e1
Oh lint!
timfish 850c19d
Correct yarn.lock
timfish 144b7db
Merge branch 'develop' into timfish/feat/pino-integration
timfish 7610cda
Tests
timfish e598c6a
Merge branch 'timfish/feat/pino-integration' of github.com:getsentry/…
timfish 2647b20
More test fixes
timfish e0f6a40
Merge branch 'develop' into timfish/feat/pino-integration
timfish 6089389
Fix size limit
timfish 687a619
Merge branch 'timfish/feat/pino-integration' of github.com:getsentry/…
timfish 6b8f76e
Merge branch 'develop' into timfish/feat/pino-integration
timfish 421ad50
Make hook tree-shakable
timfish b12150c
Pino added tracing channel
timfish d61b2e6
Merge branch 'develop' into timfish/feat/pino-integration
timfish 40bf557
Merge branch 'timfish/feat/pino-integration' of github.com:getsentry/…
timfish 30a6688
Better bundling?
timfish 412cc67
Fixes
timfish 2e14fbe
Fix Pino >= 9.10.0
timfish 1115467
Lint
timfish ab7bce3
Merge branch 'develop' into timfish/feat/pino-integration
timfish 8b5a821
Fix yarn.lock
timfish 0715f38
Update deps
timfish 7a207f4
Merge branch 'develop' into timfish/feat/pino-integration
timfish 1a72eab
Just copy attributes
timfish ac49a64
PR review
timfish 4f50a93
Merge remote-tracking branch 'upstream/develop' into timfish/feat/pin…
timfish c1f6870
update deps
timfish 1a4b10e
remove yarn.lock dupes
timfish 11f77b5
Test on latest Pino with integrated channel
timfish File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
dev-packages/node-integration-tests/suites/pino/instrument.mjs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import * as Sentry from '@sentry/node'; | ||
|
||
Sentry.init({ | ||
dsn: process.env.SENTRY_DSN, | ||
release: '1.0', | ||
enableLogs: true, | ||
integrations: [Sentry.pinoIntegration({ error: { levels: ['error', 'fatal'] } })], | ||
}); |
18 changes: 18 additions & 0 deletions
18
dev-packages/node-integration-tests/suites/pino/scenario.mjs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import * as Sentry from '@sentry/node'; | ||
import pino from 'pino'; | ||
|
||
const logger = pino({}); | ||
|
||
Sentry.withIsolationScope(() => { | ||
Sentry.startSpan({ name: 'startup' }, () => { | ||
logger.info({ user: 'user-id', something: { more: 3, complex: 'nope' } }, 'hello world'); | ||
}); | ||
}); | ||
|
||
setTimeout(() => { | ||
Sentry.withIsolationScope(() => { | ||
Sentry.startSpan({ name: 'later' }, () => { | ||
logger.error(new Error('oh no')); | ||
}); | ||
}); | ||
}, 1000); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import { join } from 'path'; | ||
import { expect, test } from 'vitest'; | ||
import { conditionalTest } from '../../utils'; | ||
import { createRunner } from '../../utils/runner'; | ||
|
||
conditionalTest({ min: 20 })('Pino integration', () => { | ||
test('has different trace ids for logs from different spans', async () => { | ||
const instrumentPath = join(__dirname, 'instrument.mjs'); | ||
|
||
await createRunner(__dirname, 'scenario.mjs') | ||
.withMockSentryServer() | ||
.withInstrument(instrumentPath) | ||
.ignore('event') | ||
.expect({ | ||
log: log => { | ||
const traceId1 = log.items?.[0]?.trace_id; | ||
const traceId2 = log.items?.[1]?.trace_id; | ||
expect(traceId1).not.toBe(traceId2); | ||
}, | ||
}) | ||
.start() | ||
.completed(); | ||
}); | ||
|
||
test('captures event and logs', async () => { | ||
// expect.assertions(1); | ||
const instrumentPath = join(__dirname, 'instrument.mjs'); | ||
|
||
await createRunner(__dirname, 'scenario.mjs') | ||
.withMockSentryServer() | ||
.withInstrument(instrumentPath) | ||
.expect({ | ||
event: { | ||
exception: { | ||
values: [ | ||
{ | ||
type: 'Error', | ||
value: 'oh no', | ||
mechanism: { | ||
type: 'pino', | ||
handled: true, | ||
}, | ||
stacktrace: { | ||
frames: expect.arrayContaining([ | ||
expect.objectContaining({ | ||
function: '?', | ||
in_app: true, | ||
module: 'scenario', | ||
context_line: " logger.error(new Error('oh no'));", | ||
}), | ||
]), | ||
}, | ||
}, | ||
], | ||
}, | ||
}, | ||
}) | ||
.expect({ | ||
log: { | ||
items: [ | ||
{ | ||
timestamp: expect.any(Number), | ||
level: 'info', | ||
body: 'hello world', | ||
trace_id: expect.any(String), | ||
severity_number: 9, | ||
attributes: expect.objectContaining({ | ||
'sentry.origin': { value: 'auto.logging.pino', type: 'string' }, | ||
'sentry.pino.level': { value: 30, type: 'integer' }, | ||
user: { value: 'user-id', type: 'string' }, | ||
something: { | ||
type: 'string', | ||
value: '{"more":3,"complex":"nope"}', | ||
}, | ||
'sentry.release': { value: '1.0', type: 'string' }, | ||
'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, | ||
}), | ||
}, | ||
{ | ||
timestamp: expect.any(Number), | ||
level: 'error', | ||
body: 'oh no', | ||
trace_id: expect.any(String), | ||
severity_number: 17, | ||
attributes: expect.objectContaining({ | ||
'sentry.origin': { value: 'auto.logging.pino', type: 'string' }, | ||
'sentry.pino.level': { value: 50, type: 'integer' }, | ||
err: { value: '{}', type: 'string' }, | ||
'sentry.release': { value: '1.0', type: 'string' }, | ||
'sentry.sdk.name': { value: 'sentry.javascript.node', type: 'string' }, | ||
}), | ||
}, | ||
], | ||
}, | ||
}) | ||
.start() | ||
.completed(); | ||
}); | ||
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
import { tracingChannel } from 'node:diagnostics_channel'; | ||
import type { IntegrationFn, LogSeverityLevel } from '@sentry/core'; | ||
import { | ||
_INTERNAL_captureLog, | ||
addExceptionMechanism, | ||
captureException, | ||
captureMessage, | ||
defineIntegration, | ||
severityLevelFromString, | ||
withScope, | ||
} from '@sentry/core'; | ||
import { addInstrumentationConfig } from '../sdk/injectLoader'; | ||
|
||
type LevelMapping = { | ||
// Fortunately pino uses the same levels as Sentry | ||
labels: { [level: number]: LogSeverityLevel }; | ||
}; | ||
|
||
type Pino = { | ||
levels: LevelMapping; | ||
}; | ||
|
||
type MergeObject = { | ||
[key: string]: unknown; | ||
err?: Error; | ||
}; | ||
|
||
type PinoHookArgs = [MergeObject, string, number]; | ||
|
||
type PinoOptions = { | ||
error: { | ||
/** | ||
* Levels that trigger capturing of events. | ||
* | ||
* @default [] | ||
*/ | ||
levels: LogSeverityLevel[]; | ||
/** | ||
* By default, Sentry will mark captured errors as handled. | ||
* Set this to `false` if you want to mark them as unhandled instead. | ||
* | ||
* @default true | ||
*/ | ||
handled: boolean; | ||
}; | ||
log: { | ||
/** | ||
* Levels that trigger capturing of logs. Logs are only captured if | ||
* `enableLogs` is enabled. | ||
* | ||
* @default ["trace", "debug", "info", "warn", "error", "fatal"] | ||
*/ | ||
levels: LogSeverityLevel[]; | ||
}; | ||
}; | ||
|
||
const DEFAULT_OPTIONS: PinoOptions = { | ||
error: { levels: [], handled: true }, | ||
log: { levels: ['trace', 'debug', 'info', 'warn', 'error', 'fatal'] }, | ||
}; | ||
|
||
type DeepPartial<T> = { | ||
[P in keyof T]?: T[P] extends object ? Partial<T[P]> : T[P]; | ||
}; | ||
|
||
/** | ||
* Integration for Pino logging library. | ||
* Captures Pino logs as Sentry logs and optionally captures some log levels as events. | ||
* | ||
* Requires Pino >=v8.0.0 and Node >=20.6.0 or >=18.19.0 | ||
*/ | ||
export const pinoIntegration = defineIntegration((userOptions: DeepPartial<PinoOptions> = {}) => { | ||
const options: PinoOptions = { | ||
error: { ...DEFAULT_OPTIONS.error, ...userOptions.error }, | ||
log: { ...DEFAULT_OPTIONS.log, ...userOptions.log }, | ||
}; | ||
|
||
return { | ||
name: 'Pino', | ||
setup: client => { | ||
const enableLogs = !!client.getOptions().enableLogs; | ||
|
||
addInstrumentationConfig({ | ||
channelName: 'pino-log', | ||
// From Pino v9.10.0 a tracing channel is available directly from Pino: | ||
// https://github.com/pinojs/pino/pull/2281 | ||
module: { name: 'pino', versionRange: '>=8.0.0 < 9.10.0', filePath: 'lib/tools.js' }, | ||
functionQuery: { | ||
functionName: 'asJson', | ||
kind: 'Sync', | ||
}, | ||
}); | ||
|
||
const injectedChannel = tracingChannel('orchestrion:pino:pino-log'); | ||
const integratedChannel = tracingChannel('tracing:pino_asJson'); | ||
|
||
function onPinoStart(self: Pino, args: PinoHookArgs): void { | ||
const [obj, message, levelNumber] = args; | ||
const level = self?.levels?.labels?.[levelNumber] || 'info'; | ||
|
||
const attributes = { | ||
...obj, | ||
'sentry.origin': 'auto.logging.pino', | ||
'sentry.pino.level': levelNumber, | ||
}; | ||
|
||
if (enableLogs && options.log.levels.includes(level)) { | ||
_INTERNAL_captureLog({ level, message, attributes }); | ||
} | ||
|
||
if (options.error.levels.includes(level)) { | ||
const captureContext = { | ||
level: severityLevelFromString(level), | ||
}; | ||
|
||
withScope(scope => { | ||
scope.addEventProcessor(event => { | ||
event.logger = 'pino'; | ||
|
||
addExceptionMechanism(event, { | ||
handled: options.error.handled, | ||
type: 'pino', | ||
}); | ||
|
||
return event; | ||
}); | ||
|
||
if (obj.err) { | ||
captureException(obj.err, captureContext); | ||
return; | ||
} | ||
|
||
captureMessage(message, captureContext); | ||
}); | ||
} | ||
} | ||
|
||
injectedChannel.start.subscribe(data => { | ||
const { self, arguments: args } = data as { self: Pino; arguments: PinoHookArgs }; | ||
onPinoStart(self, args); | ||
}); | ||
|
||
integratedChannel.start.subscribe(data => { | ||
const { instance, arguments: args } = data as { instance: Pino; arguments: PinoHookArgs }; | ||
onPinoStart(instance, args); | ||
}); | ||
}, | ||
}; | ||
}) satisfies IntegrationFn; |
11 changes: 11 additions & 0 deletions
11
packages/node-core/src/sdk/apm-js-collab-tracing-hooks.d.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
declare module '@apm-js-collab/tracing-hooks' { | ||
import type { InstrumentationConfig } from '@apm-js-collab/code-transformer'; | ||
|
||
type PatchConfig = { instrumentations: InstrumentationConfig[] }; | ||
|
||
/** Hooks require */ | ||
export default class ModulePatch { | ||
public constructor(config: PatchConfig): ModulePatch; | ||
public patch(): void; | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.