From 582e19e6a67020cd88b493d900ac16b8e02f9c19 Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Sat, 21 Feb 2026 18:13:10 +0000 Subject: [PATCH] fix(dcrouter): replace console logging with structured logger, improve metrics logging, add terminal-ready wait in ops UI, bump dees-catalog patch --- changelog.md | 10 ++ package.json | 2 +- pnpm-lock.yaml | 14 +-- ts/00_commitinfo_data.ts | 2 +- ts/classes.dcrouter.ts | 160 ++++++++---------------- ts/monitoring/classes.metricsmanager.ts | 17 +-- ts_web/00_commitinfo_data.ts | 2 +- ts_web/elements/ops-view-logs.ts | 11 ++ 8 files changed, 92 insertions(+), 126 deletions(-) diff --git a/changelog.md b/changelog.md index b5f4039..257e924 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,15 @@ # Changelog +## 2026-02-21 - 7.4.1 - fix(dcrouter) +replace console logging with structured logger, improve metrics logging, add terminal-ready wait in ops UI, bump dees-catalog patch + +- Replace console.log/console.error calls in classes.dcrouter.ts with structured logger.log (info/debug/error) including contextual data and stringified errors +- MetricsManager: create a dedicated Smartlog instance (metricsLogger) for SmartMetrics and use shared logger for lifecycle events (start/stop) +- SmartProxy/ACME: convert startup/stop/cert events and error logging to structured logs; include generated route and cert metadata where relevant +- Shutdown/startup flows: unify service start/stop/error messages through logger to provide consistent, structured output +- UI change: ops-view-logs now waits for xterm terminalReady before pushing initial logs to avoid race conditions +- Bump dependency @design.estate/dees-catalog from 3.43.0 to 3.43.1 + ## 2026-02-21 - 7.4.0 - feat(opsserver) add real-time log push to ops dashboard and recent DNS query tracking diff --git a/package.json b/package.json index a0d26e6..642abfc 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "@api.global/typedserver": "^8.3.0", "@api.global/typedsocket": "^4.1.0", "@apiclient.xyz/cloudflare": "^7.1.0", - "@design.estate/dees-catalog": "^3.43.0", + "@design.estate/dees-catalog": "^3.43.1", "@design.estate/dees-element": "^2.1.6", "@push.rocks/projectinfo": "^5.0.2", "@push.rocks/qenv": "^6.1.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f9f0498..8e1d0af 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,8 +24,8 @@ importers: specifier: ^7.1.0 version: 7.1.0 '@design.estate/dees-catalog': - specifier: ^3.43.0 - version: 3.43.0(@tiptap/pm@2.27.2) + specifier: ^3.43.1 + version: 3.43.1(@tiptap/pm@2.27.2) '@design.estate/dees-element': specifier: ^2.1.6 version: 2.1.6 @@ -351,8 +351,8 @@ packages: '@configvault.io/interfaces@1.0.17': resolution: {integrity: sha512-bEcCUR2VBDJsTin8HQh8Uw/mlYl2v8A3jMIaQ+MTB9Hrqd6CZL2dL7iJdWyFl/3EIX+LDxWFR+Oq7liIq7w+1Q==} - '@design.estate/dees-catalog@3.43.0': - resolution: {integrity: sha512-UFW8oThP9Mc4L0wVVgmuGux868Ct/TwZ1WP8hZCe4e/+5gmxDc+4EArnt5hePHENboe1Soobh9mmrMN6kQZ3xQ==} + '@design.estate/dees-catalog@3.43.1': + resolution: {integrity: sha512-WAWOV8dIgdKfAbS4Ciek8oDVIWC0OSPODhpQdLlsGBXERcFaBPaYxcpywmrjXB/TFeoAQPxBxhS7jb9/p2Rprg==} '@design.estate/dees-comms@1.0.30': resolution: {integrity: sha512-KchMlklJfKAjQiJiR0xmofXtQ27VgZtBIxcMwPE9d+h3jJRv+lPZxzBQVOM0eyM0uS44S5vJMZ11IeV4uDXSHg==} @@ -4218,13 +4218,11 @@ packages: xterm-addon-fit@0.8.0: resolution: {integrity: sha512-yj3Np7XlvxxhYF/EJ7p3KHaMt6OdwQ+HDu573Vx1lRXsVxOcnVJs51RgjZOouIZOczTsskaS+CpXspK81/DLqw==} - deprecated: This package is now deprecated. Move to @xterm/addon-fit instead. peerDependencies: xterm: ^5.0.0 xterm@5.3.0: resolution: {integrity: sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg==} - deprecated: This package is now deprecated. Move to @xterm/xterm instead. y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} @@ -4336,7 +4334,7 @@ snapshots: '@api.global/typedrequest-interfaces': 3.0.19 '@api.global/typedsocket': 4.1.0(@push.rocks/smartserve@2.0.1) '@cloudflare/workers-types': 4.20260210.0 - '@design.estate/dees-catalog': 3.43.0(@tiptap/pm@2.27.2) + '@design.estate/dees-catalog': 3.43.1(@tiptap/pm@2.27.2) '@design.estate/dees-comms': 1.0.30 '@push.rocks/lik': 6.2.2 '@push.rocks/smartdelay': 3.0.5 @@ -4934,7 +4932,7 @@ snapshots: dependencies: '@api.global/typedrequest-interfaces': 3.0.19 - '@design.estate/dees-catalog@3.43.0(@tiptap/pm@2.27.2)': + '@design.estate/dees-catalog@3.43.1(@tiptap/pm@2.27.2)': dependencies: '@design.estate/dees-domtools': 2.3.8 '@design.estate/dees-element': 2.1.6 diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 3e096c1..4c44112 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/dcrouter', - version: '7.4.0', + version: '7.4.1', description: 'A multifaceted routing service handling mail and SMS delivery functions.' } diff --git a/ts/classes.dcrouter.ts b/ts/classes.dcrouter.ts index 0c87c4e..3ebe671 100644 --- a/ts/classes.dcrouter.ts +++ b/ts/classes.dcrouter.ts @@ -252,9 +252,7 @@ export class DcRouter { } public async start() { - console.log('╔═══════════════════════════════════════════════════════════════════╗'); - console.log('║ Starting DcRouter Services ║'); - console.log('╚═══════════════════════════════════════════════════════════════════╝'); + logger.log('info', 'Starting DcRouter Services'); this.opsServer = new OpsServer(this); @@ -296,7 +294,7 @@ export class DcRouter { this.logStartupSummary(); } catch (error) { - console.error('❌ Error starting DcRouter:', error); + logger.log('error', 'Error starting DcRouter', { error: String(error) }); // Try to clean up any services that may have started await this.stop(); throw error; @@ -307,104 +305,60 @@ export class DcRouter { * Log comprehensive startup summary */ private logStartupSummary(): void { - console.log('\n╔═══════════════════════════════════════════════════════════════════╗'); - console.log('║ DcRouter Started Successfully ║'); - console.log('╚═══════════════════════════════════════════════════════════════════╝\n'); - + logger.log('info', 'DcRouter Started Successfully'); + // Metrics summary if (this.metricsManager) { - console.log('📊 Metrics Service:'); - console.log(' ├─ SmartMetrics: Active'); - console.log(' ├─ SmartProxy Stats: Active'); - console.log(' └─ Real-time tracking: Enabled'); + logger.log('info', 'Metrics Service: SmartMetrics active, SmartProxy stats active, real-time tracking enabled'); } - + // SmartProxy summary if (this.smartProxy) { - console.log('🌐 SmartProxy Service:'); const routeCount = this.options.smartProxyConfig?.routes?.length || 0; - console.log(` ├─ Routes configured: ${routeCount}`); - console.log(` ├─ ACME enabled: ${this.options.smartProxyConfig?.acme?.enabled || false}`); - if (this.options.smartProxyConfig?.acme?.enabled) { - console.log(` ├─ ACME email: ${this.options.smartProxyConfig.acme.email || 'not set'}`); - console.log(` └─ ACME mode: ${this.options.smartProxyConfig.acme.useProduction ? 'production' : 'staging'}`); - } else { - console.log(' └─ ACME: disabled'); - } + const acmeEnabled = this.options.smartProxyConfig?.acme?.enabled || false; + const acmeMode = acmeEnabled + ? `email=${this.options.smartProxyConfig!.acme!.email || 'not set'}, mode=${this.options.smartProxyConfig!.acme!.useProduction ? 'production' : 'staging'}` + : 'disabled'; + logger.log('info', `SmartProxy Service: ${routeCount} routes, ACME: ${acmeMode}`); } - + // Email service summary if (this.emailServer && this.options.emailConfig) { - console.log('\n📧 Email Service:'); const ports = this.options.emailConfig.ports || []; - console.log(` ├─ Ports: ${ports.join(', ')}`); - console.log(` ├─ Hostname: ${this.options.emailConfig.hostname || 'localhost'}`); - console.log(` ├─ Domains configured: ${this.options.emailConfig.domains?.length || 0}`); - if (this.options.emailConfig.domains && this.options.emailConfig.domains.length > 0) { - this.options.emailConfig.domains.forEach((domain, index) => { - const isLast = index === this.options.emailConfig!.domains!.length - 1; - console.log(` ${isLast ? '└─' : '├─'} ${domain.domain} (${domain.dnsMode || 'default'})`); - }); - } - console.log(` └─ DKIM: Initialized for all domains`); + const domainCount = this.options.emailConfig.domains?.length || 0; + const domainNames = this.options.emailConfig.domains?.map(d => `${d.domain} (${d.dnsMode || 'default'})`).join(', ') || 'none'; + logger.log('info', `Email Service: ports=[${ports.join(', ')}], hostname=${this.options.emailConfig.hostname || 'localhost'}, domains=${domainCount} [${domainNames}], DKIM initialized`); } - + // DNS service summary if (this.dnsServer && this.options.dnsNsDomains && this.options.dnsScopes) { - console.log('\n🌍 DNS Service:'); - console.log(` ├─ Nameservers: ${this.options.dnsNsDomains.join(', ')}`); - console.log(` ├─ Primary NS: ${this.options.dnsNsDomains[0]}`); - console.log(` ├─ Authoritative for: ${this.options.dnsScopes.length} domains`); - console.log(` ├─ UDP Port: 53`); - console.log(` ├─ DNS-over-HTTPS: Enabled via socket handler`); - console.log(` └─ DNSSEC: ${this.options.dnsNsDomains[0] ? 'Enabled' : 'Disabled'}`); - - // Show authoritative domains - if (this.options.dnsScopes.length > 0) { - console.log('\n Authoritative Domains:'); - this.options.dnsScopes.forEach((domain, index) => { - const isLast = index === this.options.dnsScopes!.length - 1; - console.log(` ${isLast ? '└─' : '├─'} ${domain}`); - }); - } + logger.log('info', `DNS Service: nameservers=[${this.options.dnsNsDomains.join(', ')}], authoritative for ${this.options.dnsScopes.length} domains [${this.options.dnsScopes.join(', ')}], UDP:53, DoH enabled`); } - + // RADIUS service summary if (this.radiusServer && this.options.radiusConfig) { - console.log('\n🔐 RADIUS Service:'); - console.log(` ├─ Auth Port: ${this.options.radiusConfig.authPort || 1812}`); - console.log(` ├─ Acct Port: ${this.options.radiusConfig.acctPort || 1813}`); - console.log(` ├─ Clients configured: ${this.options.radiusConfig.clients?.length || 0}`); const vlanStats = this.radiusServer.getVlanManager().getStats(); - console.log(` ├─ VLAN mappings: ${vlanStats.totalMappings}`); - console.log(` └─ Accounting: ${this.options.radiusConfig.accounting?.enabled ? 'Enabled' : 'Disabled'}`); + logger.log('info', `RADIUS Service: auth=${this.options.radiusConfig.authPort || 1812}, acct=${this.options.radiusConfig.acctPort || 1813}, clients=${this.options.radiusConfig.clients?.length || 0}, VLANs=${vlanStats.totalMappings}, accounting=${this.options.radiusConfig.accounting?.enabled ? 'enabled' : 'disabled'}`); } // Remote Ingress summary if (this.tunnelManager && this.options.remoteIngressConfig?.enabled) { - console.log('\n🌐 Remote Ingress:'); - console.log(` ├─ Tunnel Port: ${this.options.remoteIngressConfig.tunnelPort || 8443}`); const edgeCount = this.remoteIngressManager?.getAllEdges().length || 0; const connectedCount = this.tunnelManager.getConnectedCount(); - console.log(` ├─ Registered Edges: ${edgeCount}`); - console.log(` └─ Connected Edges: ${connectedCount}`); + logger.log('info', `Remote Ingress: tunnel port=${this.options.remoteIngressConfig.tunnelPort || 8443}, edges=${edgeCount} registered/${connectedCount} connected`); } // Storage summary if (this.storageManager && this.options.storage) { - console.log('\n💾 Storage:'); - console.log(` └─ Path: ${this.options.storage.fsPath || 'default'}`); + logger.log('info', `Storage: path=${this.options.storage.fsPath || 'default'}`); } // Cache database summary if (this.cacheDb) { - console.log('\n🗄️ Cache Database (smartdata + LocalTsmDb):'); - console.log(` ├─ Storage: ${this.cacheDb.getStoragePath()}`); - console.log(` ├─ Database: ${this.cacheDb.getDbName()}`); - console.log(` └─ Cleaner: ${this.cacheCleaner?.isActive() ? 'Active' : 'Inactive'} (${(this.options.cacheConfig?.cleanupIntervalHours || 1)}h interval)`); + logger.log('info', `Cache Database: storage=${this.cacheDb.getStoragePath()}, db=${this.cacheDb.getDbName()}, cleaner=${this.cacheCleaner?.isActive() ? 'active' : 'inactive'} (${(this.options.cacheConfig?.cleanupIntervalHours || 1)}h interval)`); } - console.log('\n✅ All services are running\n'); + logger.log('info', 'All services are running'); } /** @@ -439,7 +393,7 @@ export class DcRouter { * Set up SmartProxy with direct configuration and automatic email routes */ private async setupSmartProxy(): Promise { - console.log('[DcRouter] Setting up SmartProxy...'); + logger.log('info', 'Setting up SmartProxy...'); let routes: plugins.smartproxy.IRouteConfig[] = []; let acmeConfig: plugins.smartproxy.IAcmeOptions | undefined; @@ -447,22 +401,20 @@ export class DcRouter { if (this.options.smartProxyConfig) { routes = this.options.smartProxyConfig.routes || []; acmeConfig = this.options.smartProxyConfig.acme; - console.log(`[DcRouter] Found ${routes.length} routes in config`); - console.log(`[DcRouter] ACME config present: ${!!acmeConfig}`); + logger.log('info', `Found ${routes.length} routes in config, ACME config present: ${!!acmeConfig}`); } // If email config exists, automatically add email routes if (this.options.emailConfig) { const emailRoutes = this.generateEmailRoutes(this.options.emailConfig); - console.log(`Email Routes are:`) - console.log(emailRoutes) + logger.log('debug', 'Email routes generated', { routes: JSON.stringify(emailRoutes) }); routes = [...routes, ...emailRoutes]; // Enable email routing through SmartProxy } // If DNS is configured, add DNS routes if (this.options.dnsNsDomains && this.options.dnsNsDomains.length > 0) { const dnsRoutes = this.generateDnsRoutes(); - console.log(`DNS Routes for nameservers ${this.options.dnsNsDomains.join(', ')}:`, dnsRoutes); + logger.log('debug', `DNS routes for nameservers ${this.options.dnsNsDomains.join(', ')}`, { routes: JSON.stringify(dnsRoutes) }); routes = [...routes, ...dnsRoutes]; } @@ -480,7 +432,7 @@ export class DcRouter { // Configure DNS challenge if available let challengeHandlers: any[] = []; if (this.options.dnsChallenge?.cloudflareApiKey) { - console.log('Configuring Cloudflare DNS challenge for ACME'); + logger.log('info', 'Configuring Cloudflare DNS challenge for ACME'); const cloudflareAccount = new plugins.cloudflare.CloudflareAccount(this.options.dnsChallenge.cloudflareApiKey); const dns01Handler = new plugins.smartacme.handlers.Dns01Handler(cloudflareAccount); challengeHandlers.push(dns01Handler); @@ -488,7 +440,7 @@ export class DcRouter { // If we have routes or need a basic SmartProxy instance, create it if (routes.length > 0 || this.options.smartProxyConfig) { - console.log('Setting up SmartProxy with combined configuration'); + logger.log('info', 'Setting up SmartProxy with combined configuration'); // Track cert entries loaded from cert store so we can populate certificateStatusMap after start const loadedCertEntries: Array<{domain: string; publicKey: string; validUntil?: number; validFrom?: number}> = []; @@ -537,7 +489,7 @@ export class DcRouter { // Stop old SmartAcme if it exists (e.g., during updateSmartProxyConfig) if (this.smartAcme) { await this.smartAcme.stop().catch(err => - console.error('[DcRouter] Error stopping old SmartAcme:', err) + logger.log('error', 'Error stopping old SmartAcme', { error: String(err) }) ); } this.smartAcme = new plugins.smartacme.SmartAcme({ @@ -600,25 +552,19 @@ export class DcRouter { } // Create SmartProxy instance - console.log('[DcRouter] Creating SmartProxy instance with config:', JSON.stringify({ - routeCount: smartProxyConfig.routes?.length, - acmeEnabled: smartProxyConfig.acme?.enabled, - acmeEmail: smartProxyConfig.acme?.email, - certProvisionFunction: !!smartProxyConfig.certProvisionFunction - }, null, 2)); + logger.log('info', `Creating SmartProxy instance: routes=${smartProxyConfig.routes?.length}, acme=${smartProxyConfig.acme?.enabled}, certProvisionFunction=${!!smartProxyConfig.certProvisionFunction}`); this.smartProxy = new plugins.smartproxy.SmartProxy(smartProxyConfig); // Set up event listeners this.smartProxy.on('error', (err) => { - console.error('[DcRouter] SmartProxy error:', err); - console.error('[DcRouter] Error stack:', err.stack); + logger.log('error', `SmartProxy error: ${err.message}`, { stack: err.stack }); }); // Always listen for certificate events — emitted by both ACME and certProvisionFunction paths // Events are keyed by domain for domain-centric certificate tracking this.smartProxy.on('certificate-issued', (event: plugins.smartproxy.ICertificateIssuedEvent) => { - console.log(`[DcRouter] Certificate issued for ${event.domain} via ${event.source}, expires ${event.expiryDate}`); + logger.log('info', `Certificate issued for ${event.domain} via ${event.source}, expires ${event.expiryDate}`); const routeNames = this.findRouteNamesForDomain(event.domain); this.certificateStatusMap.set(event.domain, { status: 'valid', routeNames, @@ -628,7 +574,7 @@ export class DcRouter { }); this.smartProxy.on('certificate-renewed', (event: plugins.smartproxy.ICertificateIssuedEvent) => { - console.log(`[DcRouter] Certificate renewed for ${event.domain} via ${event.source}, expires ${event.expiryDate}`); + logger.log('info', `Certificate renewed for ${event.domain} via ${event.source}, expires ${event.expiryDate}`); const routeNames = this.findRouteNamesForDomain(event.domain); this.certificateStatusMap.set(event.domain, { status: 'valid', routeNames, @@ -638,7 +584,7 @@ export class DcRouter { }); this.smartProxy.on('certificate-failed', (event: plugins.smartproxy.ICertificateFailedEvent) => { - console.error(`[DcRouter] Certificate failed for ${event.domain} (${event.source}):`, event.error); + logger.log('error', `Certificate failed for ${event.domain} (${event.source}): ${event.error}`); const routeNames = this.findRouteNamesForDomain(event.domain); this.certificateStatusMap.set(event.domain, { status: 'failed', routeNames, error: event.error, @@ -647,9 +593,9 @@ export class DcRouter { }); // Start SmartProxy - console.log('[DcRouter] Starting SmartProxy...'); + logger.log('info', 'Starting SmartProxy...'); await this.smartProxy.start(); - console.log('[DcRouter] SmartProxy started successfully'); + logger.log('info', 'SmartProxy started successfully'); // Populate certificateStatusMap for certs loaded from store at startup for (const entry of loadedCertEntries) { @@ -701,10 +647,10 @@ export class DcRouter { } } if (loadedCertEntries.length > 0) { - console.log(`[DcRouter] Populated certificate status for ${loadedCertEntries.length} store-loaded domain(s)`); + logger.log('info', `Populated certificate status for ${loadedCertEntries.length} store-loaded domain(s)`); } - console.log(`SmartProxy started with ${routes.length} routes`); + logger.log('info', `SmartProxy started with ${routes.length} routes`); } } @@ -907,7 +853,7 @@ export class DcRouter { } public async stop() { - console.log('Stopping DcRouter services...'); + logger.log('info', 'Stopping DcRouter services...'); await this.opsServer.stop(); @@ -918,36 +864,36 @@ export class DcRouter { this.cacheCleaner ? Promise.resolve(this.cacheCleaner.stop()) : Promise.resolve(), // Stop metrics manager if running - this.metricsManager ? this.metricsManager.stop().catch(err => console.error('Error stopping MetricsManager:', err)) : Promise.resolve(), + this.metricsManager ? this.metricsManager.stop().catch(err => logger.log('error', 'Error stopping MetricsManager', { error: String(err) })) : Promise.resolve(), // Stop unified email server if running - this.emailServer ? this.emailServer.stop().catch(err => console.error('Error stopping email server:', err)) : Promise.resolve(), + this.emailServer ? this.emailServer.stop().catch(err => logger.log('error', 'Error stopping email server', { error: String(err) })) : Promise.resolve(), // Stop SmartAcme if running - this.smartAcme ? this.smartAcme.stop().catch(err => console.error('Error stopping SmartAcme:', err)) : Promise.resolve(), + this.smartAcme ? this.smartAcme.stop().catch(err => logger.log('error', 'Error stopping SmartAcme', { error: String(err) })) : Promise.resolve(), // Stop HTTP SmartProxy if running - this.smartProxy ? this.smartProxy.stop().catch(err => console.error('Error stopping SmartProxy:', err)) : Promise.resolve(), + this.smartProxy ? this.smartProxy.stop().catch(err => logger.log('error', 'Error stopping SmartProxy', { error: String(err) })) : Promise.resolve(), // Stop DNS server if running this.dnsServer ? - this.dnsServer.stop().catch(err => console.error('Error stopping DNS server:', err)) : + this.dnsServer.stop().catch(err => logger.log('error', 'Error stopping DNS server', { error: String(err) })) : Promise.resolve(), // Stop RADIUS server if running this.radiusServer ? - this.radiusServer.stop().catch(err => console.error('Error stopping RADIUS server:', err)) : + this.radiusServer.stop().catch(err => logger.log('error', 'Error stopping RADIUS server', { error: String(err) })) : Promise.resolve(), // Stop Remote Ingress tunnel manager if running this.tunnelManager ? - this.tunnelManager.stop().catch(err => console.error('Error stopping TunnelManager:', err)) : + this.tunnelManager.stop().catch(err => logger.log('error', 'Error stopping TunnelManager', { error: String(err) })) : Promise.resolve() ]); // Stop cache database after other services (they may need it during shutdown) if (this.cacheDb) { - await this.cacheDb.stop().catch(err => console.error('Error stopping CacheDb:', err)); + await this.cacheDb.stop().catch(err => logger.log('error', 'Error stopping CacheDb', { error: String(err) })); } // Clear backoff cache in cert scheduler @@ -969,9 +915,9 @@ export class DcRouter { this.remoteIngressManager = undefined; this.certificateStatusMap.clear(); - console.log('All DcRouter services stopped'); + logger.log('info', 'All DcRouter services stopped'); } catch (error) { - console.error('Error during DcRouter shutdown:', error); + logger.log('error', 'Error during DcRouter shutdown', { error: String(error) }); throw error; } } @@ -998,7 +944,7 @@ export class DcRouter { // Start new SmartProxy with updated configuration (will include email routes if configured) await this.setupSmartProxy(); - console.log('SmartProxy configuration updated'); + logger.log('info', 'SmartProxy configuration updated'); } @@ -1091,7 +1037,7 @@ export class DcRouter { // Start email handling with new configuration await this.setupUnifiedEmailHandling(); - console.log('Unified email configuration updated'); + logger.log('info', 'Unified email configuration updated'); } /** @@ -1131,7 +1077,7 @@ export class DcRouter { this.emailServer.updateEmailRoutes(routes); } - console.log(`Email routes updated with ${routes.length} routes`); + logger.log('info', `Email routes updated with ${routes.length} routes`); } /** diff --git a/ts/monitoring/classes.metricsmanager.ts b/ts/monitoring/classes.metricsmanager.ts index 08bf951..5b6ee0f 100644 --- a/ts/monitoring/classes.metricsmanager.ts +++ b/ts/monitoring/classes.metricsmanager.ts @@ -2,9 +2,10 @@ import * as plugins from '../plugins.js'; import { DcRouter } from '../classes.dcrouter.js'; import { MetricsCache } from './classes.metricscache.js'; import { SecurityLogger, SecurityEventType } from '../security/classes.securitylogger.js'; +import { logger } from '../logger.js'; export class MetricsManager { - private logger: plugins.smartlog.Smartlog; + private metricsLogger: plugins.smartlog.Smartlog; private smartMetrics: plugins.smartmetrics.SmartMetrics; private dcRouter: DcRouter; private resetInterval?: NodeJS.Timeout; @@ -56,15 +57,15 @@ export class MetricsManager { constructor(dcRouter: DcRouter) { this.dcRouter = dcRouter; - // Create a new Smartlog instance for metrics - this.logger = new plugins.smartlog.Smartlog({ + // Create a Smartlog instance for SmartMetrics (requires its own instance) + this.metricsLogger = new plugins.smartlog.Smartlog({ logContext: { environment: 'production', runtime: 'node', zone: 'dcrouter-metrics', } }); - this.smartMetrics = new plugins.smartmetrics.SmartMetrics(this.logger, 'dcrouter'); + this.smartMetrics = new plugins.smartmetrics.SmartMetrics(this.metricsLogger, 'dcrouter'); // Initialize metrics cache with 500ms TTL this.metricsCache = new MetricsCache(500); } @@ -111,18 +112,18 @@ export class MetricsManager { } }, 60000); // Check every minute - this.logger.log('info', 'MetricsManager started'); + logger.log('info', 'MetricsManager started'); } - + public async stop(): Promise { // Clear the reset interval if (this.resetInterval) { clearInterval(this.resetInterval); this.resetInterval = undefined; } - + this.smartMetrics.stop(); - this.logger.log('info', 'MetricsManager stopped'); + logger.log('info', 'MetricsManager stopped'); } // Get server metrics from SmartMetrics and SmartProxy diff --git a/ts_web/00_commitinfo_data.ts b/ts_web/00_commitinfo_data.ts index 3e096c1..4c44112 100644 --- a/ts_web/00_commitinfo_data.ts +++ b/ts_web/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/dcrouter', - version: '7.4.0', + version: '7.4.1', description: 'A multifaceted routing service handling mail and SMS delivery functions.' } diff --git a/ts_web/elements/ops-view-logs.ts b/ts_web/elements/ops-view-logs.ts index 347cd37..11313b3 100644 --- a/ts_web/elements/ops-view-logs.ts +++ b/ts_web/elements/ops-view-logs.ts @@ -133,6 +133,17 @@ export class OpsViewLogs extends DeesElement { // Ensure the chart element has finished its own initialization await chartLog.updateComplete; + // Wait for xterm terminal to finish initializing (CDN load) + if (!chartLog.terminalReady) { + await new Promise((resolve) => { + const check = () => { + if (chartLog.terminalReady) { resolve(); return; } + setTimeout(check, 50); + }; + check(); + }); + } + const allEntries = this.getMappedLogEntries(); if (this.lastPushedCount === 0 && allEntries.length > 0) { // Initial load: push all entries