UNPKG

unnbound-logger-sdk

Version:

A structured logging library with TypeScript support using Pino. Provides consistent, well-typed logging with automatic logId, workflowId, traceId, and deploymentId tracking across operational contexts.

113 lines (112 loc) 3.93 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.logger = void 0; const pino_1 = __importDefault(require("pino")); const uuid_1 = require("uuid"); const storage_1 = require("./storage"); const encode_1 = require("./encode"); const internal_1 = require("./internal"); const levels = new Set(['debug', 'info', 'warn', 'error']); const formatLevel = (level) => ({ level }); // TODO: Check if there is a better way to encode the log object. const formatLog = (log, visited = new WeakSet()) => { // Prevent infinite loops from circular references if (visited.has(log)) return log; visited.add(log); // We can't encode frozen objects. if (Object.isFrozen(log)) return log; // We don't use Object.entries() for performance reasons for (const key in log) { const value = log[key]; // We don't want to encode undefined values. if (value == undefined) continue; // We can't encode immutable properties. if (!Object.getOwnPropertyDescriptor(log, key)?.writable) continue; if (typeof value === 'string') { log[key] = (0, encode_1.encode)(value); } else if (Array.isArray(value)) { log[key] = value.map((item) => typeof item === 'string' ? (0, encode_1.encode)(item) : item && typeof item === 'object' ? formatLog(item, visited) : item); } else if (typeof value === 'object') { log[key] = formatLog(value, visited); } } return log; }; exports.logger = (0, pino_1.default)({ level: process.env.LOG_LEVEL ?? 'debug', base: { environment: process.env.ENVIRONMENT, workflowId: process.env.UNNBOUND_WORKFLOW_ID, serviceId: process.env.UNNBOUND_SERVICE_ID, deploymentId: process.env.UNNBOUND_DEPLOYMENT_ID, }, mixin: () => ({ ...storage_1.storage.getStore(), logId: (0, uuid_1.v4)() }), serializers: { req: pino_1.default.stdSerializers.req, res: pino_1.default.stdSerializers.res, err: pino_1.default.stdSerializers.err, message: encode_1.encode, }, // Let CloudWatch handle timestamps timestamp: false, // Change message field from 'msg' to 'message' messageKey: 'message', formatters: { level: formatLevel, log: formatLog }, hooks: { logMethod(args, method) { const firstArg = args[0]; if (!!firstArg && typeof firstArg === 'object') { // If the log entry is considered hidden, don't log it if ((0, internal_1.hidden)(firstArg)) return; // Dynamic log level by allowing overwriting the log level if ('level' in firstArg && typeof firstArg.level === 'string' && levels.has(firstArg.level)) { method = this[firstArg.level]; firstArg.level = undefined; } } method.apply(this, args); }, }, redact: { paths: [ 'http.request.headers.authorization', 'http.response.headers.authorization', 'http.request.headers.Authorization', 'http.response.headers.Authorization', 'err.config', ], remove: true, }, }); process.on('uncaughtException', (err, origin) => { try { exports.logger.error({ err, origin }, `Uncaught exception.`); } catch { // noop } }); process.on('unhandledRejection', (err, origin) => { try { exports.logger.error({ err, origin }, `Unhandled rejection.`); } catch { // noop } });