// Define the basic structure for your logs interface LogEntry { level: 'debug' | 'info' | 'warn' | 'error'; message: string; timestamp: string; context?: Record; error?: { message: string; stack?: string; cause?: unknown; }; } // Basic logger class export class Logger { private baseContext: Record; constructor(baseContext: Record = {}) { // Clone the context to prevent mutation issues if the source object changes this.baseContext = { ...baseContext }; } // Method to create a "child" logger with additional context child(additionalContext: Record): Logger { return new Logger({ ...this.baseContext, ...additionalContext }); } // Central logging function private log(level: LogEntry['level'], message: string, context?: Record, error?: Error) { const entry: LogEntry = { level, message, timestamp: new Date().toISOString(), // Merge base context, method-specific context context: { ...this.baseContext, ...context }, }; if (error) { entry.error = { message: error.message, stack: error.stack, // Include cause if available ...(error.cause ? { cause: error.cause } : {}), }; } // The core idea: output structured JSON via console.log // Logpush / Tail Workers will pick this up. console.log(JSON.stringify(entry)); } // Convenience methods for different levels debug(message: string, context?: Record) { this.log('debug', message, context); } info(message: string, context?: Record) { this.log('info', message, context); } warn(message: string, context?: Record, error?: Error) { this.log('warn', message, context, error); } error(message: string, context?: Record, error?: Error) { this.log('error', message, context, error); } }