@omen.foundation/node-microservice-runtime 0.1.18 → 0.1.20

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/dist/logger.cjs CHANGED
@@ -42,6 +42,7 @@ const api_logs_1 = require("@opentelemetry/api-logs");
42
42
  const sdk_logs_1 = require("@opentelemetry/sdk-logs");
43
43
  const exporter_logs_otlp_http_1 = require("@opentelemetry/exporter-logs-otlp-http");
44
44
  const resources_1 = require("@opentelemetry/resources");
45
+ const collector_manager_js_1 = require("./collector-manager.js");
45
46
  function getRequire() {
46
47
  if (typeof require !== 'undefined' && typeof require.main !== 'undefined') {
47
48
  return require;
@@ -69,14 +70,14 @@ function mapPinoLevelToBeamableLevel(level) {
69
70
  return 'Info';
70
71
  }
71
72
  }
72
- function initializeOtlpLogging(serviceName, qualifiedServiceName, env) {
73
+ async function initializeOtlpLogging(serviceName, qualifiedServiceName, env, logger) {
73
74
  const otlpEndpoint = process.env.BEAM_OTEL_EXPORTER_OTLP_ENDPOINT
74
75
  || process.env.OTEL_EXPORTER_OTLP_ENDPOINT
75
76
  || (process.env.OTEL_EXPORTER_OTLP_ENDPOINT_LOGS ? process.env.OTEL_EXPORTER_OTLP_ENDPOINT_LOGS : null);
76
77
  const isInDocker = process.env.IS_LOCAL !== '1' && process.env.IS_LOCAL !== 'true';
77
78
  const standardOtelEnabled = isInDocker && !process.env.BEAM_DISABLE_STANDARD_OTEL;
78
79
  if (!otlpEndpoint && !standardOtelEnabled) {
79
- return null;
80
+ return Promise.resolve(null);
80
81
  }
81
82
  try {
82
83
  const resourceAttributes = {};
@@ -97,11 +98,27 @@ function initializeOtlpLogging(serviceName, qualifiedServiceName, env) {
97
98
  resourceAttributes['beam.routing_key'] = String(env.routingKey);
98
99
  }
99
100
  let endpointUrl = otlpEndpoint;
101
+ if (!endpointUrl && standardOtelEnabled && logger) {
102
+ try {
103
+ const discoveredEndpoint = await (0, collector_manager_js_1.discoverOrStartCollector)(logger, standardOtelEnabled);
104
+ if (discoveredEndpoint) {
105
+ endpointUrl = discoveredEndpoint;
106
+ }
107
+ else {
108
+ console.error('[OTLP] Standard OTLP is enabled but could not discover or start collector.');
109
+ return Promise.resolve(null);
110
+ }
111
+ }
112
+ catch (error) {
113
+ console.error('[OTLP] Failed to discover/start collector:', error instanceof Error ? error.message : String(error));
114
+ return Promise.resolve(null);
115
+ }
116
+ }
100
117
  if (!endpointUrl) {
101
118
  if (standardOtelEnabled) {
102
- console.error('[OTLP] Standard OTLP is enabled but no endpoint configured. Set BEAM_OTEL_EXPORTER_OTLP_ENDPOINT to enable OTLP logging.');
119
+ console.error('[OTLP] Standard OTLP is enabled but no endpoint available. Set BEAM_OTEL_EXPORTER_OTLP_ENDPOINT to enable OTLP logging.');
103
120
  }
104
- return null;
121
+ return Promise.resolve(null);
105
122
  }
106
123
  const finalEndpointUrl = endpointUrl.includes('/v1/logs')
107
124
  ? endpointUrl
@@ -122,17 +139,17 @@ function initializeOtlpLogging(serviceName, qualifiedServiceName, env) {
122
139
  });
123
140
  api_logs_1.logs.setGlobalLoggerProvider(loggerProvider);
124
141
  console.error(`[OTLP] OpenTelemetry logging initialized. Endpoint: ${finalEndpointUrl}, Service: ${serviceName || 'unknown'}`);
125
- return loggerProvider;
142
+ return Promise.resolve(loggerProvider);
126
143
  }
127
144
  catch (error) {
128
145
  console.error('[OTLP] Failed to initialize OTLP logging:', error instanceof Error ? error.message : String(error));
129
146
  if (error instanceof Error && error.stack) {
130
147
  console.error('[OTLP] Stack trace:', error.stack);
131
148
  }
132
- return null;
149
+ return Promise.resolve(null);
133
150
  }
134
151
  }
135
- function createBeamableLogFormatter(serviceName, qualifiedServiceName, otlpLoggerProvider) {
152
+ function createBeamableLogFormatter(serviceName, qualifiedServiceName, otlpProviderRef) {
136
153
  return new node_stream_1.Transform({
137
154
  objectMode: false,
138
155
  transform(chunk, _encoding, callback) {
@@ -245,9 +262,9 @@ function createBeamableLogFormatter(serviceName, qualifiedServiceName, otlpLogge
245
262
  otelFields['ResourceAttributes'] = resourceAttributes;
246
263
  otelFields['LogAttributes'] = logAttributes;
247
264
  Object.assign(beamableLog, otelFields);
248
- if (otlpLoggerProvider) {
265
+ if (otlpProviderRef === null || otlpProviderRef === void 0 ? void 0 : otlpProviderRef.provider) {
249
266
  try {
250
- const otlpLogger = otlpLoggerProvider.getLogger(serviceName || 'beamable-node-runtime', undefined, {
267
+ const otlpLogger = otlpProviderRef.provider.getLogger(serviceName || 'beamable-node-runtime', undefined, {
251
268
  schemaUrl: undefined,
252
269
  });
253
270
  const severityNumberMap = {
@@ -287,17 +304,33 @@ function createBeamableLogFormatter(serviceName, qualifiedServiceName, otlpLogge
287
304
  });
288
305
  }
289
306
  function createLogger(env, options = {}) {
290
- var _a, _b, _c;
307
+ var _a, _b, _c, _d;
291
308
  const configuredDestination = (_a = options.destinationPath) !== null && _a !== void 0 ? _a : process.env.LOG_PATH;
292
309
  const usePrettyLogs = shouldUsePrettyLogs();
293
- const otlpLoggerProvider = initializeOtlpLogging(options.serviceName, options.qualifiedServiceName, env);
310
+ const otlpProviderRef = { provider: null };
311
+ const isInDocker = process.env.IS_LOCAL !== '1' && process.env.IS_LOCAL !== 'true';
312
+ const standardOtelEnabled = isInDocker && !process.env.BEAM_DISABLE_STANDARD_OTEL;
313
+ if (standardOtelEnabled || process.env.BEAM_OTEL_EXPORTER_OTLP_ENDPOINT) {
314
+ const tempLogger = (0, pino_1.default)({
315
+ name: (_b = options.name) !== null && _b !== void 0 ? _b : 'beamable-node-runtime',
316
+ level: env.logLevel,
317
+ }, process.stdout);
318
+ initializeOtlpLogging(options.serviceName, options.qualifiedServiceName, env, tempLogger).then((provider) => {
319
+ otlpProviderRef.provider = provider;
320
+ if (provider) {
321
+ tempLogger.info('[OTLP] OpenTelemetry logging initialized successfully');
322
+ }
323
+ }).catch((error) => {
324
+ tempLogger.error(`[OTLP] Failed to initialize: ${error instanceof Error ? error.message : String(error)}`);
325
+ });
326
+ }
294
327
  const pinoOptions = {
295
- name: (_b = options.name) !== null && _b !== void 0 ? _b : 'beamable-node-runtime',
328
+ name: (_c = options.name) !== null && _c !== void 0 ? _c : 'beamable-node-runtime',
296
329
  level: env.logLevel,
297
330
  base: {
298
331
  cid: env.cid,
299
332
  pid: env.pid,
300
- routingKey: (_c = env.routingKey) !== null && _c !== void 0 ? _c : null,
333
+ routingKey: (_d = env.routingKey) !== null && _d !== void 0 ? _d : null,
301
334
  sdkVersionExecution: env.sdkVersionExecution,
302
335
  serviceName: options.serviceName,
303
336
  qualifiedServiceName: options.qualifiedServiceName,
@@ -310,7 +343,7 @@ function createLogger(env, options = {}) {
310
343
  };
311
344
  if (!configuredDestination || configuredDestination === '-' || configuredDestination === 'stdout' || configuredDestination === 'console') {
312
345
  if (!usePrettyLogs) {
313
- const beamableFormatter = createBeamableLogFormatter(options.serviceName, options.qualifiedServiceName, otlpLoggerProvider);
346
+ const beamableFormatter = createBeamableLogFormatter(options.serviceName, options.qualifiedServiceName, otlpProviderRef);
314
347
  beamableFormatter.pipe(process.stdout);
315
348
  return (0, pino_1.default)(pinoOptions, beamableFormatter);
316
349
  }
@@ -333,7 +366,7 @@ function createLogger(env, options = {}) {
333
366
  }
334
367
  const resolvedDestination = configuredDestination === 'temp' ? (0, env_js_1.ensureWritableTempDirectory)() : configuredDestination;
335
368
  if (!usePrettyLogs) {
336
- const beamableFormatter = createBeamableLogFormatter(options.serviceName, options.qualifiedServiceName, otlpLoggerProvider);
369
+ const beamableFormatter = createBeamableLogFormatter(options.serviceName, options.qualifiedServiceName, otlpProviderRef);
337
370
  const fileStream = (0, pino_1.destination)({ dest: resolvedDestination, mkdir: true, append: true, sync: false });
338
371
  beamableFormatter.pipe(fileStream);
339
372
  return (0, pino_1.default)(pinoOptions, beamableFormatter);
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAa,EAAe,KAAK,MAAM,EAAsB,MAAM,MAAM,CAAC;AAI1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AA+BpD,UAAU,oBAAoB;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AA4XD,wBAAgB,YAAY,CAAC,GAAG,EAAE,iBAAiB,EAAE,OAAO,GAAE,oBAAyB,GAAG,MAAM,CAuF/F"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAa,EAAe,KAAK,MAAM,EAAsB,MAAM,MAAM,CAAC;AAI1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAiCpD,UAAU,oBAAoB;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AA0YD,wBAAgB,YAAY,CAAC,GAAG,EAAE,iBAAiB,EAAE,OAAO,GAAE,oBAAyB,GAAG,MAAM,CAgH/F"}
package/dist/logger.js CHANGED
@@ -6,6 +6,7 @@ import { logs } from '@opentelemetry/api-logs';
6
6
  import { LoggerProvider, SimpleLogRecordProcessor } from '@opentelemetry/sdk-logs';
7
7
  import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
8
8
  import { resourceFromAttributes, defaultResource } from '@opentelemetry/resources';
9
+ import { discoverOrStartCollector } from './collector-manager.js';
9
10
  function getRequire() {
10
11
  // Check if we're in CJS context (require.main exists)
11
12
  if (typeof require !== 'undefined' && typeof require.main !== 'undefined') {
@@ -59,7 +60,7 @@ function mapPinoLevelToBeamableLevel(level) {
59
60
  * @param env - Environment configuration
60
61
  * @returns OTLP logger provider if configured, null otherwise
61
62
  */
62
- function initializeOtlpLogging(serviceName, qualifiedServiceName, env) {
63
+ async function initializeOtlpLogging(serviceName, qualifiedServiceName, env, logger) {
63
64
  // Check for explicit OTLP endpoint (same as C#: BEAM_OTEL_EXPORTER_OTLP_ENDPOINT)
64
65
  // Also check standard OTEL environment variables
65
66
  const otlpEndpoint = process.env.BEAM_OTEL_EXPORTER_OTLP_ENDPOINT
@@ -72,7 +73,7 @@ function initializeOtlpLogging(serviceName, qualifiedServiceName, env) {
72
73
  const standardOtelEnabled = isInDocker && !process.env.BEAM_DISABLE_STANDARD_OTEL;
73
74
  // If no explicit endpoint and standard OTLP not enabled, skip OTLP
74
75
  if (!otlpEndpoint && !standardOtelEnabled) {
75
- return null;
76
+ return Promise.resolve(null);
76
77
  }
77
78
  try {
78
79
  // Build resource attributes (similar to C# resourceProvider)
@@ -95,18 +96,31 @@ function initializeOtlpLogging(serviceName, qualifiedServiceName, env) {
95
96
  }
96
97
  // Determine endpoint URL
97
98
  // If explicit endpoint provided, use it (append /v1/logs if needed for HTTP)
98
- // Note: C# microservices discover the collector endpoint via socket communication or start a collector
99
- // For Node.js, we require an explicit endpoint to be configured - we don't assume localhost
100
- // The OTLP endpoint should be provided via BEAM_OTEL_EXPORTER_OTLP_ENDPOINT environment variable
99
+ // If standard OTLP enabled but no explicit endpoint, try to discover or start collector
101
100
  let endpointUrl = otlpEndpoint;
102
- // If no explicit endpoint is provided, we cannot use OTLP
103
- // C# microservices can discover/start collectors, but Node.js requires explicit configuration
101
+ if (!endpointUrl && standardOtelEnabled && logger) {
102
+ // Try to discover existing collector or start a new one (like C# does)
103
+ try {
104
+ const discoveredEndpoint = await discoverOrStartCollector(logger, standardOtelEnabled);
105
+ if (discoveredEndpoint) {
106
+ endpointUrl = discoveredEndpoint;
107
+ }
108
+ else {
109
+ console.error('[OTLP] Standard OTLP is enabled but could not discover or start collector.');
110
+ return Promise.resolve(null);
111
+ }
112
+ }
113
+ catch (error) {
114
+ console.error('[OTLP] Failed to discover/start collector:', error instanceof Error ? error.message : String(error));
115
+ return Promise.resolve(null);
116
+ }
117
+ }
118
+ // If still no endpoint, skip OTLP
104
119
  if (!endpointUrl) {
105
120
  if (standardOtelEnabled) {
106
- // Log that OTLP is expected but endpoint not configured
107
- console.error('[OTLP] Standard OTLP is enabled but no endpoint configured. Set BEAM_OTEL_EXPORTER_OTLP_ENDPOINT to enable OTLP logging.');
121
+ console.error('[OTLP] Standard OTLP is enabled but no endpoint available. Set BEAM_OTEL_EXPORTER_OTLP_ENDPOINT to enable OTLP logging.');
108
122
  }
109
- return null;
123
+ return Promise.resolve(null);
110
124
  }
111
125
  // Ensure endpoint has /v1/logs suffix for HTTP exporter (like C# does)
112
126
  const finalEndpointUrl = endpointUrl.includes('/v1/logs')
@@ -136,7 +150,7 @@ function initializeOtlpLogging(serviceName, qualifiedServiceName, env) {
136
150
  // Log successful initialization (to stdout for debugging)
137
151
  // This helps diagnose if OTLP is working in deployed environments
138
152
  console.error(`[OTLP] OpenTelemetry logging initialized. Endpoint: ${finalEndpointUrl}, Service: ${serviceName || 'unknown'}`);
139
- return loggerProvider;
153
+ return Promise.resolve(loggerProvider);
140
154
  }
141
155
  catch (error) {
142
156
  // If OTLP initialization fails, log error but continue without OTLP
@@ -146,7 +160,7 @@ function initializeOtlpLogging(serviceName, qualifiedServiceName, env) {
146
160
  if (error instanceof Error && error.stack) {
147
161
  console.error('[OTLP] Stack trace:', error.stack);
148
162
  }
149
- return null;
163
+ return Promise.resolve(null);
150
164
  }
151
165
  }
152
166
  /**
@@ -157,7 +171,7 @@ function initializeOtlpLogging(serviceName, qualifiedServiceName, env) {
157
171
  *
158
172
  * Also sends logs via OTLP if OTLP logger provider is configured.
159
173
  */
160
- function createBeamableLogFormatter(serviceName, qualifiedServiceName, otlpLoggerProvider) {
174
+ function createBeamableLogFormatter(serviceName, qualifiedServiceName, otlpProviderRef) {
161
175
  return new Transform({
162
176
  objectMode: false, // Pino writes strings/Buffers, not objects
163
177
  transform(chunk, _encoding, callback) {
@@ -317,9 +331,10 @@ function createBeamableLogFormatter(serviceName, qualifiedServiceName, otlpLogge
317
331
  // Note: These extra fields won't break CloudWatch - it will just store them in @message
318
332
  Object.assign(beamableLog, otelFields);
319
333
  // Send log via OTLP if configured (similar to C# MicroserviceOtelLogRecordExporter)
320
- if (otlpLoggerProvider) {
334
+ // Check if provider is available (may be null if still initializing)
335
+ if (otlpProviderRef?.provider) {
321
336
  try {
322
- const otlpLogger = otlpLoggerProvider.getLogger(serviceName || 'beamable-node-runtime', undefined, // version
337
+ const otlpLogger = otlpProviderRef.provider.getLogger(serviceName || 'beamable-node-runtime', undefined, // version
323
338
  {
324
339
  schemaUrl: undefined, // optional schema URL
325
340
  });
@@ -371,9 +386,30 @@ function createBeamableLogFormatter(serviceName, qualifiedServiceName, otlpLogge
371
386
  export function createLogger(env, options = {}) {
372
387
  const configuredDestination = options.destinationPath ?? process.env.LOG_PATH;
373
388
  const usePrettyLogs = shouldUsePrettyLogs();
374
- // Initialize OTLP logging if configured (similar to C# microservices)
389
+ // Shared reference for OTLP logger provider (updated asynchronously)
390
+ const otlpProviderRef = { provider: null };
391
+ // Initialize OTLP logging in the background (similar to C# microservices)
375
392
  // This sends logs via OpenTelemetry Protocol in addition to stdout JSON
376
- const otlpLoggerProvider = initializeOtlpLogging(options.serviceName, options.qualifiedServiceName, env);
393
+ // We start this async so it doesn't block logger creation
394
+ const isInDocker = process.env.IS_LOCAL !== '1' && process.env.IS_LOCAL !== 'true';
395
+ const standardOtelEnabled = isInDocker && !process.env.BEAM_DISABLE_STANDARD_OTEL;
396
+ // Start collector discovery/start in background
397
+ if (standardOtelEnabled || process.env.BEAM_OTEL_EXPORTER_OTLP_ENDPOINT) {
398
+ // Create a temporary logger for OTLP initialization
399
+ const tempLogger = pino({
400
+ name: options.name ?? 'beamable-node-runtime',
401
+ level: env.logLevel,
402
+ }, process.stdout);
403
+ // Initialize OTLP asynchronously (fire-and-forget)
404
+ initializeOtlpLogging(options.serviceName, options.qualifiedServiceName, env, tempLogger).then((provider) => {
405
+ otlpProviderRef.provider = provider;
406
+ if (provider) {
407
+ tempLogger.info('[OTLP] OpenTelemetry logging initialized successfully');
408
+ }
409
+ }).catch((error) => {
410
+ tempLogger.error(`[OTLP] Failed to initialize: ${error instanceof Error ? error.message : String(error)}`);
411
+ });
412
+ }
377
413
  const pinoOptions = {
378
414
  name: options.name ?? 'beamable-node-runtime',
379
415
  level: env.logLevel,
@@ -400,7 +436,7 @@ export function createLogger(env, options = {}) {
400
436
  // Deployed/remote: Use Beamable JSON format for log collection
401
437
  // Include OpenTelemetry fields for ClickHouse compatibility
402
438
  // Also send logs via OTLP if configured
403
- const beamableFormatter = createBeamableLogFormatter(options.serviceName, options.qualifiedServiceName, otlpLoggerProvider);
439
+ const beamableFormatter = createBeamableLogFormatter(options.serviceName, options.qualifiedServiceName, otlpProviderRef);
404
440
  beamableFormatter.pipe(process.stdout);
405
441
  return pino(pinoOptions, beamableFormatter);
406
442
  }
@@ -433,7 +469,7 @@ export function createLogger(env, options = {}) {
433
469
  // For file logging: Use Beamable format if not local, default Pino format if local
434
470
  const resolvedDestination = configuredDestination === 'temp' ? ensureWritableTempDirectory() : configuredDestination;
435
471
  if (!usePrettyLogs) {
436
- const beamableFormatter = createBeamableLogFormatter(options.serviceName, options.qualifiedServiceName, otlpLoggerProvider);
472
+ const beamableFormatter = createBeamableLogFormatter(options.serviceName, options.qualifiedServiceName, otlpProviderRef);
437
473
  const fileStream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });
438
474
  beamableFormatter.pipe(fileStream);
439
475
  return pino(pinoOptions, beamableFormatter);
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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","sourcesContent":["import pino, { destination, type Logger, type LoggerOptions } from 'pino';\r\nimport { Transform } from 'node:stream';\r\nimport { createRequire } from 'node:module';\r\nimport { ensureWritableTempDirectory } from './env.js';\r\nimport type { EnvironmentConfig } from './types.js';\r\nimport { logs } from '@opentelemetry/api-logs';\r\nimport { LoggerProvider, SimpleLogRecordProcessor } from '@opentelemetry/sdk-logs';\r\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\r\nimport { resourceFromAttributes, defaultResource } from '@opentelemetry/resources';\r\n\r\n// Helper to get require function that works in both CJS and ESM\r\ndeclare const require: any;\r\nfunction getRequire(): any {\r\n // Check if we're in CJS context (require.main exists)\r\n if (typeof require !== 'undefined' && typeof require.main !== 'undefined') {\r\n // CJS context - use require directly\r\n return require;\r\n }\r\n // ESM context - use createRequire with import.meta.url\r\n // TypeScript will complain in CJS builds, but this code only runs in ESM\r\n // @ts-ignore - import.meta is ESM-only, TypeScript error in CJS is expected\r\n return createRequire(import.meta.url);\r\n}\r\n\r\n/**\r\n * Determines if we should use pretty logs (local dev) or raw JSON logs (deployed).\r\n * \r\n * Simple check: If IS_LOCAL=1 is set in environment, use pretty logs.\r\n * Otherwise, use raw Beamable JSON format for log collection.\r\n */\r\nfunction shouldUsePrettyLogs(): boolean {\r\n // Check for explicit IS_LOCAL flag\r\n return process.env.IS_LOCAL === '1' || process.env.IS_LOCAL === 'true';\r\n}\r\n\r\ninterface LoggerFactoryOptions {\r\n name?: string;\r\n destinationPath?: string;\r\n serviceName?: string; // Service name for log filtering (e.g., \"ExampleNodeService\")\r\n qualifiedServiceName?: string; // Full qualified service name (e.g., \"micro_ExampleNodeService\")\r\n}\r\n\r\n/**\r\n * Maps Pino log levels to Beamable log levels\r\n * Pino levels: 10=trace, 20=debug, 30=info, 40=warn, 50=error, 60=fatal\r\n * Beamable levels: Debug, Info, Warning, Error, Fatal\r\n */\r\nfunction mapPinoLevelToBeamableLevel(level: number): string {\r\n switch (level) {\r\n case 10: // trace\r\n return 'Debug';\r\n case 20: // debug\r\n return 'Debug';\r\n case 30: // info\r\n return 'Info';\r\n case 40: // warn\r\n return 'Warning';\r\n case 50: // error\r\n return 'Error';\r\n case 60: // fatal\r\n return 'Fatal';\r\n default:\r\n return 'Info';\r\n }\r\n}\r\n\r\n/**\r\n * Initializes OpenTelemetry OTLP log exporter if configured.\r\n * Similar to C# microservices, checks for BEAM_OTEL_EXPORTER_OTLP_ENDPOINT or uses standard enabled flag.\r\n * \r\n * @param serviceName - Service name for resource attributes\r\n * @param qualifiedServiceName - Qualified service name for resource attributes\r\n * @param env - Environment configuration\r\n * @returns OTLP logger provider if configured, null otherwise\r\n */\r\nfunction initializeOtlpLogging(\r\n serviceName?: string,\r\n qualifiedServiceName?: string,\r\n env?: EnvironmentConfig\r\n): LoggerProvider | null {\r\n // Check for explicit OTLP endpoint (same as C#: BEAM_OTEL_EXPORTER_OTLP_ENDPOINT)\r\n // Also check standard OTEL environment variables\r\n const otlpEndpoint = process.env.BEAM_OTEL_EXPORTER_OTLP_ENDPOINT \r\n || process.env.OTEL_EXPORTER_OTLP_ENDPOINT\r\n || (process.env.OTEL_EXPORTER_OTLP_ENDPOINT_LOGS ? process.env.OTEL_EXPORTER_OTLP_ENDPOINT_LOGS : null);\r\n \r\n // Check if standard OTLP is enabled (similar to C# OtelExporterStandardEnabled)\r\n // In Docker containers, OTLP should be enabled unless explicitly disabled\r\n // Simple check: if IS_LOCAL is not set, we're likely in a container\r\n const isInDocker = process.env.IS_LOCAL !== '1' && process.env.IS_LOCAL !== 'true';\r\n const standardOtelEnabled = isInDocker && !process.env.BEAM_DISABLE_STANDARD_OTEL;\r\n \r\n // If no explicit endpoint and standard OTLP not enabled, skip OTLP\r\n if (!otlpEndpoint && !standardOtelEnabled) {\r\n return null;\r\n }\r\n \r\n try {\r\n // Build resource attributes (similar to C# resourceProvider)\r\n const resourceAttributes: Record<string, string> = {};\r\n if (serviceName) {\r\n resourceAttributes['service.namespace'] = serviceName;\r\n resourceAttributes['service.name'] = serviceName;\r\n }\r\n if (qualifiedServiceName) {\r\n resourceAttributes['service.instance.id'] = qualifiedServiceName;\r\n }\r\n if (env?.cid) {\r\n resourceAttributes['beam.cid'] = String(env.cid);\r\n }\r\n if (env?.pid) {\r\n resourceAttributes['beam.pid'] = String(env.pid);\r\n }\r\n if (env?.routingKey) {\r\n resourceAttributes['beam.routing_key'] = String(env.routingKey);\r\n }\r\n \r\n // Determine endpoint URL\r\n // If explicit endpoint provided, use it (append /v1/logs if needed for HTTP)\r\n // Note: C# microservices discover the collector endpoint via socket communication or start a collector\r\n // For Node.js, we require an explicit endpoint to be configured - we don't assume localhost\r\n // The OTLP endpoint should be provided via BEAM_OTEL_EXPORTER_OTLP_ENDPOINT environment variable\r\n let endpointUrl = otlpEndpoint;\r\n \r\n // If no explicit endpoint is provided, we cannot use OTLP\r\n // C# microservices can discover/start collectors, but Node.js requires explicit configuration\r\n if (!endpointUrl) {\r\n if (standardOtelEnabled) {\r\n // Log that OTLP is expected but endpoint not configured\r\n console.error('[OTLP] Standard OTLP is enabled but no endpoint configured. Set BEAM_OTEL_EXPORTER_OTLP_ENDPOINT to enable OTLP logging.');\r\n }\r\n return null;\r\n }\r\n \r\n // Ensure endpoint has /v1/logs suffix for HTTP exporter (like C# does)\r\n const finalEndpointUrl = endpointUrl.includes('/v1/logs') \r\n ? endpointUrl \r\n : `${endpointUrl.replace(/\\/$/, '')}/v1/logs`;\r\n \r\n // Create OTLP HTTP exporter (C# uses HttpProtobuf by default)\r\n const exporter = new OTLPLogExporter({\r\n url: finalEndpointUrl,\r\n // Headers if provided (similar to C# OtelExporterOtlpHeaders)\r\n headers: process.env.BEAM_OTEL_EXPORTER_OTLP_HEADERS \r\n ? JSON.parse(process.env.BEAM_OTEL_EXPORTER_OTLP_HEADERS)\r\n : undefined,\r\n });\r\n \r\n // Create resource with attributes (merge with default resource)\r\n const baseResource = defaultResource();\r\n const customResource = resourceFromAttributes(resourceAttributes);\r\n const resource = baseResource.merge(customResource);\r\n \r\n // Create log record processor\r\n const processor = new SimpleLogRecordProcessor(exporter);\r\n \r\n // Create logger provider with resource and processor\r\n const loggerProvider = new LoggerProvider({\r\n resource: resource,\r\n processors: [processor],\r\n });\r\n \r\n // Set as global logger provider\r\n logs.setGlobalLoggerProvider(loggerProvider);\r\n \r\n // Log successful initialization (to stdout for debugging)\r\n // This helps diagnose if OTLP is working in deployed environments\r\n console.error(`[OTLP] OpenTelemetry logging initialized. Endpoint: ${finalEndpointUrl}, Service: ${serviceName || 'unknown'}`);\r\n \r\n return loggerProvider;\r\n } catch (error) {\r\n // If OTLP initialization fails, log error but continue without OTLP\r\n // Don't throw - we still want stdout logging to work\r\n // Log to stderr so it's visible in container logs\r\n console.error('[OTLP] Failed to initialize OTLP logging:', error instanceof Error ? error.message : String(error));\r\n if (error instanceof Error && error.stack) {\r\n console.error('[OTLP] Stack trace:', error.stack);\r\n }\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Creates a transform stream that converts Pino JSON logs to Beamable's expected format.\r\n * Beamable expects logs with __t (timestamp), __l (level), and __m (message) fields.\r\n * Also includes OpenTelemetry-compatible fields for ClickHouse compatibility.\r\n * Pino writes JSON strings (one per line) to the stream.\r\n * \r\n * Also sends logs via OTLP if OTLP logger provider is configured.\r\n */\r\nfunction createBeamableLogFormatter(\r\n serviceName?: string,\r\n qualifiedServiceName?: string,\r\n otlpLoggerProvider?: LoggerProvider | null\r\n): Transform {\r\n return new Transform({\r\n objectMode: false, // Pino writes strings/Buffers, not objects\r\n transform(chunk: Buffer, _encoding, callback) {\r\n // Ensure we have a Buffer - Pino may write strings or Buffers\r\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\r\n try {\r\n const line = buffer.toString('utf8');\r\n // Skip empty lines\r\n if (!line.trim()) {\r\n callback();\r\n return;\r\n }\r\n \r\n // Parse Pino's JSON log line\r\n const pinoLog = JSON.parse(line);\r\n \r\n // Extract timestamp - Pino uses 'time' field (ISO 8601 string or milliseconds)\r\n // Convert to ISO 8601 string for Beamable\r\n let timestamp: string;\r\n if (typeof pinoLog.time === 'string') {\r\n timestamp = pinoLog.time;\r\n } else if (typeof pinoLog.time === 'number') {\r\n timestamp = new Date(pinoLog.time).toISOString();\r\n } else {\r\n timestamp = new Date().toISOString();\r\n }\r\n \r\n // Map Pino level to Beamable level\r\n const level = mapPinoLevelToBeamableLevel(pinoLog.level);\r\n \r\n // Build the message - combine msg with any additional fields\r\n // Pino's 'msg' field contains the log message\r\n const messageParts: string[] = [];\r\n if (pinoLog.msg) {\r\n messageParts.push(pinoLog.msg);\r\n }\r\n \r\n // Include error information if present\r\n if (pinoLog.err) {\r\n const err = pinoLog.err;\r\n const errMsg = err.message || err.msg || 'Error';\r\n const errStack = err.stack ? `\\n${err.stack}` : '';\r\n messageParts.push(`${errMsg}${errStack}`);\r\n }\r\n \r\n // Build the Beamable log format (for CloudWatch Logs Insights)\r\n const beamableLog: Record<string, unknown> = {\r\n __t: timestamp,\r\n __l: level,\r\n __m: messageParts.length > 0 ? messageParts.join(' ') : 'No message',\r\n };\r\n \r\n // Include additional context fields that might be useful\r\n // These are included in the message object but not as top-level fields\r\n const contextFields: Record<string, unknown> = {};\r\n if (pinoLog.cid) contextFields.cid = pinoLog.cid;\r\n if (pinoLog.pid) contextFields.pid = pinoLog.pid;\r\n if (pinoLog.routingKey) contextFields.routingKey = pinoLog.routingKey;\r\n if (pinoLog.service) contextFields.service = pinoLog.service;\r\n if (pinoLog.component) contextFields.component = pinoLog.component;\r\n \r\n // Include service name in context for CloudWatch filtering\r\n if (serviceName) {\r\n contextFields.serviceName = serviceName;\r\n }\r\n if (qualifiedServiceName) {\r\n contextFields.qualifiedServiceName = qualifiedServiceName;\r\n }\r\n \r\n // Include any other fields that aren't standard Pino fields\r\n const standardPinoFields = ['level', 'time', 'pid', 'hostname', 'name', 'msg', 'err', 'v', 'cid', 'pid', 'routingKey', 'sdkVersionExecution', 'service', 'component'];\r\n for (const [key, value] of Object.entries(pinoLog)) {\r\n if (!standardPinoFields.includes(key) && value !== undefined && value !== null) {\r\n contextFields[key] = value;\r\n }\r\n }\r\n \r\n // If there are context fields, include them in the log\r\n if (Object.keys(contextFields).length > 0) {\r\n beamableLog.__c = contextFields;\r\n }\r\n \r\n // Add OpenTelemetry-compatible fields for ClickHouse compatibility\r\n // These fields allow an OpenTelemetry collector to parse and forward logs to ClickHouse\r\n // The Portal's realm-level logs page queries ClickHouse's otel_logs table\r\n const otelFields: Record<string, unknown> = {};\r\n \r\n // ResourceAttributes - service identification (for ClickHouse filtering)\r\n const resourceAttributes: Record<string, unknown> = {};\r\n // IMPORTANT: The Portal searches for service:ExampleNodeService (just the service name)\r\n // So we need to set service.namespace to the service name, NOT the qualified name\r\n // The qualified name (micro_ExampleNodeService) is used for CloudWatch log stream filtering\r\n // But ClickHouse uses just the service name for filtering\r\n if (serviceName) {\r\n resourceAttributes['service.namespace'] = serviceName;\r\n resourceAttributes['service.name'] = serviceName;\r\n }\r\n // Also include qualified name for reference\r\n if (qualifiedServiceName) {\r\n resourceAttributes['service.qualifiedName'] = qualifiedServiceName;\r\n }\r\n if (pinoLog.cid) {\r\n resourceAttributes['beam.cid'] = String(pinoLog.cid);\r\n }\r\n if (pinoLog.pid) {\r\n resourceAttributes['beam.pid'] = String(pinoLog.pid);\r\n }\r\n if (pinoLog.routingKey) {\r\n resourceAttributes['beam.routing_key'] = String(pinoLog.routingKey);\r\n }\r\n \r\n // LogAttributes - log-specific attributes\r\n const logAttributes: Record<string, unknown> = {};\r\n if (pinoLog.component) {\r\n logAttributes['component'] = String(pinoLog.component);\r\n }\r\n if (pinoLog.err) {\r\n const err = pinoLog.err;\r\n if (err.message) logAttributes['exception.message'] = String(err.message);\r\n if (err.stack) logAttributes['exception.stacktrace'] = String(err.stack);\r\n if (err.type) logAttributes['exception.type'] = String(err.type);\r\n }\r\n \r\n // Map Beamable log level to OpenTelemetry SeverityText\r\n // Beamable: Debug, Info, Warning, Error, Fatal\r\n // OpenTelemetry: Trace, Debug, Info, Warn, Error, Fatal, Unspecified\r\n const severityTextMap: Record<string, string> = {\r\n 'Debug': 'Debug',\r\n 'Info': 'Information',\r\n 'Warning': 'Warning',\r\n 'Error': 'Error',\r\n 'Fatal': 'Critical',\r\n };\r\n const severityText = severityTextMap[level] || 'Information';\r\n \r\n // CRITICAL: ResourceAttributes and LogAttributes MUST always be present (even if empty)\r\n // ClickHouse schema expects these fields to exist for proper querying\r\n // The Portal queries ResourceAttributes['service.namespace'] - if ResourceAttributes is missing, queries fail\r\n \r\n // Ensure ResourceAttributes always has service.namespace when serviceName is provided\r\n // This is the primary field used by Portal for service filtering\r\n if (serviceName && !resourceAttributes['service.namespace']) {\r\n resourceAttributes['service.namespace'] = serviceName;\r\n resourceAttributes['service.name'] = serviceName;\r\n }\r\n \r\n // Add OpenTelemetry fields to the log\r\n // These are in addition to the Beamable format, so both systems can parse the logs\r\n // CRITICAL: Always include all required OpenTelemetry fields for ClickHouse compatibility\r\n otelFields['Timestamp'] = timestamp; // OpenTelemetry timestamp format (ISO 8601)\r\n otelFields['SeverityText'] = severityText;\r\n otelFields['Body'] = messageParts.length > 0 ? messageParts.join(' ') : 'No message';\r\n // ALWAYS include ResourceAttributes (even if empty) - required for ClickHouse schema\r\n otelFields['ResourceAttributes'] = resourceAttributes;\r\n // ALWAYS include LogAttributes (even if empty) - required for ClickHouse schema\r\n otelFields['LogAttributes'] = logAttributes;\r\n \r\n // IMPORTANT: For CloudWatch Logs Insights, we need to ensure @message contains valid JSON\r\n // The Portal expects @message to be a JSON string that can be parsed to extract __t, __l, __m\r\n // We output the Beamable format as the primary format, and include OpenTelemetry fields\r\n // CloudWatch will store this as @message, and an OpenTelemetry collector will parse it\r\n // and forward to ClickHouse's otel_logs table\r\n \r\n // Merge OpenTelemetry fields into the log (for ClickHouse compatibility)\r\n // An OpenTelemetry collector can parse these fields and forward to ClickHouse\r\n // Note: These extra fields won't break CloudWatch - it will just store them in @message\r\n Object.assign(beamableLog, otelFields);\r\n \r\n // Send log via OTLP if configured (similar to C# MicroserviceOtelLogRecordExporter)\r\n if (otlpLoggerProvider) {\r\n try {\r\n const otlpLogger = otlpLoggerProvider.getLogger(\r\n serviceName || 'beamable-node-runtime',\r\n undefined, // version\r\n {\r\n schemaUrl: undefined, // optional schema URL\r\n }\r\n );\r\n \r\n // Map Beamable level to OpenTelemetry SeverityNumber\r\n const severityNumberMap: Record<string, number> = {\r\n 'Debug': 5, // SEVERITY_NUMBER_DEBUG\r\n 'Info': 9, // SEVERITY_NUMBER_INFO\r\n 'Warning': 13, // SEVERITY_NUMBER_WARN\r\n 'Error': 17, // SEVERITY_NUMBER_ERROR\r\n 'Fatal': 21, // SEVERITY_NUMBER_FATAL\r\n };\r\n \r\n // Create log record for OTLP\r\n otlpLogger.emit({\r\n severityNumber: severityNumberMap[level] || 9,\r\n severityText: severityText,\r\n body: messageParts.length > 0 ? messageParts.join(' ') : 'No message',\r\n attributes: {\r\n ...logAttributes,\r\n // Include additional context\r\n ...(pinoLog.component ? { component: String(pinoLog.component) } : {}),\r\n },\r\n timestamp: new Date(timestamp).getTime() * 1_000_000, // nanoseconds\r\n observedTimestamp: Date.now() * 1_000_000, // nanoseconds\r\n });\r\n } catch (otlpError) {\r\n // If OTLP send fails, continue with stdout logging\r\n // Don't block the log output\r\n // Could add retry logic here similar to C# if needed\r\n }\r\n }\r\n \r\n // Output as a single-line JSON string (required for CloudWatch)\r\n // CloudWatch Logs Insights will store this entire JSON string in the @message field\r\n const output = JSON.stringify(beamableLog) + '\\n';\r\n callback(null, Buffer.from(output, 'utf8'));\r\n } catch (error) {\r\n // If parsing fails, output a fallback log entry\r\n const fallbackLog = {\r\n __t: new Date().toISOString(),\r\n __l: 'Error',\r\n __m: `Failed to parse log entry: ${chunk.toString().substring(0, 200)}`,\r\n };\r\n callback(null, Buffer.from(JSON.stringify(fallbackLog) + '\\n', 'utf8'));\r\n }\r\n },\r\n });\r\n}\r\n\r\nexport function createLogger(env: EnvironmentConfig, options: LoggerFactoryOptions = {}): Logger {\r\n const configuredDestination = options.destinationPath ?? process.env.LOG_PATH;\r\n const usePrettyLogs = shouldUsePrettyLogs();\r\n \r\n // Initialize OTLP logging if configured (similar to C# microservices)\r\n // This sends logs via OpenTelemetry Protocol in addition to stdout JSON\r\n const otlpLoggerProvider = initializeOtlpLogging(\r\n options.serviceName,\r\n options.qualifiedServiceName,\r\n env\r\n );\r\n\r\n const pinoOptions: LoggerOptions = {\r\n name: options.name ?? 'beamable-node-runtime',\r\n level: env.logLevel,\r\n base: {\r\n cid: env.cid,\r\n pid: env.pid,\r\n routingKey: env.routingKey ?? null,\r\n sdkVersionExecution: env.sdkVersionExecution,\r\n // Include service name in base fields for filtering\r\n serviceName: options.serviceName,\r\n qualifiedServiceName: options.qualifiedServiceName,\r\n },\r\n redact: {\r\n paths: ['secret', 'refreshToken'],\r\n censor: '***',\r\n },\r\n // Use timestamp in milliseconds (Pino default) for accurate conversion\r\n timestamp: pino.stdTimeFunctions.isoTime,\r\n };\r\n\r\n // For deployed services, always log to stdout so container orchestrator can collect logs\r\n // For local development, log to stdout unless a specific file path is provided\r\n if (!configuredDestination || configuredDestination === '-' || configuredDestination === 'stdout' || configuredDestination === 'console') {\r\n if (!usePrettyLogs) {\r\n // Deployed/remote: Use Beamable JSON format for log collection\r\n // Include OpenTelemetry fields for ClickHouse compatibility\r\n // Also send logs via OTLP if configured\r\n const beamableFormatter = createBeamableLogFormatter(\r\n options.serviceName,\r\n options.qualifiedServiceName,\r\n otlpLoggerProvider\r\n );\r\n beamableFormatter.pipe(process.stdout);\r\n return pino(pinoOptions, beamableFormatter);\r\n } else {\r\n // Local development: Use Pino's pretty printing for human-readable logs\r\n // Try to use pino-pretty if available (optional dependency)\r\n // If not available, fall back to default Pino JSON output\r\n try {\r\n // Check if pino-pretty is available\r\n // Use getRequire() which handles both CJS and ESM contexts\r\n const requireFn = getRequire();\r\n const pinoPretty = requireFn('pino-pretty');\r\n // Create a pretty stream with formatting options\r\n const prettyStream = pinoPretty({\r\n colorize: true,\r\n translateTime: 'HH:MM:ss.l',\r\n ignore: 'pid,hostname',\r\n singleLine: false,\r\n });\r\n // Use pino with the pretty stream\r\n return pino(pinoOptions, prettyStream);\r\n } catch {\r\n // pino-pretty not available, use default Pino output (JSON but readable)\r\n // This is expected if pino-pretty isn't installed, so we silently fall back\r\n return pino(pinoOptions, process.stdout);\r\n }\r\n }\r\n }\r\n\r\n // For file logging: Use Beamable format if not local, default Pino format if local\r\n const resolvedDestination = configuredDestination === 'temp' ? ensureWritableTempDirectory() : configuredDestination;\r\n if (!usePrettyLogs) {\r\n const beamableFormatter = createBeamableLogFormatter(\r\n options.serviceName,\r\n options.qualifiedServiceName,\r\n otlpLoggerProvider\r\n );\r\n const fileStream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });\r\n beamableFormatter.pipe(fileStream as unknown as NodeJS.WritableStream);\r\n return pino(pinoOptions, beamableFormatter);\r\n } else {\r\n const fileStream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });\r\n return pino(pinoOptions, fileStream);\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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","sourcesContent":["import pino, { destination, type Logger, type LoggerOptions } from 'pino';\r\nimport { Transform } from 'node:stream';\r\nimport { createRequire } from 'node:module';\r\nimport { ensureWritableTempDirectory } from './env.js';\r\nimport type { EnvironmentConfig } from './types.js';\r\nimport { logs } from '@opentelemetry/api-logs';\r\nimport { LoggerProvider, SimpleLogRecordProcessor } from '@opentelemetry/sdk-logs';\r\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\r\nimport { resourceFromAttributes, defaultResource } from '@opentelemetry/resources';\r\nimport { discoverOrStartCollector } from './collector-manager.js';\r\nimport type { Logger as PinoLogger } from 'pino';\r\n\r\n// Helper to get require function that works in both CJS and ESM\r\ndeclare const require: any;\r\nfunction getRequire(): any {\r\n // Check if we're in CJS context (require.main exists)\r\n if (typeof require !== 'undefined' && typeof require.main !== 'undefined') {\r\n // CJS context - use require directly\r\n return require;\r\n }\r\n // ESM context - use createRequire with import.meta.url\r\n // TypeScript will complain in CJS builds, but this code only runs in ESM\r\n // @ts-ignore - import.meta is ESM-only, TypeScript error in CJS is expected\r\n return createRequire(import.meta.url);\r\n}\r\n\r\n/**\r\n * Determines if we should use pretty logs (local dev) or raw JSON logs (deployed).\r\n * \r\n * Simple check: If IS_LOCAL=1 is set in environment, use pretty logs.\r\n * Otherwise, use raw Beamable JSON format for log collection.\r\n */\r\nfunction shouldUsePrettyLogs(): boolean {\r\n // Check for explicit IS_LOCAL flag\r\n return process.env.IS_LOCAL === '1' || process.env.IS_LOCAL === 'true';\r\n}\r\n\r\ninterface LoggerFactoryOptions {\r\n name?: string;\r\n destinationPath?: string;\r\n serviceName?: string; // Service name for log filtering (e.g., \"ExampleNodeService\")\r\n qualifiedServiceName?: string; // Full qualified service name (e.g., \"micro_ExampleNodeService\")\r\n}\r\n\r\n/**\r\n * Maps Pino log levels to Beamable log levels\r\n * Pino levels: 10=trace, 20=debug, 30=info, 40=warn, 50=error, 60=fatal\r\n * Beamable levels: Debug, Info, Warning, Error, Fatal\r\n */\r\nfunction mapPinoLevelToBeamableLevel(level: number): string {\r\n switch (level) {\r\n case 10: // trace\r\n return 'Debug';\r\n case 20: // debug\r\n return 'Debug';\r\n case 30: // info\r\n return 'Info';\r\n case 40: // warn\r\n return 'Warning';\r\n case 50: // error\r\n return 'Error';\r\n case 60: // fatal\r\n return 'Fatal';\r\n default:\r\n return 'Info';\r\n }\r\n}\r\n\r\n/**\r\n * Initializes OpenTelemetry OTLP log exporter if configured.\r\n * Similar to C# microservices, checks for BEAM_OTEL_EXPORTER_OTLP_ENDPOINT or uses standard enabled flag.\r\n * \r\n * @param serviceName - Service name for resource attributes\r\n * @param qualifiedServiceName - Qualified service name for resource attributes\r\n * @param env - Environment configuration\r\n * @returns OTLP logger provider if configured, null otherwise\r\n */\r\nasync function initializeOtlpLogging(\r\n serviceName?: string,\r\n qualifiedServiceName?: string,\r\n env?: EnvironmentConfig,\r\n logger?: PinoLogger\r\n): Promise<LoggerProvider | null> {\r\n // Check for explicit OTLP endpoint (same as C#: BEAM_OTEL_EXPORTER_OTLP_ENDPOINT)\r\n // Also check standard OTEL environment variables\r\n const otlpEndpoint = process.env.BEAM_OTEL_EXPORTER_OTLP_ENDPOINT \r\n || process.env.OTEL_EXPORTER_OTLP_ENDPOINT\r\n || (process.env.OTEL_EXPORTER_OTLP_ENDPOINT_LOGS ? process.env.OTEL_EXPORTER_OTLP_ENDPOINT_LOGS : null);\r\n \r\n // Check if standard OTLP is enabled (similar to C# OtelExporterStandardEnabled)\r\n // In Docker containers, OTLP should be enabled unless explicitly disabled\r\n // Simple check: if IS_LOCAL is not set, we're likely in a container\r\n const isInDocker = process.env.IS_LOCAL !== '1' && process.env.IS_LOCAL !== 'true';\r\n const standardOtelEnabled = isInDocker && !process.env.BEAM_DISABLE_STANDARD_OTEL;\r\n \r\n // If no explicit endpoint and standard OTLP not enabled, skip OTLP\r\n if (!otlpEndpoint && !standardOtelEnabled) {\r\n return Promise.resolve(null);\r\n }\r\n \r\n try {\r\n // Build resource attributes (similar to C# resourceProvider)\r\n const resourceAttributes: Record<string, string> = {};\r\n if (serviceName) {\r\n resourceAttributes['service.namespace'] = serviceName;\r\n resourceAttributes['service.name'] = serviceName;\r\n }\r\n if (qualifiedServiceName) {\r\n resourceAttributes['service.instance.id'] = qualifiedServiceName;\r\n }\r\n if (env?.cid) {\r\n resourceAttributes['beam.cid'] = String(env.cid);\r\n }\r\n if (env?.pid) {\r\n resourceAttributes['beam.pid'] = String(env.pid);\r\n }\r\n if (env?.routingKey) {\r\n resourceAttributes['beam.routing_key'] = String(env.routingKey);\r\n }\r\n \r\n // Determine endpoint URL\r\n // If explicit endpoint provided, use it (append /v1/logs if needed for HTTP)\r\n // If standard OTLP enabled but no explicit endpoint, try to discover or start collector\r\n let endpointUrl = otlpEndpoint;\r\n \r\n if (!endpointUrl && standardOtelEnabled && logger) {\r\n // Try to discover existing collector or start a new one (like C# does)\r\n try {\r\n const discoveredEndpoint = await discoverOrStartCollector(logger, standardOtelEnabled);\r\n if (discoveredEndpoint) {\r\n endpointUrl = discoveredEndpoint;\r\n } else {\r\n console.error('[OTLP] Standard OTLP is enabled but could not discover or start collector.');\r\n return Promise.resolve(null);\r\n }\r\n } catch (error) {\r\n console.error('[OTLP] Failed to discover/start collector:', error instanceof Error ? error.message : String(error));\r\n return Promise.resolve(null);\r\n }\r\n }\r\n \r\n // If still no endpoint, skip OTLP\r\n if (!endpointUrl) {\r\n if (standardOtelEnabled) {\r\n console.error('[OTLP] Standard OTLP is enabled but no endpoint available. Set BEAM_OTEL_EXPORTER_OTLP_ENDPOINT to enable OTLP logging.');\r\n }\r\n return Promise.resolve(null);\r\n }\r\n \r\n // Ensure endpoint has /v1/logs suffix for HTTP exporter (like C# does)\r\n const finalEndpointUrl = endpointUrl.includes('/v1/logs') \r\n ? endpointUrl \r\n : `${endpointUrl.replace(/\\/$/, '')}/v1/logs`;\r\n \r\n // Create OTLP HTTP exporter (C# uses HttpProtobuf by default)\r\n const exporter = new OTLPLogExporter({\r\n url: finalEndpointUrl,\r\n // Headers if provided (similar to C# OtelExporterOtlpHeaders)\r\n headers: process.env.BEAM_OTEL_EXPORTER_OTLP_HEADERS \r\n ? JSON.parse(process.env.BEAM_OTEL_EXPORTER_OTLP_HEADERS)\r\n : undefined,\r\n });\r\n \r\n // Create resource with attributes (merge with default resource)\r\n const baseResource = defaultResource();\r\n const customResource = resourceFromAttributes(resourceAttributes);\r\n const resource = baseResource.merge(customResource);\r\n \r\n // Create log record processor\r\n const processor = new SimpleLogRecordProcessor(exporter);\r\n \r\n // Create logger provider with resource and processor\r\n const loggerProvider = new LoggerProvider({\r\n resource: resource,\r\n processors: [processor],\r\n });\r\n \r\n // Set as global logger provider\r\n logs.setGlobalLoggerProvider(loggerProvider);\r\n \r\n // Log successful initialization (to stdout for debugging)\r\n // This helps diagnose if OTLP is working in deployed environments\r\n console.error(`[OTLP] OpenTelemetry logging initialized. Endpoint: ${finalEndpointUrl}, Service: ${serviceName || 'unknown'}`);\r\n \r\n return Promise.resolve(loggerProvider);\r\n } catch (error) {\r\n // If OTLP initialization fails, log error but continue without OTLP\r\n // Don't throw - we still want stdout logging to work\r\n // Log to stderr so it's visible in container logs\r\n console.error('[OTLP] Failed to initialize OTLP logging:', error instanceof Error ? error.message : String(error));\r\n if (error instanceof Error && error.stack) {\r\n console.error('[OTLP] Stack trace:', error.stack);\r\n }\r\n return Promise.resolve(null);\r\n }\r\n}\r\n\r\n/**\r\n * Creates a transform stream that converts Pino JSON logs to Beamable's expected format.\r\n * Beamable expects logs with __t (timestamp), __l (level), and __m (message) fields.\r\n * Also includes OpenTelemetry-compatible fields for ClickHouse compatibility.\r\n * Pino writes JSON strings (one per line) to the stream.\r\n * \r\n * Also sends logs via OTLP if OTLP logger provider is configured.\r\n */\r\nfunction createBeamableLogFormatter(\r\n serviceName?: string,\r\n qualifiedServiceName?: string,\r\n otlpProviderRef?: { provider: LoggerProvider | null }\r\n): Transform {\r\n return new Transform({\r\n objectMode: false, // Pino writes strings/Buffers, not objects\r\n transform(chunk: Buffer, _encoding, callback) {\r\n // Ensure we have a Buffer - Pino may write strings or Buffers\r\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\r\n try {\r\n const line = buffer.toString('utf8');\r\n // Skip empty lines\r\n if (!line.trim()) {\r\n callback();\r\n return;\r\n }\r\n \r\n // Parse Pino's JSON log line\r\n const pinoLog = JSON.parse(line);\r\n \r\n // Extract timestamp - Pino uses 'time' field (ISO 8601 string or milliseconds)\r\n // Convert to ISO 8601 string for Beamable\r\n let timestamp: string;\r\n if (typeof pinoLog.time === 'string') {\r\n timestamp = pinoLog.time;\r\n } else if (typeof pinoLog.time === 'number') {\r\n timestamp = new Date(pinoLog.time).toISOString();\r\n } else {\r\n timestamp = new Date().toISOString();\r\n }\r\n \r\n // Map Pino level to Beamable level\r\n const level = mapPinoLevelToBeamableLevel(pinoLog.level);\r\n \r\n // Build the message - combine msg with any additional fields\r\n // Pino's 'msg' field contains the log message\r\n const messageParts: string[] = [];\r\n if (pinoLog.msg) {\r\n messageParts.push(pinoLog.msg);\r\n }\r\n \r\n // Include error information if present\r\n if (pinoLog.err) {\r\n const err = pinoLog.err;\r\n const errMsg = err.message || err.msg || 'Error';\r\n const errStack = err.stack ? `\\n${err.stack}` : '';\r\n messageParts.push(`${errMsg}${errStack}`);\r\n }\r\n \r\n // Build the Beamable log format (for CloudWatch Logs Insights)\r\n const beamableLog: Record<string, unknown> = {\r\n __t: timestamp,\r\n __l: level,\r\n __m: messageParts.length > 0 ? messageParts.join(' ') : 'No message',\r\n };\r\n \r\n // Include additional context fields that might be useful\r\n // These are included in the message object but not as top-level fields\r\n const contextFields: Record<string, unknown> = {};\r\n if (pinoLog.cid) contextFields.cid = pinoLog.cid;\r\n if (pinoLog.pid) contextFields.pid = pinoLog.pid;\r\n if (pinoLog.routingKey) contextFields.routingKey = pinoLog.routingKey;\r\n if (pinoLog.service) contextFields.service = pinoLog.service;\r\n if (pinoLog.component) contextFields.component = pinoLog.component;\r\n \r\n // Include service name in context for CloudWatch filtering\r\n if (serviceName) {\r\n contextFields.serviceName = serviceName;\r\n }\r\n if (qualifiedServiceName) {\r\n contextFields.qualifiedServiceName = qualifiedServiceName;\r\n }\r\n \r\n // Include any other fields that aren't standard Pino fields\r\n const standardPinoFields = ['level', 'time', 'pid', 'hostname', 'name', 'msg', 'err', 'v', 'cid', 'pid', 'routingKey', 'sdkVersionExecution', 'service', 'component'];\r\n for (const [key, value] of Object.entries(pinoLog)) {\r\n if (!standardPinoFields.includes(key) && value !== undefined && value !== null) {\r\n contextFields[key] = value;\r\n }\r\n }\r\n \r\n // If there are context fields, include them in the log\r\n if (Object.keys(contextFields).length > 0) {\r\n beamableLog.__c = contextFields;\r\n }\r\n \r\n // Add OpenTelemetry-compatible fields for ClickHouse compatibility\r\n // These fields allow an OpenTelemetry collector to parse and forward logs to ClickHouse\r\n // The Portal's realm-level logs page queries ClickHouse's otel_logs table\r\n const otelFields: Record<string, unknown> = {};\r\n \r\n // ResourceAttributes - service identification (for ClickHouse filtering)\r\n const resourceAttributes: Record<string, unknown> = {};\r\n // IMPORTANT: The Portal searches for service:ExampleNodeService (just the service name)\r\n // So we need to set service.namespace to the service name, NOT the qualified name\r\n // The qualified name (micro_ExampleNodeService) is used for CloudWatch log stream filtering\r\n // But ClickHouse uses just the service name for filtering\r\n if (serviceName) {\r\n resourceAttributes['service.namespace'] = serviceName;\r\n resourceAttributes['service.name'] = serviceName;\r\n }\r\n // Also include qualified name for reference\r\n if (qualifiedServiceName) {\r\n resourceAttributes['service.qualifiedName'] = qualifiedServiceName;\r\n }\r\n if (pinoLog.cid) {\r\n resourceAttributes['beam.cid'] = String(pinoLog.cid);\r\n }\r\n if (pinoLog.pid) {\r\n resourceAttributes['beam.pid'] = String(pinoLog.pid);\r\n }\r\n if (pinoLog.routingKey) {\r\n resourceAttributes['beam.routing_key'] = String(pinoLog.routingKey);\r\n }\r\n \r\n // LogAttributes - log-specific attributes\r\n const logAttributes: Record<string, unknown> = {};\r\n if (pinoLog.component) {\r\n logAttributes['component'] = String(pinoLog.component);\r\n }\r\n if (pinoLog.err) {\r\n const err = pinoLog.err;\r\n if (err.message) logAttributes['exception.message'] = String(err.message);\r\n if (err.stack) logAttributes['exception.stacktrace'] = String(err.stack);\r\n if (err.type) logAttributes['exception.type'] = String(err.type);\r\n }\r\n \r\n // Map Beamable log level to OpenTelemetry SeverityText\r\n // Beamable: Debug, Info, Warning, Error, Fatal\r\n // OpenTelemetry: Trace, Debug, Info, Warn, Error, Fatal, Unspecified\r\n const severityTextMap: Record<string, string> = {\r\n 'Debug': 'Debug',\r\n 'Info': 'Information',\r\n 'Warning': 'Warning',\r\n 'Error': 'Error',\r\n 'Fatal': 'Critical',\r\n };\r\n const severityText = severityTextMap[level] || 'Information';\r\n \r\n // CRITICAL: ResourceAttributes and LogAttributes MUST always be present (even if empty)\r\n // ClickHouse schema expects these fields to exist for proper querying\r\n // The Portal queries ResourceAttributes['service.namespace'] - if ResourceAttributes is missing, queries fail\r\n \r\n // Ensure ResourceAttributes always has service.namespace when serviceName is provided\r\n // This is the primary field used by Portal for service filtering\r\n if (serviceName && !resourceAttributes['service.namespace']) {\r\n resourceAttributes['service.namespace'] = serviceName;\r\n resourceAttributes['service.name'] = serviceName;\r\n }\r\n \r\n // Add OpenTelemetry fields to the log\r\n // These are in addition to the Beamable format, so both systems can parse the logs\r\n // CRITICAL: Always include all required OpenTelemetry fields for ClickHouse compatibility\r\n otelFields['Timestamp'] = timestamp; // OpenTelemetry timestamp format (ISO 8601)\r\n otelFields['SeverityText'] = severityText;\r\n otelFields['Body'] = messageParts.length > 0 ? messageParts.join(' ') : 'No message';\r\n // ALWAYS include ResourceAttributes (even if empty) - required for ClickHouse schema\r\n otelFields['ResourceAttributes'] = resourceAttributes;\r\n // ALWAYS include LogAttributes (even if empty) - required for ClickHouse schema\r\n otelFields['LogAttributes'] = logAttributes;\r\n \r\n // IMPORTANT: For CloudWatch Logs Insights, we need to ensure @message contains valid JSON\r\n // The Portal expects @message to be a JSON string that can be parsed to extract __t, __l, __m\r\n // We output the Beamable format as the primary format, and include OpenTelemetry fields\r\n // CloudWatch will store this as @message, and an OpenTelemetry collector will parse it\r\n // and forward to ClickHouse's otel_logs table\r\n \r\n // Merge OpenTelemetry fields into the log (for ClickHouse compatibility)\r\n // An OpenTelemetry collector can parse these fields and forward to ClickHouse\r\n // Note: These extra fields won't break CloudWatch - it will just store them in @message\r\n Object.assign(beamableLog, otelFields);\r\n \r\n // Send log via OTLP if configured (similar to C# MicroserviceOtelLogRecordExporter)\r\n // Check if provider is available (may be null if still initializing)\r\n if (otlpProviderRef?.provider) {\r\n try {\r\n const otlpLogger = otlpProviderRef.provider.getLogger(\r\n serviceName || 'beamable-node-runtime',\r\n undefined, // version\r\n {\r\n schemaUrl: undefined, // optional schema URL\r\n }\r\n );\r\n \r\n // Map Beamable level to OpenTelemetry SeverityNumber\r\n const severityNumberMap: Record<string, number> = {\r\n 'Debug': 5, // SEVERITY_NUMBER_DEBUG\r\n 'Info': 9, // SEVERITY_NUMBER_INFO\r\n 'Warning': 13, // SEVERITY_NUMBER_WARN\r\n 'Error': 17, // SEVERITY_NUMBER_ERROR\r\n 'Fatal': 21, // SEVERITY_NUMBER_FATAL\r\n };\r\n \r\n // Create log record for OTLP\r\n otlpLogger.emit({\r\n severityNumber: severityNumberMap[level] || 9,\r\n severityText: severityText,\r\n body: messageParts.length > 0 ? messageParts.join(' ') : 'No message',\r\n attributes: {\r\n ...logAttributes,\r\n // Include additional context\r\n ...(pinoLog.component ? { component: String(pinoLog.component) } : {}),\r\n },\r\n timestamp: new Date(timestamp).getTime() * 1_000_000, // nanoseconds\r\n observedTimestamp: Date.now() * 1_000_000, // nanoseconds\r\n });\r\n } catch (otlpError) {\r\n // If OTLP send fails, continue with stdout logging\r\n // Don't block the log output\r\n // Could add retry logic here similar to C# if needed\r\n }\r\n }\r\n \r\n // Output as a single-line JSON string (required for CloudWatch)\r\n // CloudWatch Logs Insights will store this entire JSON string in the @message field\r\n const output = JSON.stringify(beamableLog) + '\\n';\r\n callback(null, Buffer.from(output, 'utf8'));\r\n } catch (error) {\r\n // If parsing fails, output a fallback log entry\r\n const fallbackLog = {\r\n __t: new Date().toISOString(),\r\n __l: 'Error',\r\n __m: `Failed to parse log entry: ${chunk.toString().substring(0, 200)}`,\r\n };\r\n callback(null, Buffer.from(JSON.stringify(fallbackLog) + '\\n', 'utf8'));\r\n }\r\n },\r\n });\r\n}\r\n\r\nexport function createLogger(env: EnvironmentConfig, options: LoggerFactoryOptions = {}): Logger {\r\n const configuredDestination = options.destinationPath ?? process.env.LOG_PATH;\r\n const usePrettyLogs = shouldUsePrettyLogs();\r\n \r\n // Shared reference for OTLP logger provider (updated asynchronously)\r\n const otlpProviderRef: { provider: LoggerProvider | null } = { provider: null };\r\n \r\n // Initialize OTLP logging in the background (similar to C# microservices)\r\n // This sends logs via OpenTelemetry Protocol in addition to stdout JSON\r\n // We start this async so it doesn't block logger creation\r\n const isInDocker = process.env.IS_LOCAL !== '1' && process.env.IS_LOCAL !== 'true';\r\n const standardOtelEnabled = isInDocker && !process.env.BEAM_DISABLE_STANDARD_OTEL;\r\n \r\n // Start collector discovery/start in background\r\n if (standardOtelEnabled || process.env.BEAM_OTEL_EXPORTER_OTLP_ENDPOINT) {\r\n // Create a temporary logger for OTLP initialization\r\n const tempLogger = pino({\r\n name: options.name ?? 'beamable-node-runtime',\r\n level: env.logLevel,\r\n }, process.stdout);\r\n \r\n // Initialize OTLP asynchronously (fire-and-forget)\r\n initializeOtlpLogging(\r\n options.serviceName,\r\n options.qualifiedServiceName,\r\n env,\r\n tempLogger\r\n ).then((provider) => {\r\n otlpProviderRef.provider = provider;\r\n if (provider) {\r\n tempLogger.info('[OTLP] OpenTelemetry logging initialized successfully');\r\n }\r\n }).catch((error) => {\r\n tempLogger.error(`[OTLP] Failed to initialize: ${error instanceof Error ? error.message : String(error)}`);\r\n });\r\n }\r\n\r\n const pinoOptions: LoggerOptions = {\r\n name: options.name ?? 'beamable-node-runtime',\r\n level: env.logLevel,\r\n base: {\r\n cid: env.cid,\r\n pid: env.pid,\r\n routingKey: env.routingKey ?? null,\r\n sdkVersionExecution: env.sdkVersionExecution,\r\n // Include service name in base fields for filtering\r\n serviceName: options.serviceName,\r\n qualifiedServiceName: options.qualifiedServiceName,\r\n },\r\n redact: {\r\n paths: ['secret', 'refreshToken'],\r\n censor: '***',\r\n },\r\n // Use timestamp in milliseconds (Pino default) for accurate conversion\r\n timestamp: pino.stdTimeFunctions.isoTime,\r\n };\r\n\r\n // For deployed services, always log to stdout so container orchestrator can collect logs\r\n // For local development, log to stdout unless a specific file path is provided\r\n if (!configuredDestination || configuredDestination === '-' || configuredDestination === 'stdout' || configuredDestination === 'console') {\r\n if (!usePrettyLogs) {\r\n // Deployed/remote: Use Beamable JSON format for log collection\r\n // Include OpenTelemetry fields for ClickHouse compatibility\r\n // Also send logs via OTLP if configured\r\n const beamableFormatter = createBeamableLogFormatter(\r\n options.serviceName,\r\n options.qualifiedServiceName,\r\n otlpProviderRef\r\n );\r\n beamableFormatter.pipe(process.stdout);\r\n return pino(pinoOptions, beamableFormatter);\r\n } else {\r\n // Local development: Use Pino's pretty printing for human-readable logs\r\n // Try to use pino-pretty if available (optional dependency)\r\n // If not available, fall back to default Pino JSON output\r\n try {\r\n // Check if pino-pretty is available\r\n // Use getRequire() which handles both CJS and ESM contexts\r\n const requireFn = getRequire();\r\n const pinoPretty = requireFn('pino-pretty');\r\n // Create a pretty stream with formatting options\r\n const prettyStream = pinoPretty({\r\n colorize: true,\r\n translateTime: 'HH:MM:ss.l',\r\n ignore: 'pid,hostname',\r\n singleLine: false,\r\n });\r\n // Use pino with the pretty stream\r\n return pino(pinoOptions, prettyStream);\r\n } catch {\r\n // pino-pretty not available, use default Pino output (JSON but readable)\r\n // This is expected if pino-pretty isn't installed, so we silently fall back\r\n return pino(pinoOptions, process.stdout);\r\n }\r\n }\r\n }\r\n\r\n // For file logging: Use Beamable format if not local, default Pino format if local\r\n const resolvedDestination = configuredDestination === 'temp' ? ensureWritableTempDirectory() : configuredDestination;\r\n if (!usePrettyLogs) {\r\n const beamableFormatter = createBeamableLogFormatter(\r\n options.serviceName,\r\n options.qualifiedServiceName,\r\n otlpProviderRef\r\n );\r\n const fileStream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });\r\n beamableFormatter.pipe(fileStream as unknown as NodeJS.WritableStream);\r\n return pino(pinoOptions, beamableFormatter);\r\n } else {\r\n const fileStream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });\r\n return pino(pinoOptions, fileStream);\r\n }\r\n}\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omen.foundation/node-microservice-runtime",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "description": "Beamable microservice runtime for Node.js/TypeScript services.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",