159 lines
4.9 KiB
TypeScript
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); |