@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
JavaScript
/**
* 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();