UNPKG

@gati-framework/observability

Version:

Observability stack for Gati framework - Prometheus, Grafana, Loki, and Tracing

83 lines (82 loc) 2.52 kB
import winston from 'winston'; import LokiTransport from 'winston-loki'; export class LokiLogger { logger; lokiTransport; constructor(config) { this.lokiTransport = new LokiTransport({ host: config.host, basicAuth: config.basicAuth, labels: { app: 'gati', ...config.labels, }, batching: config.batching !== false, interval: config.interval || 5000, json: true, }); this.logger = winston.createLogger({ level: 'info', format: winston.format.combine(winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json()), transports: [ this.lokiTransport, new winston.transports.Console({ format: winston.format.combine(winston.format.colorize(), winston.format.simple()), }), ], }); } info(message, meta) { this.logger.info(message, meta); } warn(message, meta) { this.logger.warn(message, meta); } error(message, error, meta) { this.logger.error(message, { error: error?.message, stack: error?.stack, ...meta, }); } debug(message, meta) { this.logger.debug(message, meta); } child(labels) { const transport = this.lokiTransport; const childLogger = new LokiLogger({ host: transport.host || 'http://localhost:3100', basicAuth: transport.basicAuth, labels: { ...(transport.labels || {}), ...labels, }, batching: transport.batching !== false, interval: transport.interval || 5000, }); return childLogger; } async flush() { await new Promise((resolve) => { this.logger.on('finish', resolve); this.logger.end(); }); } } export function createLokiMiddleware(logger) { return (req, res, next) => { const start = Date.now(); res.on('finish', () => { const duration = Date.now() - start; logger.info('HTTP Request', { method: req.method, path: req.path, statusCode: res.statusCode, duration, userAgent: req.get('user-agent'), ip: req.ip, }); }); next(); }; }