2025-08-11 10:32:08 +02:00

159 lines
4.9 KiB
TypeScript

// src/services/logger.ts
export enum LogLevel {
ERROR = 0,
WARN = 1,
INFO = 2,
DEBUG = 3
}
interface LogContext {
[key: string]: any;
}
class Logger {
private logLevel: LogLevel;
private enabledContexts: Set<string>;
constructor() {
this.logLevel = this.getLogLevel();
this.enabledContexts = this.getEnabledContexts();
}
private getLogLevel(): LogLevel {
const level = process.env.LOG_LEVEL?.toUpperCase() || 'INFO';
switch (level) {
case 'ERROR': return LogLevel.ERROR;
case 'WARN': return LogLevel.WARN;
case 'DEBUG': return LogLevel.DEBUG;
default: return LogLevel.INFO;
}
}
private getEnabledContexts(): Set<string> {
const contexts = process.env.LOG_CONTEXTS?.split(',') || [];
return new Set(contexts.map(c => c.trim().toLowerCase()));
}
private shouldLog(level: LogLevel, context: string): boolean {
if (level > this.logLevel) return false;
if (this.enabledContexts.size > 0 && !this.enabledContexts.has(context.toLowerCase())) {
return false;
}
return true;
}
private formatMessage(level: string, context: string, message: string, data?: LogContext): string {
const timestamp = new Date().toISOString();
const contextStr = context.toUpperCase().padEnd(12);
let logMessage = `[${timestamp}] ${level.padEnd(5)} [${contextStr}] ${message}`;
if (data && Object.keys(data).length > 0) {
const dataStr = Object.entries(data)
.map(([key, value]) => `${key}=${typeof value === 'object' ? JSON.stringify(value) : value}`)
.join(' ');
logMessage += ` | ${dataStr}`;
}
return logMessage;
}
error(context: string, message: string, error?: Error, data?: LogContext): void {
if (!this.shouldLog(LogLevel.ERROR, context)) return;
const logData = { ...data };
if (error) {
logData.error = error.message;
logData.stack = error.stack;
}
console.error(this.formatMessage('ERROR', context, message, logData));
}
warn(context: string, message: string, data?: LogContext): void {
if (!this.shouldLog(LogLevel.WARN, context)) return;
console.warn(this.formatMessage('WARN', context, message, data));
}
info(context: string, message: string, data?: LogContext): void {
if (!this.shouldLog(LogLevel.INFO, context)) return;
console.log(this.formatMessage('INFO', context, message, data));
}
debug(context: string, message: string, data?: LogContext): void {
if (!this.shouldLog(LogLevel.DEBUG, context)) return;
console.log(this.formatMessage('DEBUG', context, message, data));
}
// Specialized logging methods for common contexts
pipeline(phase: string, action: string, details?: LogContext): void {
this.info('pipeline', `${phase}/${action}`, details);
}
audit(phase: string, action: string, confidence: number, timeMs: number, metadata?: LogContext): void {
this.info('audit', `${phase}/${action} completed`, {
confidence: `${confidence}%`,
duration: `${timeMs}ms`,
...metadata
});
}
api(method: string, path: string, status: number, details?: LogContext): void {
const level = status >= 400 ? LogLevel.ERROR : status >= 300 ? LogLevel.WARN : LogLevel.INFO;
if (level === LogLevel.ERROR) {
this.error('api', `${method} ${path}`, undefined, { status, ...details });
} else if (level === LogLevel.WARN) {
this.warn('api', `${method} ${path}`, { status, ...details });
} else {
this.info('api', `${method} ${path}`, { status, ...details });
}
}
embedding(action: string, details?: LogContext): void {
this.info('embedding', action, details);
}
selection(method: string, toolCount: number, conceptCount: number, details?: LogContext): void {
this.info('selection', `${method} selection completed`, {
tools: toolCount,
concepts: conceptCount,
...details
});
}
confidence(toolName: string, overall: number, semantic: number, suitability: number): void {
this.debug('confidence', `${toolName} scored`, {
overall: `${overall}%`,
semantic: `${semantic}%`,
suitability: `${suitability}%`
});
}
queue(action: string, details?: LogContext): void {
this.info('queue', action, details);
}
// Performance timing
time(context: string, label: string): string {
const timerId = `${context}:${label}:${Date.now()}`;
console.time(timerId);
return timerId;
}
timeEnd(timerId: string, message?: string): void {
console.timeEnd(timerId);
if (message) {
const [context] = timerId.split(':');
this.debug(context, message);
}
}
}
export const logger = new Logger();
// Export convenience functions for common patterns
export const logPipeline = logger.pipeline.bind(logger);
export const logAudit = logger.audit.bind(logger);
export const logAPI = logger.api.bind(logger);
export const logError = logger.error.bind(logger);
export const logInfo = logger.info.bind(logger);