UNPKG

@layr-labs/hourglass-performer

Version:

TypeScript SDK for building Hourglass AVS performers

305 lines 10.1 kB
"use strict"; // Enhanced structured logging with correlation IDs and context var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.StructuredLogger = exports.FileLogAppender = exports.ConsoleLogAppender = exports.LogLevel = void 0; /** * Log levels */ var LogLevel; (function (LogLevel) { LogLevel["ERROR"] = "error"; LogLevel["WARN"] = "warn"; LogLevel["INFO"] = "info"; LogLevel["DEBUG"] = "debug"; LogLevel["TRACE"] = "trace"; })(LogLevel || (exports.LogLevel = LogLevel = {})); /** * Console log appender with structured output */ class ConsoleLogAppender { constructor() { this.name = 'console'; } async write(entry) { const colorMap = { [LogLevel.ERROR]: '\x1b[31m', // Red [LogLevel.WARN]: '\x1b[33m', // Yellow [LogLevel.INFO]: '\x1b[36m', // Cyan [LogLevel.DEBUG]: '\x1b[37m', // White [LogLevel.TRACE]: '\x1b[90m', // Gray }; const reset = '\x1b[0m'; const color = colorMap[entry.level] || reset; // Format the main log line const time = new Date(entry.timestamp).toISOString(); const level = entry.level.toUpperCase().padEnd(5); const component = entry.context.component ? `[${entry.context.component}]` : ''; const correlation = entry.context.correlationId ? `{${entry.context.correlationId.slice(0, 8)}}` : ''; console.log(`${color}${time} ${level}${reset} ${component}${correlation} ${entry.message}`); // Add context details if present const contextKeys = Object.keys(entry.context).filter(k => !['component', 'correlationId'].includes(k) && entry.context[k] !== undefined); if (contextKeys.length > 0) { const contextStr = contextKeys.map(k => `${k}=${entry.context[k]}`).join(' '); console.log(` └─ ${contextStr}`); } // Add metadata if present if (entry.metadata && Object.keys(entry.metadata).length > 0) { console.log(` └─ ${JSON.stringify(entry.metadata)}`); } // Add stack trace for errors if (entry.stack) { console.log(` └─ Stack:\n${entry.stack.split('\n').map(line => ` ${line}`).join('\n')}`); } } } exports.ConsoleLogAppender = ConsoleLogAppender; /** * File log appender with JSON format */ class FileLogAppender { constructor(filePath) { this.filePath = filePath; this.name = 'file'; } async write(entry) { const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); const logLine = JSON.stringify(entry) + '\n'; try { await fs.appendFile(this.filePath, logLine); } catch (error) { // Fallback to console if file write fails console.error('Failed to write to log file:', error); console.log(logLine); } } } exports.FileLogAppender = FileLogAppender; /** * Enhanced structured logger */ class StructuredLogger { constructor(config = {}) { this.appenders = []; this.config = { level: config.level ?? LogLevel.INFO, defaultContext: config.defaultContext ?? {}, enabled: config.enabled ?? true, appenders: config.appenders ?? [new ConsoleLogAppender()], includeStack: config.includeStack ?? true, }; this.appenders = [...this.config.appenders]; this.correlationIdGenerator = this.createCorrelationIdGenerator(); } /** * Create a child logger with additional context */ child(context) { return new StructuredLogger({ ...this.config, defaultContext: { ...this.config.defaultContext, ...context }, }); } /** * Generate a new correlation ID */ generateCorrelationId() { return this.correlationIdGenerator(); } /** * Add log appender */ addAppender(appender) { this.appenders.push(appender); } /** * Remove log appender */ removeAppender(name) { this.appenders = this.appenders.filter(a => a.name !== name); } /** * Log an error */ error(message, meta) { this.log(LogLevel.ERROR, message, meta); } /** * Log a warning */ warn(message, meta) { this.log(LogLevel.WARN, message, meta); } /** * Log info */ info(message, meta) { this.log(LogLevel.INFO, message, meta); } /** * Log debug information */ debug(message, meta) { this.log(LogLevel.DEBUG, message, meta); } /** * Log trace information */ trace(message, meta) { this.log(LogLevel.TRACE, message, meta); } /** * Log with specific level and context */ logWithContext(level, message, context, meta) { this.log(level, message, meta, context); } /** * Core logging method */ log(level, message, meta, additionalContext) { if (!this.config.enabled || !this.shouldLog(level)) { return; } // Extract error stack if meta is an Error let stack; let metadata = meta; if (meta instanceof Error) { stack = meta.stack; metadata = { errorName: meta.name, errorMessage: meta.message, ...meta, }; } else if (this.config.includeStack && (level === LogLevel.ERROR || level === LogLevel.WARN)) { // Capture stack trace for errors and warnings const error = new Error(); stack = error.stack?.split('\n').slice(3).join('\n'); // Remove the first 3 lines (Error creation) } const entry = { level, message, timestamp: new Date().toISOString(), context: { ...this.config.defaultContext, ...additionalContext, // Add correlation ID if not present correlationId: additionalContext?.correlationId || this.config.defaultContext.correlationId || this.generateCorrelationId(), }, stack, metadata, }; // Write to all appenders this.writeToAppenders(entry); } /** * Check if we should log at this level */ shouldLog(level) { const levels = [LogLevel.ERROR, LogLevel.WARN, LogLevel.INFO, LogLevel.DEBUG, LogLevel.TRACE]; const currentIndex = levels.indexOf(this.config.level); const messageIndex = levels.indexOf(level); return messageIndex <= currentIndex; } /** * Write log entry to all appenders */ async writeToAppenders(entry) { const promises = this.appenders.map(async (appender) => { try { await appender.write(entry); } catch (error) { // Fallback: log to console if appender fails console.error(`Log appender ${appender.name} failed:`, error); console.log(JSON.stringify(entry)); } }); // Don't await to avoid blocking the calling code Promise.allSettled(promises); } /** * Create correlation ID generator */ createCorrelationIdGenerator() { let counter = 0; const instanceId = Math.random().toString(36).substr(2, 4); return () => { counter = (counter + 1) % 10000; const timestamp = Date.now().toString(36); return `${instanceId}-${timestamp}-${counter.toString().padStart(4, '0')}`; }; } /** * Create request-scoped logger */ static forRequest(requestId, additionalContext) { return new StructuredLogger({ defaultContext: { requestId, ...additionalContext, }, }); } /** * Create task-scoped logger */ static forTask(taskId, additionalContext) { return new StructuredLogger({ defaultContext: { taskId, component: 'task-processor', ...additionalContext, }, }); } /** * Create component-scoped logger */ static forComponent(component, additionalContext) { return new StructuredLogger({ defaultContext: { component, ...additionalContext, }, }); } } exports.StructuredLogger = StructuredLogger; //# sourceMappingURL=structuredLogger.js.map