UNPKG

azify-logger

Version:

Azify Logger Client - Centralized logging for OpenSearch

326 lines (299 loc) 10 kB
const axios = require('axios') let context, propagation, trace, W3CTraceContextPropagator try { const otelApi = require('@opentelemetry/api') const otelCore = require('@opentelemetry/core') context = otelApi.context propagation = otelApi.propagation trace = otelApi.trace W3CTraceContextPropagator = otelCore.W3CTraceContextPropagator } catch (_) { context = { active: () => ({}) } propagation = { setGlobalPropagator: () => {} } trace = { getSpan: () => null } W3CTraceContextPropagator = class {} } if (process.env.AZIFY_LOGGER_AUTOREG_DISABLE !== '1') { try { require('./register-otel') } catch (_) {} try { require('./register') } catch (_) {} try { require('./register-restify') } catch (_) {} } /** * AzifyLogger class for structured logging with OpenTelemetry integration * @class */ class AzifyLogger { /** * Creates an instance of AzifyLogger * @param {Object} options - Configuration options * @param {string} [options.serviceName='azipay'] - Name of the service * @param {string} [options.loggerUrl='http://localhost:3001'] - URL of the azify-logger service * @param {string} [options.environment] - Environment name (defaults to NODE_ENV or 'development') */ constructor(options = {}) { this.options = { serviceName: options.serviceName || 'azipay', loggerUrl: options.loggerUrl || 'http://localhost:3001', environment: options.environment || process.env.NODE_ENV || 'development', ...options } this.propagator = new W3CTraceContextPropagator() propagation.setGlobalPropagator(this.propagator) } /** * Sends a log entry to the azify-logger service * @param {string} level - Log level (info, error, warn, debug) * @param {string} message - Log message * @param {Object} [meta={}] - Additional metadata to include in the log * @returns {Promise<void>} */ async log(level, message, meta = {}) { const span = trace.getSpan(context.active()) const spanContext = span && span.spanContext() const logData = { level, message, meta: { ...meta, service: { name: this.options.serviceName, version: (meta.service && meta.service.version) || '1.0.0' }, environment: this.options.environment, timestamp: new Date().toISOString(), hostname: require('os').hostname() } } if (spanContext) { logData.meta.traceId = spanContext.traceId logData.meta.spanId = spanContext.spanId } try { const headers = {} try { propagation.inject(context.active(), headers, { set (carrier, key, value) { carrier[key] = value } }) } catch (_) {} await axios.post(`${this.options.loggerUrl}`, logData, { timeout: 5000, headers }) } catch (error) { console.error('Erro ao enviar log:', error.message) } } /** * Logs an info level message * @param {string} message - Log message * @param {Object} [meta={}] - Additional metadata * @returns {Promise<void>} */ info(message, meta = {}) { return this.log('info', message, meta) } /** * Logs an error level message with optional error object * @param {string} message - Log message * @param {Error} [error=null] - Error object to include in metadata * @param {Object} [meta={}] - Additional metadata * @returns {Promise<void>} */ error(message, error = null, meta = {}) { const logMeta = { ...meta } if (error) { logMeta.error = { message: error.message, stack: error.stack, name: error.name } } return this.log('error', message, logMeta) } /** * Logs a warning level message * @param {string} message - Log message * @param {Object} [meta={}] - Additional metadata * @returns {Promise<void>} */ warn(message, meta = {}) { return this.log('warn', message, meta) } /** * Logs a debug level message * @param {string} message - Log message * @param {Object} [meta={}] - Additional metadata * @returns {Promise<void>} */ debug(message, meta = {}) { return this.log('debug', message, meta) } } /** * Creates a new AzifyLogger instance with custom options * @param {Object} options - Configuration options * @param {string} [options.serviceName] - Name of the service * @param {string} [options.loggerUrl] - URL of the azify-logger service * @param {string} [options.environment] - Environment name * @returns {AzifyLogger} New AzifyLogger instance * @example * const logger = createAzifyLogger({ serviceName: 'my-app' }); * logger.info('Hello world'); */ function createAzifyLogger(options = {}) { return new AzifyLogger(options) } /** * Creates a new AzifyLogger instance using environment variables and automatically intercepts console * @returns {AzifyLogger} New AzifyLogger instance configured from environment * @example * const logger = createAzifyLoggerFromEnv(); * logger.info('Hello world'); * console.log('This will also appear in OpenSearch!'); */ function createAzifyLoggerFromEnv() { const logger = createAzifyLogger({ serviceName: process.env.APP_NAME || 'azipay', loggerUrl: process.env.AZIFY_LOGGER_URL || 'http://localhost:3001', environment: process.env.NODE_ENV || 'development' }) interceptConsole(logger) return logger } /** * Intercepts all console methods to send logs to azify-logger * @param {AzifyLogger} logger - AzifyLogger instance to use for logging */ function interceptConsole(logger) { const originalConsoleLog = console.log; const originalConsoleError = console.error; const originalConsoleWarn = console.warn; const originalConsoleInfo = console.info; const originalConsoleDebug = console.debug; console.log = (...args) => { originalConsoleLog(...args); const { getRequestContext } = require('./store'); const ctx = getRequestContext(); const message = args.join(' '); if (message.includes('failed after')) { } if (ctx && ctx.traceId) { void logger.info(message, { traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId }); } else { void logger.info(message); } }; console.error = (...args) => { originalConsoleError(...args); const { getRequestContext } = require('./store'); const ctx = getRequestContext(); if (ctx && ctx.traceId) { void logger.error(args.join(' '), { traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId }); } else { void logger.error(args.join(' ')); } }; console.warn = (...args) => { originalConsoleWarn(...args); const { getRequestContext } = require('./store'); const ctx = getRequestContext(); if (ctx && ctx.traceId) { void logger.warn(args.join(' '), { traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId }); } else { void logger.warn(args.join(' ')); } }; console.info = (...args) => { originalConsoleInfo(...args); const { getRequestContext } = require('./store'); const ctx = getRequestContext(); if (ctx && ctx.traceId) { void logger.info(args.join(' '), { traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId }); } else { void logger.info(args.join(' ')); } }; console.debug = (...args) => { originalConsoleDebug(...args); const { getRequestContext } = require('./store'); const ctx = getRequestContext(); if (ctx && ctx.traceId) { void logger.debug(args.join(' '), { traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId }); } else { void logger.debug(args.join(' ')); } }; } /** * Creates a NestJS logger configuration that sends logs to azify-logger * @returns {Object} NestJS logger configuration * @example * const app = await NestFactory.create(AppModule, { * logger: createNestLogger() * }); */ function createNestLogger() { const { getRequestContext } = require('./store') const pino = require('pino') return { log: (message, context) => { const ctx = getRequestContext() if (ctx && ctx.traceId) { pino().info(message, context || '') } else { pino().info(message, context || '') } }, error: (message, trace, context) => { const ctx = getRequestContext() if (ctx && ctx.traceId) { pino().error(message, trace || '', context || '') } else { pino().error(message, trace || '', context || '') } }, warn: (message, context) => { const ctx = getRequestContext() if (ctx && ctx.traceId) { pino().warn(message, context || '') } else { pino().warn(message, context || '') } }, debug: (message, context) => { const ctx = getRequestContext() if (ctx && ctx.traceId) { pino().debug(message, context || '') } else { pino().debug(message, context || '') } }, verbose: (message, context) => { const ctx = getRequestContext() if (ctx && ctx.traceId) { pino().info(message, context || '') } else { pino().info(message, context || '') } } } } if (process.env.AZIFY_LOGGER_DISABLE !== '1') { require('./init') require('./register') } module.exports = AzifyLogger module.exports.createAzifyLogger = createAzifyLogger module.exports.createAzifyLoggerFromEnv = createAzifyLoggerFromEnv module.exports.createNestLogger = createNestLogger module.exports.interceptConsole = interceptConsole module.exports.streams = { createBunyanStream: require('./streams/bunyan'), createPinoStream: require('./streams/pino') } module.exports.middleware = { restify: require('./middleware-restify'), express: require('./middleware-express') }