@omen.foundation/node-microservice-runtime 0.1.43 → 0.1.45

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.
package/src/logger.ts CHANGED
@@ -41,6 +41,7 @@ interface LoggerFactoryOptions {
41
41
  destinationPath?: string;
42
42
  serviceName?: string; // Service name for log filtering (e.g., "ExampleNodeService")
43
43
  qualifiedServiceName?: string; // Full qualified service name (e.g., "micro_ExampleNodeService")
44
+ otlpEndpoint?: string; // OTLP endpoint if collector is already set up (avoids re-discovery)
44
45
  }
45
46
 
46
47
  /**
@@ -560,6 +561,10 @@ function initializeOtlpSync(
560
561
  name: 'beamable-otlp-init',
561
562
  level: 'info',
562
563
  }, process.stdout);
564
+
565
+ // If collector was already set up via setupCollectorBeforeLogging,
566
+ // discoverOrStartCollector will find it and use a shorter timeout.
567
+ // If not, it will start it (with full timeout).
563
568
 
564
569
  // Use deasync to wait synchronously for the async initialization
565
570
  // This allows the event loop to process while we wait, enabling async operations
@@ -640,15 +645,75 @@ export function createLogger(env: EnvironmentConfig, options: LoggerFactoryOptio
640
645
  const usePrettyLogs = shouldUsePrettyLogs();
641
646
 
642
647
  // Initialize OTLP synchronously BEFORE creating the logger
643
- // This ensures all logs from this point forward are captured via OTLP
644
- // Similar to C# microservices which configure logging early in startup
645
- // CRITICAL: We wait for collector to be fully initialized and ready before proceeding
646
- const otlpProvider = initializeOtlpSync(
647
- options.serviceName,
648
- options.qualifiedServiceName,
649
- env,
650
- 60000 // 60 second timeout to allow collector download, startup, and readiness verification
651
- );
648
+ // If otlpEndpoint is provided (collector already set up), create provider directly
649
+ // Otherwise, try to discover/start collector (with timeout)
650
+ let otlpProvider: LoggerProvider | null = null;
651
+
652
+ if (options.otlpEndpoint) {
653
+ // Collector is already set up, create OTLP provider directly without discovery/startup
654
+ // Set endpoint in env temporarily so initializeOtlpLogging uses it directly
655
+ const originalEndpoint = process.env.BEAM_OTEL_EXPORTER_OTLP_ENDPOINT;
656
+ process.env.BEAM_OTEL_EXPORTER_OTLP_ENDPOINT = options.otlpEndpoint;
657
+
658
+ // Create OTLP provider directly (collector is already running, so this should be fast)
659
+ const initLogger = pino({
660
+ name: 'beamable-otlp-init',
661
+ level: 'info',
662
+ }, process.stdout);
663
+
664
+ // Create provider synchronously - since endpoint is explicitly provided,
665
+ // initializeOtlpLogging will skip discovery and create provider directly
666
+ let provider: LoggerProvider | null = null;
667
+ let completed = false;
668
+
669
+ initializeOtlpLogging(
670
+ options.serviceName,
671
+ options.qualifiedServiceName,
672
+ env,
673
+ initLogger
674
+ ).then((result) => {
675
+ provider = result;
676
+ completed = true;
677
+ if (result) {
678
+ initLogger.info(`[OTLP] OTLP provider created using existing collector at ${options.otlpEndpoint}`);
679
+ }
680
+ }).catch((error) => {
681
+ initLogger.error(`[OTLP] Failed to create OTLP provider: ${error instanceof Error ? error.message : String(error)}`);
682
+ completed = true;
683
+ provider = null;
684
+ });
685
+
686
+ // Wait briefly for provider creation (should be very fast, < 1 second)
687
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
688
+ const deasync = require('deasync');
689
+ const startTime = Date.now();
690
+ const timeout = 2000; // 2 second timeout (should be fast since no discovery needed)
691
+
692
+ deasync.loopWhile(() => {
693
+ if (Date.now() - startTime >= timeout) {
694
+ return false;
695
+ }
696
+ return !completed;
697
+ });
698
+
699
+ otlpProvider = provider;
700
+
701
+ // Restore original endpoint if it existed
702
+ if (originalEndpoint !== undefined) {
703
+ process.env.BEAM_OTEL_EXPORTER_OTLP_ENDPOINT = originalEndpoint;
704
+ } else {
705
+ delete process.env.BEAM_OTEL_EXPORTER_OTLP_ENDPOINT;
706
+ }
707
+ } else {
708
+ // No endpoint provided - try to discover/start collector (full initialization)
709
+ // This ensures all logs from this point forward are captured via OTLP
710
+ otlpProvider = initializeOtlpSync(
711
+ options.serviceName,
712
+ options.qualifiedServiceName,
713
+ env,
714
+ 60000 // 60 second timeout to allow collector download, startup, and readiness verification
715
+ );
716
+ }
652
717
 
653
718
  // Shared reference for OTLP logger provider
654
719
  const otlpProviderRef: { provider: LoggerProvider | null } = { provider: otlpProvider };
package/src/runtime.ts CHANGED
@@ -4,6 +4,8 @@ import { GatewayRequester } from './requester.js';
4
4
  import { AuthManager } from './auth.js';
5
5
  import { createLogger } from './logger.js';
6
6
  import { loadEnvironmentConfig } from './env.js';
7
+ import { setupCollectorBeforeLogging } from './collector-manager.js';
8
+ import pino from 'pino';
7
9
  import { listRegisteredServices, getServiceOptions, getConfigureServicesHandlers, getInitializeServicesHandlers } from './decorators.js';
8
10
  import { generateOpenApiDocument } from './docs.js';
9
11
  import { VERSION } from './index.js';
@@ -60,7 +62,15 @@ export class MicroserviceRuntime {
60
62
  constructor(env?: EnvironmentConfig) {
61
63
  this.env = env ?? loadEnvironmentConfig();
62
64
 
63
- // Get registered services first to extract service name for logger
65
+ // STEP 1: Create minimal console logger for startup messages (before collector setup)
66
+ // This ensures we have logging available immediately, even before collector is ready
67
+ const startupLogger = pino({
68
+ name: 'beamable-runtime-startup',
69
+ level: 'info',
70
+ }, process.stdout);
71
+ startupLogger.info('Starting Beamable Node microservice runtime.');
72
+
73
+ // STEP 2: Get registered services to extract service name
64
74
  const registered = listRegisteredServices();
65
75
  if (registered.length === 0) {
66
76
  throw new Error('No microservices registered. Use the @Microservice decorator to register at least one class.');
@@ -70,10 +80,27 @@ export class MicroserviceRuntime {
70
80
  const primaryService = registered[0];
71
81
  const qualifiedServiceName = `micro_${primaryService.qualifiedName}`;
72
82
 
83
+ // STEP 3: Setup collector BEFORE creating the main logger
84
+ // This ensures all logs from the main logger are captured via OTLP
85
+ startupLogger.info('Setting up OpenTelemetry collector...');
86
+ const otlpEndpoint = setupCollectorBeforeLogging(
87
+ this.env,
88
+ 60000 // 60 second timeout
89
+ );
90
+
91
+ if (otlpEndpoint) {
92
+ startupLogger.info(`Collector ready at ${otlpEndpoint}, creating main logger...`);
93
+ } else {
94
+ startupLogger.warn('Collector setup did not complete, continuing without OTLP logging');
95
+ }
96
+
97
+ // STEP 4: Create main logger (collector is now ready, so all logs will be captured)
98
+ // Pass the OTLP endpoint to skip re-discovery/startup
73
99
  this.logger = createLogger(this.env, {
74
100
  name: 'beamable-node-microservice',
75
101
  serviceName: primaryService.name,
76
102
  qualifiedServiceName: qualifiedServiceName,
103
+ otlpEndpoint: otlpEndpoint || undefined, // Pass endpoint if collector was set up
77
104
  });
78
105
  this.serviceManager = new BeamableServiceManager(this.env, this.logger);
79
106