diff --git a/lib/services/create-services.server.ts b/lib/services/create-services.server.ts index 90162109..f3ac79f5 100644 --- a/lib/services/create-services.server.ts +++ b/lib/services/create-services.server.ts @@ -12,17 +12,62 @@ let singleton: AppServices | undefined; export function getServerAppServices(): AppServices { if (singleton) return singleton; + // Create logger first to log initialization + const logger = new PinoLoggerService('server'); + + // Log environment variables (safely masked) + const envLog = { + // Mask sensitive values - show only last 4 characters or '***' for empty + NEXT_PUBLIC_UMAMI_WEBSITE_ID: process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID + ? `***${process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID.slice(-4)}` + : 'not set', + SENTRY_DSN: process.env.SENTRY_DSN + ? `***${process.env.SENTRY_DSN.slice(-4)}` + : 'not set', + REDIS_URL: process.env.REDIS_URL + ? `***${process.env.REDIS_URL.slice(-4)}` + : 'not set', + REDIS_KEY_PREFIX: process.env.REDIS_KEY_PREFIX ?? 'klz:', + LOG_LEVEL: process.env.LOG_LEVEL ?? 'info', + NODE_ENV: process.env.NODE_ENV ?? 'not set', + // Safe to show - no sensitive data + NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL ?? 'not set', + }; + + logger.info('Initializing server application services', { + environment: envLog, + timestamp: new Date().toISOString(), + }); + const umamiEnabled = Boolean(process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID); const sentryEnabled = Boolean(process.env.SENTRY_DSN); + logger.info('Service configuration', { + umamiEnabled, + sentryEnabled, + redisEnabled: Boolean(process.env.REDIS_URL), + }); + const analytics = umamiEnabled ? new UmamiAnalyticsService({ enabled: true }) : new NoopAnalyticsService(); + if (umamiEnabled) { + logger.info('Umami analytics service initialized'); + } else { + logger.info('Noop analytics service initialized (analytics disabled)'); + } + const errors = sentryEnabled ? new GlitchtipErrorReportingService({ enabled: true }) : new NoopErrorReportingService(); + if (sentryEnabled) { + logger.info('GlitchTip error reporting service initialized'); + } else { + logger.info('Noop error reporting service initialized (error reporting disabled)'); + } + const redisUrl = process.env.REDIS_URL; const cache = redisUrl ? new RedisCacheService({ @@ -31,9 +76,23 @@ export function getServerAppServices(): AppServices { }) : new MemoryCacheService(); - const logger = new PinoLoggerService('server'); + if (redisUrl) { + logger.info('Redis cache service initialized', { + keyPrefix: process.env.REDIS_KEY_PREFIX ?? 'klz:' + }); + } else { + logger.info('Memory cache service initialized (Redis not configured)'); + } + + logger.info('Pino logger service initialized', { + name: 'server', + level: process.env.LOG_LEVEL ?? 'info', + }); singleton = new AppServices(analytics, errors, cache, logger); + + logger.info('All application services initialized successfully'); + return singleton; } diff --git a/lib/services/create-services.ts b/lib/services/create-services.ts index 39ed75f8..7cafa486 100644 --- a/lib/services/create-services.ts +++ b/lib/services/create-services.ts @@ -68,11 +68,56 @@ export function getAppServices(): AppServices { // Return cached instance if available if (singleton) return singleton; + // Create logger first to log initialization + const logger = + typeof window === 'undefined' + ? new PinoLoggerService('server') + : new NoopLoggerService(); + + // Log environment variables (safely masked) + const envLog = { + // Mask sensitive values - show only last 4 characters or '***' for empty + NEXT_PUBLIC_UMAMI_WEBSITE_ID: process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID + ? `***${process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID.slice(-4)}` + : 'not set', + NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN + ? `***${process.env.NEXT_PUBLIC_SENTRY_DSN.slice(-4)}` + : 'not set', + SENTRY_DSN: process.env.SENTRY_DSN + ? `***${process.env.SENTRY_DSN.slice(-4)}` + : 'not set', + // Safe to show - no sensitive data + NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL ?? 'not set', + NODE_ENV: process.env.NODE_ENV ?? 'not set', + }; + + // Log initialization + if (typeof window === 'undefined') { + // Server-side + logger.info('Initializing server application services', { + environment: envLog, + timestamp: new Date().toISOString(), + }); + } else { + // Client-side + logger.info('Initializing client application services', { + environment: envLog, + timestamp: new Date().toISOString(), + }); + } + // Determine which services to enable based on environment variables const umamiEnabled = Boolean(process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID); const sentryClientEnabled = Boolean(process.env.NEXT_PUBLIC_SENTRY_DSN); const sentryServerEnabled = Boolean(process.env.SENTRY_DSN); + logger.info('Service configuration', { + umamiEnabled, + sentryClientEnabled, + sentryServerEnabled, + isServer: typeof window === 'undefined', + }); + // Create analytics service (Umami or no-op) // Use dynamic import to avoid importing server-only code in client components const analytics = umamiEnabled @@ -82,6 +127,12 @@ export function getAppServices(): AppServices { })() : new NoopAnalyticsService(); + if (umamiEnabled) { + logger.info('Umami analytics service initialized'); + } else { + logger.info('Noop analytics service initialized (analytics disabled)'); + } + // Create error reporting service (GlitchTip/Sentry or no-op) // Server-side and client-side have separate DSNs const errors = @@ -93,17 +144,29 @@ export function getAppServices(): AppServices { ? new GlitchtipErrorReportingService({ enabled: true }) : new NoopErrorReportingService(); + if (typeof window === 'undefined' && sentryServerEnabled) { + logger.info('GlitchTip error reporting service initialized (server)'); + } else if (typeof window !== 'undefined' && sentryClientEnabled) { + logger.info('GlitchTip error reporting service initialized (client)'); + } else { + logger.info('Noop error reporting service initialized (error reporting disabled)'); + } + // IMPORTANT: This module is imported by client components. // Do not import Node-only modules (like the `redis` client) here. // Use [`getServerAppServices()`](lib/services/create-services.server.ts:1) on the server. const cache = new MemoryCacheService(); + logger.info('Memory cache service initialized'); - const logger = - typeof window === 'undefined' - ? new PinoLoggerService('server') - : new NoopLoggerService(); + logger.info('Pino logger service initialized', { + name: typeof window === 'undefined' ? 'server' : 'client', + level: process.env.LOG_LEVEL ?? 'info', + }); // Create and cache the singleton singleton = new AppServices(analytics, errors, cache, logger); + + logger.info('All application services initialized successfully'); + return singleton; }