update
This commit is contained in:
246
ts/mail/delivery/smtpserver/utils/logging.ts
Normal file
246
ts/mail/delivery/smtpserver/utils/logging.ts
Normal file
@ -0,0 +1,246 @@
|
||||
/**
|
||||
* SMTP Logging Utilities
|
||||
* Provides structured logging for SMTP server components
|
||||
*/
|
||||
|
||||
import * as plugins from '../../../../plugins.js';
|
||||
import { logger } from '../../../../logger.js';
|
||||
import { SecurityLogLevel, SecurityEventType } from '../constants.js';
|
||||
import type { ISmtpSession } from '../interfaces.js';
|
||||
|
||||
/**
|
||||
* SMTP connection metadata to include in logs
|
||||
*/
|
||||
export interface IConnectionMetadata {
|
||||
remoteAddress?: string;
|
||||
remotePort?: number;
|
||||
socketId?: string;
|
||||
secure?: boolean;
|
||||
sessionId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log levels for SMTP server
|
||||
*/
|
||||
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
||||
|
||||
/**
|
||||
* Options for SMTP log
|
||||
*/
|
||||
export interface ISmtpLogOptions {
|
||||
level?: LogLevel;
|
||||
sessionId?: string;
|
||||
sessionState?: string;
|
||||
remoteAddress?: string;
|
||||
remotePort?: number;
|
||||
command?: string;
|
||||
error?: Error;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* SMTP logger - provides structured logging for SMTP server
|
||||
*/
|
||||
export class SmtpLogger {
|
||||
/**
|
||||
* Log a message with context
|
||||
* @param level - Log level
|
||||
* @param message - Log message
|
||||
* @param options - Additional log options
|
||||
*/
|
||||
public static log(level: LogLevel, message: string, options: ISmtpLogOptions = {}): void {
|
||||
// Extract error information if provided
|
||||
const errorInfo = options.error ? {
|
||||
errorMessage: options.error.message,
|
||||
errorStack: options.error.stack,
|
||||
errorName: options.error.name
|
||||
} : {};
|
||||
|
||||
// Structure log data
|
||||
const logData = {
|
||||
component: 'smtp-server',
|
||||
...options,
|
||||
...errorInfo
|
||||
};
|
||||
|
||||
// Remove error from log data to avoid duplication
|
||||
if (logData.error) {
|
||||
delete logData.error;
|
||||
}
|
||||
|
||||
// Log through the main logger
|
||||
logger.log(level, message, logData);
|
||||
|
||||
// Also console log for immediate visibility during development
|
||||
if (level === 'error' || level === 'warn') {
|
||||
console[level](`[SMTP] ${message}`, logData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log debug level message
|
||||
* @param message - Log message
|
||||
* @param options - Additional log options
|
||||
*/
|
||||
public static debug(message: string, options: ISmtpLogOptions = {}): void {
|
||||
this.log('debug', message, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log info level message
|
||||
* @param message - Log message
|
||||
* @param options - Additional log options
|
||||
*/
|
||||
public static info(message: string, options: ISmtpLogOptions = {}): void {
|
||||
this.log('info', message, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log warning level message
|
||||
* @param message - Log message
|
||||
* @param options - Additional log options
|
||||
*/
|
||||
public static warn(message: string, options: ISmtpLogOptions = {}): void {
|
||||
this.log('warn', message, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log error level message
|
||||
* @param message - Log message
|
||||
* @param options - Additional log options
|
||||
*/
|
||||
public static error(message: string, options: ISmtpLogOptions = {}): void {
|
||||
this.log('error', message, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log command received from client
|
||||
* @param command - The command string
|
||||
* @param socket - The client socket
|
||||
* @param session - The SMTP session
|
||||
*/
|
||||
public static logCommand(command: string, socket: plugins.net.Socket | plugins.tls.TLSSocket, session?: ISmtpSession): void {
|
||||
const clientInfo = {
|
||||
remoteAddress: socket.remoteAddress,
|
||||
remotePort: socket.remotePort,
|
||||
secure: socket instanceof plugins.tls.TLSSocket,
|
||||
sessionId: session?.id,
|
||||
sessionState: session?.state
|
||||
};
|
||||
|
||||
this.info(`Command received: ${command}`, {
|
||||
...clientInfo,
|
||||
command: command.split(' ')[0]?.toUpperCase()
|
||||
});
|
||||
|
||||
// Also log to console for easy debugging
|
||||
console.log(`← ${command}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log response sent to client
|
||||
* @param response - The response string
|
||||
* @param socket - The client socket
|
||||
*/
|
||||
public static logResponse(response: string, socket: plugins.net.Socket | plugins.tls.TLSSocket): void {
|
||||
const clientInfo = {
|
||||
remoteAddress: socket.remoteAddress,
|
||||
remotePort: socket.remotePort,
|
||||
secure: socket instanceof plugins.tls.TLSSocket
|
||||
};
|
||||
|
||||
// Get the response code from the beginning of the response
|
||||
const responseCode = response.substring(0, 3);
|
||||
|
||||
// Log different levels based on response code
|
||||
if (responseCode.startsWith('2') || responseCode.startsWith('3')) {
|
||||
this.debug(`Response sent: ${response}`, clientInfo);
|
||||
} else if (responseCode.startsWith('4')) {
|
||||
this.warn(`Temporary error response: ${response}`, clientInfo);
|
||||
} else if (responseCode.startsWith('5')) {
|
||||
this.error(`Permanent error response: ${response}`, clientInfo);
|
||||
}
|
||||
|
||||
// Also log to console for easy debugging
|
||||
console.log(`→ ${response}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log client connection event
|
||||
* @param socket - The client socket
|
||||
* @param eventType - Type of connection event (connect, close, error)
|
||||
* @param session - The SMTP session
|
||||
* @param error - Optional error object for error events
|
||||
*/
|
||||
public static logConnection(
|
||||
socket: plugins.net.Socket | plugins.tls.TLSSocket,
|
||||
eventType: 'connect' | 'close' | 'error',
|
||||
session?: ISmtpSession,
|
||||
error?: Error
|
||||
): void {
|
||||
const clientInfo = {
|
||||
remoteAddress: socket.remoteAddress,
|
||||
remotePort: socket.remotePort,
|
||||
secure: socket instanceof plugins.tls.TLSSocket,
|
||||
sessionId: session?.id,
|
||||
sessionState: session?.state
|
||||
};
|
||||
|
||||
switch (eventType) {
|
||||
case 'connect':
|
||||
this.info(`New ${clientInfo.secure ? 'secure ' : ''}connection from ${clientInfo.remoteAddress}:${clientInfo.remotePort}`, clientInfo);
|
||||
break;
|
||||
|
||||
case 'close':
|
||||
this.info(`Connection closed from ${clientInfo.remoteAddress}:${clientInfo.remotePort}`, clientInfo);
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
this.error(`Connection error from ${clientInfo.remoteAddress}:${clientInfo.remotePort}`, {
|
||||
...clientInfo,
|
||||
error
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log security event
|
||||
* @param level - Security log level
|
||||
* @param type - Security event type
|
||||
* @param message - Log message
|
||||
* @param details - Event details
|
||||
* @param ipAddress - Client IP address
|
||||
* @param domain - Optional domain involved
|
||||
* @param success - Whether the security check was successful
|
||||
*/
|
||||
public static logSecurityEvent(
|
||||
level: SecurityLogLevel,
|
||||
type: SecurityEventType,
|
||||
message: string,
|
||||
details: Record<string, any>,
|
||||
ipAddress?: string,
|
||||
domain?: string,
|
||||
success?: boolean
|
||||
): void {
|
||||
// Map security log level to system log level
|
||||
const logLevel: LogLevel = level === SecurityLogLevel.DEBUG ? 'debug' :
|
||||
level === SecurityLogLevel.INFO ? 'info' :
|
||||
level === SecurityLogLevel.WARN ? 'warn' : 'error';
|
||||
|
||||
// Log the security event
|
||||
this.log(logLevel, message, {
|
||||
component: 'smtp-security',
|
||||
eventType: type,
|
||||
success,
|
||||
ipAddress,
|
||||
domain,
|
||||
...details
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default instance for backward compatibility
|
||||
*/
|
||||
export const smtpLogger = SmtpLogger;
|
Reference in New Issue
Block a user