@ciq-dev/neoiq-foundation-node 1.1.2-beta.1 → 1.1.2-beta.11

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/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import { AutoInstrumentationConfigSchema, FeaturesConfigSchema, FoundationConfigSchema, LoggingConfigSchema, OtelConfigSchema, RedactionConfigSchema, RequestLoggingConfigSchema, ShutdownConfigSchema, SpanStatusCode, VaultConfigSchema, context, getActiveSpan, getDefaultOtelEndpoint, getTraceContext, getTracer, isTracingEnabled, parseConfig, propagation, setupTracing, shutdownTracing, trace } from "./tracing-Mxxp4y94.mjs";
1
+ import { AutoInstrumentationConfigSchema, FeaturesConfigSchema, FoundationConfigSchema, LoggingConfigSchema, OtelConfigSchema, RedactionConfigSchema, RequestLoggingConfigSchema, ShutdownConfigSchema, SpanStatusCode, VaultConfigSchema, context, getActiveSpan, getDefaultOtelEndpoint, getTraceContext, getTracer, isTracingEnabled, parseConfig, propagation, setupTracing, shutdownTracing, trace } from "./tracing-CVXLySRJ.mjs";
2
2
  import { resourceFromAttributes } from "@opentelemetry/resources";
3
3
  import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
4
- import { DiagLogLevel, SpanStatusCode as SpanStatusCode$1, context as context$1, diag, metrics, propagation as propagation$1, trace as trace$1 } from "@opentelemetry/api";
4
+ import { SpanStatusCode as SpanStatusCode$1, context as context$1, metrics, propagation as propagation$1, trace as trace$1 } from "@opentelemetry/api";
5
5
  import { AsyncLocalStorage } from "async_hooks";
6
6
  import pino from "pino";
7
7
  import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-grpc";
@@ -341,7 +341,34 @@ function getGlobalLogger() {
341
341
  //#region src/features/metrics.ts
342
342
  let meterProvider = null;
343
343
  let isInitialized = false;
344
- /** Initialize OpenTelemetry metrics */
344
+ /**
345
+ * Build a gRPC OTLP metric reader for the tracing NodeSDK to own.
346
+ *
347
+ * Preferred path: hand this reader to `setupTracing({ metricReader })` so a single
348
+ * NodeSDK owns both traces and metrics — one global MeterProvider, exporting to the
349
+ * same gRPC collector. This avoids the duplicate global-MeterProvider registration
350
+ * that occurs when NodeSDK and a standalone `setupMetrics` both try to register.
351
+ *
352
+ * The MeterProvider, resource, and global registration are owned by NodeSDK; this
353
+ * function only constructs the reader/exporter.
354
+ */
355
+ function createMetricReader(options = {}) {
356
+ const { endpoint = getDefaultOtelEndpoint(), intervalMs = 5e3 } = options;
357
+ const exporter = new OTLPMetricExporter({ url: endpoint });
358
+ const reader = new PeriodicExportingMetricReader({
359
+ exporter,
360
+ exportIntervalMillis: intervalMs
361
+ });
362
+ isInitialized = true;
363
+ return reader;
364
+ }
365
+ /**
366
+ * Initialize a standalone metrics pipeline with its own MeterProvider.
367
+ *
368
+ * Fallback for when tracing (and thus the NodeSDK) is disabled but metrics are
369
+ * enabled. When tracing is on, prefer {@link createMetricReader} + NodeSDK so a
370
+ * single SDK owns both signals.
371
+ */
345
372
  function setupMetrics(options) {
346
373
  if (isInitialized) {
347
374
  console.warn("[neoiq-foundation] Metrics already initialized");
@@ -447,6 +474,11 @@ function createObservabilityPlugin(options) {
447
474
  const activeSpan = tracingEnabled ? trace$1.getActiveSpan() : void 0;
448
475
  if (activeSpan) {
449
476
  activeSpan.setAttribute("http.correlation_id", correlationId);
477
+ const routeTemplate = request.routeOptions?.url;
478
+ if (routeTemplate) {
479
+ activeSpan.setAttribute("http.route", routeTemplate);
480
+ activeSpan.updateName(`${request.method} ${routeTemplate}`);
481
+ }
450
482
  const spanContext = activeSpan.spanContext();
451
483
  traceId = spanContext.traceId;
452
484
  spanId = spanContext.spanId;
@@ -913,7 +945,7 @@ function createFoundation(input) {
913
945
  serviceVersion,
914
946
  environment,
915
947
  level: loggingConfig.level ?? "info",
916
- prettyPrint: loggingConfig.prettyPrint ?? environment === "development",
948
+ prettyPrint: loggingConfig.prettyPrint ?? process.stdout.isTTY === true,
917
949
  contextManager,
918
950
  additionalRedactPaths: redactionConfig.additionalPaths
919
951
  });
@@ -944,38 +976,16 @@ function createFoundation(input) {
944
976
  };
945
977
  console.error("[neoiq-foundation] Logger setup failed, using console:", loggingError);
946
978
  }
947
- const diagLevelName = (process.env.OTEL_DIAG_LOG_LEVEL || otel.diagLogLevel).toLowerCase();
948
- if (diagLevelName !== "none") {
949
- const diagLevels = {
950
- error: DiagLogLevel.ERROR,
951
- warn: DiagLogLevel.WARN,
952
- info: DiagLogLevel.INFO,
953
- debug: DiagLogLevel.DEBUG,
954
- verbose: DiagLogLevel.VERBOSE
955
- };
956
- const diagLogger = {
957
- error: (message, ...args) => logger.error({
958
- source: "otel",
959
- args
960
- }, message),
961
- warn: (message, ...args) => logger.warn({
962
- source: "otel",
963
- args
964
- }, message),
965
- info: (message, ...args) => logger.info({
966
- source: "otel",
967
- args
968
- }, message),
969
- debug: (message, ...args) => logger.debug({
970
- source: "otel",
971
- args
972
- }, message),
973
- verbose: (message, ...args) => logger.debug({
974
- source: "otel",
975
- args
976
- }, message)
977
- };
978
- diag.setLogger(diagLogger, diagLevels[diagLevelName] ?? DiagLogLevel.WARN);
979
+ let metricReader;
980
+ let metricsError;
981
+ if (features.metrics && features.tracing) try {
982
+ metricReader = createMetricReader({
983
+ endpoint: otel.endpoint,
984
+ intervalMs: otel.metricsIntervalMs
985
+ });
986
+ } catch (err) {
987
+ metricsError = err.message;
988
+ logger.error({ error: metricsError }, "Metrics setup failed - continuing without metrics");
979
989
  }
980
990
  let tracerInstance = null;
981
991
  let tracingError;
@@ -985,7 +995,8 @@ function createFoundation(input) {
985
995
  serviceVersion,
986
996
  environment,
987
997
  endpoint: otel.endpoint,
988
- autoInstrumentation: featuresConfig.autoInstrumentation
998
+ autoInstrumentation: featuresConfig.autoInstrumentation,
999
+ metricReader
989
1000
  });
990
1001
  tracerInstance = getTracer(serviceName);
991
1002
  logger.info({
@@ -996,9 +1007,7 @@ function createFoundation(input) {
996
1007
  tracingError = err.message;
997
1008
  logger.error({ error: tracingError }, "Tracing setup failed - continuing without tracing");
998
1009
  }
999
- let meterInstance = null;
1000
- let metricsError;
1001
- if (features.metrics) try {
1010
+ if (features.metrics && !features.tracing && !metricsError) try {
1002
1011
  setupMetrics({
1003
1012
  serviceName,
1004
1013
  serviceVersion,
@@ -1006,14 +1015,18 @@ function createFoundation(input) {
1006
1015
  endpoint: otel.endpoint,
1007
1016
  intervalMs: otel.metricsIntervalMs
1008
1017
  });
1018
+ } catch (err) {
1019
+ metricsError = err.message;
1020
+ logger.error({ error: metricsError }, "Metrics setup failed - continuing without metrics");
1021
+ }
1022
+ else if (metricReader && tracingError && !metricsError) metricsError = `metrics SDK (tracing) failed to start: ${tracingError}`;
1023
+ let meterInstance = null;
1024
+ if (features.metrics && !metricsError) {
1009
1025
  meterInstance = getMeter(serviceName);
1010
1026
  logger.info({
1011
1027
  feature: "metrics",
1012
1028
  interval: `${otel.metricsIntervalMs}ms`
1013
1029
  }, "Metrics enabled");
1014
- } catch (err) {
1015
- metricsError = err.message;
1016
- logger.error({ error: metricsError }, "Metrics setup failed - continuing without metrics");
1017
1030
  }
1018
1031
  logger.info({
1019
1032
  serviceName,