UNPKG

ds-sfcoe-ailabs

Version:

AI-powered code review tool with static analysis integration for comprehensive code quality assessment.

311 lines (310 loc) 10 kB
import winston from 'winston'; import DatadogWinston from 'datadog-winston'; import * as console from 'node:console'; import { DATADOG_DEFAULT_CONSTANTS } from '../../utils/constants.js'; /** * Datadog Winston logger configuration and setup * Provides a wrapper around Winston logger with Datadog transport integration * Supports both console and Datadog HTTP transports with fallback mechanisms * * @example * ```typescript * // Initialize the logger * const config = { * apiKey: 'your-datadog-api-key', * service: 'my-service', * environment: 'production' * }; * DatadogLogger.initialize(config); * * // Use logging methods * DatadogLogger.info('Application started', { version: '1.0.0' }); * DatadogLogger.error('Database error', new Error('Connection failed')); * ``` */ export class DatadogLogger { static instance = null; static config = null; /** * Initialize the Datadog logger with configuration * * @param config - The Datadog logger configuration object * @returns The initialized Winston logger instance * @example * ```typescript * const config = { * apiKey: 'dd-api-key', * service: 'ai-pr-review', * environment: 'production', * logLevel: 'info' * }; * const logger = DatadogLogger.initialize(config); * ``` */ static initialize(config) { if (this.instance) { return this.instance; } this.config = config; const transports = [ // Console transport for local development new winston.transports.Console({ format: winston.format.combine(winston.format.timestamp({ format: DATADOG_DEFAULT_CONSTANTS.timestampFormat, }), winston.format.colorize(), winston.format.printf(({ timestamp, level, message, ...meta }) => `${String(timestamp)} [${String(level)}]: ${String(message)} ${Object.keys(meta).length ? JSON.stringify(meta) : ''}`)), }), ]; // Add Datadog HTTP transport if API key is provided if (config.apiKey) { try { const datadogTransport = new DatadogWinston({ apiKey: config.apiKey, ddsource: config.ddsource, hostname: config.hostname, service: config.service, ddtags: config.environment ? `env:${config.environment}` : undefined, level: config.logLevel, }); transports.push(datadogTransport); } catch (error) { console.warn(`Failed to initialize Datadog HTTP transport: ${JSON.stringify(error)}. Continuing with console logging only.`); } } else { console.warn('No Datadog API key provided - using console logging only'); } this.instance = winston.createLogger({ level: config.logLevel, format: winston.format.combine(winston.format.timestamp({ format: DATADOG_DEFAULT_CONSTANTS.timestampFormat, }), winston.format.errors({ stack: true }), winston.format.json()), defaultMeta: { service: config.service, environment: config.environment, ...config.customMeta, // Include custom metadata }, transports, }); this.instance.info('Datadog logging initialized'); return this.instance; } /** * Get the configured logger instance * * @returns The Winston logger instance * @throws Error if logger is not initialized * @example * ```typescript * try { * const logger = DatadogLogger.getInstance(); * logger.info('Direct Winston usage'); * } catch (error) { * console.error('Logger not initialized'); * } * ``` */ static getInstance() { if (!this.instance) { throw new Error('DatadogLogger not initialized. Call initialize() first.'); } return this.instance; } /** * Check if logger is initialized * * @returns True if the logger is initialized, false otherwise * @example * ```typescript * if (!DatadogLogger.isInitialized()) { * DatadogLogger.initialize(config); * } * ``` */ static isInitialized() { return this.instance !== null; } /** * Get current configuration * * @returns The current Datadog logger configuration or null if not set * @example * ```typescript * const config = DatadogLogger.getConfig(); * if (config) { * console.log(`Service: ${config.service}`); * } * ``` */ static getConfig() { return this.config; } /** * Log debug message * * @param message - The debug message to log * @param meta - Optional metadata object to include with the log * @example * ```typescript * DatadogLogger.debug('Debug information', { userId: '123', action: 'validate' }); * ``` */ static debug(message, meta) { if (this.instance) { this.instance.debug(message, meta); } else { console.debug(`[DEBUG]: ${message}`, meta ? JSON.stringify(meta) : ''); } } /** * Log info message * * @param message - The info message to log * @param meta - Optional metadata object to include with the log * @example * ```typescript * DatadogLogger.info('User logged in', { userId: '123', sessionId: 'abc' }); * ``` */ static info(message, meta) { if (this.instance) { this.instance.info(message, meta); } else { console.info(`[INFO]: ${message}`, meta ? JSON.stringify(meta) : ''); } } /** * Log warning message * * @param message - The warning message to log * @param meta - Optional metadata object to include with the log * @example * ```typescript * DatadogLogger.warn('Rate limit approaching', { currentRate: 90, limit: 100 }); * ``` */ static warn(message, meta) { if (this.instance) { this.instance.warn(message, meta); } else { console.warn(`[WARN]: ${message}`, meta ? JSON.stringify(meta) : ''); } } /** * Log error message * * @param message - The error message to log * @param error - Optional error object or metadata to include with the log * @example * ```typescript * DatadogLogger.error('Database connection failed', new Error('Timeout')); * DatadogLogger.error('Validation failed', { field: 'email', value: 'invalid' }); * ``` */ static error(message, error) { if (this.instance) { if (error instanceof Error) { this.instance.error(message, { error: error.message, stack: error.stack, }); } else { this.instance.error(message, error); } } else { console.error(`[ERROR]: ${message}`, error); } } /** * Log with custom level * * @param level - The log level (e.g., 'info', 'debug', 'error') * @param message - The message to log * @param meta - Optional metadata object to include with the log * @example * ```typescript * DatadogLogger.log('info', 'Custom log entry', { customField: 'value' }); * DatadogLogger.log('debug', 'Debug information', debugData); * ``` */ static log(level, message, meta) { if (this.instance) { this.instance.log(level, message, meta); } else { console.log(`[${level.toUpperCase()}]: ${message}`, meta ? JSON.stringify(meta) : ''); } } /** * Update custom metadata that will be included in all future logs * * @param meta - The metadata object to merge with existing custom metadata * @example * ```typescript * DatadogLogger.updateCustomMeta({ userId: '123', sessionId: 'abc' }); * // All subsequent logs will include this metadata * ``` */ static updateCustomMeta(meta) { if (this.config) { this.config.customMeta = { ...this.config.customMeta, ...meta }; // Update the logger instance's defaultMeta if it exists if (this.instance) { this.instance.defaultMeta = { ...this.instance.defaultMeta, ...meta, }; } } } /** * Set a specific metadata field * * @param key - The metadata field key * @param value - The value to set for the metadata field * @example * ```typescript * DatadogLogger.setMetaField('userId', '123'); * DatadogLogger.setMetaField('feature', 'pr-review'); * ``` */ static setMetaField(key, value) { this.updateCustomMeta({ [key]: value }); } /** * Remove a metadata field * * @param key - The metadata field key to remove * @example * ```typescript * DatadogLogger.removeMetaField('temporaryField'); * // Future logs will not include this field * ``` */ static removeMetaField(key) { if (this.config?.customMeta) { delete this.config.customMeta[key]; } if (this.instance?.defaultMeta) { delete this.instance.defaultMeta[key]; } } /** * Get current custom metadata * * @returns The current custom metadata object * @example * ```typescript * const currentMeta = DatadogLogger.getCustomMeta(); * console.log('Current metadata:', currentMeta); * ``` */ static getCustomMeta() { return this.config?.customMeta ?? {}; } }