sfcoe-ailabs
Version:
AI-powered code review tool with static analysis integration for comprehensive code quality assessment.
220 lines (219 loc) • 7.16 kB
JavaScript
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
*/
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
*/
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
*/
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
*/
static isInitialized() {
return this.instance !== null;
}
/**
* Get current configuration
*
* @returns The current Datadog logger configuration or null if not set
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
static setMetaField(key, value) {
this.updateCustomMeta({ [key]: value });
}
/**
* Remove a metadata field
*
* @param key - The metadata field key to remove
*/
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
*/
static getCustomMeta() {
return this.config?.customMeta ?? {};
}
}