From a3f2f199b480f8f136a34bf4122a68a3f1699b12 Mon Sep 17 00:00:00 2001 From: Boki Date: Mon, 23 Jun 2025 11:34:58 -0400 Subject: [PATCH] switched all console logs to logger --- .../document_symbols_cache_v20-05-25.pkl | Bin 10333537 -> 10374072 bytes apps/stock/config/src/config-instance.ts | 170 +++++++++--------- .../src/handlers/qm/actions/session.action.ts | 2 +- apps/stock/data-ingestion/src/index.ts | 2 +- apps/stock/data-pipeline/src/index.ts | 2 +- apps/stock/web-api/src/index.ts | 2 +- libs/core/config/src/config-manager.ts | 5 +- .../src/registrations/cache.registration.ts | 8 +- libs/core/di/src/service-application.ts | 2 +- libs/core/di/src/utils/lifecycle.ts | 18 +- libs/core/types/src/handler-registry.ts | 6 +- libs/data/questdb/src/client.ts | 18 +- libs/services/queue/src/service-cache.ts | 8 +- libs/services/shutdown/src/shutdown.ts | 4 +- 14 files changed, 125 insertions(+), 122 deletions(-) diff --git a/.serena/cache/typescript/document_symbols_cache_v20-05-25.pkl b/.serena/cache/typescript/document_symbols_cache_v20-05-25.pkl index b5aa38e63806b1d86cbd7c59f1fd98227d9e06f1..8a3db7eed325d07c39901587426fc8549cf53f4e 100644 GIT binary patch delta 101923 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 delta 80506 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 nK&${!4x)w$KKp$X=0>|ioEiXPG>CE# | null = null; - -/** - * Initialize the stock application configuration - * @param serviceName - Optional service name to override port configuration - */ -export function initializeStockConfig(serviceName?: 'dataIngestion' | 'dataPipeline' | 'webApi'): StockAppConfig { - try { - if (!configInstance) { - configInstance = createAppConfig(stockAppSchema, { - configPath: path.join(__dirname, '../config'), - }); - } - - const config = configInstance.initialize(stockAppSchema); - - // If a service name is provided, override the service port - if (serviceName && config.services?.[serviceName]) { - const kebabName = serviceName.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, ''); - return { - ...config, - service: { - ...config.service, - port: config.services[serviceName].port, - name: serviceName, // Keep original for backward compatibility - serviceName: kebabName // Standard kebab-case name - } - }; - } - - return config; - } catch (error: any) { - console.error('Failed to initialize stock configuration:', error.message); - if (error.errors) { - console.error('Validation errors:', JSON.stringify(error.errors, null, 2)); - } - throw error; - } -} - -/** - * Get the current stock configuration - */ -export function getStockConfig(): StockAppConfig { - if (!configInstance) { - // Auto-initialize if not already done - return initializeStockConfig(); - } - return configInstance.get(); -} - -/** - * Get configuration for a specific service - */ -export function getServiceConfig(service: 'dataIngestion' | 'dataPipeline' | 'webApi') { - const config = getStockConfig(); - return config.services?.[service]; -} - -/** - * Get configuration for a specific provider - */ -export function getProviderConfig(provider: 'eod' | 'ib' | 'qm' | 'yahoo') { - const config = getStockConfig(); - return config.providers[provider]; -} - -/** - * Check if a feature is enabled - */ -export function isFeatureEnabled(feature: keyof StockAppConfig['features']): boolean { - const config = getStockConfig(); - return config.features[feature]; -} - -/** - * Reset configuration (useful for testing) - */ -export function resetStockConfig(): void { - configInstance = null; +import { ConfigManager, createAppConfig } from '@stock-bot/config'; +import { stockAppSchema, type StockAppConfig } from './schemas'; +import * as path from 'path'; +import { getLogger } from '@stock-bot/logger'; + +let configInstance: ConfigManager | null = null; + +/** + * Initialize the stock application configuration + * @param serviceName - Optional service name to override port configuration + */ +export function initializeStockConfig(serviceName?: 'dataIngestion' | 'dataPipeline' | 'webApi'): StockAppConfig { + try { + if (!configInstance) { + configInstance = createAppConfig(stockAppSchema, { + configPath: path.join(__dirname, '../config'), + }); + } + + const config = configInstance.initialize(stockAppSchema); + + // If a service name is provided, override the service port + if (serviceName && config.services?.[serviceName]) { + const kebabName = serviceName.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, ''); + return { + ...config, + service: { + ...config.service, + port: config.services[serviceName].port, + name: serviceName, // Keep original for backward compatibility + serviceName: kebabName // Standard kebab-case name + } + }; + } + + return config; + } catch (error: any) { + const logger = getLogger('stock-config'); + logger.error('Failed to initialize stock configuration:', error.message); + if (error.errors) { + logger.error('Validation errors:', error.errors); + } + throw error; + } +} + +/** + * Get the current stock configuration + */ +export function getStockConfig(): StockAppConfig { + if (!configInstance) { + // Auto-initialize if not already done + return initializeStockConfig(); + } + return configInstance.get(); +} + +/** + * Get configuration for a specific service + */ +export function getServiceConfig(service: 'dataIngestion' | 'dataPipeline' | 'webApi') { + const config = getStockConfig(); + return config.services?.[service]; +} + +/** + * Get configuration for a specific provider + */ +export function getProviderConfig(provider: 'eod' | 'ib' | 'qm' | 'yahoo') { + const config = getStockConfig(); + return config.providers[provider]; +} + +/** + * Check if a feature is enabled + */ +export function isFeatureEnabled(feature: keyof StockAppConfig['features']): boolean { + const config = getStockConfig(); + return config.features[feature]; +} + +/** + * Reset configuration (useful for testing) + */ +export function resetStockConfig(): void { + configInstance = null; } \ No newline at end of file diff --git a/apps/stock/data-ingestion/src/handlers/qm/actions/session.action.ts b/apps/stock/data-ingestion/src/handlers/qm/actions/session.action.ts index ff0df45..2c1feea 100644 --- a/apps/stock/data-ingestion/src/handlers/qm/actions/session.action.ts +++ b/apps/stock/data-ingestion/src/handlers/qm/actions/session.action.ts @@ -19,7 +19,7 @@ export async function checkSessions(handler: BaseHandler): Promise<{ // Check which session IDs need more sessions and queue creation jobs let queuedCount = 0; for (const [sessionType, sessionId] of Object.entries(QM_SESSION_IDS)) { - console.log(`Checking session ID: ${sessionId}`); + handler.logger.debug(`Checking session ID: ${sessionId}`); if (sessionManager.needsMoreSessions(sessionId)) { const currentCount = sessionManager.getSessions(sessionId).length; const neededSessions = SESSION_CONFIG.MAX_SESSIONS - currentCount; diff --git a/apps/stock/data-ingestion/src/index.ts b/apps/stock/data-ingestion/src/index.ts index 45aeeea..aa9920b 100644 --- a/apps/stock/data-ingestion/src/index.ts +++ b/apps/stock/data-ingestion/src/index.ts @@ -15,7 +15,7 @@ import { createRoutes } from './routes/create-routes'; // Initialize configuration with service-specific overrides const config = initializeStockConfig('dataIngestion'); -console.log('Data Ingestion Service Configuration:', JSON.stringify(config, null, 2)); + // Create service application const app = new ServiceApplication( diff --git a/apps/stock/data-pipeline/src/index.ts b/apps/stock/data-pipeline/src/index.ts index b6fe633..9b5f9db 100644 --- a/apps/stock/data-pipeline/src/index.ts +++ b/apps/stock/data-pipeline/src/index.ts @@ -18,7 +18,7 @@ import { setupServiceContainer } from './container-setup'; // Initialize configuration with service-specific overrides const config = initializeStockConfig('dataPipeline'); -console.log('Data Pipeline Service Configuration:', JSON.stringify(config, null, 2)); + // Create service application const app = new ServiceApplication( diff --git a/apps/stock/web-api/src/index.ts b/apps/stock/web-api/src/index.ts index d4f6663..4058aab 100644 --- a/apps/stock/web-api/src/index.ts +++ b/apps/stock/web-api/src/index.ts @@ -21,7 +21,7 @@ if (config.queue) { config.queue.delayWorkerStart = true; } -console.log('Web API Service Configuration:', JSON.stringify(config, null, 2)); + // Create service application const app = new ServiceApplication( diff --git a/libs/core/config/src/config-manager.ts b/libs/core/config/src/config-manager.ts index 8dc2dde..5da0b44 100644 --- a/libs/core/config/src/config-manager.ts +++ b/libs/core/config/src/config-manager.ts @@ -3,6 +3,7 @@ import { z } from 'zod'; import { EnvLoader } from './loaders/env.loader'; import { FileLoader } from './loaders/file.loader'; import { ConfigError, ConfigValidationError } from './errors'; +import { getLogger } from '@stock-bot/logger'; import type { ConfigLoader, ConfigManagerOptions, @@ -12,6 +13,7 @@ import type { } from './types'; export class ConfigManager> { + private readonly logger = getLogger('config-manager'); private config: T | null = null; private loaders: ConfigLoader[]; private environment: Environment; @@ -81,8 +83,7 @@ export class ConfigManager> { received: (err as any).received, })); - console.error('Configuration validation failed:'); - console.error(JSON.stringify(errorDetails, null, 2)); + this.logger.error('Configuration validation failed:', errorDetails); throw new ConfigValidationError('Configuration validation failed', error.errors); } diff --git a/libs/core/di/src/registrations/cache.registration.ts b/libs/core/di/src/registrations/cache.registration.ts index 3c4737a..a9d7574 100644 --- a/libs/core/di/src/registrations/cache.registration.ts +++ b/libs/core/di/src/registrations/cache.registration.ts @@ -9,7 +9,7 @@ export function registerCacheServices( ): void { if (config.redis.enabled) { container.register({ - cache: asFunction(() => { + cache: asFunction(({ logger }) => { const { createServiceCache } = require('@stock-bot/queue'); // Get standardized service name from config const serviceName = config.service?.serviceName || config.service?.name || 'unknown'; @@ -20,11 +20,11 @@ export function registerCacheServices( port: config.redis.port, password: config.redis.password, db: config.redis.db, // This will be overridden by ServiceCache - }); + }, { logger }); }).singleton(), // Also provide global cache for shared data - globalCache: asFunction(() => { + globalCache: asFunction(({ logger }) => { const { createServiceCache } = require('@stock-bot/queue'); const serviceName = config.service?.serviceName || config.service?.name || 'unknown'; @@ -32,7 +32,7 @@ export function registerCacheServices( host: config.redis.host, port: config.redis.port, password: config.redis.password, - }, { global: true }); + }, { global: true, logger }); }).singleton(), }); } else { diff --git a/libs/core/di/src/service-application.ts b/libs/core/di/src/service-application.ts index 57d5547..a6e6a17 100644 --- a/libs/core/di/src/service-application.ts +++ b/libs/core/di/src/service-application.ts @@ -313,7 +313,7 @@ export class ServiceApplication { } } catch (error) { - console.error('DETAILED ERROR:', error); + this.logger.error('DETAILED ERROR:', error); this.logger.error('Failed to start service', { error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, diff --git a/libs/core/di/src/utils/lifecycle.ts b/libs/core/di/src/utils/lifecycle.ts index 528bb49..b415b85 100644 --- a/libs/core/di/src/utils/lifecycle.ts +++ b/libs/core/di/src/utils/lifecycle.ts @@ -1,5 +1,6 @@ import type { AwilixContainer } from 'awilix'; import type { ServiceDefinitions } from '../container/types'; +import { getLogger } from '@stock-bot/logger'; interface ServiceWithLifecycle { connect?: () => Promise; @@ -10,6 +11,7 @@ interface ServiceWithLifecycle { } export class ServiceLifecycleManager { + private readonly logger = getLogger('service-lifecycle'); private readonly services = [ { name: 'cache', key: 'cache' as const }, { name: 'mongoClient', key: 'mongoClient' as const }, @@ -40,7 +42,7 @@ export class ServiceLifecycleManager { } await Promise.all(initPromises); - console.log('✅ All services initialized successfully'); + this.logger.info('All services initialized successfully'); } async shutdownServices(container: AwilixContainer): Promise { @@ -56,20 +58,20 @@ export class ServiceLifecycleManager { } await Promise.allSettled(shutdownPromises); - console.log('✅ All services shut down'); + this.logger.info('All services shut down'); } private async initializeService(name: string, service: ServiceWithLifecycle): Promise { try { if (typeof service.connect === 'function') { await service.connect(); - console.log(`✅ ${name} connected`); + this.logger.info(`${name} connected`); } else if (typeof service.initialize === 'function') { await service.initialize(); - console.log(`✅ ${name} initialized`); + this.logger.info(`${name} initialized`); } } catch (error) { - console.error(`❌ Failed to initialize ${name}:`, error); + this.logger.error(`Failed to initialize ${name}:`, error); throw error; } } @@ -83,9 +85,9 @@ export class ServiceLifecycleManager { } else if (typeof service.shutdown === 'function') { await service.shutdown(); } - console.log(`✅ ${name} shut down`); + this.logger.info(`${name} shut down`); } catch (error) { - console.error(`❌ Error shutting down ${name}:`, error); + this.logger.error(`Error shutting down ${name}:`, error); } } @@ -94,4 +96,4 @@ export class ServiceLifecycleManager { setTimeout(() => reject(new Error(message)), timeout); }); } -} \ No newline at end of file +} diff --git a/libs/core/types/src/handler-registry.ts b/libs/core/types/src/handler-registry.ts index fa7710d..b300431 100644 --- a/libs/core/types/src/handler-registry.ts +++ b/libs/core/types/src/handler-registry.ts @@ -9,8 +9,10 @@ import type { JobHandler, ScheduledJob, } from './handlers'; +import { getLogger } from '@stock-bot/logger'; class HandlerRegistry { + private readonly logger = getLogger('handler-registry'); private handlers = new Map(); private handlerSchedules = new Map(); @@ -18,7 +20,7 @@ class HandlerRegistry { * Register a handler with its operations (simple config) */ register(handlerName: string, config: HandlerConfig): void { - console.log(`Registering handler: ${handlerName}`, { + this.logger.info(`Registering handler: ${handlerName}`, { operations: Object.keys(config), }); @@ -29,7 +31,7 @@ class HandlerRegistry { * Register a handler with scheduled jobs (enhanced config) */ registerWithSchedule(config: HandlerConfigWithSchedule): void { - console.log(`Registering handler with schedule: ${config.name}`, { + this.logger.info(`Registering handler with schedule: ${config.name}`, { operations: Object.keys(config.operations), scheduledJobs: config.scheduledJobs?.length || 0, }); diff --git a/libs/data/questdb/src/client.ts b/libs/data/questdb/src/client.ts index 8fac7a9..e7ead4b 100644 --- a/libs/data/questdb/src/client.ts +++ b/libs/data/questdb/src/client.ts @@ -38,11 +38,6 @@ export class QuestDBClient { }; // Debug: log the received config - console.log('DEBUG: QuestDB client constructor called with config:', { - ...config, - user: config.user || '[NOT PROVIDED]', - password: config.password ? '[PROVIDED]' : '[NOT PROVIDED]', - }); this.logger.debug('QuestDB client created with config:', { ...config, user: config.user || '[NOT PROVIDED]', @@ -435,27 +430,24 @@ export class QuestDBClient { // Only add user/password if they are provided if (this.config.user) { - console.log('DEBUG: Adding user to QuestDB pool config:', this.config.user); + this.logger.debug('Adding user to QuestDB pool config:', this.config.user); config.user = this.config.user; } else { - console.log('DEBUG: No user provided for QuestDB connection'); + this.logger.debug('No user provided for QuestDB connection'); } if (this.config.password) { - console.log('DEBUG: Adding password to QuestDB pool config'); + this.logger.debug('Adding password to QuestDB pool config'); config.password = this.config.password; } else { - console.log('DEBUG: No password provided for QuestDB connection'); + this.logger.debug('No password provided for QuestDB connection'); } - console.log('DEBUG: Final QuestDB pool config:', { - ...config, - password: config.password ? '[REDACTED]' : undefined, - }); + this.logger.debug('Final QuestDB pool config:', { ...config, password: config.password ? '[REDACTED]' : undefined, diff --git a/libs/services/queue/src/service-cache.ts b/libs/services/queue/src/service-cache.ts index f8ade29..5d3aae4 100644 --- a/libs/services/queue/src/service-cache.ts +++ b/libs/services/queue/src/service-cache.ts @@ -13,7 +13,8 @@ export class ServiceCache implements CacheProvider { constructor( serviceName: string, redisConfig: RedisConfig, - isGlobalCache: boolean = false + isGlobalCache: boolean = false, + logger?: any ) { // Get service configuration const serviceConfig = getServiceConfig(serviceName); @@ -42,6 +43,7 @@ export class ServiceCache implements CacheProvider { db, }, keyPrefix: prefix + ':', + logger, }; this.cache = createCache(cacheConfig); @@ -161,7 +163,7 @@ export class ServiceCache implements CacheProvider { export function createServiceCache( serviceName: string, redisConfig: RedisConfig, - options: { global?: boolean } = {} + options: { global?: boolean; logger?: any } = {} ): ServiceCache { - return new ServiceCache(serviceName, redisConfig, options.global); + return new ServiceCache(serviceName, redisConfig, options.global, options.logger); } \ No newline at end of file diff --git a/libs/services/shutdown/src/shutdown.ts b/libs/services/shutdown/src/shutdown.ts index 36e0276..d31a853 100644 --- a/libs/services/shutdown/src/shutdown.ts +++ b/libs/services/shutdown/src/shutdown.ts @@ -14,6 +14,7 @@ import type { ShutdownOptions, ShutdownResult, } from './types'; +import { getLogger } from '@stock-bot/logger'; // Global flag that works across all processes/workers declare global { @@ -22,6 +23,7 @@ declare global { export class Shutdown { private static instance: Shutdown | null = null; + private readonly logger = getLogger('shutdown'); private isShuttingDown = false; private signalReceived = false; // Track if shutdown signal was received private shutdownTimeout = 30000; // 30 seconds default @@ -200,7 +202,7 @@ export class Shutdown { } catch (error) { failed++; if (name) { - console.error(`✗ Shutdown failed: ${name} (priority: ${priority})`, error); + this.logger.error(`Shutdown failed: ${name} (priority: ${priority})`, error); } } }