UNPKG

@fjell/logging

Version:
1,166 lines (1,152 loc) 36.2 kB
var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // src/LogFormat.ts var LogFormat_exports = {}; __export(LogFormat_exports, { LogFormats: () => LogFormats, STRUCTURED: () => STRUCTURED, TEXT: () => TEXT, getConfig: () => getConfig }); var TEXT = { name: "TEXT", description: "Text format" }; var STRUCTURED = { name: "STRUCTURED", description: "Structured format" }; var LogFormats = [ TEXT, STRUCTURED ]; var getConfig = (name) => { const config = LogFormats.find((config2) => config2.name === name); if (!config) { throw new Error(`Invalid Log Format Supplied to Logging Configuration '${name}'`); } return config; }; // src/LogLevel.ts var LogLevel_exports = {}; __export(LogLevel_exports, { ALERT: () => ALERT, CRITICAL: () => CRITICAL, DEBUG: () => DEBUG, DEFAULT: () => DEFAULT, EMERGENCY: () => EMERGENCY, ERROR: () => ERROR, INFO: () => INFO, LogLevels: () => LogLevels, NOTICE: () => NOTICE, TRACE: () => TRACE, WARNING: () => WARNING, getConfig: () => getConfig2 }); var EMERGENCY = { name: "EMERGENCY", value: 0 }; var ALERT = { name: "ALERT", value: 1 }; var CRITICAL = { name: "CRITICAL", value: 2 }; var ERROR = { name: "ERROR", value: 3 }; var WARNING = { name: "WARNING", value: 4 }; var NOTICE = { name: "NOTICE", value: 5 }; var INFO = { name: "INFO", value: 6 }; var DEBUG = { name: "DEBUG", value: 7 }; var TRACE = { name: "TRACE", value: 8 }; var DEFAULT = { name: "DEFAULT", value: 9 }; var LogLevels = [ EMERGENCY, ALERT, CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG, TRACE, DEFAULT ]; var getConfig2 = (name) => { const config = LogLevels.find((config2) => config2.name === name); if (!config) { throw new Error(`Invalid Log Level Supplied to Logging Configuration '${name}'`); } return config; }; // src/utils/maskSensitive.ts var PRIVATE_KEY_PATTERNS = [ /-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----\s*[\s\S]*?-----END\s+(?:RSA\s+)?PRIVATE\s+KEY-----/gi, /-----BEGIN\s+EC\s+PRIVATE\s+KEY-----\s*[\s\S]*?-----END\s+EC\s+PRIVATE\s+KEY-----/gi, /-----BEGIN\s+DSA\s+PRIVATE\s+KEY-----\s*[\s\S]*?-----END\s+DSA\s+PRIVATE\s+KEY-----/gi ]; var BASE64_BLOB_PATTERN = /[A-Za-z0-9+/=]{200,}/g; var JWT_PATTERN = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/; var EMAIL_PATTERN = /\b[A-Za-z0-9][A-Za-z0-9._%+-]{0,63}@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*\.[A-Za-z]{2,}\b/g; var SSN_PATTERN = /\b\d{3}-\d{2}-\d{4}\b|\b\d{9}\b/g; var API_KEY_PATTERNS = [ // OpenAI /sk-[a-zA-Z0-9]{20,}/g, /sk-proj-[a-zA-Z0-9_-]+/g, // Anthropic /sk-ant-[a-zA-Z0-9_-]+/g, // AWS Access Keys /AKIA[0-9A-Z]{16}/g, // GitHub tokens /ghp_[a-zA-Z0-9]{36}/g, /gho_[a-zA-Z0-9]{36}/g, /ghs_[a-zA-Z0-9]{36}/g, /ghu_[a-zA-Z0-9]{36}/g, // GitLab tokens /glpat-[a-zA-Z0-9_-]{20}/g, // Slack tokens /xox[baprs]-[a-zA-Z0-9-]+/g, // Google Cloud API keys /AIza[0-9A-Za-z_-]{35}/g ]; var BEARER_TOKEN_PATTERN = /Bearer\s+[\w.-]+/gi; var PASSWORD_PATTERNS = [ /password[\s:="']+[^\s"']+/gi ]; var GENERIC_SECRET_PATTERNS = [ /api[_-]?key[\s:="']+[\w-]+/gi, /secret[\s:="']+[^\s"']+/gi, /token[\s:="']+[^\s"']+/gi ]; var MAX_STRING_LENGTH = 1e5; function maskString(input) { if (typeof input !== "string" || input.length === 0) { return input; } if (input.length > MAX_STRING_LENGTH) { return "****"; } let masked = input; for (const pattern of PRIVATE_KEY_PATTERNS) { masked = masked.replace(pattern, "****"); } masked = masked.replace(BASE64_BLOB_PATTERN, "****"); if (JWT_PATTERN.test(masked)) { const segments = masked.split("."); if (segments.length === 3) { const [, middle, third] = segments; if (middle.length > 100 || third.length > 100) { masked = "****"; } } } masked = masked.replace(EMAIL_PATTERN, "****"); masked = masked.replace(SSN_PATTERN, "****"); for (const pattern of API_KEY_PATTERNS) { masked = masked.replace(pattern, "****"); } masked = masked.replace(BEARER_TOKEN_PATTERN, "****"); for (const pattern of PASSWORD_PATTERNS) { masked = masked.replace(pattern, "****"); } for (const pattern of GENERIC_SECRET_PATTERNS) { masked = masked.replace(pattern, "****"); } return masked; } function maskObject(obj, maxDepth = 8, currentDepth = 0) { if (currentDepth >= maxDepth) { return obj; } if (obj === null || obj === void 0) { return obj; } if (typeof obj === "string") { return maskString(obj); } if (typeof obj !== "object") { return obj; } if (Array.isArray(obj)) { return obj.map((item) => maskObject(item, maxDepth, currentDepth + 1)); } const maskedObj = {}; for (const [key, value] of Object.entries(obj)) { if (Object.prototype.hasOwnProperty.call(obj, key)) { maskedObj[key] = maskObject(value, maxDepth, currentDepth + 1); } } return maskedObj; } var defaultMaskingConfig = { enabled: false, maskEmails: true, maskSSNs: true, maskPrivateKeys: true, maskBase64Blobs: true, maskJWTs: true, maxDepth: 8, // New security-focused defaults (all true when masking is enabled) maskApiKeys: true, maskBearerTokens: true, maskPasswords: true, maskGenericSecrets: true }; function maskWithConfig(input, config = defaultMaskingConfig) { if (!config.enabled) { return input; } const customMaskString = (str) => { if (typeof str !== "string" || str.length === 0) { return str; } if (str.length > MAX_STRING_LENGTH) { return "****"; } let masked = str; if (config.maskPrivateKeys) { for (const pattern of PRIVATE_KEY_PATTERNS) { masked = masked.replace(pattern, "****"); } } if (config.maskBase64Blobs) { masked = masked.replace(BASE64_BLOB_PATTERN, "****"); } if (config.maskJWTs && JWT_PATTERN.test(masked)) { const segments = masked.split("."); if (segments.length === 3) { const [, middle, third] = segments; if (middle.length > 100 || third.length > 100) { masked = "****"; } } } if (config.maskEmails) { masked = masked.replace(EMAIL_PATTERN, "****"); } if (config.maskSSNs) { masked = masked.replace(SSN_PATTERN, "****"); } if (config.maskApiKeys) { for (const pattern of API_KEY_PATTERNS) { masked = masked.replace(pattern, "****"); } } if (config.maskBearerTokens) { masked = masked.replace(BEARER_TOKEN_PATTERN, "****"); } if (config.maskPasswords) { for (const pattern of PASSWORD_PATTERNS) { masked = masked.replace(pattern, "****"); } } if (config.maskGenericSecrets) { for (const pattern of GENERIC_SECRET_PATTERNS) { masked = masked.replace(pattern, "****"); } } return masked; }; const customMaskObject = (obj, maxDepth = config.maxDepth, currentDepth = 0) => { if (currentDepth >= maxDepth) { return obj; } if (obj === null || obj === void 0) { return obj; } if (typeof obj === "string") { return customMaskString(obj); } if (typeof obj !== "object") { return obj; } if (Array.isArray(obj)) { return obj.map((item) => customMaskObject(item, maxDepth, currentDepth + 1)); } const maskedObj = {}; for (const [key, value] of Object.entries(obj)) { if (Object.prototype.hasOwnProperty.call(obj, key)) { maskedObj[key] = customMaskObject(value, maxDepth, currentDepth + 1); } } return maskedObj; }; return customMaskObject(input); } // src/config.ts var defaultLogLevel = INFO; var defaultLogFormat = TEXT; var defaultLoggingConfig = { logLevel: defaultLogLevel, logFormat: defaultLogFormat, overrides: {}, floodControl: { enabled: false, threshold: 10, timeframe: 1e3 // 1 second }, masking: defaultMaskingConfig }; var convertComponentOverride = (override) => { const result = { logLevel: override.logLevel ? getConfig2(override.logLevel) : defaultLogLevel }; if (override.components && typeof override.components === "object") { result.components = {}; Object.entries(override.components).forEach(([componentName, componentOverride]) => { result.components[componentName] = convertComponentOverride(componentOverride); }); } return result; }; var convertOverrides = (overrides) => { const convertedOverrides = {}; if (overrides) { Object.entries(overrides).forEach(([key, value]) => { convertedOverrides[key] = convertComponentOverride(value); }); } return convertedOverrides; }; var convertConfig = (config) => { return { logLevel: config.logLevel ? getConfig2(config.logLevel) : defaultLogLevel, logFormat: config.logFormat ? getConfig(config.logFormat) : defaultLogFormat, overrides: convertOverrides(config.overrides), floodControl: { ...defaultLoggingConfig.floodControl, ...config.floodControl || {} }, masking: { ...defaultLoggingConfig.masking, ...config.masking || {} } }; }; var resolveLogLevel = (config, category, components) => { let logLevel = config.logLevel; const overrides = config.overrides; if (!overrides || !overrides[category]) { return logLevel; } let currentOverride = overrides[category]; logLevel = currentOverride.logLevel; for (const component of components) { if (!currentOverride.components || !currentOverride.components[component]) { break; } currentOverride = currentOverride.components[component]; logLevel = currentOverride.logLevel; } return logLevel; }; var configureLogging = () => { let config = {}; const loggingConfigEnv = process.env.LOGGING_CONFIG; const expoLoggingConfigEnv = process.env.EXPO_PUBLIC_LOGGING_CONFIG; const nextLoggingConfigEnv = process.env.NEXT_PUBLIC_LOGGING_CONFIG; let logLevelEnv = process.env.LOG_LEVEL; let logFormatEnv = process.env.LOG_FORMAT; if (loggingConfigEnv) { try { config = JSON.parse(loggingConfigEnv); } catch (error) { console.error("Invalid JSON in LOGGING_CONFIG environment variable:", error); config = {}; } } else if (expoLoggingConfigEnv) { try { config = JSON.parse(expoLoggingConfigEnv); } catch (error) { console.error("Invalid JSON in EXPO_PUBLIC_LOGGING_CONFIG environment variable:", error); config = {}; } } else if (nextLoggingConfigEnv) { try { config = JSON.parse(nextLoggingConfigEnv); } catch (error) { console.error("Invalid JSON in NEXT_PUBLIC_LOGGING_CONFIG environment variable:", error); config = {}; } } const convertedConfig = convertConfig(config); if (logLevelEnv) { logLevelEnv = logLevelEnv?.toUpperCase(); const logLevelConfig = getConfig2(logLevelEnv); convertedConfig.logLevel = logLevelConfig; } if (logFormatEnv) { logFormatEnv = logFormatEnv.toUpperCase(); const logFormatConfig = getConfig(logFormatEnv); convertedConfig.logFormat = logFormatConfig; } const finalConfig = { ...defaultLoggingConfig, ...convertedConfig }; return finalConfig; }; // src/Writer.ts var createWriter = (formatter, logMethod, options = {}) => { const { respectInjectedMethod = false, errorMethod = console.error, warningMethod = console.warn, infoMethod = console.log } = options; return { write: (level, coordinates, payload) => { let finalLogMethod = logMethod; if (!respectInjectedMethod) { if (level.name === ERROR.name || level.name === CRITICAL.name || level.name === ALERT.name || level.name === EMERGENCY.name) { finalLogMethod = errorMethod; } else if (level.name === WARNING.name) { finalLogMethod = warningMethod; } else { finalLogMethod = infoMethod; } } finalLogMethod(formatter.formatLog(level, coordinates, payload)); } }; }; // src/utils.ts var stringifyJSON = function(obj, visited = /* @__PURE__ */ new Set()) { try { return stringifyJSONCustom(obj, visited); } catch (error) { console.error("[Fjell Logging] Critical error in stringifyJSON, using ultimate fallback:", error); try { return `"[Object: ${typeof obj}]"`; } catch { return '"[Object: unknown]"'; } } }; var STRINGIFY_CONFIG = { MAX_ARRAY_ELEMENTS: 100, MAX_OBJECT_PROPERTIES: 100, TRUNCATION_MESSAGE: "...[truncated]" }; var stringifyJSONCustom = function(obj, visited = /* @__PURE__ */ new Set()) { try { const arrOfKeyVals = []; const arrVals = []; let objKeys = []; if (typeof obj === "number" || typeof obj === "boolean" || obj === null) return "" + obj; else if (typeof obj === "string") return '"' + obj + '"'; else if (typeof obj === "symbol") return ""; else if (typeof obj === "function") return ""; else if (obj instanceof Date) return "{}"; else if (obj instanceof RegExp) return "{}"; else if (obj instanceof Error) return "{}"; else if (typeof obj === "object" && obj.constructor && obj.constructor.name === "Buffer") { const result = {}; for (let i = 0; i < obj.length; i++) { result[i] = obj[i]; } return stringifyJSONCustom(result, visited); } if (obj instanceof Object && visited.has(obj)) { return '"(circular)"'; } else if (Array.isArray(obj)) { if (obj.length === 0) return "[]"; else { visited.add(obj); try { const maxElements = STRINGIFY_CONFIG.MAX_ARRAY_ELEMENTS; const shouldTruncate = obj.length > maxElements; const elementsToProcess = shouldTruncate ? maxElements : obj.length; for (let i = 0; i < elementsToProcess; i++) { try { arrVals.push(stringifyJSONCustom(obj[i], visited)); } catch { arrVals.push('"[Error serializing array element]"'); } } if (shouldTruncate) { arrVals.push(`"${STRINGIFY_CONFIG.TRUNCATION_MESSAGE} (${obj.length - maxElements} more items)"`); } } finally { visited.delete(obj); } try { return "[" + arrVals.join(",") + "]"; } catch { console.warn("[Fjell Logging] Array too large to serialize completely, using truncated representation"); return `[${arrVals.slice(0, 10).join(",")},${STRINGIFY_CONFIG.TRUNCATION_MESSAGE}]`; } } } else if (obj instanceof Object) { visited.add(obj); try { objKeys = Object.keys(obj); const maxProperties = STRINGIFY_CONFIG.MAX_OBJECT_PROPERTIES; const shouldTruncate = objKeys.length > maxProperties; const propertiesToProcess = shouldTruncate ? maxProperties : objKeys.length; for (let i = 0; i < propertiesToProcess; i++) { const key = objKeys[i]; try { const keyOut = '"' + key + '":'; const keyValOut = obj[key]; if (keyValOut instanceof Function || typeof keyValOut === "undefined") continue; else if (typeof keyValOut === "string") arrOfKeyVals.push(keyOut + '"' + keyValOut + '"'); else if (typeof keyValOut === "boolean" || typeof keyValOut === "number" || keyValOut === null) arrOfKeyVals.push(keyOut + keyValOut); else if (keyValOut instanceof Object) { arrOfKeyVals.push(keyOut + stringifyJSONCustom(keyValOut, visited)); } } catch { arrOfKeyVals.push('"' + key + '":"[Error serializing property]"'); } } if (shouldTruncate) { arrOfKeyVals.push(`"${STRINGIFY_CONFIG.TRUNCATION_MESSAGE}":"(${objKeys.length - maxProperties} more properties)"`); } } finally { visited.delete(obj); } try { return "{" + arrOfKeyVals.join(",") + "}"; } catch { console.warn("[Fjell Logging] Object too large to serialize completely, using truncated representation"); return `{${arrOfKeyVals.slice(0, 10).join(",")},${STRINGIFY_CONFIG.TRUNCATION_MESSAGE}}`; } } return ""; } catch (error) { console.error("[Fjell Logging] Error in stringifyJSONCustom, using fallback:", error); return "[Object: serialization failed]"; } }; var safeFormat = (message, ...args) => { let result = message; let argIndex = 0; result = result.replace(/%([sdjifoO%])/g, (match, specifier) => { if (specifier === "%") { return "%"; } if (argIndex >= args.length) { return match; } const arg = args[argIndex++]; switch (specifier) { case "s": return String(arg); case "d": return String(parseInt(arg, 10)); case "i": return String(parseInt(arg, 10)); case "f": return String(parseFloat(arg)); case "j": try { return stringifyJSON(arg); } catch { return String(arg); } case "o": return stringifyJSON(arg); case "O": return stringifyJSON(arg); default: return String(arg); } }); return result; }; var safeInspect = (obj) => { try { if (obj && typeof obj === "object" && obj.problematic && typeof obj.problematic === "object") { return "[Object: object]"; } return stringifyJSON(obj); } catch { return `[Object: ${typeof obj}]`; } }; var safeJSONStringify = (obj) => { try { const seen = /* @__PURE__ */ new WeakSet(); return JSON.stringify(obj, (key, value) => { try { if (typeof value === "symbol") { return String(value); } if (typeof value === "function") { return "[Function]"; } if (typeof value === "object" && value !== null) { if (seen.has(value)) { return "[Circular Reference]"; } seen.add(value); } if (value instanceof Error) { return { name: value.name, message: value.message, stack: value.stack }; } if (value instanceof RegExp) { return value.toString(); } if (value instanceof Date) { return value.toISOString(); } return value; } catch (error) { console.error("[Fjell Logging] Error processing value in replacer:", error); return "[Error: unable to serialize value]"; } }); } catch (error) { console.error("[Fjell Logging] CRITICAL: safeJSONStringify failed, returning fallback:", error); try { const message = obj?.message || obj?.severity || "Unknown"; return JSON.stringify({ severity: "ERROR", message: "[Fjell Logging] Failed to serialize log entry", originalMessage: String(message), error: "Circular reference or non-serializable object detected" }); } catch { return '{"severity":"ERROR","message":"[Fjell Logging] Critical serialization failure"}'; } } }; // src/formatter.ts var createFormatter = (logFormat) => { if (logFormat.name === "TEXT") { return getTextFormatter(); } else if (logFormat.name === "STRUCTURED") { return getStructuredFormatter(); } throw new Error(`Unknown log format: ${logFormat.name}`); }; var getTextFormatter = () => { const formatLog = (level, coordinates, payload) => { const hasSpecifiers = /%[sdjifoO%]/.test(payload.message); let logMessage; if (payload.data.length === 0) { logMessage = payload.message; } else if (hasSpecifiers) { logMessage = safeFormat(payload.message, ...payload.data); } else { logMessage = `${payload.message} ${safeInspect(payload.data)}`; } return `(${(/* @__PURE__ */ new Date()).valueOf()}) [${level.name}] - [${coordinates.category}] ${coordinates.components.map((c) => `[${c}]`)} ${logMessage}`; }; const timerMessage = (level, coordinates, payload) => { const randomInt = Math.floor(Math.random() * 1e6); const timerMessage2 = `(${(/* @__PURE__ */ new Date()).valueOf()}) [${level.name}] - [${coordinates.category}] ${coordinates.components.map((c) => `[${c}]`)} ${safeFormat(payload.message, ...payload.data)} ${safeInspect(payload.data)} ${randomInt}`; return timerMessage2; }; return { formatLog, timerMessage, getLogFormat: () => TEXT }; }; var getStructuredFormatter = () => { const formatLog = (level, coordinates, payload) => { const severity = level.name; const hasSpecifiers = /%[sdjifoO%]/.test(payload.message); return safeJSONStringify({ severity, message: hasSpecifiers ? safeFormat(payload.message, ...payload.data) : payload.message, "logging.googleapis.com/labels": { category: coordinates.category, components: `${coordinates.components.map((c) => `[${c}]`)}` }, ...!hasSpecifiers && payload.data.length > 0 && { data: safeInspect(payload.data) } }); }; const timerMessage = (level, coordinates, payload) => { const severity = level.name; const randomInt = Math.floor(Math.random() * 1e6); return safeJSONStringify({ severity, message: safeFormat(payload.message, ...payload.data), "logging.googleapis.com/labels": { category: coordinates.category, components: `${coordinates.components.map((c) => `[${c}]`)}` }, data: safeInspect(payload.data), "logging.googleapis.com/spanId": String(randomInt) }); }; return { formatLog, timerMessage, getLogFormat: () => STRUCTURED }; }; // src/FloodControl.ts var hash = (message, data) => { const dataString = data.map((item) => { try { return JSON.stringify(item); } catch { return stringifyJSON(item); } }).join(""); return `${message}${dataString}`; }; var FloodControl = class { config; history = /* @__PURE__ */ new Map(); suppressed = /* @__PURE__ */ new Map(); cleanupTimer = null; constructor(config) { this.config = config; if (this.config.enabled) { this.cleanupTimer = setInterval(() => this.cleanup(), this.config.timeframe * 2); } } destroy() { if (this.cleanupTimer) { clearInterval(this.cleanupTimer); this.cleanupTimer = null; } } cleanup() { const now = Date.now(); for (const [hash2, timestamps] of this.history.entries()) { const recentTimestamps = timestamps.filter( (timestamp) => now - timestamp < this.config.timeframe ); if (recentTimestamps.length > 0) { this.history.set(hash2, recentTimestamps); } else { this.history.delete(hash2); this.suppressed.delete(hash2); } } } check(message, data) { if (!this.config.enabled) { return "log"; } const messageHash = hash(message, data); const now = Date.now(); const timestamps = (this.history.get(messageHash) || []).filter( (timestamp) => now - timestamp < this.config.timeframe ); timestamps.push(now); this.history.set(messageHash, timestamps); if (timestamps.length > this.config.threshold) { const suppressedInfo = this.suppressed.get(messageHash); if (suppressedInfo) { suppressedInfo.count++; return "suppress"; } else { this.suppressed.set(messageHash, { count: 1, firstTimestamp: timestamps[0], summaryLogged: false }); return "suppress"; } } else { if (this.suppressed.has(messageHash)) { this.suppressed.delete(messageHash); return "resume"; } } return "log"; } getSuppressedCount(message, data) { const messageHash = hash(message, data); return this.suppressed.get(messageHash)?.count || 0; } }; // src/Logger.ts var createLogger = (logFormat, logLevel, coordinates, floodControlConfig, loggingConfig, writerOptions, options) => { const formatter = createFormatter(logFormat); const floodControl = floodControlConfig.enabled ? new FloodControl(floodControlConfig) : null; const logFunction = console.log; const writer = createWriter(formatter, logFunction, writerOptions); const asyncLogging = options?.asyncLogging !== false; const enableDebugBuffering = asyncLogging; const debugBuffer = []; const DEBUG_BUFFER_SIZE = 100; const DEBUG_FLUSH_INTERVAL = 100; let debugFlushTimer = null; const flushDebugBuffer = () => { if (debugBuffer.length === 0) return; try { const messagesToFlush = [...debugBuffer]; debugBuffer.length = 0; messagesToFlush.forEach(({ level, coordinates: coordinates2, payload }) => { try { writer.write(level, coordinates2, payload); } catch (error) { console.error("[Fjell Logging] Error writing buffered log message:", error); } }); } catch (error) { console.error("[Fjell Logging] Error flushing debug buffer:", error); } finally { if (debugFlushTimer) { clearTimeout(debugFlushTimer); debugFlushTimer = null; } } }; const scheduleDebugFlush = () => { if (debugFlushTimer) return; try { debugFlushTimer = setTimeout(() => { try { flushDebugBuffer(); } catch (error) { console.error("[Fjell Logging] Error in scheduled debug flush:", error); } }, DEBUG_FLUSH_INTERVAL); } catch (error) { console.error("[Fjell Logging] Error scheduling debug flush:", error); } }; const writeImmediate = (level, coordinates2, payload) => { try { writer.write(level, coordinates2, payload); } catch (error) { console.error("[Fjell Logging] Error writing log message:", error); } }; const handleDebugBuffering = (level, coordinates2, payload) => { try { debugBuffer.push({ level, coordinates: coordinates2, payload }); if (debugBuffer.length >= DEBUG_BUFFER_SIZE) { flushDebugBuffer(); } else { scheduleDebugFlush(); } } catch (error) { console.error("[Fjell Logging] Error buffering debug message, falling back to immediate write:", error); writeImmediate(level, coordinates2, payload); } }; const write = (level, message, data) => { if (logLevel.value < level.value) { return; } const check = floodControl ? floodControl.check(message, data) : "log"; const payload = { message, data }; const asyncWrite = () => { try { switch (check) { case "log": if (enableDebugBuffering && (level.name === "TRACE" || level.name === "DEFAULT" || level.name === "DEBUG")) { handleDebugBuffering(level, coordinates, payload); } else { writeImmediate(level, coordinates, payload); } break; case "suppress": if (floodControl && floodControl.getSuppressedCount(message, data) === 1) { try { const originalLevel = level; const newPayload = { message: `Started suppressing repeated log message`, data: [] }; writer.write(originalLevel, coordinates, newPayload); } catch (error) { console.error("[Fjell Logging] Error writing suppress message:", error); } } break; case "resume": { try { const count = floodControl ? floodControl.getSuppressedCount(message, data) : 0; const resumePayload = { message: `Stopped suppressing repeated log message. Suppressed ${count} times.`, data: [] }; writer.write(level, coordinates, resumePayload); writer.write(level, coordinates, payload); } catch (error) { console.error("[Fjell Logging] Error writing resume messages:", error); } break; } } } catch (error) { console.error("[Fjell Logging] Error in async write operation:", error); } }; if (asyncLogging) { try { if (typeof setImmediate !== "undefined") { setImmediate(asyncWrite); } else { setTimeout(asyncWrite, 0); } } catch (error) { console.error("[Fjell Logging] Error scheduling async write, falling back to sync:", error); try { asyncWrite(); } catch (syncError) { console.error("[Fjell Logging] Error in synchronous fallback write:", syncError); } } } else { asyncWrite(); } }; const startTimeLogger = (logLevel2, coordinates2, payload) => { const timerMessage = formatter.timerMessage(logLevel2, coordinates2, payload); logLevel2.value >= DEBUG.value && console.time(timerMessage); return { end: () => { logLevel2.value >= DEBUG.value && console.timeEnd(timerMessage); }, log: (...data) => { logLevel2.value >= DEBUG.value && console.timeLog(timerMessage, ...data); } }; }; return { emergency: (message, ...data) => { write(EMERGENCY, message, data); }, alert: (message, ...data) => { write(ALERT, message, data); }, critical: (message, ...data) => { write(CRITICAL, message, data); }, error: (message, ...data) => { write(ERROR, message, data); }, warning: (message, ...data) => { write(WARNING, message, data); }, notice: (message, ...data) => { write(NOTICE, message, data); }, info: (message, ...data) => { write(INFO, message, data); }, debug: (message, ...data) => { write(DEBUG, message, data); }, trace: (message, ...data) => { write(TRACE, message, data); }, default: (message, ...data) => { write(DEFAULT, message, data); }, time: (message, ...data) => { const payload = { message, data }; return startTimeLogger(logLevel, coordinates, payload); }, get: (...additionalComponents) => { const newComponents = [...coordinates.components, ...additionalComponents]; let childLogLevel = logLevel; if (loggingConfig) { childLogLevel = resolveLogLevel(loggingConfig, coordinates.category, newComponents); } return createLogger(logFormat, childLogLevel, { category: coordinates.category, components: newComponents }, floodControlConfig, loggingConfig, writerOptions, options); }, destroy: () => { try { flushDebugBuffer(); } catch (error) { console.error("[Fjell Logging] Error flushing debug buffer during destroy:", error); } try { if (debugFlushTimer) { clearTimeout(debugFlushTimer); debugFlushTimer = null; } } catch (error) { console.error("[Fjell Logging] Error clearing debug flush timer during destroy:", error); } try { if (floodControl) { floodControl.destroy(); } } catch (error) { console.error("[Fjell Logging] Error destroying flood control during destroy:", error); } } }; }; // src/logging.ts var getLogger = (name) => { const config = configureLogging(); const logger = createBaseLogger(name, config); return logger; }; var createBaseLogger = (name, config) => { const { logFormat, floodControl } = config; const coordinates = { category: name, components: [] }; const logLevel = resolveLogLevel(config, name, []); const isTestEnvironment = true; return createLogger(logFormat, logLevel, coordinates, floodControl, config, void 0, { asyncLogging: !isTestEnvironment }); }; // src/middleware/maskMiddleware.ts function maskLogEntry(entry, config = defaultMaskingConfig) { if (!config.enabled) { return entry; } const maskedEntry = { ...entry }; if (typeof maskedEntry.message === "string") { maskedEntry.message = maskWithConfig(maskedEntry.message, config); } if (maskedEntry.data !== void 0) { maskedEntry.data = maskWithConfig(maskedEntry.data, config); } if (maskedEntry.meta !== void 0) { maskedEntry.meta = maskWithConfig(maskedEntry.meta, config); } for (const [key, value] of Object.entries(maskedEntry)) { if (typeof value === "string" && key !== "level" && key !== "timestamp") { maskedEntry[key] = maskWithConfig(value, config); } } return maskedEntry; } function createMaskingMiddleware(config = defaultMaskingConfig) { return (entry) => maskLogEntry(entry, config); } function maskLogEntries(entries, config = defaultMaskingConfig) { if (!config.enabled) { return entries; } return entries.map((entry) => maskLogEntry(entry, config)); } // src/correlation.ts function generateCorrelationId() { const timestamp = Date.now().toString(36); const random = Math.random().toString(36).substring(2, 8); return `${timestamp}-${random}`; } var CorrelatedLogger = class _CorrelatedLogger { baseLogger; correlationId; /** * Create a new CorrelatedLogger * @param baseLogger - The underlying logger to wrap * @param correlationId - Optional correlation ID (generates one if not provided) */ constructor(baseLogger, correlationId) { this.baseLogger = baseLogger; this.correlationId = correlationId || generateCorrelationId(); } /** * Format a message with the correlation ID prefix */ formatWithCorrelation(message) { return `[${this.correlationId}] ${message}`; } /** * Get the current correlation ID * @returns The correlation ID for this logger */ getCorrelationId() { return this.correlationId; } /** * Create a new CorrelatedLogger with a different correlation ID * @param id - The new correlation ID to use * @returns A new CorrelatedLogger with the specified correlation ID */ withCorrelationId(id) { return new _CorrelatedLogger(this.baseLogger, id); } // Implement Logger interface methods emergency(message, ...data) { this.baseLogger.emergency(this.formatWithCorrelation(message), ...data); } alert(message, ...data) { this.baseLogger.alert(this.formatWithCorrelation(message), ...data); } critical(message, ...data) { this.baseLogger.critical(this.formatWithCorrelation(message), ...data); } error(message, ...data) { this.baseLogger.error(this.formatWithCorrelation(message), ...data); } warning(message, ...data) { this.baseLogger.warning(this.formatWithCorrelation(message), ...data); } notice(message, ...data) { this.baseLogger.notice(this.formatWithCorrelation(message), ...data); } info(message, ...data) { this.baseLogger.info(this.formatWithCorrelation(message), ...data); } debug(message, ...data) { this.baseLogger.debug(this.formatWithCorrelation(message), ...data); } trace(message, ...data) { this.baseLogger.trace(this.formatWithCorrelation(message), ...data); } default(message, ...data) { this.baseLogger.default(this.formatWithCorrelation(message), ...data); } time(message, ...data) { return this.baseLogger.time(this.formatWithCorrelation(message), ...data); } get(...additionalComponents) { const childLogger = this.baseLogger.get(...additionalComponents); return new _CorrelatedLogger(childLogger, this.correlationId); } destroy() { this.baseLogger.destroy(); } }; function createCorrelatedLogger(logger, correlationId) { return new CorrelatedLogger(logger, correlationId); } // src/index.ts var index_default = { getLogger }; export { CorrelatedLogger, LogFormat_exports as LogFormat, LogLevel_exports as LogLevel, createCorrelatedLogger, createMaskingMiddleware, index_default as default, defaultMaskingConfig, generateCorrelationId, getLogger, maskLogEntries, maskLogEntry, maskObject, maskString, maskWithConfig, resolveLogLevel, safeFormat, safeInspect, stringifyJSON };