@xynehq/jaf
Version:
Juspay Agent Framework - A purely functional agent framework with immutable state and composable tools
250 lines • 8.2 kB
JavaScript
/**
* 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