UNPKG

@xynehq/jaf

Version:

Juspay Agent Framework - A purely functional agent framework with immutable state and composable tools

250 lines 8.2 kB
/** * JAF Logging System * * Provides structured logging with different levels and output targets * Automatically sanitizes sensitive data before logging */ // Import sanitizeObject from tracing to apply sanitization import { sanitizeObject } from '../core/tracing.js'; export var LogLevel; (function (LogLevel) { LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG"; LogLevel[LogLevel["INFO"] = 1] = "INFO"; LogLevel[LogLevel["WARN"] = 2] = "WARN"; LogLevel[LogLevel["ERROR"] = 3] = "ERROR"; LogLevel[LogLevel["FATAL"] = 4] = "FATAL"; LogLevel[LogLevel["SILENT"] = 5] = "SILENT"; })(LogLevel || (LogLevel = {})); // Global configuration let globalLogLevel = LogLevel.INFO; let globalOutput = 'console'; let globalFormat = 'text'; // Set from environment if (process.env.LOG_LEVEL) { const level = process.env.LOG_LEVEL.toUpperCase(); globalLogLevel = LogLevel[level] || LogLevel.INFO; } if (process.env.NODE_ENV === 'test') { globalOutput = 'silent'; } else if (process.env.NODE_ENV === 'production') { globalFormat = 'json'; } /** * Format log entry based on format type * Applies sanitization to all metadata before formatting */ const formatLogEntry = (entry, format) => { // Sanitize metadata before formatting const sanitizedMetadata = entry.metadata ? sanitizeObject(entry.metadata) : undefined; switch (format) { case 'json': return JSON.stringify({ level: LogLevel[entry.level], timestamp: entry.timestamp.toISOString(), message: entry.message, context: entry.context, ...sanitizedMetadata, ...(entry.error && { error: { message: entry.error.message, stack: entry.error.stack, name: entry.error.name } }) }); case 'pretty': { const levelColors = { [LogLevel.DEBUG]: '\x1b[36m', // Cyan [LogLevel.INFO]: '\x1b[32m', // Green [LogLevel.WARN]: '\x1b[33m', // Yellow [LogLevel.ERROR]: '\x1b[31m', // Red [LogLevel.FATAL]: '\x1b[35m', // Magenta [LogLevel.SILENT]: '' // No color for silent }; const reset = '\x1b[0m'; const color = levelColors[entry.level] || ''; const levelStr = LogLevel[entry.level].padEnd(5); const contextStr = entry.context ? `[${entry.context}] ` : ''; const metaStr = sanitizedMetadata ? ` ${JSON.stringify(sanitizedMetadata)}` : ''; const errorStr = entry.error ? `\n ${entry.error.stack || entry.error.message}` : ''; return `${color}${levelStr}${reset} ${contextStr}${entry.message}${metaStr}${errorStr}`; } case 'text': default: { const level = LogLevel[entry.level].padEnd(5); const context = entry.context ? `[${entry.context}] ` : ''; const meta = sanitizedMetadata ? ` ${JSON.stringify(sanitizedMetadata)}` : ''; const error = entry.error ? ` Error: ${entry.error.message}` : ''; return `${level} ${context}${entry.message}${meta}${error}`; } } }; /** * Output log entry based on output type */ const outputLogEntry = (entry, output, format) => { if (output === 'silent') { return; } const formatted = formatLogEntry(entry, format); if (output === 'console') { switch (entry.level) { case LogLevel.DEBUG: case LogLevel.INFO: console.log(formatted); break; case LogLevel.WARN: console.warn(formatted); break; case LogLevel.ERROR: case LogLevel.FATAL: console.error(formatted); break; } } }; /** * Create a logger instance */ export const createLogger = (config) => { const level = config?.level ?? globalLogLevel; const context = config?.context; const output = config?.output ?? globalOutput; const format = config?.format ?? globalFormat; const log = (logLevel, message, error, metadata) => { if (logLevel < level) { return; } const entry = { level: logLevel, message, timestamp: new Date(), context, metadata, error: error instanceof Error ? error : undefined }; outputLogEntry(entry, output, format); }; return { debug: (message, metadata) => { log(LogLevel.DEBUG, message, undefined, metadata); }, info: (message, metadata) => { log(LogLevel.INFO, message, undefined, metadata); }, warn: (message, metadata) => { log(LogLevel.WARN, message, undefined, metadata); }, error: (message, error, metadata) => { log(LogLevel.ERROR, message, error, metadata); }, fatal: (message, error, metadata) => { log(LogLevel.FATAL, message, error, metadata); }, child: (childContext) => { return createLogger({ level, context: context ? `${context}:${childContext}` : childContext, output, format }); }, setLevel: (newLevel) => { // Note: This only affects this instance, not the global level return createLogger({ level: newLevel, context, output, format }); } }; }; /** * Default logger instance */ export const logger = createLogger(); /** * Configure global logger settings */ export const configureLogger = (config) => { if (config.level !== undefined) { globalLogLevel = config.level; } if (config.output !== undefined) { globalOutput = config.output; } if (config.format !== undefined) { globalFormat = config.format; } }; /** * Create a context-specific logger */ export const getLogger = (context) => { return createLogger({ context }); }; /** * Utility to safely stringify errors */ export const stringifyError = (error) => { if (error instanceof Error) { return error.message; } if (typeof error === 'string') { return error; } try { return JSON.stringify(error); } catch { return String(error); } }; /** * Re-export sanitizeObject for use in custom logging */ export { sanitizeObject } from '../core/tracing.js'; /** * Safe console logging with automatic sanitization * Use these instead of direct console.log/warn/error calls */ export const safeConsole = { /** * Log a message with sanitized data */ log: (message, ...data) => { const sanitizedData = data.map(item => typeof item === 'object' && item !== null ? sanitizeObject(item) : item); console.log(message, ...sanitizedData); }, /** * Warn with sanitized data */ warn: (message, ...data) => { const sanitizedData = data.map(item => typeof item === 'object' && item !== null ? sanitizeObject(item) : item); console.warn(message, ...sanitizedData); }, /** * Error with sanitized data */ error: (message, ...data) => { const sanitizedData = data.map(item => typeof item === 'object' && item !== null ? sanitizeObject(item) : item); console.error(message, ...sanitizedData); }, /** * Info with sanitized data */ info: (message, ...data) => { const sanitizedData = data.map(item => typeof item === 'object' && item !== null ? sanitizeObject(item) : item); console.info(message, ...sanitizedData); }, /** * Debug with sanitized data */ debug: (message, ...data) => { const sanitizedData = data.map(item => typeof item === 'object' && item !== null ? sanitizeObject(item) : item); console.debug(message, ...sanitizedData); } }; //# sourceMappingURL=logger.js.map