UNPKG

@debugg-ai/debugg-ai-mcp

Version:

Zero-Config, Fully AI-Managed End-to-End Testing for all code gen platforms.

155 lines (154 loc) 4.85 kB
/** * Centralized logging utility using Winston * Replaces all console.error calls with structured logging */ import winston from 'winston'; import { config } from '../config/index.js'; /** * Create winston logger instance */ const createLogger = () => { const { level, format } = config.logging; const logFormat = format === 'json' ? winston.format.combine(winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json()) : winston.format.combine(winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.errors({ stack: true }), winston.format.printf(({ timestamp, level, message, ...meta }) => { const metaStr = Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : ''; return `[${timestamp}] ${level.toUpperCase()}: ${message}${metaStr}`; })); return winston.createLogger({ level, format: logFormat, transports: [ new winston.transports.Console({ stderrLevels: ['error', 'warn', 'info', 'debug'], }), ], // Ensure uncaught exceptions and rejections go to stderr (NOT stdout — stdout is the MCP transport) exceptionHandlers: [ new winston.transports.Console({ stderrLevels: ['error', 'warn', 'info', 'debug'], format: winston.format.combine(winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json()), }), ], rejectionHandlers: [ new winston.transports.Console({ stderrLevels: ['error', 'warn', 'info', 'debug'], format: winston.format.combine(winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json()), }), ], }); }; /** * Global logger instance - created lazily to avoid import-time config loading */ let _logger; export const logger = { error: (message, meta) => getLogger().error(message, meta), warn: (message, meta) => getLogger().warn(message, meta), info: (message, meta) => getLogger().info(message, meta), debug: (message, meta) => getLogger().debug(message, meta), }; function getLogger() { if (!_logger) { _logger = createLogger(); } return _logger; } /** * Enhanced logging functions with context support */ export class Logger { context; constructor(context = {}) { this.context = context; } /** * Create a child logger with additional context */ child(additionalContext) { return new Logger({ ...this.context, ...additionalContext }); } /** * Log error messages */ error(message, meta) { logger.error(message, { ...this.context, ...meta }); } /** * Log warning messages */ warn(message, meta) { logger.warn(message, { ...this.context, ...meta }); } /** * Log info messages */ info(message, meta) { logger.info(message, { ...this.context, ...meta }); } /** * Log debug messages */ debug(message, meta) { logger.debug(message, { ...this.context, ...meta }); } /** * Log tool execution start */ toolStart(toolName, input) { this.info(`Tool execution started: ${toolName}`, { toolName, input: this.sanitizeInput(input) }); } /** * Log tool execution completion */ toolComplete(toolName, duration) { this.info(`Tool execution completed: ${toolName}`, { toolName, duration: `${duration}ms` }); } /** * Log tool execution error */ toolError(toolName, error, duration) { this.error(`Tool execution failed: ${toolName}`, { toolName, error: error.message, stack: error.stack, duration: `${duration}ms` }); } /** * Log progress updates */ progress(message, progress, total) { this.info(`Progress: ${message}`, { progress, total, percentage: Math.round((progress / total) * 100) }); } /** * Sanitize input data for logging (remove sensitive information) */ sanitizeInput(input) { if (typeof input !== 'object' || input === null) { return input; } const sanitized = { ...input }; const sensitiveKeys = ['password', 'token', 'key', 'secret', 'apiKey']; for (const key of Object.keys(sanitized)) { if (sensitiveKeys.some(sensitive => key.toLowerCase().includes(sensitive))) { sanitized[key] = '[REDACTED]'; } } return sanitized; } } /** * Default logger instance */ export const defaultLogger = new Logger();