184 lines
6.0 KiB
TypeScript
184 lines
6.0 KiB
TypeScript
import { createLogger, format, transports } from 'winston';
|
|
import { DATABASE_LOG_CONFIG, PERFORMANCE_THRESHOLDS, LOG_FILTERS, getEnvironmentConfig } from './LoggingConfig';
|
|
|
|
// Database operation logger
|
|
export const dbLogger = createLogger(DATABASE_LOG_CONFIG);
|
|
|
|
// Apply environment-specific configuration
|
|
const envConfig = getEnvironmentConfig();
|
|
dbLogger.level = envConfig.level;
|
|
|
|
// Performance monitoring
|
|
export class PerformanceMonitor {
|
|
private static timers: Map<string, number> = new Map();
|
|
|
|
static startTimer(operationId: string): void {
|
|
this.timers.set(operationId, Date.now());
|
|
}
|
|
|
|
static endTimer(operationId: string, operation: string, entityName: string): number {
|
|
const startTime = this.timers.get(operationId);
|
|
if (!startTime) {
|
|
dbLogger.warn('Timer not found for operation', { operationId, operation, entityName });
|
|
return 0;
|
|
}
|
|
|
|
const duration = Date.now() - startTime;
|
|
this.timers.delete(operationId);
|
|
|
|
// Log based on performance thresholds
|
|
if (duration > PERFORMANCE_THRESHOLDS.VERY_SLOW_OPERATION) {
|
|
dbLogger.error('Very slow database operation detected', {
|
|
operation,
|
|
entityName,
|
|
duration,
|
|
operationId,
|
|
threshold: PERFORMANCE_THRESHOLDS.VERY_SLOW_OPERATION,
|
|
severity: 'critical'
|
|
});
|
|
} else if (duration > PERFORMANCE_THRESHOLDS.SLOW_OPERATION) {
|
|
dbLogger.warn('Slow database operation detected', {
|
|
operation,
|
|
entityName,
|
|
duration,
|
|
operationId,
|
|
threshold: PERFORMANCE_THRESHOLDS.SLOW_OPERATION,
|
|
severity: 'warning'
|
|
});
|
|
} else {
|
|
dbLogger.debug('Database operation completed', {
|
|
operation,
|
|
entityName,
|
|
duration,
|
|
operationId
|
|
});
|
|
}
|
|
|
|
return duration;
|
|
}
|
|
|
|
static generateOperationId(): string {
|
|
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
}
|
|
}
|
|
|
|
// Database operation types for logging
|
|
export enum DBOperation {
|
|
CREATE = 'CREATE',
|
|
READ = 'READ',
|
|
UPDATE = 'UPDATE',
|
|
DELETE = 'DELETE',
|
|
SOFT_DELETE = 'SOFT_DELETE',
|
|
BULK_CREATE = 'BULK_CREATE',
|
|
BULK_UPDATE = 'BULK_UPDATE',
|
|
BULK_DELETE = 'BULK_DELETE',
|
|
FIND = 'FIND',
|
|
SEARCH = 'SEARCH',
|
|
COUNT = 'COUNT',
|
|
AGGREGATE = 'AGGREGATE',
|
|
TRANSACTION = 'TRANSACTION'
|
|
}
|
|
|
|
// Logging decorators
|
|
export function LogDBOperation(operation: DBOperation, entityName: string) {
|
|
return function (target: any, propertyName: string, descriptor: PropertyDescriptor) {
|
|
const method = descriptor.value;
|
|
|
|
descriptor.value = async function (...args: any[]) {
|
|
const operationId = PerformanceMonitor.generateOperationId();
|
|
const startTime = Date.now();
|
|
|
|
PerformanceMonitor.startTimer(operationId);
|
|
|
|
dbLogger.info('Database operation started', {
|
|
operation,
|
|
entityName,
|
|
method: propertyName,
|
|
operationId,
|
|
args: args.length > 0 ? 'provided' : 'none'
|
|
});
|
|
|
|
try {
|
|
const result = await method.apply(this, args);
|
|
|
|
const duration = PerformanceMonitor.endTimer(operationId, operation, entityName);
|
|
|
|
dbLogger.info('Database operation successful', {
|
|
operation,
|
|
entityName,
|
|
method: propertyName,
|
|
operationId,
|
|
duration,
|
|
resultCount: Array.isArray(result) ? result.length : result ? 1 : 0
|
|
});
|
|
|
|
return result;
|
|
} catch (error) {
|
|
const duration = Date.now() - startTime;
|
|
|
|
dbLogger.error('Database operation failed', {
|
|
operation,
|
|
entityName,
|
|
method: propertyName,
|
|
operationId,
|
|
duration,
|
|
error: error instanceof Error ? error.message : String(error),
|
|
stack: error instanceof Error ? error.stack : undefined
|
|
});
|
|
|
|
throw error;
|
|
}
|
|
};
|
|
};
|
|
}
|
|
|
|
// Connection logging
|
|
export function logDatabaseConnection(status: 'connecting' | 'connected' | 'disconnected' | 'error', details?: any) {
|
|
const logData = {
|
|
event: 'database_connection',
|
|
status,
|
|
timestamp: new Date().toISOString(),
|
|
...details
|
|
};
|
|
|
|
if (status === 'error') {
|
|
dbLogger.error('Database connection error', logData);
|
|
} else {
|
|
dbLogger.info('Database connection status', logData);
|
|
}
|
|
}
|
|
|
|
// Query logging
|
|
export function logQuery(query: string, parameters?: any[], duration?: number) {
|
|
const envConfig = getEnvironmentConfig();
|
|
|
|
if (!envConfig.logQueries) return;
|
|
|
|
const sanitizedQuery = LOG_FILTERS.sanitizeQuery(query);
|
|
const sanitizedParams = parameters ? LOG_FILTERS.sanitizeParameters(parameters) : [];
|
|
|
|
const logData = {
|
|
event: 'database_query',
|
|
query: sanitizedQuery,
|
|
parameters: sanitizedParams,
|
|
duration: duration || 0,
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
|
|
if (duration && duration > PERFORMANCE_THRESHOLDS.VERY_SLOW_QUERY) {
|
|
dbLogger.error('Very slow query detected', {
|
|
...logData,
|
|
severity: 'critical',
|
|
threshold: PERFORMANCE_THRESHOLDS.VERY_SLOW_QUERY
|
|
});
|
|
} else if (duration && duration > PERFORMANCE_THRESHOLDS.SLOW_QUERY) {
|
|
dbLogger.warn('Slow query detected', {
|
|
...logData,
|
|
severity: 'warning',
|
|
threshold: PERFORMANCE_THRESHOLDS.SLOW_QUERY
|
|
});
|
|
} else {
|
|
dbLogger.debug('Database query executed', logData);
|
|
}
|
|
}
|