better logging

This commit is contained in:
Juergen Kunz
2025-07-03 02:32:17 +00:00
parent 67aff4bb30
commit 5d011ba84c
18 changed files with 1604 additions and 410 deletions

View File

@ -1,6 +1,7 @@
import * as plugins from '../../plugins.js';
import type { IConnectionRecord } from './models/interfaces.js';
import { logger } from '../../core/utils/logger.js';
import { connectionLogDeduplicator } from '../../core/utils/log-deduplicator.js';
import { LifecycleComponent } from '../../core/utils/lifecycle-component.js';
import { cleanupSocket } from '../../core/utils/socket-utils.js';
import { WrappedSocket } from '../../core/models/wrapped-socket.js';
@ -26,6 +27,10 @@ export class ConnectionManager extends LifecycleComponent {
// Cleanup queue for batched processing
private cleanupQueue: Set<string> = new Set();
private cleanupTimer: NodeJS.Timeout | null = null;
private isProcessingCleanup: boolean = false;
// Route-level connection tracking
private connectionsByRoute: Map<string, Set<string>> = new Map();
constructor(
private smartProxy: SmartProxy
@ -56,11 +61,19 @@ export class ConnectionManager extends LifecycleComponent {
public createConnection(socket: plugins.net.Socket | WrappedSocket): IConnectionRecord | null {
// Enforce connection limit
if (this.connectionRecords.size >= this.maxConnections) {
logger.log('warn', `Connection limit reached (${this.maxConnections}). Rejecting new connection.`, {
currentConnections: this.connectionRecords.size,
maxConnections: this.maxConnections,
component: 'connection-manager'
});
// Use deduplicated logging for connection limit
connectionLogDeduplicator.log(
'connection-rejected',
'warn',
'Global connection limit reached',
{
reason: 'global-limit',
currentConnections: this.connectionRecords.size,
maxConnections: this.maxConnections,
component: 'connection-manager'
},
'global-limit'
);
socket.destroy();
return null;
}
@ -165,18 +178,53 @@ export class ConnectionManager extends LifecycleComponent {
return this.connectionRecords.size;
}
/**
* Track connection by route
*/
public trackConnectionByRoute(routeId: string, connectionId: string): void {
if (!this.connectionsByRoute.has(routeId)) {
this.connectionsByRoute.set(routeId, new Set());
}
this.connectionsByRoute.get(routeId)!.add(connectionId);
}
/**
* Remove connection tracking for a route
*/
public removeConnectionByRoute(routeId: string, connectionId: string): void {
if (this.connectionsByRoute.has(routeId)) {
const connections = this.connectionsByRoute.get(routeId)!;
connections.delete(connectionId);
if (connections.size === 0) {
this.connectionsByRoute.delete(routeId);
}
}
}
/**
* Get connection count by route
*/
public getConnectionCountByRoute(routeId: string): number {
return this.connectionsByRoute.get(routeId)?.size || 0;
}
/**
* Initiates cleanup once for a connection
*/
public initiateCleanupOnce(record: IConnectionRecord, reason: string = 'normal'): void {
if (this.smartProxy.settings.enableDetailedLogging) {
logger.log('info', `Connection cleanup initiated`, {
// Use deduplicated logging for cleanup events
connectionLogDeduplicator.log(
'connection-cleanup',
'info',
`Connection cleanup: ${reason}`,
{
connectionId: record.id,
remoteIP: record.remoteIP,
reason,
component: 'connection-manager'
});
}
},
reason
);
if (record.incomingTerminationReason == null) {
record.incomingTerminationReason = reason;
@ -200,10 +248,10 @@ export class ConnectionManager extends LifecycleComponent {
this.cleanupQueue.add(connectionId);
// Process immediately if queue is getting large
if (this.cleanupQueue.size >= this.cleanupBatchSize) {
// Process immediately if queue is getting large and not already processing
if (this.cleanupQueue.size >= this.cleanupBatchSize && !this.isProcessingCleanup) {
this.processCleanupQueue();
} else if (!this.cleanupTimer) {
} else if (!this.cleanupTimer && !this.isProcessingCleanup) {
// Otherwise, schedule batch processing
this.cleanupTimer = this.setTimeout(() => {
this.processCleanupQueue();
@ -215,27 +263,40 @@ export class ConnectionManager extends LifecycleComponent {
* Process the cleanup queue in batches
*/
private processCleanupQueue(): void {
// Prevent concurrent processing
if (this.isProcessingCleanup) {
return;
}
this.isProcessingCleanup = true;
if (this.cleanupTimer) {
this.clearTimeout(this.cleanupTimer);
this.cleanupTimer = null;
}
const toCleanup = Array.from(this.cleanupQueue).slice(0, this.cleanupBatchSize);
// Remove only the items we're processing, not the entire queue!
for (const connectionId of toCleanup) {
this.cleanupQueue.delete(connectionId);
const record = this.connectionRecords.get(connectionId);
if (record) {
this.cleanupConnection(record, record.incomingTerminationReason || 'normal');
try {
// Take a snapshot of items to process
const toCleanup = Array.from(this.cleanupQueue).slice(0, this.cleanupBatchSize);
// Remove only the items we're processing from the queue
for (const connectionId of toCleanup) {
this.cleanupQueue.delete(connectionId);
const record = this.connectionRecords.get(connectionId);
if (record) {
this.cleanupConnection(record, record.incomingTerminationReason || 'normal');
}
}
} finally {
// Always reset the processing flag
this.isProcessingCleanup = false;
// Check if more items were added while we were processing
if (this.cleanupQueue.size > 0) {
this.cleanupTimer = this.setTimeout(() => {
this.processCleanupQueue();
}, 10);
}
}
// If there are more in queue, schedule next batch
if (this.cleanupQueue.size > 0) {
this.cleanupTimer = this.setTimeout(() => {
this.processCleanupQueue();
}, 10);
}
}
@ -252,6 +313,11 @@ export class ConnectionManager extends LifecycleComponent {
// Track connection termination
this.smartProxy.securityManager.removeConnectionByIP(record.remoteIP, record.id);
// Remove from route tracking
if (record.routeId) {
this.removeConnectionByRoute(record.routeId, record.id);
}
// Remove from metrics tracking
if (this.smartProxy.metricsCollector) {
this.smartProxy.metricsCollector.removeConnection(record.id);