UNPKG

logs-interceptor

Version:

High-performance, production-ready log interceptor for Node.js applications with Loki integration. Built with Clean Architecture principles. Supports Node.js, Browser, and Node-RED.

179 lines 6.59 kB
"use strict"; 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 (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LogService = void 0; const LogLevel_1 = require("../../domain/value-objects/LogLevel"); const LogEntry_1 = require("../../domain/entities/LogEntry"); const crypto = __importStar(require("crypto")); const os = __importStar(require("os")); class LogService { constructor(filter, buffer, transport, contextProvider, config) { this.filter = filter; this.buffer = buffer; this.transport = transport; this.contextProvider = contextProvider; this.config = config; this.startTime = Date.now(); this.metrics = this.initializeMetrics(); } initializeMetrics() { return { logsProcessed: 0, logsDropped: 0, logsSanitized: 0, flushCount: 0, errorCount: 0, bufferSize: 0, avgFlushTime: 0, lastFlushTime: 0, memoryUsage: 0, cpuUsage: 0, circuitBreakerTrips: 0, }; } debug(message, context) { this.log('debug', message, context); } info(message, context) { this.log('info', message, context); } warn(message, context) { this.log('warn', message, context); } error(message, context) { this.log('error', message, context); } fatal(message, context) { this.log('fatal', message, context); // Force immediate flush for fatal errors this.flush().catch(() => { // Silent fail for fatal errors }); } log(level, message, context) { if (!this.filter.isLevelEnabled(level)) { return; } const entry = this.createLogEntry(level, message, context); const filteredEntry = this.filter.filter(entry); if (!this.filter.shouldProcess(filteredEntry)) { this.metrics.logsDropped++; return; } this.buffer.add(filteredEntry); this.metrics.logsProcessed++; this.updateMetrics(); } trackEvent(eventName, properties) { this.info(`[EVENT] ${eventName}`, properties); } createLogEntry(level, message, context) { const levelVO = new LogLevel_1.LogLevelVO(level); const asyncContext = this.contextProvider.getContext(); const logId = crypto.randomBytes(8).toString('hex'); // Compute dynamic labels const dynamicLabels = Object.entries(this.config.dynamicLabels).reduce((acc, [key, fn]) => { try { acc[key] = String(fn()); } catch (error) { acc[key] = 'error'; } return acc; }, {}); return new LogEntry_1.LogEntryEntity(logId, new Date().toISOString(), level, message, { ...asyncContext, ...context, }, dynamicLabels.trace_id, dynamicLabels.span_id, dynamicLabels.request_id, { app: this.config.appName, version: this.config.version, environment: this.config.environment, level: level, hostname: os.hostname(), pid: String(process.pid), ...this.config.labels, ...dynamicLabels, }, { memoryUsage: this.metrics.memoryUsage, cpuUsage: this.metrics.cpuUsage, }); } async flush() { if (this.buffer.size() === 0) { return; } const entries = this.buffer.flush(); const startTime = Date.now(); try { await this.transport.send(entries); const flushTime = Date.now() - startTime; this.metrics.flushCount++; this.metrics.lastFlushTime = Date.now(); this.metrics.avgFlushTime = (this.metrics.avgFlushTime * (this.metrics.flushCount - 1) + flushTime) / this.metrics.flushCount; this.updateMetrics(); } catch (error) { this.metrics.errorCount++; // Re-add entries to buffer on failure entries.forEach((entry) => this.buffer.add(entry)); throw error; } } getMetrics() { return { ...this.metrics, bufferSize: this.buffer.size() }; } getHealth() { const bufferMetrics = this.buffer.getMetrics(); const transportHealth = this.transport.getHealth(); return { healthy: this.metrics.errorCount < 10 && Date.now() - this.metrics.lastFlushTime < 30000 && bufferMetrics.size / bufferMetrics.maxSize < 0.9 && transportHealth.healthy, lastSuccessfulFlush: this.metrics.lastFlushTime, consecutiveErrors: this.metrics.errorCount, bufferUtilization: bufferMetrics.size / bufferMetrics.maxSize, uptime: Date.now() - this.startTime, memoryUsageMB: this.metrics.memoryUsage, circuitBreakerState: transportHealth.healthy ? 'closed' : 'open', }; } async destroy() { await this.flush(); await this.transport.destroy(); } updateMetrics() { if (!this.config.enableMetrics) return; const memUsage = process.memoryUsage(); this.metrics.memoryUsage = memUsage.heapUsed / 1024 / 1024; this.metrics.cpuUsage = process.cpuUsage().user / 1000000; this.metrics.bufferSize = this.buffer.size(); } } exports.LogService = LogService; //# sourceMappingURL=LogService.js.map