diff --git a/changelog.md b/changelog.md index 42af299..07f9e21 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ # Changelog +## 2025-05-19 - 19.3.11 - fix(logger) +Replace raw console logging calls with structured logger usage across certificate management, connection handling, and route processing for improved observability. + +- Replaced console.log, console.warn, and console.error in SmartCertManager with logger.log for more consistent logging. +- Updated ConnectionManager and RouteConnectionHandler to log detailed connection events using a structured logger. +- Enhanced logging statements with contextual metadata such as connection IDs, remote IPs, target information, and component identifiers. +- Standardized log output across proxy modules to aid in debugging and monitoring. + ## 2025-05-19 - 19.3.10 - fix(certificate-manager, smart-proxy) Fix race condition in ACME certificate provisioning and refactor certificate manager initialization to defer provisioning until after port listeners are active diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 8996eb9..17ec30f 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@push.rocks/smartproxy', - version: '19.3.10', + version: '19.3.11', description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.' } diff --git a/ts/proxies/smart-proxy/certificate-manager.ts b/ts/proxies/smart-proxy/certificate-manager.ts index fc8ca58..0a1c6c0 100644 --- a/ts/proxies/smart-proxy/certificate-manager.ts +++ b/ts/proxies/smart-proxy/certificate-manager.ts @@ -4,6 +4,7 @@ import type { IRouteConfig, IRouteTls } from './models/route-types.js'; import type { IAcmeOptions } from './models/interfaces.js'; import { CertStore } from './cert-store.js'; import type { AcmeStateManager } from './acme-state-manager.js'; +import { logger } from '../../core/utils/logger.js'; export interface ICertStatus { domain: string; @@ -125,16 +126,16 @@ export class SmartCertManager { // Add challenge route once at initialization if not already active if (!this.challengeRouteActive) { - console.log('Adding ACME challenge route during initialization'); + logger.log('info', 'Adding ACME challenge route during initialization', { component: 'certificate-manager' }); await this.addChallengeRoute(); } else { - console.log('Challenge route already active from previous instance'); + logger.log('info', 'Challenge route already active from previous instance', { component: 'certificate-manager' }); } } // Skip automatic certificate provisioning during initialization // This will be called later after ports are listening - console.log('Certificate manager initialized. Deferring certificate provisioning until after ports are listening.'); + logger.log('info', 'Certificate manager initialized. Deferring certificate provisioning until after ports are listening.', { component: 'certificate-manager' }); // Start renewal timer this.startRenewalTimer(); @@ -157,7 +158,7 @@ export class SmartCertManager { try { await this.provisionCertificate(route, true); // Allow concurrent since we're managing it here } catch (error) { - console.error(`Failed to provision certificate for route ${route.name}: ${error}`); + logger.log('error', `Failed to provision certificate for route ${route.name}`, { routeName: route.name, error, component: 'certificate-manager' }); } } } finally { @@ -176,13 +177,13 @@ export class SmartCertManager { // Check if provisioning is already in progress (prevent concurrent provisioning) if (!allowConcurrent && this.isProvisioning) { - console.log(`Certificate provisioning already in progress, skipping ${route.name}`); + logger.log('info', `Certificate provisioning already in progress, skipping ${route.name}`, { routeName: route.name, component: 'certificate-manager' }); return; } const domains = this.extractDomainsFromRoute(route); if (domains.length === 0) { - console.warn(`Route ${route.name} has TLS termination but no domains`); + logger.log('warn', `Route ${route.name} has TLS termination but no domains`, { routeName: route.name, component: 'certificate-manager' }); return; } @@ -219,7 +220,7 @@ export class SmartCertManager { // Check if we already have a valid certificate const existingCert = await this.certStore.getCertificate(routeName); if (existingCert && this.isCertificateValid(existingCert)) { - console.log(`Using existing valid certificate for ${primaryDomain}`); + logger.log('info', `Using existing valid certificate for ${primaryDomain}`, { domain: primaryDomain, component: 'certificate-manager' }); await this.applyCertificate(primaryDomain, existingCert); this.updateCertStatus(routeName, 'valid', 'acme', existingCert); return; @@ -230,7 +231,7 @@ export class SmartCertManager { this.globalAcmeDefaults?.renewThresholdDays || 30; - console.log(`Requesting ACME certificate for ${domains.join(', ')} (renew ${renewThreshold} days before expiry)`); + logger.log('info', `Requesting ACME certificate for ${domains.join(', ')} (renew ${renewThreshold} days before expiry)`, { domains: domains.join(', '), renewThreshold, component: 'certificate-manager' }); this.updateCertStatus(routeName, 'pending', 'acme'); try { @@ -252,7 +253,7 @@ export class SmartCertManager { hasDnsChallenge; if (shouldIncludeWildcard) { - console.log(`Requesting wildcard certificate for ${primaryDomain} (DNS-01 available)`); + logger.log('info', `Requesting wildcard certificate for ${primaryDomain} (DNS-01 available)`, { domain: primaryDomain, challengeType: 'DNS-01', component: 'certificate-manager' }); } // Use smartacme to get certificate with optional wildcard @@ -279,9 +280,9 @@ export class SmartCertManager { await this.applyCertificate(primaryDomain, certData); this.updateCertStatus(routeName, 'valid', 'acme', certData); - console.log(`Successfully provisioned ACME certificate for ${primaryDomain}`); + logger.log('info', `Successfully provisioned ACME certificate for ${primaryDomain}`, { domain: primaryDomain, component: 'certificate-manager' }); } catch (error) { - console.error(`Failed to provision ACME certificate for ${primaryDomain}: ${error}`); + logger.log('error', `Failed to provision ACME certificate for ${primaryDomain}: ${error.message}`, { domain: primaryDomain, error: error.message, component: 'certificate-manager' }); this.updateCertStatus(routeName, 'error', 'acme', undefined, error.message); throw error; } @@ -328,9 +329,9 @@ export class SmartCertManager { await this.applyCertificate(domain, certData); this.updateCertStatus(routeName, 'valid', 'static', certData); - console.log(`Successfully loaded static certificate for ${domain}`); + logger.log('info', `Successfully loaded static certificate for ${domain}`, { domain, component: 'certificate-manager' }); } catch (error) { - console.error(`Failed to provision static certificate for ${domain}: ${error}`); + logger.log('error', `Failed to provision static certificate for ${domain}: ${error.message}`, { domain, error: error.message, component: 'certificate-manager' }); this.updateCertStatus(routeName, 'error', 'static', undefined, error.message); throw error; } @@ -341,7 +342,7 @@ export class SmartCertManager { */ private async applyCertificate(domain: string, certData: ICertificateData): Promise { if (!this.httpProxy) { - console.warn('HttpProxy not set, cannot apply certificate'); + logger.log('warn', `HttpProxy not set, cannot apply certificate for domain ${domain}`, { domain, component: 'certificate-manager' }); return; } @@ -398,13 +399,13 @@ export class SmartCertManager { private async addChallengeRoute(): Promise { // Check with state manager first if (this.acmeStateManager && this.acmeStateManager.isChallengeRouteActive()) { - console.log('Challenge route already active in global state, skipping'); + logger.log('info', 'Challenge route already active in global state, skipping', { component: 'certificate-manager' }); this.challengeRouteActive = true; return; } if (this.challengeRouteActive) { - console.log('Challenge route already active locally, skipping'); + logger.log('info', 'Challenge route already active locally, skipping', { component: 'certificate-manager' }); return; } @@ -427,9 +428,9 @@ export class SmartCertManager { this.acmeStateManager.addChallengeRoute(challengeRoute); } - console.log('ACME challenge route successfully added'); + logger.log('info', 'ACME challenge route successfully added', { component: 'certificate-manager' }); } catch (error) { - console.error('Failed to add challenge route:', error); + logger.log('error', `Failed to add challenge route: ${error.message}`, { error: error.message, component: 'certificate-manager' }); if ((error as any).code === 'EADDRINUSE') { throw new Error(`Port ${this.globalAcmeDefaults?.port || 80} is already in use for ACME challenges`); } @@ -442,7 +443,7 @@ export class SmartCertManager { */ private async removeChallengeRoute(): Promise { if (!this.challengeRouteActive) { - console.log('Challenge route not active, skipping removal'); + logger.log('info', 'Challenge route not active, skipping removal', { component: 'certificate-manager' }); return; } @@ -460,9 +461,9 @@ export class SmartCertManager { this.acmeStateManager.removeChallengeRoute('acme-challenge'); } - console.log('ACME challenge route successfully removed'); + logger.log('info', 'ACME challenge route successfully removed', { component: 'certificate-manager' }); } catch (error) { - console.error('Failed to remove challenge route:', error); + logger.log('error', `Failed to remove challenge route: ${error.message}`, { error: error.message, component: 'certificate-manager' }); // Reset the flag even on error to avoid getting stuck this.challengeRouteActive = false; throw error; @@ -492,11 +493,11 @@ export class SmartCertManager { const cert = await this.certStore.getCertificate(routeName); if (cert && !this.isCertificateValid(cert)) { - console.log(`Certificate for ${routeName} needs renewal`); + logger.log('info', `Certificate for ${routeName} needs renewal`, { routeName, component: 'certificate-manager' }); try { await this.provisionCertificate(route); } catch (error) { - console.error(`Failed to renew certificate for ${routeName}: ${error}`); + logger.log('error', `Failed to renew certificate for ${routeName}: ${error.message}`, { routeName, error: error.message, component: 'certificate-manager' }); } } } @@ -621,7 +622,7 @@ export class SmartCertManager { // Always remove challenge route on shutdown if (this.challengeRoute) { - console.log('Removing ACME challenge route during shutdown'); + logger.log('info', 'Removing ACME challenge route during shutdown', { component: 'certificate-manager' }); await this.removeChallengeRoute(); } diff --git a/ts/proxies/smart-proxy/connection-manager.ts b/ts/proxies/smart-proxy/connection-manager.ts index 2ba0b29..010a156 100644 --- a/ts/proxies/smart-proxy/connection-manager.ts +++ b/ts/proxies/smart-proxy/connection-manager.ts @@ -2,6 +2,7 @@ import * as plugins from '../../plugins.js'; import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js'; import { SecurityManager } from './security-manager.js'; import { TimeoutManager } from './timeout-manager.js'; +import { logger } from '../../core/utils/logger.js'; /** * Manages connection lifecycle, tracking, and cleanup @@ -97,7 +98,7 @@ export class ConnectionManager { */ public initiateCleanupOnce(record: IConnectionRecord, reason: string = 'normal'): void { if (this.settings.enableDetailedLogging) { - console.log(`[${record.id}] Connection cleanup initiated for ${record.remoteIP} (${reason})`); + logger.log('info', `Connection cleanup initiated`, { connectionId: record.id, remoteIP: record.remoteIP, reason, component: 'connection-manager' }); } if ( @@ -139,7 +140,7 @@ export class ConnectionManager { // Reset the handler references record.renegotiationHandler = undefined; } catch (err) { - console.log(`[${record.id}] Error removing data handlers: ${err}`); + logger.log('error', `Error removing data handlers for connection ${record.id}: ${err}`, { connectionId: record.id, error: err, component: 'connection-manager' }); } } @@ -160,16 +161,36 @@ export class ConnectionManager { // Log connection details if (this.settings.enableDetailedLogging) { - console.log( - `[${record.id}] Connection from ${record.remoteIP} on port ${record.localPort} terminated (${reason}).` + - ` Duration: ${plugins.prettyMs(duration)}, Bytes IN: ${bytesReceived}, OUT: ${bytesSent}, ` + - `TLS: ${record.isTLS ? 'Yes' : 'No'}, Keep-Alive: ${record.hasKeepAlive ? 'Yes' : 'No'}` + - `${record.usingNetworkProxy ? ', Using NetworkProxy' : ''}` + - `${record.domainSwitches ? `, Domain switches: ${record.domainSwitches}` : ''}` + logger.log('info', + `Connection from ${record.remoteIP} on port ${record.localPort} terminated (${reason}). ` + + `Duration: ${plugins.prettyMs(duration)}, Bytes IN: ${bytesReceived}, OUT: ${bytesSent}, ` + + `TLS: ${record.isTLS ? 'Yes' : 'No'}, Keep-Alive: ${record.hasKeepAlive ? 'Yes' : 'No'}` + + `${record.usingNetworkProxy ? ', Using NetworkProxy' : ''}` + + `${record.domainSwitches ? `, Domain switches: ${record.domainSwitches}` : ''}`, + { + connectionId: record.id, + remoteIP: record.remoteIP, + localPort: record.localPort, + reason, + duration: plugins.prettyMs(duration), + bytes: { in: bytesReceived, out: bytesSent }, + tls: record.isTLS, + keepAlive: record.hasKeepAlive, + usingNetworkProxy: record.usingNetworkProxy, + domainSwitches: record.domainSwitches || 0, + component: 'connection-manager' + } ); } else { - console.log( - `[${record.id}] Connection from ${record.remoteIP} terminated (${reason}). Active connections: ${this.connectionRecords.size}` + logger.log('info', + `Connection from ${record.remoteIP} terminated (${reason}). Active connections: ${this.connectionRecords.size}`, + { + connectionId: record.id, + remoteIP: record.remoteIP, + reason, + activeConnections: this.connectionRecords.size, + component: 'connection-manager' + } ); } } @@ -189,7 +210,7 @@ export class ConnectionManager { socket.destroy(); } } catch (err) { - console.log(`[${record.id}] Error destroying ${side} socket: ${err}`); + logger.log('error', `Error destroying ${side} socket for connection ${record.id}: ${err}`, { connectionId: record.id, side, error: err, component: 'connection-manager' }); } }, 1000); @@ -199,13 +220,13 @@ export class ConnectionManager { } } } catch (err) { - console.log(`[${record.id}] Error closing ${side} socket: ${err}`); + logger.log('error', `Error closing ${side} socket for connection ${record.id}: ${err}`, { connectionId: record.id, side, error: err, component: 'connection-manager' }); try { if (!socket.destroyed) { socket.destroy(); } } catch (destroyErr) { - console.log(`[${record.id}] Error destroying ${side} socket: ${destroyErr}`); + logger.log('error', `Error destroying ${side} socket for connection ${record.id}: ${destroyErr}`, { connectionId: record.id, side, error: destroyErr, component: 'connection-manager' }); } } } @@ -224,21 +245,36 @@ export class ConnectionManager { if (code === 'ECONNRESET') { reason = 'econnreset'; - console.log( - `[${record.id}] ECONNRESET on ${side} side from ${record.remoteIP}: ${err.message}. ` + - `Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)} ago` - ); + logger.log('warn', `ECONNRESET on ${side} connection from ${record.remoteIP}. Error: ${err.message}. Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)}`, { + connectionId: record.id, + side, + remoteIP: record.remoteIP, + error: err.message, + duration: plugins.prettyMs(connectionDuration), + lastActivity: plugins.prettyMs(lastActivityAge), + component: 'connection-manager' + }); } else if (code === 'ETIMEDOUT') { reason = 'etimedout'; - console.log( - `[${record.id}] ETIMEDOUT on ${side} side from ${record.remoteIP}: ${err.message}. ` + - `Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)} ago` - ); + logger.log('warn', `ETIMEDOUT on ${side} connection from ${record.remoteIP}. Error: ${err.message}. Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)}`, { + connectionId: record.id, + side, + remoteIP: record.remoteIP, + error: err.message, + duration: plugins.prettyMs(connectionDuration), + lastActivity: plugins.prettyMs(lastActivityAge), + component: 'connection-manager' + }); } else { - console.log( - `[${record.id}] Error on ${side} side from ${record.remoteIP}: ${err.message}. ` + - `Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)} ago` - ); + logger.log('error', `Error on ${side} connection from ${record.remoteIP}: ${err.message}. Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)}`, { + connectionId: record.id, + side, + remoteIP: record.remoteIP, + error: err.message, + duration: plugins.prettyMs(connectionDuration), + lastActivity: plugins.prettyMs(lastActivityAge), + component: 'connection-manager' + }); } if (side === 'incoming' && record.incomingTerminationReason === null) { @@ -259,7 +295,12 @@ export class ConnectionManager { public handleClose(side: 'incoming' | 'outgoing', record: IConnectionRecord) { return () => { if (this.settings.enableDetailedLogging) { - console.log(`[${record.id}] Connection closed on ${side} side from ${record.remoteIP}`); + logger.log('info', `Connection closed on ${side} side`, { + connectionId: record.id, + side, + remoteIP: record.remoteIP, + component: 'connection-manager' + }); } if (side === 'incoming' && record.incomingTerminationReason === null) { @@ -321,11 +362,13 @@ export class ConnectionManager { if (inactivityTime > effectiveTimeout && !record.connectionClosed) { // For keep-alive connections, issue a warning first if (record.hasKeepAlive && !record.inactivityWarningIssued) { - console.log( - `[${id}] Warning: Keep-alive connection from ${record.remoteIP} inactive for ${ - plugins.prettyMs(inactivityTime) - }. Will close in 10 minutes if no activity.` - ); + logger.log('warn', `Keep-alive connection ${id} from ${record.remoteIP} inactive for ${plugins.prettyMs(inactivityTime)}. Will close in 10 minutes if no activity.`, { + connectionId: id, + remoteIP: record.remoteIP, + inactiveFor: plugins.prettyMs(inactivityTime), + closureWarning: '10 minutes', + component: 'connection-manager' + }); // Set warning flag and add grace period record.inactivityWarningIssued = true; @@ -337,27 +380,30 @@ export class ConnectionManager { record.outgoing.write(Buffer.alloc(0)); if (this.settings.enableDetailedLogging) { - console.log(`[${id}] Sent probe packet to test keep-alive connection`); + logger.log('info', `Sent probe packet to test keep-alive connection ${id}`, { connectionId: id, component: 'connection-manager' }); } } catch (err) { - console.log(`[${id}] Error sending probe packet: ${err}`); + logger.log('error', `Error sending probe packet to connection ${id}: ${err}`, { connectionId: id, error: err, component: 'connection-manager' }); } } } else { // For non-keep-alive or after warning, close the connection - console.log( - `[${id}] Inactivity check: No activity on connection from ${record.remoteIP} ` + - `for ${plugins.prettyMs(inactivityTime)}.` + - (record.hasKeepAlive ? ' Despite keep-alive being enabled.' : '') - ); + logger.log('warn', `Closing inactive connection ${id} from ${record.remoteIP} (inactive for ${plugins.prettyMs(inactivityTime)}, keep-alive: ${record.hasKeepAlive ? 'Yes' : 'No'})`, { + connectionId: id, + remoteIP: record.remoteIP, + inactiveFor: plugins.prettyMs(inactivityTime), + hasKeepAlive: record.hasKeepAlive, + component: 'connection-manager' + }); this.cleanupConnection(record, 'inactivity'); } } else if (inactivityTime <= effectiveTimeout && record.inactivityWarningIssued) { // If activity detected after warning, clear the warning if (this.settings.enableDetailedLogging) { - console.log( - `[${id}] Connection activity detected after inactivity warning, resetting warning` - ); + logger.log('info', `Connection ${id} activity detected after inactivity warning`, { + connectionId: id, + component: 'connection-manager' + }); } record.inactivityWarningIssued = false; } @@ -369,11 +415,12 @@ export class ConnectionManager { !record.connectionClosed && now - record.outgoingClosedTime > 120000 ) { - console.log( - `[${id}] Parity check: Incoming socket for ${record.remoteIP} still active ${ - plugins.prettyMs(now - record.outgoingClosedTime) - } after outgoing closed.` - ); + logger.log('warn', `Parity check: Connection ${id} from ${record.remoteIP} has incoming socket still active ${plugins.prettyMs(now - record.outgoingClosedTime)} after outgoing socket closed`, { + connectionId: id, + remoteIP: record.remoteIP, + timeElapsed: plugins.prettyMs(now - record.outgoingClosedTime), + component: 'connection-manager' + }); this.cleanupConnection(record, 'parity_check'); } } @@ -406,7 +453,7 @@ export class ConnectionManager { record.outgoing.end(); } } catch (err) { - console.log(`Error during graceful connection end for ${id}: ${err}`); + logger.log('error', `Error during graceful end of connection ${id}: ${err}`, { connectionId: id, error: err, component: 'connection-manager' }); } } } @@ -433,7 +480,7 @@ export class ConnectionManager { } } } catch (err) { - console.log(`Error during forced connection destruction for ${id}: ${err}`); + logger.log('error', `Error during forced destruction of connection ${id}: ${err}`, { connectionId: id, error: err, component: 'connection-manager' }); } } } diff --git a/ts/proxies/smart-proxy/route-connection-handler.ts b/ts/proxies/smart-proxy/route-connection-handler.ts index 2499ef3..014c019 100644 --- a/ts/proxies/smart-proxy/route-connection-handler.ts +++ b/ts/proxies/smart-proxy/route-connection-handler.ts @@ -1,5 +1,6 @@ import * as plugins from '../../plugins.js'; import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js'; +import { logger } from '../../core/utils/logger.js'; // Route checking functions have been removed import type { IRouteConfig, IRouteAction, IRouteContext } from './models/route-types.js'; import { ConnectionManager } from './connection-manager.js'; @@ -83,7 +84,7 @@ export class RouteConnectionHandler { // Validate IP against rate limits and connection limits const ipValidation = this.securityManager.validateIP(remoteIP); if (!ipValidation.allowed) { - console.log(`Connection rejected from ${remoteIP}: ${ipValidation.reason}`); + logger.log('warn', `Connection rejected`, { remoteIP, reason: ipValidation.reason, component: 'route-handler' }); socket.end(); socket.destroy(); return; @@ -114,21 +115,35 @@ export class RouteConnectionHandler { } catch (err) { // Ignore errors - these are optional enhancements if (this.settings.enableDetailedLogging) { - console.log(`[${connectionId}] Enhanced TCP keep-alive settings not supported: ${err}`); + logger.log('warn', `Enhanced TCP keep-alive settings not supported`, { connectionId, error: err, component: 'route-handler' }); } } } } if (this.settings.enableDetailedLogging) { - console.log( - `[${connectionId}] New connection from ${remoteIP} on port ${localPort}. ` + - `Keep-Alive: ${record.hasKeepAlive ? 'Enabled' : 'Disabled'}. ` + - `Active connections: ${this.connectionManager.getConnectionCount()}` + logger.log('info', + `New connection from ${remoteIP} on port ${localPort}. ` + + `Keep-Alive: ${record.hasKeepAlive ? 'Enabled' : 'Disabled'}. ` + + `Active connections: ${this.connectionManager.getConnectionCount()}`, + { + connectionId, + remoteIP, + localPort, + keepAlive: record.hasKeepAlive ? 'Enabled' : 'Disabled', + activeConnections: this.connectionManager.getConnectionCount(), + component: 'route-handler' + } ); } else { - console.log( - `New connection from ${remoteIP} on port ${localPort}. Active connections: ${this.connectionManager.getConnectionCount()}` + logger.log('info', + `New connection from ${remoteIP} on port ${localPort}. Active connections: ${this.connectionManager.getConnectionCount()}`, + { + remoteIP, + localPort, + activeConnections: this.connectionManager.getConnectionCount(), + component: 'route-handler' + } ); } @@ -147,14 +162,20 @@ export class RouteConnectionHandler { // Set an initial timeout for handshake data let initialTimeout: NodeJS.Timeout | null = setTimeout(() => { if (!initialDataReceived) { - console.log( - `[${connectionId}] Initial data warning (${this.settings.initialDataTimeout}ms) for connection from ${record.remoteIP}` - ); + logger.log('warn', `No initial data received from ${record.remoteIP} after ${this.settings.initialDataTimeout}ms for connection ${connectionId}`, { + connectionId, + timeout: this.settings.initialDataTimeout, + remoteIP: record.remoteIP, + component: 'route-handler' + }); // Add a grace period setTimeout(() => { if (!initialDataReceived) { - console.log(`[${connectionId}] Final initial data timeout after grace period`); + logger.log('warn', `Final initial data timeout after grace period for connection ${connectionId}`, { + connectionId, + component: 'route-handler' + }); if (record.incomingTerminationReason === null) { record.incomingTerminationReason = 'initial_timeout'; this.connectionManager.incrementTerminationStat('incoming', 'initial_timeout'); @@ -187,10 +208,11 @@ export class RouteConnectionHandler { // Block non-TLS connections on port 443 if (!this.tlsManager.isTlsHandshake(chunk) && localPort === 443) { - console.log( - `[${connectionId}] Non-TLS connection detected on port 443. ` + - `Terminating connection - only TLS traffic is allowed on standard HTTPS port.` - ); + logger.log('warn', `Non-TLS connection ${connectionId} detected on port 443. Terminating connection - only TLS traffic is allowed on standard HTTPS port.`, { + connectionId, + message: 'Terminating connection - only TLS traffic is allowed on standard HTTPS port.', + component: 'route-handler' + }); if (record.incomingTerminationReason === null) { record.incomingTerminationReason = 'non_tls_blocked'; this.connectionManager.incrementTerminationStat('incoming', 'non_tls_blocked'); @@ -223,7 +245,10 @@ export class RouteConnectionHandler { // Check if we should reject connections without SNI if (!serverName && this.settings.allowSessionTicket === false) { - console.log(`[${connectionId}] No SNI detected in TLS ClientHello; sending TLS alert.`); + logger.log('warn', `No SNI detected in TLS ClientHello for connection ${connectionId}; sending TLS alert`, { + connectionId, + component: 'route-handler' + }); if (record.incomingTerminationReason === null) { record.incomingTerminationReason = 'session_ticket_blocked_no_sni'; this.connectionManager.incrementTerminationStat( @@ -245,7 +270,11 @@ export class RouteConnectionHandler { } if (this.settings.enableDetailedLogging) { - console.log(`[${connectionId}] TLS connection with SNI: ${serverName || '(empty)'}`); + logger.log('info', `TLS connection with SNI`, { + connectionId, + serverName: serverName || '(empty)', + component: 'route-handler' + }); } } } @@ -278,12 +307,18 @@ export class RouteConnectionHandler { }); if (!routeMatch) { - console.log( - `[${connectionId}] No route found for ${serverName || 'connection'} on port ${localPort}` - ); + logger.log('warn', `No route found for ${serverName || 'connection'} on port ${localPort} (connection: ${connectionId})`, { + connectionId, + serverName: serverName || 'connection', + localPort, + component: 'route-handler' + }); // No matching route, use default/fallback handling - console.log(`[${connectionId}] Using default route handling for connection`); + logger.log('info', `Using default route handling for connection ${connectionId}`, { + connectionId, + component: 'route-handler' + }); // Check default security settings const defaultSecuritySettings = this.settings.defaults?.security; @@ -296,7 +331,11 @@ export class RouteConnectionHandler { ); if (!isAllowed) { - console.log(`[${connectionId}] IP ${remoteIP} not in default allowed list`); + logger.log('warn', `IP ${remoteIP} not in default allowed list for connection ${connectionId}`, { + connectionId, + remoteIP, + component: 'route-handler' + }); socket.end(); this.connectionManager.cleanupConnection(record, 'ip_blocked'); return; @@ -321,7 +360,10 @@ export class RouteConnectionHandler { ); } else { // No default target available, terminate the connection - console.log(`[${connectionId}] No default target configured. Closing connection.`); + logger.log('warn', `No default target configured for connection ${connectionId}. Closing connection`, { + connectionId, + component: 'route-handler' + }); socket.end(); this.connectionManager.cleanupConnection(record, 'no_default_target'); return; @@ -332,11 +374,13 @@ export class RouteConnectionHandler { const route = routeMatch.route; if (this.settings.enableDetailedLogging) { - console.log( - `[${connectionId}] Route matched: "${route.name || 'unnamed'}" for ${ - serverName || 'connection' - } on port ${localPort}` - ); + logger.log('info', `Route matched`, { + connectionId, + routeName: route.name || 'unnamed', + serverName: serverName || 'connection', + localPort, + component: 'route-handler' + }); } @@ -356,7 +400,11 @@ export class RouteConnectionHandler { return; default: - console.log(`[${connectionId}] Unknown action type: ${(route.action as any).type}`); + logger.log('error', `Unknown action type '${(route.action as any).type}' for connection ${connectionId}`, { + connectionId, + actionType: (route.action as any).type, + component: 'route-handler' + }); socket.end(); this.connectionManager.cleanupConnection(record, 'unknown_action'); } @@ -381,30 +429,36 @@ export class RouteConnectionHandler { // Log the connection for monitoring purposes if (this.settings.enableDetailedLogging) { - console.log( - `[${record.id}] NFTables forwarding (kernel-level): ` + - `${record.remoteIP}:${socket.remotePort} -> ${socket.localAddress}:${record.localPort}` + - ` (Route: "${route.name || 'unnamed'}", Domain: ${record.lockedDomain || 'n/a'})` - ); + logger.log('info', `NFTables forwarding (kernel-level)`, { + connectionId: record.id, + source: `${record.remoteIP}:${socket.remotePort}`, + destination: `${socket.localAddress}:${record.localPort}`, + routeName: route.name || 'unnamed', + domain: record.lockedDomain || 'n/a', + component: 'route-handler' + }); } else { - console.log( - `[${record.id}] NFTables forwarding: ${record.remoteIP} -> port ${ - record.localPort - } (Route: "${route.name || 'unnamed'}")` - ); + logger.log('info', `NFTables forwarding`, { + connectionId: record.id, + remoteIP: record.remoteIP, + localPort: record.localPort, + routeName: route.name || 'unnamed', + component: 'route-handler' + }); } // Additional NFTables-specific logging if configured if (action.nftables) { const nftConfig = action.nftables; if (this.settings.enableDetailedLogging) { - console.log( - `[${record.id}] NFTables config: ` + - `protocol=${nftConfig.protocol || 'tcp'}, ` + - `preserveSourceIP=${nftConfig.preserveSourceIP || false}, ` + - `priority=${nftConfig.priority || 'default'}, ` + - `maxRate=${nftConfig.maxRate || 'unlimited'}` - ); + logger.log('info', `NFTables config`, { + connectionId: record.id, + protocol: nftConfig.protocol || 'tcp', + preserveSourceIP: nftConfig.preserveSourceIP || false, + priority: nftConfig.priority || 'default', + maxRate: nftConfig.maxRate || 'unlimited', + component: 'route-handler' + }); } } @@ -419,7 +473,10 @@ export class RouteConnectionHandler { // We should have a target configuration for forwarding if (!action.target) { - console.log(`[${connectionId}] Forward action missing target configuration`); + logger.log('error', `Forward action missing target configuration for connection ${connectionId}`, { + connectionId, + component: 'route-handler' + }); socket.end(); this.connectionManager.cleanupConnection(record, 'missing_target'); return; @@ -447,14 +504,18 @@ export class RouteConnectionHandler { try { targetHost = action.target.host(routeContext); if (this.settings.enableDetailedLogging) { - console.log( - `[${connectionId}] Dynamic host resolved to: ${ - Array.isArray(targetHost) ? targetHost.join(', ') : targetHost - }` - ); + logger.log('info', `Dynamic host resolved to ${Array.isArray(targetHost) ? targetHost.join(', ') : targetHost} for connection ${connectionId}`, { + connectionId, + targetHost: Array.isArray(targetHost) ? targetHost.join(', ') : targetHost, + component: 'route-handler' + }); } } catch (err) { - console.log(`[${connectionId}] Error in host mapping function: ${err}`); + logger.log('error', `Error in host mapping function for connection ${connectionId}: ${err}`, { + connectionId, + error: err, + component: 'route-handler' + }); socket.end(); this.connectionManager.cleanupConnection(record, 'host_mapping_error'); return; @@ -474,14 +535,21 @@ export class RouteConnectionHandler { try { targetPort = action.target.port(routeContext); if (this.settings.enableDetailedLogging) { - console.log( - `[${connectionId}] Dynamic port mapping: ${record.localPort} -> ${targetPort}` - ); + logger.log('info', `Dynamic port mapping from ${record.localPort} to ${targetPort} for connection ${connectionId}`, { + connectionId, + sourcePort: record.localPort, + targetPort, + component: 'route-handler' + }); } // Store the resolved target port in the context for potential future use routeContext.targetPort = targetPort; } catch (err) { - console.log(`[${connectionId}] Error in port mapping function: ${err}`); + logger.log('error', `Error in port mapping function for connection ${connectionId}: ${err}`, { + connectionId, + error: err, + component: 'route-handler' + }); socket.end(); this.connectionManager.cleanupConnection(record, 'port_mapping_error'); return; @@ -503,7 +571,12 @@ export class RouteConnectionHandler { case 'passthrough': // For TLS passthrough, just forward directly if (this.settings.enableDetailedLogging) { - console.log(`[${connectionId}] Using TLS passthrough to ${selectedHost}:${targetPort}`); + logger.log('info', `Using TLS passthrough to ${selectedHost}:${targetPort} for connection ${connectionId}`, { + connectionId, + targetHost: selectedHost, + targetPort, + component: 'route-handler' + }); } return this.setupDirectConnection( @@ -521,9 +594,11 @@ export class RouteConnectionHandler { // For TLS termination, use HttpProxy if (this.httpProxyBridge.getHttpProxy()) { if (this.settings.enableDetailedLogging) { - console.log( - `[${connectionId}] Using HttpProxy for TLS termination to ${action.target.host}` - ); + logger.log('info', `Using HttpProxy for TLS termination to ${Array.isArray(action.target.host) ? action.target.host.join(', ') : action.target.host} for connection ${connectionId}`, { + connectionId, + targetHost: action.target.host, + component: 'route-handler' + }); } // If we have an initial chunk with TLS data, start processing it @@ -540,12 +615,18 @@ export class RouteConnectionHandler { } // This shouldn't normally happen - we should have TLS data at this point - console.log(`[${connectionId}] TLS termination route without TLS data`); + logger.log('error', `TLS termination route without TLS data for connection ${connectionId}`, { + connectionId, + component: 'route-handler' + }); socket.end(); this.connectionManager.cleanupConnection(record, 'tls_error'); return; } else { - console.log(`[${connectionId}] HttpProxy not available for TLS termination`); + logger.log('error', `HttpProxy not available for TLS termination for connection ${connectionId}`, { + connectionId, + component: 'route-handler' + }); socket.end(); this.connectionManager.cleanupConnection(record, 'no_http_proxy'); return; @@ -558,9 +639,11 @@ export class RouteConnectionHandler { if (isHttpProxyPort && this.httpProxyBridge.getHttpProxy()) { // Forward non-TLS connections to HttpProxy if configured if (this.settings.enableDetailedLogging) { - console.log( - `[${connectionId}] Using HttpProxy for non-TLS connection on port ${record.localPort}` - ); + logger.log('info', `Using HttpProxy for non-TLS connection ${connectionId} on port ${record.localPort}`, { + connectionId, + port: record.localPort, + component: 'route-handler' + }); } this.httpProxyBridge.forwardToHttpProxy( @@ -575,9 +658,12 @@ export class RouteConnectionHandler { } else { // Basic forwarding if (this.settings.enableDetailedLogging) { - console.log( - `[${connectionId}] Using basic forwarding to ${action.target.host}:${action.target.port}` - ); + logger.log('info', `Using basic forwarding to ${Array.isArray(action.target.host) ? action.target.host.join(', ') : action.target.host}:${action.target.port} for connection ${connectionId}`, { + connectionId, + targetHost: action.target.host, + targetPort: action.target.port, + component: 'route-handler' + }); } // Get the appropriate host value @@ -633,7 +719,10 @@ export class RouteConnectionHandler { ): void { // For TLS connections, we can't do redirects at the TCP level if (record.isTLS) { - console.log(`[${record.id}] Cannot redirect TLS connection at TCP level`); + logger.log('warn', `Cannot redirect TLS connection ${record.id} at TCP level`, { + connectionId: record.id, + component: 'route-handler' + }); socket.end(); this.connectionManager.cleanupConnection(record, 'tls_redirect_error'); return; @@ -658,9 +747,11 @@ export class RouteConnectionHandler { const connectionId = record.id; if (this.settings.enableDetailedLogging) { - console.log( - `[${connectionId}] Blocking connection based on route "${route.name || 'unnamed'}"` - ); + logger.log('info', `Blocking connection ${connectionId} based on route '${route.name || 'unnamed'}'`, { + connectionId, + routeName: route.name || 'unnamed', + component: 'route-handler' + }); } // Simply close the connection @@ -699,8 +790,16 @@ export class RouteConnectionHandler { targetSocket.once('error', (err) => { // This handler runs only once during the initial connection phase const code = (err as any).code; - console.log( - `[${connectionId}] Connection setup error to ${finalTargetHost}:${finalTargetPort}: ${err.message} (${code})` + logger.log('error', + `Connection setup error for ${connectionId} to ${finalTargetHost}:${finalTargetPort}: ${err.message} (${code})`, + { + connectionId, + targetHost: finalTargetHost, + targetPort: finalTargetPort, + errorMessage: err.message, + errorCode: code, + component: 'route-handler' + } ); // Resume the incoming socket to prevent it from hanging @@ -708,29 +807,57 @@ export class RouteConnectionHandler { // Log specific error types for easier debugging if (code === 'ECONNREFUSED') { - console.log( - `[${connectionId}] Target ${finalTargetHost}:${finalTargetPort} refused connection. ` + - `Check if the target service is running and listening on that port.` + logger.log('error', + `Connection ${connectionId}: Target ${finalTargetHost}:${finalTargetPort} refused connection. Check if the target service is running and listening on that port.`, + { + connectionId, + targetHost: finalTargetHost, + targetPort: finalTargetPort, + recommendation: 'Check if the target service is running and listening on that port.', + component: 'route-handler' + } ); } else if (code === 'ETIMEDOUT') { - console.log( - `[${connectionId}] Connection to ${finalTargetHost}:${finalTargetPort} timed out. ` + - `Check network conditions, firewall rules, or if the target is too far away.` + logger.log('error', + `Connection ${connectionId} to ${finalTargetHost}:${finalTargetPort} timed out. Check network conditions, firewall rules, or if the target is too far away.`, + { + connectionId, + targetHost: finalTargetHost, + targetPort: finalTargetPort, + recommendation: 'Check network conditions, firewall rules, or if the target is too far away.', + component: 'route-handler' + } ); } else if (code === 'ECONNRESET') { - console.log( - `[${connectionId}] Connection to ${finalTargetHost}:${finalTargetPort} was reset. ` + - `The target might have closed the connection abruptly.` + logger.log('error', + `Connection ${connectionId} to ${finalTargetHost}:${finalTargetPort} was reset. The target might have closed the connection abruptly.`, + { + connectionId, + targetHost: finalTargetHost, + targetPort: finalTargetPort, + recommendation: 'The target might have closed the connection abruptly.', + component: 'route-handler' + } ); } else if (code === 'EHOSTUNREACH') { - console.log( - `[${connectionId}] Host ${finalTargetHost} is unreachable. ` + - `Check DNS settings, network routing, or firewall rules.` + logger.log('error', + `Connection ${connectionId}: Host ${finalTargetHost} is unreachable. Check DNS settings, network routing, or firewall rules.`, + { + connectionId, + targetHost: finalTargetHost, + recommendation: 'Check DNS settings, network routing, or firewall rules.', + component: 'route-handler' + } ); } else if (code === 'ENOTFOUND') { - console.log( - `[${connectionId}] DNS lookup failed for ${finalTargetHost}. ` + - `Check your DNS settings or if the hostname is correct.` + logger.log('error', + `Connection ${connectionId}: DNS lookup failed for ${finalTargetHost}. Check your DNS settings or if the hostname is correct.`, + { + connectionId, + targetHost: finalTargetHost, + recommendation: 'Check your DNS settings or if the hostname is correct.', + component: 'route-handler' + } ); } @@ -779,9 +906,12 @@ export class RouteConnectionHandler { record.targetPort = finalTargetPort; if (this.settings.enableDetailedLogging) { - console.log( - `[${connectionId}] Setting up direct connection to ${finalTargetHost}:${finalTargetPort}` - ); + logger.log('info', `Setting up direct connection ${connectionId} to ${finalTargetHost}:${finalTargetPort}`, { + connectionId, + targetHost: finalTargetHost, + targetPort: finalTargetPort, + component: 'route-handler' + }); } // Setup connection options @@ -826,9 +956,11 @@ export class RouteConnectionHandler { } catch (err) { // Ignore errors - these are optional enhancements if (this.settings.enableDetailedLogging) { - console.log( - `[${connectionId}] Enhanced TCP keep-alive not supported for outgoing socket: ${err}` - ); + logger.log('warn', `Enhanced TCP keep-alive not supported for outgoing socket on connection ${connectionId}: ${err}`, { + connectionId, + error: err, + component: 'route-handler' + }); } } } @@ -848,22 +980,23 @@ export class RouteConnectionHandler { socket.on('timeout', () => { // For keep-alive connections, just log a warning instead of closing if (record.hasKeepAlive) { - console.log( - `[${connectionId}] Timeout event on incoming keep-alive connection from ${ - record.remoteIP - } after ${plugins.prettyMs( - this.settings.socketTimeout || 3600000 - )}. Connection preserved.` - ); + logger.log('warn', `Timeout event on incoming keep-alive connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}. Connection preserved.`, { + connectionId, + remoteIP: record.remoteIP, + timeout: plugins.prettyMs(this.settings.socketTimeout || 3600000), + status: 'Connection preserved', + component: 'route-handler' + }); return; } // For non-keep-alive connections, proceed with normal cleanup - console.log( - `[${connectionId}] Timeout on incoming side from ${ - record.remoteIP - } after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}` - ); + logger.log('warn', `Timeout on incoming side for connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}`, { + connectionId, + remoteIP: record.remoteIP, + timeout: plugins.prettyMs(this.settings.socketTimeout || 3600000), + component: 'route-handler' + }); if (record.incomingTerminationReason === null) { record.incomingTerminationReason = 'timeout'; this.connectionManager.incrementTerminationStat('incoming', 'timeout'); @@ -874,22 +1007,23 @@ export class RouteConnectionHandler { targetSocket.on('timeout', () => { // For keep-alive connections, just log a warning instead of closing if (record.hasKeepAlive) { - console.log( - `[${connectionId}] Timeout event on outgoing keep-alive connection from ${ - record.remoteIP - } after ${plugins.prettyMs( - this.settings.socketTimeout || 3600000 - )}. Connection preserved.` - ); + logger.log('warn', `Timeout event on outgoing keep-alive connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}. Connection preserved.`, { + connectionId, + remoteIP: record.remoteIP, + timeout: plugins.prettyMs(this.settings.socketTimeout || 3600000), + status: 'Connection preserved', + component: 'route-handler' + }); return; } // For non-keep-alive connections, proceed with normal cleanup - console.log( - `[${connectionId}] Timeout on outgoing side from ${ - record.remoteIP - } after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}` - ); + logger.log('warn', `Timeout on outgoing side for connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}`, { + connectionId, + remoteIP: record.remoteIP, + timeout: plugins.prettyMs(this.settings.socketTimeout || 3600000), + component: 'route-handler' + }); if (record.outgoingTerminationReason === null) { record.outgoingTerminationReason = 'timeout'; this.connectionManager.incrementTerminationStat('outgoing', 'timeout'); @@ -909,9 +1043,12 @@ export class RouteConnectionHandler { // Wait for the outgoing connection to be ready before setting up piping targetSocket.once('connect', () => { if (this.settings.enableDetailedLogging) { - console.log( - `[${connectionId}] Connection established to target: ${finalTargetHost}:${finalTargetPort}` - ); + logger.log('info', `Connection ${connectionId} established to target ${finalTargetHost}:${finalTargetPort}`, { + connectionId, + targetHost: finalTargetHost, + targetPort: finalTargetPort, + component: 'route-handler' + }); } // Clear the initial connection error handler @@ -933,7 +1070,11 @@ export class RouteConnectionHandler { // Write pending data immediately targetSocket.write(combinedData, (err) => { if (err) { - console.log(`[${connectionId}] Error writing pending data to target: ${err.message}`); + logger.log('error', `Error writing pending data to target for connection ${connectionId}: ${err.message}`, { + connectionId, + error: err.message, + component: 'route-handler' + }); return this.connectionManager.initiateCleanupOnce(record, 'write_error'); } }); @@ -954,15 +1095,17 @@ export class RouteConnectionHandler { }); // Log successful connection - console.log( + logger.log('info', `Connection established: ${record.remoteIP} -> ${finalTargetHost}:${finalTargetPort}` + - `${ - serverName - ? ` (SNI: ${serverName})` - : record.lockedDomain - ? ` (Domain: ${record.lockedDomain})` - : '' - }` + `${serverName ? ` (SNI: ${serverName})` : record.lockedDomain ? ` (Domain: ${record.lockedDomain})` : ''}`, + { + remoteIP: record.remoteIP, + targetHost: finalTargetHost, + targetPort: finalTargetPort, + sni: serverName || undefined, + domain: !serverName && record.lockedDomain ? record.lockedDomain : undefined, + component: 'route-handler' + } ); // Add TLS renegotiation handler if needed @@ -990,17 +1133,21 @@ export class RouteConnectionHandler { socket.on('data', renegotiationHandler); if (this.settings.enableDetailedLogging) { - console.log( - `[${connectionId}] TLS renegotiation handler installed for SNI domain: ${serverName}` - ); + logger.log('info', `TLS renegotiation handler installed for connection ${connectionId} with SNI ${serverName}`, { + connectionId, + serverName, + component: 'route-handler' + }); } } // Set connection timeout record.cleanupTimer = this.timeoutManager.setupConnectionTimeout(record, (record, reason) => { - console.log( - `[${connectionId}] Connection from ${record.remoteIP} exceeded max lifetime, forcing cleanup.` - ); + logger.log('warn', `Connection ${connectionId} from ${record.remoteIP} exceeded max lifetime, forcing cleanup`, { + connectionId, + remoteIP: record.remoteIP, + component: 'route-handler' + }); this.connectionManager.initiateCleanupOnce(record, reason); }); diff --git a/ts/proxies/smart-proxy/smart-proxy.ts b/ts/proxies/smart-proxy/smart-proxy.ts index d625119..f66db7f 100644 --- a/ts/proxies/smart-proxy/smart-proxy.ts +++ b/ts/proxies/smart-proxy/smart-proxy.ts @@ -1,4 +1,5 @@ import * as plugins from '../../plugins.js'; +import { logger } from '../../core/utils/logger.js'; // Importing required components import { ConnectionManager } from './connection-manager.js'; @@ -239,7 +240,7 @@ export class SmartProxy extends plugins.EventEmitter { ); if (autoRoutes.length === 0 && !this.hasStaticCertRoutes()) { - console.log('No routes require certificate management'); + logger.log('info', 'No routes require certificate management', { component: 'certificate-manager' }); return; } @@ -256,7 +257,7 @@ export class SmartProxy extends plugins.EventEmitter { useProduction: this.settings.acme.useProduction || false, port: this.settings.acme.port || 80 }; - console.log(`Using top-level ACME configuration with email: ${acmeOptions.email}`); + logger.log('info', `Using top-level ACME configuration with email: ${acmeOptions.email}`, { component: 'certificate-manager' }); } else if (autoRoutes.length > 0) { // Check for route-level ACME config const routeWithAcme = autoRoutes.find(r => r.action.tls?.acme?.email); @@ -267,7 +268,7 @@ export class SmartProxy extends plugins.EventEmitter { useProduction: routeAcme.useProduction || false, port: routeAcme.challengePort || 80 }; - console.log(`Using route-level ACME configuration from route '${routeWithAcme.name}' with email: ${acmeOptions.email}`); + logger.log('info', `Using route-level ACME configuration from route '${routeWithAcme.name}' with email: ${acmeOptions.email}`, { component: 'certificate-manager' }); } } @@ -305,7 +306,7 @@ export class SmartProxy extends plugins.EventEmitter { public async start() { // Don't start if already shutting down if (this.isShuttingDown) { - console.log("Cannot start SmartProxy while it's shutting down"); + logger.log('warn', "Cannot start SmartProxy while it's in the shutdown process"); return; } @@ -332,9 +333,9 @@ export class SmartProxy extends plugins.EventEmitter { const allWarnings = [...configWarnings, ...acmeWarnings]; if (allWarnings.length > 0) { - console.log("Configuration warnings:"); + logger.log('warn', `${allWarnings.length} configuration warnings found`, { count: allWarnings.length }); for (const warning of allWarnings) { - console.log(` - ${warning}`); + logger.log('warn', `${warning}`); } } @@ -353,7 +354,7 @@ export class SmartProxy extends plugins.EventEmitter { // Now that ports are listening, provision any required certificates if (this.certManager) { - console.log('Starting certificate provisioning now that ports are ready'); + logger.log('info', 'Starting certificate provisioning now that ports are ready', { component: 'certificate-manager' }); await this.certManager.provisionAllCertificates(); } @@ -411,16 +412,26 @@ export class SmartProxy extends plugins.EventEmitter { const terminationStats = this.connectionManager.getTerminationStats(); // Log detailed stats - console.log( - `Active connections: ${connectionRecords.size}. ` + - `Types: TLS=${tlsConnections} (Completed=${completedTlsHandshakes}, Pending=${pendingTlsHandshakes}), ` + - `Non-TLS=${nonTlsConnections}, KeepAlive=${keepAliveConnections}, HttpProxy=${httpProxyConnections}. ` + - `Longest running: IN=${plugins.prettyMs(maxIncoming)}, OUT=${plugins.prettyMs(maxOutgoing)}. ` + - `Termination stats: ${JSON.stringify({ - IN: terminationStats.incoming, - OUT: terminationStats.outgoing, - })}` - ); + logger.log('info', 'Connection statistics', { + activeConnections: connectionRecords.size, + tls: { + total: tlsConnections, + completed: completedTlsHandshakes, + pending: pendingTlsHandshakes + }, + nonTls: nonTlsConnections, + keepAlive: keepAliveConnections, + httpProxy: httpProxyConnections, + longestRunning: { + incoming: plugins.prettyMs(maxIncoming), + outgoing: plugins.prettyMs(maxOutgoing) + }, + terminationStats: { + incoming: terminationStats.incoming, + outgoing: terminationStats.outgoing + }, + component: 'connection-manager' + }); }, this.settings.inactivityCheckInterval || 60000); // Make sure the interval doesn't keep the process alive @@ -439,19 +450,19 @@ export class SmartProxy extends plugins.EventEmitter { * Stop the proxy server */ public async stop() { - console.log('SmartProxy shutting down...'); + logger.log('info', 'SmartProxy shutting down...'); this.isShuttingDown = true; this.portManager.setShuttingDown(true); // Stop certificate manager if (this.certManager) { await this.certManager.stop(); - console.log('Certificate manager stopped'); + logger.log('info', 'Certificate manager stopped'); } // Stop NFTablesManager await this.nftablesManager.stop(); - console.log('NFTablesManager stopped'); + logger.log('info', 'NFTablesManager stopped'); // Stop the connection logger if (this.connectionLogger) { @@ -461,7 +472,7 @@ export class SmartProxy extends plugins.EventEmitter { // Stop all port listeners await this.portManager.closeAll(); - console.log('All servers closed. Cleaning up active connections...'); + logger.log('info', 'All servers closed. Cleaning up active connections...'); // Clean up all active connections this.connectionManager.clearConnections(); @@ -472,7 +483,7 @@ export class SmartProxy extends plugins.EventEmitter { // Clear ACME state manager this.acmeStateManager.clear(); - console.log('SmartProxy shutdown complete.'); + logger.log('info', 'SmartProxy shutdown complete.'); } /** @@ -481,7 +492,7 @@ export class SmartProxy extends plugins.EventEmitter { * Note: This legacy method has been removed. Use updateRoutes instead. */ public async updateDomainConfigs(): Promise { - console.warn('Method updateDomainConfigs() is deprecated. Use updateRoutes() instead.'); + logger.log('warn', 'Method updateDomainConfigs() is deprecated. Use updateRoutes() instead.'); throw new Error('updateDomainConfigs() is deprecated - use updateRoutes() instead'); } @@ -497,7 +508,7 @@ export class SmartProxy extends plugins.EventEmitter { const challengeRouteExists = this.settings.routes.some(r => r.name === 'acme-challenge'); if (!challengeRouteExists) { - console.log('Challenge route successfully removed from routes'); + logger.log('info', 'Challenge route successfully removed from routes'); return; } @@ -505,7 +516,9 @@ export class SmartProxy extends plugins.EventEmitter { await plugins.smartdelay.delayFor(retryDelay); } - throw new Error('Failed to verify challenge route removal after ' + maxRetries + ' attempts'); + const error = `Failed to verify challenge route removal after ${maxRetries} attempts`; + logger.log('error', error); + throw new Error(error); } /** @@ -533,7 +546,7 @@ export class SmartProxy extends plugins.EventEmitter { */ public async updateRoutes(newRoutes: IRouteConfig[]): Promise { return this.routeUpdateLock.runExclusive(async () => { - console.log(`Updating routes (${newRoutes.length} routes)`); + logger.log('info', `Updating routes (${newRoutes.length} routes)`, { routeCount: newRoutes.length, component: 'route-manager' }); // Get existing routes that use NFTables const oldNfTablesRoutes = this.settings.routes.filter( @@ -658,14 +671,14 @@ export class SmartProxy extends plugins.EventEmitter { // Check for wildcard domains (they can't get ACME certs) if (domain.includes('*')) { - console.log(`Wildcard domains like "${domain}" are not supported for ACME certificates`); + logger.log('warn', `Wildcard domains like "${domain}" are not supported for automatic ACME certificates`, { domain, component: 'certificate-manager' }); return false; } // Check if domain has at least one dot and no invalid characters const validDomainRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; if (!validDomainRegex.test(domain)) { - console.log(`Domain "${domain}" has invalid format`); + logger.log('warn', `Domain "${domain}" has invalid format for certificate issuance`, { domain, component: 'certificate-manager' }); return false; }