From a028e22e9cacfb0322ecfd7c46a6bb93cd94d315 Mon Sep 17 00:00:00 2001 From: naaa760 Date: Fri, 23 Jan 2026 12:41:58 +0530 Subject: [PATCH 1/3] added proxy target detection for manual tracking scenarios --- packages/node-core/src/integrations/pino.ts | 89 +++++++++++++++++++-- 1 file changed, 82 insertions(+), 7 deletions(-) diff --git a/packages/node-core/src/integrations/pino.ts b/packages/node-core/src/integrations/pino.ts index bd8c8ba40b4f..2836734b342d 100644 --- a/packages/node-core/src/integrations/pino.ts +++ b/packages/node-core/src/integrations/pino.ts @@ -13,6 +13,9 @@ import { addInstrumentationConfig } from '../sdk/injectLoader'; const SENTRY_TRACK_SYMBOL = Symbol('sentry-track-pino-logger'); +const trackedLoggers = new WeakSet(); +const ignoredLoggers = new WeakSet(); + type LevelMapping = { // Fortunately pino uses the same levels as Sentry labels: { [level: number]: LogSeverityLevel }; @@ -24,6 +27,29 @@ type Pino = { [SENTRY_TRACK_SYMBOL]?: 'track' | 'ignore'; }; +function isPinoLogger(obj: unknown): obj is Pino { + if (!obj || typeof obj !== 'object') { + return false; + } + + if ('levels' in obj && obj.levels && typeof obj.levels === 'object' && 'labels' in obj.levels) { + return true; + } + + const hasPinoSymbols = Object.getOwnPropertySymbols(obj).some(sym => + sym.toString().includes('pino') + ); + let proto = Object.getPrototypeOf(obj); + while (proto && proto !== Object.prototype) { + if ('levels' in proto && proto.levels && typeof proto.levels === 'object' && 'labels' in proto.levels) { + return true; + } + proto = Object.getPrototypeOf(proto); + } + + return hasPinoSymbols; +} + /** * Gets a custom Pino key from a logger instance by searching for the symbol. * Pino uses non-global symbols like Symbol('pino.messageKey'): https://github.com/pinojs/pino/blob/8a816c0b1f72de5ae9181f3bb402109b66f7d812/lib/symbols.js @@ -118,9 +144,22 @@ const _pinoIntegration = defineIntegration((userOptions: DeepPartial=v8.0.0 and Node >=20.6.0 or >=18.19.0 */ +function trackLoggerInstance(logger: Pino): void { + trackedLoggers.add(logger); + ignoredLoggers.delete(logger); + + try { + // @ts-expect-error - Checking proxy internals + if (logger.constructor === Proxy) { + const target = (logger as any).__target__ || (logger as any).target; + if (target && isPinoLogger(target)) { + trackedLoggers.add(target); + ignoredLoggers.delete(target); + } + } + } catch { + // Ignore errors when trying to access proxy internals + } +} + +function untrackLoggerInstance(logger: Pino): void { + ignoredLoggers.add(logger); + trackedLoggers.delete(logger); + + try { + // @ts-expect-error - Checking proxy internals + if (logger.constructor === Proxy) { + const target = (logger as any).__target__ || (logger as any).target; + if (target && isPinoLogger(target)) { + ignoredLoggers.add(target); + trackedLoggers.delete(target); + } + } + } catch { + // Ignore errors + } +} + export const pinoIntegration = Object.assign(_pinoIntegration, { trackLogger(logger: unknown): void { - if (logger && typeof logger === 'object' && 'levels' in logger) { - (logger as Pino)[SENTRY_TRACK_SYMBOL] = 'track'; + if (isPinoLogger(logger)) { + trackLoggerInstance(logger as Pino); } }, untrackLogger(logger: unknown): void { - if (logger && typeof logger === 'object' && 'levels' in logger) { - (logger as Pino)[SENTRY_TRACK_SYMBOL] = 'ignore'; + if (isPinoLogger(logger)) { + untrackLoggerInstance(logger as Pino); } }, }) as PinoIntegrationFunction; From fa40481e253b632c36f15219b7ab5eedddfef21b Mon Sep 17 00:00:00 2001 From: naaa760 Date: Fri, 23 Jan 2026 12:42:38 +0530 Subject: [PATCH 2/3] added test case simulating cls-proxify proxy behavior --- .../suites/pino/scenario-proxied.mjs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 dev-packages/node-integration-tests/suites/pino/scenario-proxied.mjs diff --git a/dev-packages/node-integration-tests/suites/pino/scenario-proxied.mjs b/dev-packages/node-integration-tests/suites/pino/scenario-proxied.mjs new file mode 100644 index 000000000000..1dae19a2d809 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/pino/scenario-proxied.mjs @@ -0,0 +1,36 @@ +import * as Sentry from '@sentry/node'; +import pino from 'pino'; + +function createProxiedLogger(originalLogger) { + return new Proxy(originalLogger, { + get(target, prop) { + const value = target[prop]; + if (typeof value === 'function') { + return (...args) => { + return value.apply(target, args); + }; + } + return value; + } + }); +} + +const baseLogger = pino({ name: 'proxied-app' }); +const proxiedLogger = createProxiedLogger(baseLogger); + +Sentry.pinoIntegration.trackLogger(proxiedLogger); + +Sentry.withIsolationScope(() => { + Sentry.startSpan({ name: 'startup' }, () => { + proxiedLogger.info({ user: 'user-id', context: 'proxied' }, 'hello from proxied logger'); + }); +}); + +setTimeout(() => { + Sentry.withIsolationScope(() => { + Sentry.startSpan({ name: 'later' }, () => { + const child = proxiedLogger.child({ module: 'authentication' }); + child.error(new Error('oh no from proxied logger')); + }); + }); +}, 1000); From 7224cdc4c6433c15e919f882d6e98be4754dd6e9 Mon Sep 17 00:00:00 2001 From: naaa760 Date: Fri, 23 Jan 2026 12:43:05 +0530 Subject: [PATCH 3/3] test configuration for proxied logger scenarios --- .../suites/pino/instrument-proxied.mjs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 dev-packages/node-integration-tests/suites/pino/instrument-proxied.mjs diff --git a/dev-packages/node-integration-tests/suites/pino/instrument-proxied.mjs b/dev-packages/node-integration-tests/suites/pino/instrument-proxied.mjs new file mode 100644 index 000000000000..1e72e58fb043 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/pino/instrument-proxied.mjs @@ -0,0 +1,9 @@ +import * as Sentry from '@sentry/node'; + +Sentry.init({ + dsn: process.env.SENTRY_DSN, + release: '1.0', + tracesSampleRate: 1.0, + enableLogs: true, + integrations: [Sentry.pinoIntegration({ autoInstrument: false })], +});