import * as diagnosticsChannel from 'node:diagnostics_channel'; import { defineIntegration, _INTERNAL_captureLog, severityLevelFromString, withScope, addExceptionMechanism, captureException, captureMessage } from '@sentry/core'; import { addInstrumentationConfig } from '../sdk/injectLoader.js'; const SENTRY_TRACK_SYMBOL = Symbol('sentry-track-pino-logger'); /** * 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 */ function getPinoKey(logger, symbolName, defaultKey) { const symbols = Object.getOwnPropertySymbols(logger); const symbolString = `Symbol(${symbolName})`; for (const sym of symbols) { if (sym.toString() === symbolString) { const value = logger[sym]; return typeof value === 'string' ? value : defaultKey; } } return defaultKey; } const DEFAULT_OPTIONS = { error: { levels: [], handled: true }, log: { levels: ['trace', 'debug', 'info', 'warn', 'error', 'fatal'] }, }; function stripIgnoredFields(result) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { level, time, pid, hostname, ...rest } = result; return rest; } const _pinoIntegration = defineIntegration((userOptions = {}) => { const options = { autoInstrument: userOptions.autoInstrument !== false, error: { ...DEFAULT_OPTIONS.error, ...userOptions.error }, log: { ...DEFAULT_OPTIONS.log, ...userOptions.log }, }; function shouldTrackLogger(logger) { const override = logger[SENTRY_TRACK_SYMBOL]; return override === 'track' || (override !== 'ignore' && options.autoInstrument); } 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 = diagnosticsChannel.tracingChannel('orchestrion:pino:pino-log'); const integratedChannel = diagnosticsChannel.tracingChannel('pino_asJson'); function onPinoStart(self, args, result) { if (!shouldTrackLogger(self)) { return; } const resultObj = stripIgnoredFields(result); const [captureObj, message, levelNumber] = args; const level = self?.levels?.labels?.[levelNumber] || 'info'; const messageKey = getPinoKey(self, 'pino.messageKey', 'msg'); const logMessage = message || (resultObj?.[messageKey] ) || ''; if (enableLogs && options.log.levels.includes(level)) { const attributes = { ...resultObj, 'sentry.origin': 'auto.log.pino', 'pino.logger.level': levelNumber, }; _INTERNAL_captureLog({ level, message: logMessage, 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; }); const error = captureObj[getPinoKey(self, 'pino.errorKey', 'err')]; if (error) { captureException(error, captureContext); return; } captureMessage(logMessage, captureContext); }); } } injectedChannel.end.subscribe(data => { const { self, arguments: args, result } = data ; onPinoStart(self, args, JSON.parse(result)); }); integratedChannel.end.subscribe(data => { const { instance, arguments: args, result, } = data ; onPinoStart(instance, args, JSON.parse(result)); }); }, }; }) ; /** * Integration for Pino logging library. * Captures Pino logs as Sentry logs and optionally captures some log levels as events. * * By default, all Pino loggers will be captured. To ignore a specific logger, use `pinoIntegration.untrackLogger(logger)`. * * If you disable automatic instrumentation with `autoInstrument: false`, you can mark specific loggers to be tracked with `pinoIntegration.trackLogger(logger)`. * * Requires Pino >=v8.0.0 and Node >=20.6.0 or >=18.19.0 */ const pinoIntegration = Object.assign(_pinoIntegration, { trackLogger(logger) { if (logger && typeof logger === 'object' && 'levels' in logger) { (logger )[SENTRY_TRACK_SYMBOL] = 'track'; } }, untrackLogger(logger) { if (logger && typeof logger === 'object' && 'levels' in logger) { (logger )[SENTRY_TRACK_SYMBOL] = 'ignore'; } }, }) ; export { pinoIntegration }; //# sourceMappingURL=pino.js.map