@push.rocks/smartproxy 19.3.9 → 19.3.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,7 @@ import type { IRouteConfig, IRouteTls } from './models/route-types.js';
4
4
  import type { IAcmeOptions } from './models/interfaces.js';
5
5
  import { CertStore } from './cert-store.js';
6
6
  import type { AcmeStateManager } from './acme-state-manager.js';
7
+ import { logger } from '../../core/utils/logger.js';
7
8
 
8
9
  export interface ICertStatus {
9
10
  domain: string;
@@ -125,15 +126,16 @@ export class SmartCertManager {
125
126
 
126
127
  // Add challenge route once at initialization if not already active
127
128
  if (!this.challengeRouteActive) {
128
- console.log('Adding ACME challenge route during initialization');
129
+ logger.log('info', 'Adding ACME challenge route during initialization', { component: 'certificate-manager' });
129
130
  await this.addChallengeRoute();
130
131
  } else {
131
- console.log('Challenge route already active from previous instance');
132
+ logger.log('info', 'Challenge route already active from previous instance', { component: 'certificate-manager' });
132
133
  }
133
134
  }
134
135
 
135
- // Provision certificates for all routes
136
- await this.provisionAllCertificates();
136
+ // Skip automatic certificate provisioning during initialization
137
+ // This will be called later after ports are listening
138
+ logger.log('info', 'Certificate manager initialized. Deferring certificate provisioning until after ports are listening.', { component: 'certificate-manager' });
137
139
 
138
140
  // Start renewal timer
139
141
  this.startRenewalTimer();
@@ -142,7 +144,7 @@ export class SmartCertManager {
142
144
  /**
143
145
  * Provision certificates for all routes that need them
144
146
  */
145
- private async provisionAllCertificates(): Promise<void> {
147
+ public async provisionAllCertificates(): Promise<void> {
146
148
  const certRoutes = this.routes.filter(r =>
147
149
  r.action.tls?.mode === 'terminate' ||
148
150
  r.action.tls?.mode === 'terminate-and-reencrypt'
@@ -156,7 +158,7 @@ export class SmartCertManager {
156
158
  try {
157
159
  await this.provisionCertificate(route, true); // Allow concurrent since we're managing it here
158
160
  } catch (error) {
159
- console.error(`Failed to provision certificate for route ${route.name}: ${error}`);
161
+ logger.log('error', `Failed to provision certificate for route ${route.name}`, { routeName: route.name, error, component: 'certificate-manager' });
160
162
  }
161
163
  }
162
164
  } finally {
@@ -175,13 +177,13 @@ export class SmartCertManager {
175
177
 
176
178
  // Check if provisioning is already in progress (prevent concurrent provisioning)
177
179
  if (!allowConcurrent && this.isProvisioning) {
178
- console.log(`Certificate provisioning already in progress, skipping ${route.name}`);
180
+ logger.log('info', `Certificate provisioning already in progress, skipping ${route.name}`, { routeName: route.name, component: 'certificate-manager' });
179
181
  return;
180
182
  }
181
183
 
182
184
  const domains = this.extractDomainsFromRoute(route);
183
185
  if (domains.length === 0) {
184
- console.warn(`Route ${route.name} has TLS termination but no domains`);
186
+ logger.log('warn', `Route ${route.name} has TLS termination but no domains`, { routeName: route.name, component: 'certificate-manager' });
185
187
  return;
186
188
  }
187
189
 
@@ -218,7 +220,7 @@ export class SmartCertManager {
218
220
  // Check if we already have a valid certificate
219
221
  const existingCert = await this.certStore.getCertificate(routeName);
220
222
  if (existingCert && this.isCertificateValid(existingCert)) {
221
- console.log(`Using existing valid certificate for ${primaryDomain}`);
223
+ logger.log('info', `Using existing valid certificate for ${primaryDomain}`, { domain: primaryDomain, component: 'certificate-manager' });
222
224
  await this.applyCertificate(primaryDomain, existingCert);
223
225
  this.updateCertStatus(routeName, 'valid', 'acme', existingCert);
224
226
  return;
@@ -229,7 +231,7 @@ export class SmartCertManager {
229
231
  this.globalAcmeDefaults?.renewThresholdDays ||
230
232
  30;
231
233
 
232
- console.log(`Requesting ACME certificate for ${domains.join(', ')} (renew ${renewThreshold} days before expiry)`);
234
+ logger.log('info', `Requesting ACME certificate for ${domains.join(', ')} (renew ${renewThreshold} days before expiry)`, { domains: domains.join(', '), renewThreshold, component: 'certificate-manager' });
233
235
  this.updateCertStatus(routeName, 'pending', 'acme');
234
236
 
235
237
  try {
@@ -251,7 +253,7 @@ export class SmartCertManager {
251
253
  hasDnsChallenge;
252
254
 
253
255
  if (shouldIncludeWildcard) {
254
- console.log(`Requesting wildcard certificate for ${primaryDomain} (DNS-01 available)`);
256
+ logger.log('info', `Requesting wildcard certificate for ${primaryDomain} (DNS-01 available)`, { domain: primaryDomain, challengeType: 'DNS-01', component: 'certificate-manager' });
255
257
  }
256
258
 
257
259
  // Use smartacme to get certificate with optional wildcard
@@ -278,9 +280,9 @@ export class SmartCertManager {
278
280
  await this.applyCertificate(primaryDomain, certData);
279
281
  this.updateCertStatus(routeName, 'valid', 'acme', certData);
280
282
 
281
- console.log(`Successfully provisioned ACME certificate for ${primaryDomain}`);
283
+ logger.log('info', `Successfully provisioned ACME certificate for ${primaryDomain}`, { domain: primaryDomain, component: 'certificate-manager' });
282
284
  } catch (error) {
283
- console.error(`Failed to provision ACME certificate for ${primaryDomain}: ${error}`);
285
+ logger.log('error', `Failed to provision ACME certificate for ${primaryDomain}: ${error.message}`, { domain: primaryDomain, error: error.message, component: 'certificate-manager' });
284
286
  this.updateCertStatus(routeName, 'error', 'acme', undefined, error.message);
285
287
  throw error;
286
288
  }
@@ -327,9 +329,9 @@ export class SmartCertManager {
327
329
  await this.applyCertificate(domain, certData);
328
330
  this.updateCertStatus(routeName, 'valid', 'static', certData);
329
331
 
330
- console.log(`Successfully loaded static certificate for ${domain}`);
332
+ logger.log('info', `Successfully loaded static certificate for ${domain}`, { domain, component: 'certificate-manager' });
331
333
  } catch (error) {
332
- console.error(`Failed to provision static certificate for ${domain}: ${error}`);
334
+ logger.log('error', `Failed to provision static certificate for ${domain}: ${error.message}`, { domain, error: error.message, component: 'certificate-manager' });
333
335
  this.updateCertStatus(routeName, 'error', 'static', undefined, error.message);
334
336
  throw error;
335
337
  }
@@ -340,7 +342,7 @@ export class SmartCertManager {
340
342
  */
341
343
  private async applyCertificate(domain: string, certData: ICertificateData): Promise<void> {
342
344
  if (!this.httpProxy) {
343
- console.warn('HttpProxy not set, cannot apply certificate');
345
+ logger.log('warn', `HttpProxy not set, cannot apply certificate for domain ${domain}`, { domain, component: 'certificate-manager' });
344
346
  return;
345
347
  }
346
348
 
@@ -397,13 +399,13 @@ export class SmartCertManager {
397
399
  private async addChallengeRoute(): Promise<void> {
398
400
  // Check with state manager first
399
401
  if (this.acmeStateManager && this.acmeStateManager.isChallengeRouteActive()) {
400
- console.log('Challenge route already active in global state, skipping');
402
+ logger.log('info', 'Challenge route already active in global state, skipping', { component: 'certificate-manager' });
401
403
  this.challengeRouteActive = true;
402
404
  return;
403
405
  }
404
406
 
405
407
  if (this.challengeRouteActive) {
406
- console.log('Challenge route already active locally, skipping');
408
+ logger.log('info', 'Challenge route already active locally, skipping', { component: 'certificate-manager' });
407
409
  return;
408
410
  }
409
411
 
@@ -426,9 +428,9 @@ export class SmartCertManager {
426
428
  this.acmeStateManager.addChallengeRoute(challengeRoute);
427
429
  }
428
430
 
429
- console.log('ACME challenge route successfully added');
431
+ logger.log('info', 'ACME challenge route successfully added', { component: 'certificate-manager' });
430
432
  } catch (error) {
431
- console.error('Failed to add challenge route:', error);
433
+ logger.log('error', `Failed to add challenge route: ${error.message}`, { error: error.message, component: 'certificate-manager' });
432
434
  if ((error as any).code === 'EADDRINUSE') {
433
435
  throw new Error(`Port ${this.globalAcmeDefaults?.port || 80} is already in use for ACME challenges`);
434
436
  }
@@ -441,7 +443,7 @@ export class SmartCertManager {
441
443
  */
442
444
  private async removeChallengeRoute(): Promise<void> {
443
445
  if (!this.challengeRouteActive) {
444
- console.log('Challenge route not active, skipping removal');
446
+ logger.log('info', 'Challenge route not active, skipping removal', { component: 'certificate-manager' });
445
447
  return;
446
448
  }
447
449
 
@@ -459,9 +461,9 @@ export class SmartCertManager {
459
461
  this.acmeStateManager.removeChallengeRoute('acme-challenge');
460
462
  }
461
463
 
462
- console.log('ACME challenge route successfully removed');
464
+ logger.log('info', 'ACME challenge route successfully removed', { component: 'certificate-manager' });
463
465
  } catch (error) {
464
- console.error('Failed to remove challenge route:', error);
466
+ logger.log('error', `Failed to remove challenge route: ${error.message}`, { error: error.message, component: 'certificate-manager' });
465
467
  // Reset the flag even on error to avoid getting stuck
466
468
  this.challengeRouteActive = false;
467
469
  throw error;
@@ -491,11 +493,11 @@ export class SmartCertManager {
491
493
  const cert = await this.certStore.getCertificate(routeName);
492
494
 
493
495
  if (cert && !this.isCertificateValid(cert)) {
494
- console.log(`Certificate for ${routeName} needs renewal`);
496
+ logger.log('info', `Certificate for ${routeName} needs renewal`, { routeName, component: 'certificate-manager' });
495
497
  try {
496
498
  await this.provisionCertificate(route);
497
499
  } catch (error) {
498
- console.error(`Failed to renew certificate for ${routeName}: ${error}`);
500
+ logger.log('error', `Failed to renew certificate for ${routeName}: ${error.message}`, { routeName, error: error.message, component: 'certificate-manager' });
499
501
  }
500
502
  }
501
503
  }
@@ -620,7 +622,7 @@ export class SmartCertManager {
620
622
 
621
623
  // Always remove challenge route on shutdown
622
624
  if (this.challengeRoute) {
623
- console.log('Removing ACME challenge route during shutdown');
625
+ logger.log('info', 'Removing ACME challenge route during shutdown', { component: 'certificate-manager' });
624
626
  await this.removeChallengeRoute();
625
627
  }
626
628
 
@@ -2,6 +2,7 @@ import * as plugins from '../../plugins.js';
2
2
  import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js';
3
3
  import { SecurityManager } from './security-manager.js';
4
4
  import { TimeoutManager } from './timeout-manager.js';
5
+ import { logger } from '../../core/utils/logger.js';
5
6
 
6
7
  /**
7
8
  * Manages connection lifecycle, tracking, and cleanup
@@ -97,7 +98,7 @@ export class ConnectionManager {
97
98
  */
98
99
  public initiateCleanupOnce(record: IConnectionRecord, reason: string = 'normal'): void {
99
100
  if (this.settings.enableDetailedLogging) {
100
- console.log(`[${record.id}] Connection cleanup initiated for ${record.remoteIP} (${reason})`);
101
+ logger.log('info', `Connection cleanup initiated`, { connectionId: record.id, remoteIP: record.remoteIP, reason, component: 'connection-manager' });
101
102
  }
102
103
 
103
104
  if (
@@ -139,7 +140,7 @@ export class ConnectionManager {
139
140
  // Reset the handler references
140
141
  record.renegotiationHandler = undefined;
141
142
  } catch (err) {
142
- console.log(`[${record.id}] Error removing data handlers: ${err}`);
143
+ logger.log('error', `Error removing data handlers for connection ${record.id}: ${err}`, { connectionId: record.id, error: err, component: 'connection-manager' });
143
144
  }
144
145
  }
145
146
 
@@ -160,16 +161,36 @@ export class ConnectionManager {
160
161
 
161
162
  // Log connection details
162
163
  if (this.settings.enableDetailedLogging) {
163
- console.log(
164
- `[${record.id}] Connection from ${record.remoteIP} on port ${record.localPort} terminated (${reason}).` +
165
- ` Duration: ${plugins.prettyMs(duration)}, Bytes IN: ${bytesReceived}, OUT: ${bytesSent}, ` +
166
- `TLS: ${record.isTLS ? 'Yes' : 'No'}, Keep-Alive: ${record.hasKeepAlive ? 'Yes' : 'No'}` +
167
- `${record.usingNetworkProxy ? ', Using NetworkProxy' : ''}` +
168
- `${record.domainSwitches ? `, Domain switches: ${record.domainSwitches}` : ''}`
164
+ logger.log('info',
165
+ `Connection from ${record.remoteIP} on port ${record.localPort} terminated (${reason}). ` +
166
+ `Duration: ${plugins.prettyMs(duration)}, Bytes IN: ${bytesReceived}, OUT: ${bytesSent}, ` +
167
+ `TLS: ${record.isTLS ? 'Yes' : 'No'}, Keep-Alive: ${record.hasKeepAlive ? 'Yes' : 'No'}` +
168
+ `${record.usingNetworkProxy ? ', Using NetworkProxy' : ''}` +
169
+ `${record.domainSwitches ? `, Domain switches: ${record.domainSwitches}` : ''}`,
170
+ {
171
+ connectionId: record.id,
172
+ remoteIP: record.remoteIP,
173
+ localPort: record.localPort,
174
+ reason,
175
+ duration: plugins.prettyMs(duration),
176
+ bytes: { in: bytesReceived, out: bytesSent },
177
+ tls: record.isTLS,
178
+ keepAlive: record.hasKeepAlive,
179
+ usingNetworkProxy: record.usingNetworkProxy,
180
+ domainSwitches: record.domainSwitches || 0,
181
+ component: 'connection-manager'
182
+ }
169
183
  );
170
184
  } else {
171
- console.log(
172
- `[${record.id}] Connection from ${record.remoteIP} terminated (${reason}). Active connections: ${this.connectionRecords.size}`
185
+ logger.log('info',
186
+ `Connection from ${record.remoteIP} terminated (${reason}). Active connections: ${this.connectionRecords.size}`,
187
+ {
188
+ connectionId: record.id,
189
+ remoteIP: record.remoteIP,
190
+ reason,
191
+ activeConnections: this.connectionRecords.size,
192
+ component: 'connection-manager'
193
+ }
173
194
  );
174
195
  }
175
196
  }
@@ -189,7 +210,7 @@ export class ConnectionManager {
189
210
  socket.destroy();
190
211
  }
191
212
  } catch (err) {
192
- console.log(`[${record.id}] Error destroying ${side} socket: ${err}`);
213
+ logger.log('error', `Error destroying ${side} socket for connection ${record.id}: ${err}`, { connectionId: record.id, side, error: err, component: 'connection-manager' });
193
214
  }
194
215
  }, 1000);
195
216
 
@@ -199,13 +220,13 @@ export class ConnectionManager {
199
220
  }
200
221
  }
201
222
  } catch (err) {
202
- console.log(`[${record.id}] Error closing ${side} socket: ${err}`);
223
+ logger.log('error', `Error closing ${side} socket for connection ${record.id}: ${err}`, { connectionId: record.id, side, error: err, component: 'connection-manager' });
203
224
  try {
204
225
  if (!socket.destroyed) {
205
226
  socket.destroy();
206
227
  }
207
228
  } catch (destroyErr) {
208
- console.log(`[${record.id}] Error destroying ${side} socket: ${destroyErr}`);
229
+ logger.log('error', `Error destroying ${side} socket for connection ${record.id}: ${destroyErr}`, { connectionId: record.id, side, error: destroyErr, component: 'connection-manager' });
209
230
  }
210
231
  }
211
232
  }
@@ -224,21 +245,36 @@ export class ConnectionManager {
224
245
 
225
246
  if (code === 'ECONNRESET') {
226
247
  reason = 'econnreset';
227
- console.log(
228
- `[${record.id}] ECONNRESET on ${side} side from ${record.remoteIP}: ${err.message}. ` +
229
- `Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)} ago`
230
- );
248
+ logger.log('warn', `ECONNRESET on ${side} connection from ${record.remoteIP}. Error: ${err.message}. Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)}`, {
249
+ connectionId: record.id,
250
+ side,
251
+ remoteIP: record.remoteIP,
252
+ error: err.message,
253
+ duration: plugins.prettyMs(connectionDuration),
254
+ lastActivity: plugins.prettyMs(lastActivityAge),
255
+ component: 'connection-manager'
256
+ });
231
257
  } else if (code === 'ETIMEDOUT') {
232
258
  reason = 'etimedout';
233
- console.log(
234
- `[${record.id}] ETIMEDOUT on ${side} side from ${record.remoteIP}: ${err.message}. ` +
235
- `Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)} ago`
236
- );
259
+ logger.log('warn', `ETIMEDOUT on ${side} connection from ${record.remoteIP}. Error: ${err.message}. Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)}`, {
260
+ connectionId: record.id,
261
+ side,
262
+ remoteIP: record.remoteIP,
263
+ error: err.message,
264
+ duration: plugins.prettyMs(connectionDuration),
265
+ lastActivity: plugins.prettyMs(lastActivityAge),
266
+ component: 'connection-manager'
267
+ });
237
268
  } else {
238
- console.log(
239
- `[${record.id}] Error on ${side} side from ${record.remoteIP}: ${err.message}. ` +
240
- `Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)} ago`
241
- );
269
+ logger.log('error', `Error on ${side} connection from ${record.remoteIP}: ${err.message}. Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)}`, {
270
+ connectionId: record.id,
271
+ side,
272
+ remoteIP: record.remoteIP,
273
+ error: err.message,
274
+ duration: plugins.prettyMs(connectionDuration),
275
+ lastActivity: plugins.prettyMs(lastActivityAge),
276
+ component: 'connection-manager'
277
+ });
242
278
  }
243
279
 
244
280
  if (side === 'incoming' && record.incomingTerminationReason === null) {
@@ -259,7 +295,12 @@ export class ConnectionManager {
259
295
  public handleClose(side: 'incoming' | 'outgoing', record: IConnectionRecord) {
260
296
  return () => {
261
297
  if (this.settings.enableDetailedLogging) {
262
- console.log(`[${record.id}] Connection closed on ${side} side from ${record.remoteIP}`);
298
+ logger.log('info', `Connection closed on ${side} side`, {
299
+ connectionId: record.id,
300
+ side,
301
+ remoteIP: record.remoteIP,
302
+ component: 'connection-manager'
303
+ });
263
304
  }
264
305
 
265
306
  if (side === 'incoming' && record.incomingTerminationReason === null) {
@@ -321,11 +362,13 @@ export class ConnectionManager {
321
362
  if (inactivityTime > effectiveTimeout && !record.connectionClosed) {
322
363
  // For keep-alive connections, issue a warning first
323
364
  if (record.hasKeepAlive && !record.inactivityWarningIssued) {
324
- console.log(
325
- `[${id}] Warning: Keep-alive connection from ${record.remoteIP} inactive for ${
326
- plugins.prettyMs(inactivityTime)
327
- }. Will close in 10 minutes if no activity.`
328
- );
365
+ logger.log('warn', `Keep-alive connection ${id} from ${record.remoteIP} inactive for ${plugins.prettyMs(inactivityTime)}. Will close in 10 minutes if no activity.`, {
366
+ connectionId: id,
367
+ remoteIP: record.remoteIP,
368
+ inactiveFor: plugins.prettyMs(inactivityTime),
369
+ closureWarning: '10 minutes',
370
+ component: 'connection-manager'
371
+ });
329
372
 
330
373
  // Set warning flag and add grace period
331
374
  record.inactivityWarningIssued = true;
@@ -337,27 +380,30 @@ export class ConnectionManager {
337
380
  record.outgoing.write(Buffer.alloc(0));
338
381
 
339
382
  if (this.settings.enableDetailedLogging) {
340
- console.log(`[${id}] Sent probe packet to test keep-alive connection`);
383
+ logger.log('info', `Sent probe packet to test keep-alive connection ${id}`, { connectionId: id, component: 'connection-manager' });
341
384
  }
342
385
  } catch (err) {
343
- console.log(`[${id}] Error sending probe packet: ${err}`);
386
+ logger.log('error', `Error sending probe packet to connection ${id}: ${err}`, { connectionId: id, error: err, component: 'connection-manager' });
344
387
  }
345
388
  }
346
389
  } else {
347
390
  // For non-keep-alive or after warning, close the connection
348
- console.log(
349
- `[${id}] Inactivity check: No activity on connection from ${record.remoteIP} ` +
350
- `for ${plugins.prettyMs(inactivityTime)}.` +
351
- (record.hasKeepAlive ? ' Despite keep-alive being enabled.' : '')
352
- );
391
+ logger.log('warn', `Closing inactive connection ${id} from ${record.remoteIP} (inactive for ${plugins.prettyMs(inactivityTime)}, keep-alive: ${record.hasKeepAlive ? 'Yes' : 'No'})`, {
392
+ connectionId: id,
393
+ remoteIP: record.remoteIP,
394
+ inactiveFor: plugins.prettyMs(inactivityTime),
395
+ hasKeepAlive: record.hasKeepAlive,
396
+ component: 'connection-manager'
397
+ });
353
398
  this.cleanupConnection(record, 'inactivity');
354
399
  }
355
400
  } else if (inactivityTime <= effectiveTimeout && record.inactivityWarningIssued) {
356
401
  // If activity detected after warning, clear the warning
357
402
  if (this.settings.enableDetailedLogging) {
358
- console.log(
359
- `[${id}] Connection activity detected after inactivity warning, resetting warning`
360
- );
403
+ logger.log('info', `Connection ${id} activity detected after inactivity warning`, {
404
+ connectionId: id,
405
+ component: 'connection-manager'
406
+ });
361
407
  }
362
408
  record.inactivityWarningIssued = false;
363
409
  }
@@ -369,11 +415,12 @@ export class ConnectionManager {
369
415
  !record.connectionClosed &&
370
416
  now - record.outgoingClosedTime > 120000
371
417
  ) {
372
- console.log(
373
- `[${id}] Parity check: Incoming socket for ${record.remoteIP} still active ${
374
- plugins.prettyMs(now - record.outgoingClosedTime)
375
- } after outgoing closed.`
376
- );
418
+ logger.log('warn', `Parity check: Connection ${id} from ${record.remoteIP} has incoming socket still active ${plugins.prettyMs(now - record.outgoingClosedTime)} after outgoing socket closed`, {
419
+ connectionId: id,
420
+ remoteIP: record.remoteIP,
421
+ timeElapsed: plugins.prettyMs(now - record.outgoingClosedTime),
422
+ component: 'connection-manager'
423
+ });
377
424
  this.cleanupConnection(record, 'parity_check');
378
425
  }
379
426
  }
@@ -406,7 +453,7 @@ export class ConnectionManager {
406
453
  record.outgoing.end();
407
454
  }
408
455
  } catch (err) {
409
- console.log(`Error during graceful connection end for ${id}: ${err}`);
456
+ logger.log('error', `Error during graceful end of connection ${id}: ${err}`, { connectionId: id, error: err, component: 'connection-manager' });
410
457
  }
411
458
  }
412
459
  }
@@ -433,7 +480,7 @@ export class ConnectionManager {
433
480
  }
434
481
  }
435
482
  } catch (err) {
436
- console.log(`Error during forced connection destruction for ${id}: ${err}`);
483
+ logger.log('error', `Error during forced destruction of connection ${id}: ${err}`, { connectionId: id, error: err, component: 'connection-manager' });
437
484
  }
438
485
  }
439
486
  }