Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394/** * Logging utility for SMB2 Server * Provides structured logging with different levels and formatting */
import winston from 'winston';
export type LogLevel = 'error' | 'warn' | 'info' | 'debug';
export interface LoggerOptions { level?: LogLevel; component?: string; enableConsole?: boolean; enableFile?: boolean; logFile?: string; format?: 'json' | 'simple' | 'detailed';}
export class Logger { private readonly winston: winston.Logger; private readonly component: string;
constructor(component: string = 'SMB2Server', options: LoggerOptions = {}) { this.component = component;
const { level = 'debug', enableConsole = true, enableFile = false, logFile = 'smb-server.log', format = 'detailed', } = options;
// Create winston transports const transports: winston.transport[] = [];
if (enableConsole) { transports.push( new winston.transports.Console({ level, format: this.getConsoleFormat(format), }) ); }
if (enableFile) { transports.push( new winston.transports.File({ filename: logFile, level, format: this.getFileFormat(format), maxsize: 10 * 1024 * 1024, // 10MB maxFiles: 5, }) ); }
// Create winston logger this.winston = winston.createLogger({ level, transports, exitOnError: false, }); }
/** * Log error message */ public error(message: string, meta?: any): void { this.winston.error(message, { component: this.component, ...meta }); }
/** * Log warning message */ public warn(message: string, meta?: any): void { this.winston.warn(message, { component: this.component, ...meta }); }
/** * Log info message */ public info(message: string, meta?: any): void { this.winston.info(message, { component: this.component, ...meta }); }
/** * Log debug message */ public debug(message: string, meta?: any): void { this.winston.debug(message, { component: this.component, ...meta }); }
/** * Log with specific level */ public log(level: LogLevel, message: string, meta?: any): void { this.winston.log(level, message, { component: this.component, ...meta }); }
/** * Create a child logger with additional context */ public child(context: Record<string, any>): Logger { const childLogger = new Logger(this.component); childLogger.winston.defaultMeta = { ...this.winston.defaultMeta, ...context }; return childLogger; }
/** * Set log level */ public setLevel(level: LogLevel): void { this.winston.level = level; }
/** * Get current log level */ public getLevel(): string { return this.winston.level; }
/** * Check if level is enabled */ public isLevelEnabled(level: LogLevel): boolean { return this.winston.isLevelEnabled(level); }
/** * Log SMB protocol message */ public logProtocol( direction: 'incoming' | 'outgoing', command: string, connectionId?: string, sessionId?: string, meta?: any ): void { this.debug(`${direction.toUpperCase()} ${command}`, { direction, command, connectionId, sessionId, ...meta, }); }
/** * Log connection event */ public logConnection( event: 'established' | 'closed', connectionId: string, clientAddress: string, meta?: any ): void { this.info(`Connection ${event}: ${connectionId} from ${clientAddress}`, { event: `connection:${event}`, connectionId, clientAddress, ...meta, }); }
/** * Log session event */ public logSession( event: 'established' | 'closed', sessionId: string, connectionId: string, userId?: string, meta?: any ): void { this.info(`Session ${event}: ${sessionId}`, { event: `session:${event}`, sessionId, connectionId, userId, ...meta, }); }
/** * Log file operation */ public logFileOperation( operation: 'open' | 'close' | 'read' | 'write' | 'delete', filename: string, connectionId?: string, sessionId?: string, meta?: any ): void { this.info(`File ${operation}: ${filename}`, { operation: `file:${operation}`, filename, connectionId, sessionId, ...meta, }); }
/** * Log performance metrics */ public logPerformance( operation: string, duration: number, meta?: any ): void { this.debug(`Performance: ${operation} took ${duration}ms`, { operation, duration, type: 'performance', ...meta, }); }
/** * Log security event */ public logSecurity( event: 'auth_success' | 'auth_failure' | 'access_denied' | 'suspicious_activity', details: string, connectionId?: string, sessionId?: string, meta?: any ): void { this.warn(`Security event: ${event} - ${details}`, { event: `security:${event}`, details, connectionId, sessionId, type: 'security', ...meta, }); }
/** * Get console format based on format type */ private getConsoleFormat(format: string): winston.Logform.Format { switch (format) { case 'json': return winston.format.combine( winston.format.timestamp(), winston.format.json() );
case 'simple': return winston.format.combine( winston.format.colorize(), winston.format.simple() );
case 'detailed': default: return winston.format.combine( winston.format.colorize(), winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.errors({ stack: true }), winston.format.printf(this.detailedFormatter) ); } }
/** * Get file format based on format type */ private getFileFormat(format: string): winston.Logform.Format { switch (format) { case 'simple': return winston.format.combine( winston.format.timestamp(), winston.format.simple() );
case 'detailed': return winston.format.combine( winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.errors({ stack: true }), winston.format.printf(this.detailedFormatter) );
case 'json': default: return winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json() ); } }
/** * Detailed log formatter */ private detailedFormatter = (info: winston.Logform.TransformableInfo): string => { const { timestamp, level, message, component, ...meta } = info; const componentStr = component ? `[${component}]` : '';
let metaStr = ''; if (Object.keys(meta).length > 0) { // Filter out common fields and format remaining metadata const filteredMeta = { ...meta }; delete filteredMeta['stack']; // Stack traces are handled separately
if (Object.keys(filteredMeta).length > 0) { metaStr = ` ${JSON.stringify(filteredMeta)}`; } }
let result = `${timestamp} ${level.toUpperCase().padEnd(5)} ${componentStr} ${message}${metaStr}`;
// Add stack trace for errors if (info['stack']) { result += `\n${info['stack']}`; }
return result; };
/** * Create a logger for a specific SMB command */ public static createCommandLogger(command: string, connectionId: string): Logger { return new Logger(`SMB2:${command}:${connectionId}`); }
/** * Create a logger with connection context */ public static createConnectionLogger(connectionId: string, clientAddress: string): Logger { const logger = new Logger('SMB2Connection'); return logger.child({ connectionId, clientAddress }); }
/** * Create a logger with session context */ public static createSessionLogger(sessionId: string, connectionId: string): Logger { const logger = new Logger('SMB2Session'); return logger.child({ sessionId, connectionId }); }
/** * Gracefully close the logger */ public close(): Promise<void> { return new Promise((resolve) => { this.winston.close(); // Winston doesn't provide a global shutdown, so this is a placeholder // for any cleanup that might be needed setTimeout(resolve, 100) }); }
/** * Handle process shutdown */ public static async shutdown(): Promise<void> { // Winston doesn't provide a global shutdown, so this is a placeholder // for any cleanup that might be needed await new Promise(resolve => setTimeout(resolve, 100)); }}
/** * Default logger instance */export const defaultLogger = new Logger('SMB2Server', { level: process.env['LOG_LEVEL'] as LogLevel || 'info', enableConsole: true, enableFile: process.env['ENABLE_FILE_LOGGING'] === 'true', logFile: process.env['LOG_FILE'] || 'smb-server.log', format: (process.env['LOG_FORMAT'] as any) || 'detailed',});
/** * Utility function to create a logger */export function createLogger(component: string, options?: LoggerOptions): Logger { return new Logger(component, options);}
/** * Set global log level */export function setGlobalLogLevel(level: LogLevel): void { defaultLogger.setLevel(level);}