const pino = require('pino'); const logger = pino({ level: 'info', redact: { paths: [ // List of Paths to redact from the logs (https://getpino.io/#/docs/redaction) 'env.OPENAI_API_KEY', 'env.BINGAI_TOKEN', 'env.CHATGPT_TOKEN', 'env.MEILI_MASTER_KEY', 'env.GOOGLE_CLIENT_SECRET', 'env.JWT_SECRET', 'env.JWT_SECRET_DEV', 'env.JWT_SECRET_PROD', 'newUser.password', ], // See example to filter object class instances censor: '***', // Redaction character }, }); // Sanitize outside the logger paths. This is useful for sanitizing variables directly with Regex and patterns. const redactPatterns = [ // Array of regular expressions for redacting patterns /api[-_]?key/i, /password/i, /token/i, /secret/i, /key/i, /certificate/i, /client[-_]?id/i, /authorization[-_]?code/i, /authorization[-_]?login[-_]?hint/i, /authorization[-_]?acr[-_]?values/i, /authorization[-_]?response[-_]?mode/i, /authorization[-_]?nonce/i, ]; /* // Example of redacting sensitive data from object class instances function redactSensitiveData(obj) { if (obj instanceof User) { return { ...obj.toObject(), password: '***', // Redact the password field }; } return obj; } // Example of redacting sensitive data from object class instances logger.info({ newUser: redactSensitiveData(newUser) }, 'newUser'); */ const levels = { TRACE: 10, DEBUG: 20, INFO: 30, WARN: 40, ERROR: 50, FATAL: 60, }; let level = levels.INFO; module.exports = { levels, setLevel: (l) => (level = l), log: { trace: (msg) => { if (level <= levels.TRACE) { return; } logger.trace(msg); }, debug: (msg) => { if (level <= levels.DEBUG) { return; } logger.debug(msg); }, info: (msg) => { if (level <= levels.INFO) { return; } logger.info(msg); }, warn: (msg) => { if (level <= levels.WARN) { return; } logger.warn(msg); }, error: (msg) => { if (level <= levels.ERROR) { return; } logger.error(msg); }, fatal: (msg) => { if (level <= levels.FATAL) { return; } logger.fatal(msg); }, // Custom loggers parameters: (parameters) => { if (level <= levels.TRACE) { return; } logger.debug({ parameters }, 'Function Parameters'); }, functionName: (name) => { if (level <= levels.TRACE) { return; } logger.debug(`EXECUTING: ${name}`); }, flow: (flow) => { if (level <= levels.INFO) { return; } logger.debug(`BEGIN FLOW: ${flow}`); }, variable: ({ name, value }) => { if (level <= levels.DEBUG) { return; } // Check if the variable name matches any of the redact patterns and redact the value let sanitizedValue = value; for (const pattern of redactPatterns) { if (pattern.test(name)) { sanitizedValue = '***'; break; } } logger.debug({ variable: { name, value: sanitizedValue } }, `VARIABLE ${name}`); }, request: () => (req, res, next) => { if (level < levels.DEBUG) { return next(); } logger.debug({ query: req.query, body: req.body }, `Hit URL ${req.url} with following`); return next(); }, }, };