UNPKG

@200systems/mf-logger

Version:

Structured logging with multiple outputs and performance timing for TypeScript applications

213 lines 7.3 kB
import { createOutput } from './outputs.js'; import { defaultFormatter } from './formatter.js'; import { ENV } from './environment.js'; class MicroLogger { config; persistentMetadata = {}; outputs = []; constructor(config) { this.config = config; this.persistentMetadata = { ...config.metadata }; this.outputs = config.outputs?.map(createOutput) || []; } debug(message, metadata) { if (this.shouldLog('debug')) { this.log('debug', message, undefined, metadata); } } info(message, metadata) { if (this.shouldLog('info')) { this.log('info', message, undefined, metadata); } } warn(message, metadata) { if (this.shouldLog('warn')) { this.log('warn', message, undefined, metadata); } } error(message, error, metadata) { if (this.shouldLog('error')) { this.log('error', message, error, metadata); } } child(context, metadata) { const childContext = this.config.context ? `${this.config.context}.${context}` : context; return new MicroLogger({ ...this.config, context: childContext, metadata: { ...this.persistentMetadata, ...metadata } }); } addMetadata(key, value) { this.persistentMetadata[key] = value; } removeMetadata(key) { delete this.persistentMetadata[key]; } clearMetadata() { this.persistentMetadata = {}; } startTimer(name) { const startTime = performance.now(); const startMemory = ENV.isNode && typeof process !== 'undefined' ? process.memoryUsage() : undefined; return () => { const duration = performance.now() - startTime; const endMemory = ENV.isNode && typeof process !== 'undefined' ? process.memoryUsage() : undefined; this.info(`Timer ${name} completed`, { timer: name, duration: Math.round(duration * 100) / 100, // Round to 2 decimal places ...(startMemory && endMemory && { memoryDelta: { rss: endMemory.rss - startMemory.rss, heapUsed: endMemory.heapUsed - startMemory.heapUsed, heapTotal: endMemory.heapTotal - startMemory.heapTotal } }) }); }; } // Browser-specific utility methods retrieveStoredLogs() { if (!ENV.isBrowser) return null; for (const output of this.outputs) { if ('retrieveLogs' in output && typeof output.retrieveLogs === 'function') { return output.retrieveLogs(); } } return null; } clearStoredLogs() { if (!ENV.isBrowser) return; for (const output of this.outputs) { if ('clearLogs' in output && typeof output.clearLogs === 'function') { output.clearLogs(); } } } shouldLog(level) { const levels = ['debug', 'info', 'warn', 'error']; const configLevel = this.config.level || 'info'; return levels.indexOf(level) >= levels.indexOf(configLevel); } log(level, message, error, metadata) { const entry = this.createLogEntry(level, message, error, metadata); const formatted = this.formatEntry(entry); // Write to all outputs for (const output of this.outputs) { try { const result = output.write(entry, formatted); if (result instanceof Promise) { result.catch(err => { console.error('Failed to write to output:', err); }); } } catch (err) { console.error('Failed to write to output:', err); } } } createLogEntry(level, message, error, metadata) { const entry = { timestamp: this.config.includeTimestamp ? new Date().toISOString() : '', level, message, }; if (this.config.context) { entry.context = this.config.context; } // Merge persistent metadata with provided metadata const allMetadata = { ...this.persistentMetadata, ...metadata }; if (Object.keys(allMetadata).length > 0) { entry.metadata = allMetadata; } if (error) { entry.error = { name: error.name, message: error.message, code: error.code, }; if (this.config.includeStack && error.stack) { entry.error.stack = error.stack; } } // Add memory usage if available and enabled if (ENV.isNode && typeof process !== 'undefined' && process.memoryUsage) { entry.performance = { memory: process.memoryUsage(), }; } return entry; } formatEntry(entry) { return this.config.format === 'json' ? defaultFormatter.formatJson(entry) : defaultFormatter.formatPretty(entry); } } // Environment-aware default configuration function getDefaultConfig() { const processEnv = (typeof process !== 'undefined' && process.env) || {}; const level = processEnv.LOG_LEVEL || 'info'; const format = processEnv.LOG_FORMAT || 'pretty'; return { level, format, includeTimestamp: true, includeStack: !ENV.isBrowser && processEnv.NODE_ENV !== 'production', outputs: [{ type: 'console', options: ENV.isBrowser ? { enhanced: true } : {} }], }; } // Factory function with environment detection export function createLogger(config = {}) { const defaultConfig = getDefaultConfig(); const finalConfig = { ...defaultConfig, ...config, }; return new MicroLogger(finalConfig); } // Browser-specific factory with enhanced defaults export function createBrowserLogger(config = {}) { const browserDefaults = { format: 'pretty', includeTimestamp: true, includeStack: false, outputs: [ { type: 'console', options: { enhanced: true } }, ...(config.outputs || []) ] }; return createLogger({ ...browserDefaults, ...config }); } // Node.js-specific factory with enhanced defaults export function createServerLogger(config = {}) { const processEnv = (typeof process !== 'undefined' && process.env) || {}; const level = processEnv.LOG_LEVEL || 'info'; const format = processEnv.LOG_FORMAT || 'pretty'; const serverDefaults = { level, format, includeTimestamp: true, includeStack: !ENV.isBrowser && processEnv.NODE_ENV !== 'production', outputs: [{ type: 'console', options: ENV.isBrowser ? { enhanced: true } : {} }], }; return createLogger({ ...serverDefaults, ...config }); } // Convenience export export const defaultConfig = getDefaultConfig(); //# sourceMappingURL=logger.js.map