diff --git a/package.json b/package.json index cf31664..4e221b3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@serverless-guru/logger", - "version": "1.0.1", + "version": "1.0.2", "description": "Common logger utility", "main": "./lib/cjs/index.js", "types": "./lib/cjs/index.d.ts", diff --git a/src/index.ts b/src/index.ts index b6d31ce..65437d5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -48,162 +48,168 @@ class Logger { context: JSONObject = {}, sensitiveAttributes: StringArray = [] ) { - // Default sensitive attributes - const defaultSensitiveAttributes: StringArray = [ - "password", - "userid", - "token", - "secret", - "key", - "x-api-key", - "bearer", - "authorization", - ]; - - const arrayToLowerCase = (array: StringArray): StringArray => { - if (Array.isArray(array)) { - return array - .map((el) => { - if (typeof el === "string") { - return el.toLowerCase(); - } - return undefined; - }) - .filter((el) => typeof el !== "undefined"); - } - return []; - }; + try { + // Default sensitive attributes + const defaultSensitiveAttributes: StringArray = [ + "password", + "userid", + "token", + "secret", + "key", + "x-api-key", + "bearer", + "authorization", + ]; + + const arrayToLowerCase = (array: StringArray): StringArray => { + if (Array.isArray(array)) { + return array + .map((el) => { + if (typeof el === "string") { + return el.toLowerCase(); + } + return undefined; + }) + .filter((el) => typeof el !== "undefined"); + } + return []; + }; - // Merge default sensitive attributes with custom ones - const attributesToMask = new Set([...defaultSensitiveAttributes, ...arrayToLowerCase(sensitiveAttributes)]); + // Merge default sensitive attributes with custom ones + const attributesToMask = new Set([...defaultSensitiveAttributes, ...arrayToLowerCase(sensitiveAttributes)]); - // Mask sensitive attributes, remove null - const maskSensitiveAttributes = (key: string, value: JSONObject): JSONObject | string | undefined => { - if (value === null) { - return undefined; - } - if (typeof value === "object" && isEmptyObject(value)) { - return undefined; - } - if (SKIP_MASK === true) { + // Mask sensitive attributes, remove null + const maskSensitiveAttributes = (key: string, value: JSONObject): JSONObject | string | undefined => { + if (value === null) { + return undefined; + } + if (typeof value === "object" && isEmptyObject(value)) { + return undefined; + } + if (SKIP_MASK === true) { + return value; + } + if (attributesToMask.has(key.toLowerCase())) { + return "****"; + } return value; - } - if (attributesToMask.has(key.toLowerCase())) { - return "****"; - } - return value; - }; - - const isEmptyObject = (value: JSONObject): boolean => { - if (value == null) { - return false; - } - if (typeof value !== "object") { - return false; - } - const proto = Object.getPrototypeOf(value); - if (proto !== null && proto !== Object.prototype) { - return false; - } - return isEmpty(value); - }; + }; - const isEmpty = (obj: JSONObject): boolean => { - for (const prop in obj) { - if (Object.hasOwn(obj, prop)) { + const isEmptyObject = (value: JSONObject): boolean => { + if (value == null) { return false; } - } - return true; - }; + if (typeof value !== "object") { + return false; + } + const proto = Object.getPrototypeOf(value); + if (proto !== null && proto !== Object.prototype) { + return false; + } + return isEmpty(value); + }; - const getPayloadToPrint = (payload: JSONObject | Error): PayloadToPrintResponse => { - if (payload instanceof Error) { - return { gzip: false, error: formatError(payload) }; - } - if (level === "warn" && message === MAX_PAYLOAD_MESSAGE) { - return { gzip: false, payload }; - } - const stringifiedPayload = JSON.stringify(payload, maskSensitiveAttributes); - if (stringifiedPayload.length > MAX_SIZE && !NO_SKIP) { - this.warn(MAX_PAYLOAD_MESSAGE, { size: stringifiedPayload.length, MAX_SIZE }); - return { gzip: false, payload: undefined }; - } - if (stringifiedPayload.length > COMPRESS_SIZE && !NO_COMPRESS) { - return { gzip: true, payload: gzipSync(stringifiedPayload).toString("base64") }; - } - return { gzip: false, payload }; - }; + const isEmpty = (obj: JSONObject): boolean => { + for (const prop in obj) { + if (Object.hasOwn(obj, prop)) { + return false; + } + } + return true; + }; - const formatError = (error: Error): ErrorLogAttributes => { - return { - name: error.name, - location: getCodeLocation(error.stack), - message: error.message, - stack: error.stack, - cause: error.cause instanceof Error ? formatError(error.cause) : error.cause, + const getPayloadToPrint = (payload: JSONObject | Error | undefined): PayloadToPrintResponse => { + try { + if (payload instanceof Error) { + return { gzip: false, error: formatError(payload) }; + } + if (level === "warn" && message === MAX_PAYLOAD_MESSAGE) { + return { gzip: false, payload }; + } + const stringifiedPayload = JSON.stringify(payload, maskSensitiveAttributes); + if (stringifiedPayload?.length > MAX_SIZE && !NO_SKIP) { + this.warn(MAX_PAYLOAD_MESSAGE, { size: stringifiedPayload.length, MAX_SIZE }); + return { gzip: false, payload: undefined }; + } + if (stringifiedPayload?.length > COMPRESS_SIZE && !NO_COMPRESS) { + return { gzip: true, payload: gzipSync(stringifiedPayload).toString("base64") }; + } + return { gzip: false, payload }; + } catch { + return {}; + } }; - }; - const getCodeLocation = (stack?: string) => { - if (!stack) { - return ""; - } + const formatError = (error: Error): ErrorLogAttributes => { + return { + name: error.name, + location: getCodeLocation(error.stack), + message: error.message, + stack: error.stack, + cause: error.cause instanceof Error ? formatError(error.cause) : error.cause, + }; + }; - const stackLines = stack.split("\n"); - const regex = /\(([^)]*?):(\d+?):(\d+?)\)\\?$/; + const getCodeLocation = (stack?: string) => { + if (!stack) { + return ""; + } - for (const item of stackLines) { - const match = regex.exec(item); + const stackLines = stack.split("\n"); + const regex = /\(([^)]*?):(\d+?):(\d+?)\)\\?$/; - if (Array.isArray(match)) { - return `${match[1]}:${Number(match[2])}`; - } - } + for (const item of stackLines) { + const match = regex.exec(item); - return ""; - }; + if (Array.isArray(match)) { + return `${match[1]}:${Number(match[2])}`; + } + } - const getTimestamp = (): number | undefined => { - if (LOG_TS) { - return new Date().getTime(); - } - return undefined; - }; + return ""; + }; - const payloadToPrint = getPayloadToPrint(payload); + const getTimestamp = (): number | undefined => { + if (LOG_TS) { + return new Date().getTime(); + } + return undefined; + }; - const logEntry: LogEntry = { - timestamp: getTimestamp(), - service: this.serviceName, - correlationId: this.correlationId, - message, - context: { - ...this.persistentContext, - ...(typeof context === "object" && context !== null && Object.keys(context).length ? context : {}), - gzip: payloadToPrint.gzip === true ? true : undefined, - }, - payload: payloadToPrint.payload, - error: payloadToPrint.error, - }; + const payloadToPrint = getPayloadToPrint(payload); + + const logEntry: LogEntry = { + timestamp: getTimestamp(), + service: this.serviceName, + correlationId: this.correlationId, + message, + context: { + ...this.persistentContext, + ...(typeof context === "object" && context !== null && Object.keys(context).length ? context : {}), + gzip: payloadToPrint.gzip === true ? true : undefined, + }, + payload: payloadToPrint.payload, + error: payloadToPrint.error, + }; - const stringifiedLogEntry = JSON.stringify(logEntry, maskSensitiveAttributes); - switch (level) { - case "info": - this.console.info(stringifiedLogEntry); - break; - case "debug": - this.console.debug(stringifiedLogEntry); - break; - case "warn": - this.console.warn(stringifiedLogEntry); - break; - case "error": - this.console.error(stringifiedLogEntry); - break; - default: - break; - } + const stringifiedLogEntry = JSON.stringify(logEntry, maskSensitiveAttributes); + switch (level) { + case "info": + this.console.info(stringifiedLogEntry); + break; + case "debug": + this.console.debug(stringifiedLogEntry); + break; + case "warn": + this.console.warn(stringifiedLogEntry); + break; + case "error": + this.console.error(stringifiedLogEntry); + break; + default: + break; + } + } catch {} } info(