UNPKG

@200systems/mf-logger

Version:

Structured logging with multiple outputs and performance timing for TypeScript applications

156 lines 4.99 kB
import { ENV } from './environment.js'; // Node.js File Handler export class NodeFileHandler { config; currentFileSize = 0; fileIndex = 0; initialized = false; constructor(config) { this.config = config; } async initialize() { if (this.initialized) return; try { const { mkdir, stat, writeFile } = await import('fs/promises'); const { dirname, resolve } = await import('path'); const dirPath = dirname(resolve(this.config.filePath)); await mkdir(dirPath, { recursive: true }); try { const stats = await stat(this.getCurrentFilePath()); this.currentFileSize = stats.size; } catch { // File doesn't exist, create it await writeFile(this.getCurrentFilePath(), '', 'utf8'); this.currentFileSize = 0; } this.initialized = true; } catch (error) { throw new Error(`Failed to initialize log file: ${error}`); } } async write(content) { if (!this.initialized) { await this.initialize(); } try { const { appendFile } = await import('fs/promises'); const logSize = Buffer.byteLength(content, 'utf8'); if (this.config.maxSize && this.currentFileSize + logSize > this.config.maxSize) { await this.rotate(); } await appendFile(this.getCurrentFilePath(), content, 'utf8'); this.currentFileSize += logSize; } catch (error) { console.error('Failed to write to log file:', error); } } async rotate() { this.fileIndex++; this.currentFileSize = 0; // Clean up old files if (this.config.maxFiles && this.fileIndex >= this.config.maxFiles) { const oldFileIndex = this.fileIndex - this.config.maxFiles; const oldFilePath = this.getFilePathForIndex(oldFileIndex); try { const { unlink } = await import('fs/promises'); await unlink(oldFilePath); } catch { // File might not exist, ignore error } } } getCurrentFilePath() { return this.getFilePathForIndex(this.fileIndex); } getFilePathForIndex(index) { if (index === 0) { return this.config.filePath; } const extension = this.config.filePath.split('.').pop() || ''; const baseName = this.config.filePath.slice(0, -(extension.length + 1)); return `${baseName}.${index}.${extension}`; } } // Browser Storage Handler export class BrowserStorageHandler { storageKey; maxEntries; storage; constructor(config) { // Use filename as storage key const filename = config.filePath.split('/').pop() || 'app-logs'; this.storageKey = `mf-logger-${filename}`; this.maxEntries = 1000; // Reasonable limit for browser storage this.storage = globalThis.localStorage; } async initialize() { // No initialization needed for browser storage } async write(content) { try { const logs = this.getLogs(); logs.push({ timestamp: new Date().toISOString(), content: content.trim() }); if (logs.length > this.maxEntries) { logs.splice(0, logs.length - this.maxEntries); } this.storage.setItem(this.storageKey, JSON.stringify(logs)); } catch (error) { console.warn('Failed to write to browser storage:', error); } } getLogs() { try { return JSON.parse(this.storage.getItem(this.storageKey) || '[]'); } catch { return []; } } // Utility method to retrieve logs retrieveLogs() { return this.getLogs(); } // Utility method to clear logs clearLogs() { this.storage.removeItem(this.storageKey); } } // No-op Handler for unsupported environments export class NoOpFileHandler { config; warned = false; constructor(config) { this.config = config; } async initialize() { // No initialization needed } async write(content) { if (!this.warned) { console.warn(`File logging to '${this.config.filePath}' not available in this environment`); this.warned = true; } } } // Factory function export function createFileHandler(config) { if (ENV.isNode) { return new NodeFileHandler(config); } else if (ENV.isBrowser && typeof globalThis !== 'undefined' && 'localStorage' in globalThis) { return new BrowserStorageHandler(config); } else { return new NoOpFileHandler(config); } } //# sourceMappingURL=file-handlers.js.map