@ciq-dev/neoiq-foundation-node 1.1.2-beta.12 → 1.1.2-beta.14
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/bootstrap.js +1 -1
- package/dist/bootstrap.mjs +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/{tracing-CVXLySRJ.mjs → tracing-BTJDb_sr.mjs} +63 -6
- package/dist/tracing-BTJDb_sr.mjs.map +1 -0
- package/dist/{tracing-BYBASOyy.js → tracing-CCmYF9oC.js} +61 -4
- package/dist/tracing-CCmYF9oC.js.map +1 -0
- package/package.json +2 -1
- package/dist/tracing-BYBASOyy.js.map +0 -1
- package/dist/tracing-CVXLySRJ.mjs.map +0 -1
|
@@ -2,9 +2,10 @@ import { createRequire } from "module";
|
|
|
2
2
|
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
3
3
|
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
|
|
4
4
|
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-grpc";
|
|
5
|
+
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
5
6
|
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
6
7
|
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
|
|
7
|
-
import { SpanStatusCode as SpanStatusCode$1, context as context$1, propagation as propagation$1, trace as trace$1 } from "@opentelemetry/api";
|
|
8
|
+
import { DiagConsoleLogger, DiagLogLevel, SpanStatusCode as SpanStatusCode$1, context as context$1, diag, propagation as propagation$1, trace as trace$1 } from "@opentelemetry/api";
|
|
8
9
|
import { z } from "zod";
|
|
9
10
|
|
|
10
11
|
//#region rolldown:runtime
|
|
@@ -21,7 +22,7 @@ const AutoInstrumentationConfigSchema = z.object({
|
|
|
21
22
|
mysql: z.boolean().default(true),
|
|
22
23
|
redis: z.boolean().default(true),
|
|
23
24
|
ioredis: z.boolean().default(true),
|
|
24
|
-
grpc: z.boolean().default(
|
|
25
|
+
grpc: z.boolean().default(false),
|
|
25
26
|
fs: z.boolean().default(false),
|
|
26
27
|
dns: z.boolean().default(false)
|
|
27
28
|
}).partial();
|
|
@@ -128,6 +129,20 @@ function setupTracing(options) {
|
|
|
128
129
|
console.warn("[neoiq-foundation] Tracing already initialized");
|
|
129
130
|
return;
|
|
130
131
|
}
|
|
132
|
+
if (process.env.OTEL_DEBUG === "true") diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);
|
|
133
|
+
else if (process.env.OTEL_LOG_LEVEL) {
|
|
134
|
+
const levelMap = {
|
|
135
|
+
none: DiagLogLevel.NONE,
|
|
136
|
+
error: DiagLogLevel.ERROR,
|
|
137
|
+
warn: DiagLogLevel.WARN,
|
|
138
|
+
info: DiagLogLevel.INFO,
|
|
139
|
+
debug: DiagLogLevel.DEBUG,
|
|
140
|
+
verbose: DiagLogLevel.VERBOSE,
|
|
141
|
+
all: DiagLogLevel.ALL
|
|
142
|
+
};
|
|
143
|
+
const level = levelMap[process.env.OTEL_LOG_LEVEL.toLowerCase()];
|
|
144
|
+
if (level !== void 0) diag.setLogger(new DiagConsoleLogger(), level);
|
|
145
|
+
}
|
|
131
146
|
warnPreloadedModules();
|
|
132
147
|
const { serviceName, serviceVersion, environment, endpoint = getDefaultOtelEndpoint(), autoInstrumentation = {}, metricReader } = options;
|
|
133
148
|
const resource = resourceFromAttributes({
|
|
@@ -136,16 +151,58 @@ function setupTracing(options) {
|
|
|
136
151
|
"deployment.environment": environment
|
|
137
152
|
});
|
|
138
153
|
const traceExporter = new OTLPTraceExporter({ url: endpoint });
|
|
154
|
+
const spanProcessor = new BatchSpanProcessor(traceExporter, {
|
|
155
|
+
maxQueueSize: intEnv("OTEL_BSP_MAX_QUEUE_SIZE", 4096),
|
|
156
|
+
maxExportBatchSize: intEnv("OTEL_BSP_MAX_EXPORT_BATCH_SIZE", 512),
|
|
157
|
+
scheduledDelayMillis: intEnv("OTEL_BSP_SCHEDULE_DELAY", 2e3),
|
|
158
|
+
exportTimeoutMillis: intEnv("OTEL_BSP_EXPORT_TIMEOUT", 3e4)
|
|
159
|
+
});
|
|
139
160
|
if (!metricReader && !process.env.OTEL_METRICS_EXPORTER) process.env.OTEL_METRICS_EXPORTER = "none";
|
|
140
161
|
sdk = new NodeSDK({
|
|
141
162
|
resource,
|
|
142
|
-
|
|
163
|
+
spanProcessors: [spanProcessor],
|
|
143
164
|
...metricReader ? { metricReaders: [metricReader] } : {},
|
|
144
165
|
logRecordProcessors: [],
|
|
145
166
|
instrumentations: buildInstrumentations(autoInstrumentation)
|
|
146
167
|
});
|
|
147
168
|
sdk.start();
|
|
148
169
|
isInitialized = true;
|
|
170
|
+
probeEndpoint(endpoint);
|
|
171
|
+
}
|
|
172
|
+
function intEnv(name, fallback) {
|
|
173
|
+
const v = process.env[name];
|
|
174
|
+
if (v === void 0) return fallback;
|
|
175
|
+
const n = parseInt(v, 10);
|
|
176
|
+
return Number.isNaN(n) ? fallback : n;
|
|
177
|
+
}
|
|
178
|
+
/** Probe the OTLP endpoint to surface connectivity issues at startup. */
|
|
179
|
+
function probeEndpoint(endpoint) {
|
|
180
|
+
try {
|
|
181
|
+
const net = __require("net");
|
|
182
|
+
const url = new URL(endpoint);
|
|
183
|
+
const host = url.hostname;
|
|
184
|
+
const port = parseInt(url.port, 10) || 4317;
|
|
185
|
+
const socket = net.createConnection({
|
|
186
|
+
host,
|
|
187
|
+
port,
|
|
188
|
+
timeout: 5e3
|
|
189
|
+
}, () => {
|
|
190
|
+
console.log(`[neoiq-foundation] OTLP endpoint reachable at ${host}:${port}`);
|
|
191
|
+
socket.destroy();
|
|
192
|
+
});
|
|
193
|
+
let resolved = false;
|
|
194
|
+
socket.on("timeout", () => {
|
|
195
|
+
if (resolved) return;
|
|
196
|
+
resolved = true;
|
|
197
|
+
console.error(`[neoiq-foundation] WARNING: Cannot reach ${host}:${port} — connection timed out. Traces may NOT be exported.`);
|
|
198
|
+
socket.destroy();
|
|
199
|
+
});
|
|
200
|
+
socket.on("error", (err) => {
|
|
201
|
+
if (resolved) return;
|
|
202
|
+
resolved = true;
|
|
203
|
+
console.error(`[neoiq-foundation] WARNING: Cannot reach ${host}:${port} — ${err.message}. Traces may NOT be exported.`);
|
|
204
|
+
});
|
|
205
|
+
} catch {}
|
|
149
206
|
}
|
|
150
207
|
/**
|
|
151
208
|
* Build the full instrumentation list for the NodeSDK.
|
|
@@ -174,7 +231,7 @@ function buildInstrumentationConfig(config) {
|
|
|
174
231
|
const result = {};
|
|
175
232
|
for (const [key, instrumentationName] of Object.entries(mapping)) {
|
|
176
233
|
const userSetting = config[key];
|
|
177
|
-
const defaultValue = key !== "fs" && key !== "dns";
|
|
234
|
+
const defaultValue = key !== "fs" && key !== "dns" && key !== "grpc";
|
|
178
235
|
result[instrumentationName] = { enabled: userSetting ?? defaultValue };
|
|
179
236
|
}
|
|
180
237
|
return result;
|
|
@@ -211,5 +268,5 @@ function isTracingEnabled() {
|
|
|
211
268
|
}
|
|
212
269
|
|
|
213
270
|
//#endregion
|
|
214
|
-
export { AutoInstrumentationConfigSchema, FeaturesConfigSchema, FoundationConfigSchema, LoggingConfigSchema, OtelConfigSchema, RedactionConfigSchema, RequestLoggingConfigSchema, ShutdownConfigSchema, SpanStatusCode$1 as SpanStatusCode, VaultConfigSchema, context$1 as context, getActiveSpan, getDefaultOtelEndpoint, getTraceContext, getTracer, isTracingEnabled, parseConfig, propagation$1 as propagation, setupTracing, shutdownTracing, trace$1 as trace };
|
|
215
|
-
//# sourceMappingURL=tracing-
|
|
271
|
+
export { AutoInstrumentationConfigSchema, FeaturesConfigSchema, FoundationConfigSchema, LoggingConfigSchema, OtelConfigSchema, RedactionConfigSchema, RequestLoggingConfigSchema, ShutdownConfigSchema, SpanStatusCode$1 as SpanStatusCode, VaultConfigSchema, __require, context$1 as context, getActiveSpan, getDefaultOtelEndpoint, getTraceContext, getTracer, isTracingEnabled, parseConfig, propagation$1 as propagation, setupTracing, shutdownTracing, trace$1 as trace };
|
|
272
|
+
//# sourceMappingURL=tracing-BTJDb_sr.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracing-BTJDb_sr.mjs","names":["input: FoundationConfigInput","sdk: NodeSDK | null","options: TracingOptions","levelMap: Record<string, DiagLogLevel>","name: string","fallback: number","endpoint: string","err: Error","autoInstrumentation: AutoInstrumentationConfig","config: AutoInstrumentationConfig","mapping: Record<string, string>","result: Record<string, { enabled: boolean }>"],"sources":["../src/config.ts","../src/features/tracing.ts"],"sourcesContent":["/**\n * Foundation Configuration with Zod Validation\n */\n\nimport { z } from 'zod';\n\n// Auto-Instrumentation Config\nexport const AutoInstrumentationConfigSchema = z\n .object({\n http: z.boolean().default(true),\n fastify: z.boolean().default(true),\n express: z.boolean().default(true),\n mongodb: z.boolean().default(true),\n pg: z.boolean().default(true),\n mysql: z.boolean().default(true),\n redis: z.boolean().default(true),\n ioredis: z.boolean().default(true),\n grpc: z.boolean().default(false),\n fs: z.boolean().default(false),\n dns: z.boolean().default(false),\n })\n .partial();\n\nexport type AutoInstrumentationConfig = z.infer<typeof AutoInstrumentationConfigSchema>;\n\n// Features Config\nexport const FeaturesConfigSchema = z\n .object({\n tracing: z.boolean().default(true),\n metrics: z.boolean().default(true),\n logging: z.boolean().default(true),\n autoInstrumentation: AutoInstrumentationConfigSchema.default({}),\n })\n .partial();\n\nexport type FeaturesConfig = z.infer<typeof FeaturesConfigSchema>;\n\n// OTEL Config\nconst DEFAULT_OTEL_ENDPOINT =\n 'http://otel-stack-deployment-collector.observability.svc.cluster.local:4317';\n\nexport const OtelConfigSchema = z.object({\n endpoint: z\n .string()\n .default(() => process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTEL_ENDPOINT),\n metricsIntervalMs: z.number().min(1000).default(5000),\n});\n\nexport type OtelConfig = z.infer<typeof OtelConfigSchema>;\n\n// Logging Config\nexport const LoggingConfigSchema = z\n .object({\n level: z.enum(['debug', 'info', 'warn', 'error']).default('info'),\n prettyPrint: z.boolean().optional(),\n })\n .partial();\n\nexport type LoggingConfig = z.infer<typeof LoggingConfigSchema>;\n\n// Request Logging Config\nexport const RequestLoggingConfigSchema = z\n .object({\n logHeaders: z.boolean().default(true),\n logBody: z.boolean().default(false),\n logResponseBody: z.boolean().default(false),\n maxBodySize: z.number().default(10 * 1024),\n redactHeaders: z.array(z.string()).optional(),\n })\n .partial();\n\nexport type RequestLoggingConfig = z.infer<typeof RequestLoggingConfigSchema>;\n\n// Redaction Config\nexport const RedactionConfigSchema = z\n .object({\n additionalPaths: z.array(z.string()).default([]),\n })\n .partial();\n\nexport type RedactionConfig = z.infer<typeof RedactionConfigSchema>;\n\n// Shutdown Config\nexport const ShutdownConfigSchema = z\n .object({\n /** Flush telemetry on uncaughtException / unhandledRejection before process exits */\n flushOnCrash: z.boolean().default(false),\n /** Grace period (ms) for flushing before forcing exit */\n flushTimeoutMs: z.number().min(100).default(5000),\n })\n .partial();\n\nexport type ShutdownConfig = z.infer<typeof ShutdownConfigSchema>;\n\n// Vault Config\nexport const VaultConfigSchema = z.object({\n enabled: z.boolean().default(false),\n address: z.string().default('https://vault.beta.commerceiq.ai'),\n authMethod: z.enum(['kubernetes', 'token']).default('kubernetes'),\n role: z.string().optional(),\n mountPoint: z.string().default('secret'),\n tokenPath: z.string().default('/var/run/secrets/kubernetes.io/serviceaccount/token'),\n k8sAuthMount: z.string().default('kubernetes'),\n namespace: z.string().optional(),\n});\n\nexport type VaultConfig = z.infer<typeof VaultConfigSchema>;\n\n// Main Foundation Config\nexport const FoundationConfigSchema = z.object({\n serviceName: z.string().min(1, 'serviceName is required'),\n serviceVersion: z.string().default(process.env.SERVICE_VERSION || '1.0.0'),\n environment: z\n .enum(['development', 'staging', 'qa', 'production'])\n .default(\n ['development', 'staging', 'qa', 'production'].includes(process.env.NODE_ENV ?? '')\n ? (process.env.NODE_ENV as 'development' | 'staging' | 'qa' | 'production')\n : 'development'\n ),\n features: FeaturesConfigSchema.default({}),\n otel: OtelConfigSchema.default({}),\n logging: LoggingConfigSchema.default({}),\n requestLogging: RequestLoggingConfigSchema.default({}),\n redaction: RedactionConfigSchema.default({}),\n shutdown: ShutdownConfigSchema.default({}),\n vault: VaultConfigSchema.default({}),\n});\n\nexport type FoundationConfig = z.infer<typeof FoundationConfigSchema>;\n\nexport type FoundationConfigInput = z.input<typeof FoundationConfigSchema> & {\n serviceName: string;\n};\n\n/** Parse and validate configuration */\nexport function parseConfig(input: FoundationConfigInput): FoundationConfig {\n const result = FoundationConfigSchema.safeParse(input);\n\n if (!result.success) {\n const errors = result.error.errors\n .map((e) => ` - ${e.path.join('.')}: ${e.message}`)\n .join('\\n');\n throw new Error(`Invalid foundation configuration:\\n${errors}`);\n }\n\n return result.data;\n}\n\n/** Get default OTEL endpoint */\nexport function getDefaultOtelEndpoint(): string {\n return process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTEL_ENDPOINT;\n}\n","/**\n * OpenTelemetry Tracing Setup\n */\n\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';\nimport { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';\nimport {\n trace,\n context,\n propagation,\n SpanStatusCode,\n diag,\n DiagConsoleLogger,\n DiagLogLevel,\n type Tracer,\n} from '@opentelemetry/api';\nimport { type IMetricReader } from '@opentelemetry/sdk-metrics';\nimport { type AutoInstrumentationConfig, getDefaultOtelEndpoint } from '../config';\n\n/**\n * Element type of the NodeSDK instrumentations list. Derived from\n * getNodeAutoInstrumentations so we don't take a direct import on\n * @opentelemetry/instrumentation (only a transitive dependency here).\n */\ntype Instrumentation = ReturnType<typeof getNodeAutoInstrumentations>[number];\n\nexport interface TracingOptions {\n serviceName: string;\n serviceVersion: string;\n environment: string;\n endpoint?: string;\n autoInstrumentation?: AutoInstrumentationConfig;\n /**\n * Metric reader to attach to the NodeSDK so a single SDK owns both traces and\n * metrics (one global MeterProvider, same gRPC collector). When provided, NodeSDK\n * is the sole owner of metrics; when omitted, NodeSDK is opted out of metrics\n * entirely (see below) so it never registers a stray default MeterProvider.\n */\n metricReader?: IMetricReader;\n}\n\nlet sdk: NodeSDK | null = null;\nlet isInitialized = false;\n\nconst MONITORED_MODULES = ['pg', 'mongodb', 'ioredis', 'mysql2', 'express', '@grpc/grpc-js'];\n\nfunction warnPreloadedModules(): void {\n // require.resolve / require.cache are only available in CJS contexts\n if (typeof require === 'undefined' || typeof require.cache === 'undefined') return;\n\n for (const mod of MONITORED_MODULES) {\n try {\n const resolved = require.resolve(mod);\n if (require.cache[resolved]) {\n console.warn(\n `[neoiq-foundation] \"${mod}\" was imported before tracing init. ` +\n 'Auto-instrumentation may not work for this module. ' +\n 'Use node -r @ciq-dev/neoiq-foundation-node/bootstrap or call createFoundation() first.'\n );\n }\n } catch {\n // Module not installed — no warning needed\n }\n }\n}\n\n/** Initialize OpenTelemetry tracing */\nexport function setupTracing(options: TracingOptions): void {\n if (isInitialized) {\n console.warn('[neoiq-foundation] Tracing already initialized');\n return;\n }\n\n // Enable OTEL diagnostic logging when OTEL_DEBUG=true so export failures,\n // connection errors, and dropped spans are visible in application logs.\n if (process.env.OTEL_DEBUG === 'true') {\n diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);\n } else if (process.env.OTEL_LOG_LEVEL) {\n const levelMap: Record<string, DiagLogLevel> = {\n none: DiagLogLevel.NONE,\n error: DiagLogLevel.ERROR,\n warn: DiagLogLevel.WARN,\n info: DiagLogLevel.INFO,\n debug: DiagLogLevel.DEBUG,\n verbose: DiagLogLevel.VERBOSE,\n all: DiagLogLevel.ALL,\n };\n const level = levelMap[process.env.OTEL_LOG_LEVEL.toLowerCase()];\n if (level !== undefined) {\n diag.setLogger(new DiagConsoleLogger(), level);\n }\n }\n\n warnPreloadedModules();\n\n const {\n serviceName,\n serviceVersion,\n environment,\n endpoint = getDefaultOtelEndpoint(),\n autoInstrumentation = {},\n metricReader,\n } = options;\n\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: serviceName,\n [ATTR_SERVICE_VERSION]: serviceVersion,\n 'deployment.environment': environment,\n });\n\n const traceExporter = new OTLPTraceExporter({ url: endpoint });\n\n // Use an explicit BatchSpanProcessor so we control queue sizes and timeouts\n // rather than relying on NodeSDK's internal defaults. Env vars\n // OTEL_BSP_MAX_QUEUE_SIZE, OTEL_BSP_MAX_EXPORT_BATCH_SIZE,\n // OTEL_BSP_SCHEDULE_DELAY, and OTEL_BSP_EXPORT_TIMEOUT override these.\n const spanProcessor = new BatchSpanProcessor(traceExporter, {\n maxQueueSize: intEnv('OTEL_BSP_MAX_QUEUE_SIZE', 4096),\n maxExportBatchSize: intEnv('OTEL_BSP_MAX_EXPORT_BATCH_SIZE', 512),\n scheduledDelayMillis: intEnv('OTEL_BSP_SCHEDULE_DELAY', 2000),\n exportTimeoutMillis: intEnv('OTEL_BSP_EXPORT_TIMEOUT', 30000),\n });\n\n // Foundation owns every OpenTelemetry signal explicitly. Left to its own devices,\n // NodeSDK auto-configures metrics AND logs exporters from env defaults — both OTLP\n // over HTTP to localhost:4318 — which nothing listens on, producing ECONNREFUSED\n // spam every export interval. We therefore configure each signal on NodeSDK directly:\n // - traces: the explicit BatchSpanProcessor above.\n // - metrics: the explicit gRPC reader when provided (single SDK, single global\n // MeterProvider, same collector as traces). When metrics are disabled\n // and the deployer hasn't chosen an exporter, opt NodeSDK out via\n // OTEL_METRICS_EXPORTER=none (the only switch NodeSDK exposes for that).\n // - logs: foundation logs via pino, never OTLP. Passing an empty processor list\n // stops NodeSDK from falling back to its env-default localhost:4318 log\n // exporter (`logRecordProcessors: []` takes the explicit branch and skips\n // configureLoggerProviderFromEnv()).\n if (!metricReader && !process.env.OTEL_METRICS_EXPORTER) {\n process.env.OTEL_METRICS_EXPORTER = 'none';\n }\n\n sdk = new NodeSDK({\n resource,\n spanProcessors: [spanProcessor],\n ...(metricReader ? { metricReaders: [metricReader] } : {}),\n logRecordProcessors: [],\n instrumentations: buildInstrumentations(autoInstrumentation),\n });\n\n sdk.start();\n isInitialized = true;\n\n probeEndpoint(endpoint);\n}\n\nfunction intEnv(name: string, fallback: number): number {\n const v = process.env[name];\n if (v === undefined) return fallback;\n const n = parseInt(v, 10);\n return Number.isNaN(n) ? fallback : n;\n}\n\n/** Probe the OTLP endpoint to surface connectivity issues at startup. */\nfunction probeEndpoint(endpoint: string): void {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const net = require('net') as typeof import('net');\n const url = new URL(endpoint);\n const host = url.hostname;\n const port = parseInt(url.port, 10) || 4317;\n const socket = net.createConnection({ host, port, timeout: 5000 }, () => {\n console.log(`[neoiq-foundation] OTLP endpoint reachable at ${host}:${port}`);\n socket.destroy();\n });\n let resolved = false;\n socket.on('timeout', () => {\n if (resolved) return;\n resolved = true;\n console.error(\n `[neoiq-foundation] WARNING: Cannot reach ${host}:${port} — connection timed out. Traces may NOT be exported.`\n );\n socket.destroy();\n });\n socket.on('error', (err: Error) => {\n if (resolved) return;\n resolved = true;\n console.error(\n `[neoiq-foundation] WARNING: Cannot reach ${host}:${port} — ${err.message}. Traces may NOT be exported.`\n );\n });\n } catch {\n // Don't crash if probe fails\n }\n}\n\n/**\n * Build the full instrumentation list for the NodeSDK.\n *\n * auto-instrumentations-node owns the framework/library instrumentation set. Keep\n * Fastify in the same config map as the other OpenTelemetry instrumentations so we\n * get route/server spans without adding @fastify/otel's lifecycle hook waterfall.\n */\nexport function buildInstrumentations(\n autoInstrumentation: AutoInstrumentationConfig = {}\n): Instrumentation[] {\n return [...getNodeAutoInstrumentations(buildInstrumentationConfig(autoInstrumentation))];\n}\n\nfunction buildInstrumentationConfig(\n config: AutoInstrumentationConfig\n): Record<string, { enabled: boolean }> {\n const mapping: Record<string, string> = {\n http: '@opentelemetry/instrumentation-http',\n fastify: '@opentelemetry/instrumentation-fastify',\n express: '@opentelemetry/instrumentation-express',\n mongodb: '@opentelemetry/instrumentation-mongodb',\n pg: '@opentelemetry/instrumentation-pg',\n mysql: '@opentelemetry/instrumentation-mysql',\n redis: '@opentelemetry/instrumentation-redis',\n ioredis: '@opentelemetry/instrumentation-ioredis',\n grpc: '@opentelemetry/instrumentation-grpc',\n fs: '@opentelemetry/instrumentation-fs',\n dns: '@opentelemetry/instrumentation-dns',\n };\n\n const result: Record<string, { enabled: boolean }> = {};\n for (const [key, instrumentationName] of Object.entries(mapping)) {\n const userSetting = config[key as keyof AutoInstrumentationConfig];\n const defaultValue = key !== 'fs' && key !== 'dns' && key !== 'grpc';\n result[instrumentationName] = { enabled: userSetting ?? defaultValue };\n }\n return result;\n}\n\n/** Shutdown tracing gracefully */\nexport async function shutdownTracing(): Promise<void> {\n if (!sdk) return;\n try {\n await sdk.shutdown();\n isInitialized = false;\n sdk = null;\n } catch (error) {\n console.error('[neoiq-foundation] Error shutting down tracing:', error);\n throw error;\n }\n}\n\nexport function getTracer(name: string): Tracer {\n return trace.getTracer(name);\n}\n\nexport function getActiveSpan(): ReturnType<typeof trace.getActiveSpan> {\n return trace.getActiveSpan();\n}\n\nexport function getTraceContext(): { traceId?: string; spanId?: string } {\n const span = trace.getActiveSpan();\n if (!span) return {};\n const ctx = span.spanContext();\n return { traceId: ctx.traceId, spanId: ctx.spanId };\n}\n\nexport function isTracingEnabled(): boolean {\n return isInitialized;\n}\n\nexport { trace, context, propagation, SpanStatusCode };\nexport type { Tracer };\n"],"mappings":"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"}
|
|
@@ -25,6 +25,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
const __opentelemetry_sdk_node = __toESM(require("@opentelemetry/sdk-node"));
|
|
26
26
|
const __opentelemetry_auto_instrumentations_node = __toESM(require("@opentelemetry/auto-instrumentations-node"));
|
|
27
27
|
const __opentelemetry_exporter_trace_otlp_grpc = __toESM(require("@opentelemetry/exporter-trace-otlp-grpc"));
|
|
28
|
+
const __opentelemetry_sdk_trace_base = __toESM(require("@opentelemetry/sdk-trace-base"));
|
|
28
29
|
const __opentelemetry_resources = __toESM(require("@opentelemetry/resources"));
|
|
29
30
|
const __opentelemetry_semantic_conventions = __toESM(require("@opentelemetry/semantic-conventions"));
|
|
30
31
|
const __opentelemetry_api = __toESM(require("@opentelemetry/api"));
|
|
@@ -40,7 +41,7 @@ const AutoInstrumentationConfigSchema = zod.z.object({
|
|
|
40
41
|
mysql: zod.z.boolean().default(true),
|
|
41
42
|
redis: zod.z.boolean().default(true),
|
|
42
43
|
ioredis: zod.z.boolean().default(true),
|
|
43
|
-
grpc: zod.z.boolean().default(
|
|
44
|
+
grpc: zod.z.boolean().default(false),
|
|
44
45
|
fs: zod.z.boolean().default(false),
|
|
45
46
|
dns: zod.z.boolean().default(false)
|
|
46
47
|
}).partial();
|
|
@@ -147,6 +148,20 @@ function setupTracing(options) {
|
|
|
147
148
|
console.warn("[neoiq-foundation] Tracing already initialized");
|
|
148
149
|
return;
|
|
149
150
|
}
|
|
151
|
+
if (process.env.OTEL_DEBUG === "true") __opentelemetry_api.diag.setLogger(new __opentelemetry_api.DiagConsoleLogger(), __opentelemetry_api.DiagLogLevel.DEBUG);
|
|
152
|
+
else if (process.env.OTEL_LOG_LEVEL) {
|
|
153
|
+
const levelMap = {
|
|
154
|
+
none: __opentelemetry_api.DiagLogLevel.NONE,
|
|
155
|
+
error: __opentelemetry_api.DiagLogLevel.ERROR,
|
|
156
|
+
warn: __opentelemetry_api.DiagLogLevel.WARN,
|
|
157
|
+
info: __opentelemetry_api.DiagLogLevel.INFO,
|
|
158
|
+
debug: __opentelemetry_api.DiagLogLevel.DEBUG,
|
|
159
|
+
verbose: __opentelemetry_api.DiagLogLevel.VERBOSE,
|
|
160
|
+
all: __opentelemetry_api.DiagLogLevel.ALL
|
|
161
|
+
};
|
|
162
|
+
const level = levelMap[process.env.OTEL_LOG_LEVEL.toLowerCase()];
|
|
163
|
+
if (level !== void 0) __opentelemetry_api.diag.setLogger(new __opentelemetry_api.DiagConsoleLogger(), level);
|
|
164
|
+
}
|
|
150
165
|
warnPreloadedModules();
|
|
151
166
|
const { serviceName, serviceVersion, environment, endpoint = getDefaultOtelEndpoint(), autoInstrumentation = {}, metricReader } = options;
|
|
152
167
|
const resource = (0, __opentelemetry_resources.resourceFromAttributes)({
|
|
@@ -155,16 +170,58 @@ function setupTracing(options) {
|
|
|
155
170
|
"deployment.environment": environment
|
|
156
171
|
});
|
|
157
172
|
const traceExporter = new __opentelemetry_exporter_trace_otlp_grpc.OTLPTraceExporter({ url: endpoint });
|
|
173
|
+
const spanProcessor = new __opentelemetry_sdk_trace_base.BatchSpanProcessor(traceExporter, {
|
|
174
|
+
maxQueueSize: intEnv("OTEL_BSP_MAX_QUEUE_SIZE", 4096),
|
|
175
|
+
maxExportBatchSize: intEnv("OTEL_BSP_MAX_EXPORT_BATCH_SIZE", 512),
|
|
176
|
+
scheduledDelayMillis: intEnv("OTEL_BSP_SCHEDULE_DELAY", 2e3),
|
|
177
|
+
exportTimeoutMillis: intEnv("OTEL_BSP_EXPORT_TIMEOUT", 3e4)
|
|
178
|
+
});
|
|
158
179
|
if (!metricReader && !process.env.OTEL_METRICS_EXPORTER) process.env.OTEL_METRICS_EXPORTER = "none";
|
|
159
180
|
sdk = new __opentelemetry_sdk_node.NodeSDK({
|
|
160
181
|
resource,
|
|
161
|
-
|
|
182
|
+
spanProcessors: [spanProcessor],
|
|
162
183
|
...metricReader ? { metricReaders: [metricReader] } : {},
|
|
163
184
|
logRecordProcessors: [],
|
|
164
185
|
instrumentations: buildInstrumentations(autoInstrumentation)
|
|
165
186
|
});
|
|
166
187
|
sdk.start();
|
|
167
188
|
isInitialized = true;
|
|
189
|
+
probeEndpoint(endpoint);
|
|
190
|
+
}
|
|
191
|
+
function intEnv(name, fallback) {
|
|
192
|
+
const v = process.env[name];
|
|
193
|
+
if (v === void 0) return fallback;
|
|
194
|
+
const n = parseInt(v, 10);
|
|
195
|
+
return Number.isNaN(n) ? fallback : n;
|
|
196
|
+
}
|
|
197
|
+
/** Probe the OTLP endpoint to surface connectivity issues at startup. */
|
|
198
|
+
function probeEndpoint(endpoint) {
|
|
199
|
+
try {
|
|
200
|
+
const net = require("net");
|
|
201
|
+
const url = new URL(endpoint);
|
|
202
|
+
const host = url.hostname;
|
|
203
|
+
const port = parseInt(url.port, 10) || 4317;
|
|
204
|
+
const socket = net.createConnection({
|
|
205
|
+
host,
|
|
206
|
+
port,
|
|
207
|
+
timeout: 5e3
|
|
208
|
+
}, () => {
|
|
209
|
+
console.log(`[neoiq-foundation] OTLP endpoint reachable at ${host}:${port}`);
|
|
210
|
+
socket.destroy();
|
|
211
|
+
});
|
|
212
|
+
let resolved = false;
|
|
213
|
+
socket.on("timeout", () => {
|
|
214
|
+
if (resolved) return;
|
|
215
|
+
resolved = true;
|
|
216
|
+
console.error(`[neoiq-foundation] WARNING: Cannot reach ${host}:${port} — connection timed out. Traces may NOT be exported.`);
|
|
217
|
+
socket.destroy();
|
|
218
|
+
});
|
|
219
|
+
socket.on("error", (err) => {
|
|
220
|
+
if (resolved) return;
|
|
221
|
+
resolved = true;
|
|
222
|
+
console.error(`[neoiq-foundation] WARNING: Cannot reach ${host}:${port} — ${err.message}. Traces may NOT be exported.`);
|
|
223
|
+
});
|
|
224
|
+
} catch {}
|
|
168
225
|
}
|
|
169
226
|
/**
|
|
170
227
|
* Build the full instrumentation list for the NodeSDK.
|
|
@@ -193,7 +250,7 @@ function buildInstrumentationConfig(config) {
|
|
|
193
250
|
const result = {};
|
|
194
251
|
for (const [key, instrumentationName] of Object.entries(mapping)) {
|
|
195
252
|
const userSetting = config[key];
|
|
196
|
-
const defaultValue = key !== "fs" && key !== "dns";
|
|
253
|
+
const defaultValue = key !== "fs" && key !== "dns" && key !== "grpc";
|
|
197
254
|
result[instrumentationName] = { enabled: userSetting ?? defaultValue };
|
|
198
255
|
}
|
|
199
256
|
return result;
|
|
@@ -338,4 +395,4 @@ Object.defineProperty(exports, 'shutdownTracing', {
|
|
|
338
395
|
return shutdownTracing;
|
|
339
396
|
}
|
|
340
397
|
});
|
|
341
|
-
//# sourceMappingURL=tracing-
|
|
398
|
+
//# sourceMappingURL=tracing-CCmYF9oC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracing-CCmYF9oC.js","names":["input: FoundationConfigInput","sdk: NodeSDK | null","options: TracingOptions","levelMap: Record<string, DiagLogLevel>","name: string","fallback: number","endpoint: string","err: Error","autoInstrumentation: AutoInstrumentationConfig","config: AutoInstrumentationConfig","mapping: Record<string, string>","result: Record<string, { enabled: boolean }>"],"sources":["../src/config.ts","../src/features/tracing.ts"],"sourcesContent":["/**\n * Foundation Configuration with Zod Validation\n */\n\nimport { z } from 'zod';\n\n// Auto-Instrumentation Config\nexport const AutoInstrumentationConfigSchema = z\n .object({\n http: z.boolean().default(true),\n fastify: z.boolean().default(true),\n express: z.boolean().default(true),\n mongodb: z.boolean().default(true),\n pg: z.boolean().default(true),\n mysql: z.boolean().default(true),\n redis: z.boolean().default(true),\n ioredis: z.boolean().default(true),\n grpc: z.boolean().default(false),\n fs: z.boolean().default(false),\n dns: z.boolean().default(false),\n })\n .partial();\n\nexport type AutoInstrumentationConfig = z.infer<typeof AutoInstrumentationConfigSchema>;\n\n// Features Config\nexport const FeaturesConfigSchema = z\n .object({\n tracing: z.boolean().default(true),\n metrics: z.boolean().default(true),\n logging: z.boolean().default(true),\n autoInstrumentation: AutoInstrumentationConfigSchema.default({}),\n })\n .partial();\n\nexport type FeaturesConfig = z.infer<typeof FeaturesConfigSchema>;\n\n// OTEL Config\nconst DEFAULT_OTEL_ENDPOINT =\n 'http://otel-stack-deployment-collector.observability.svc.cluster.local:4317';\n\nexport const OtelConfigSchema = z.object({\n endpoint: z\n .string()\n .default(() => process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTEL_ENDPOINT),\n metricsIntervalMs: z.number().min(1000).default(5000),\n});\n\nexport type OtelConfig = z.infer<typeof OtelConfigSchema>;\n\n// Logging Config\nexport const LoggingConfigSchema = z\n .object({\n level: z.enum(['debug', 'info', 'warn', 'error']).default('info'),\n prettyPrint: z.boolean().optional(),\n })\n .partial();\n\nexport type LoggingConfig = z.infer<typeof LoggingConfigSchema>;\n\n// Request Logging Config\nexport const RequestLoggingConfigSchema = z\n .object({\n logHeaders: z.boolean().default(true),\n logBody: z.boolean().default(false),\n logResponseBody: z.boolean().default(false),\n maxBodySize: z.number().default(10 * 1024),\n redactHeaders: z.array(z.string()).optional(),\n })\n .partial();\n\nexport type RequestLoggingConfig = z.infer<typeof RequestLoggingConfigSchema>;\n\n// Redaction Config\nexport const RedactionConfigSchema = z\n .object({\n additionalPaths: z.array(z.string()).default([]),\n })\n .partial();\n\nexport type RedactionConfig = z.infer<typeof RedactionConfigSchema>;\n\n// Shutdown Config\nexport const ShutdownConfigSchema = z\n .object({\n /** Flush telemetry on uncaughtException / unhandledRejection before process exits */\n flushOnCrash: z.boolean().default(false),\n /** Grace period (ms) for flushing before forcing exit */\n flushTimeoutMs: z.number().min(100).default(5000),\n })\n .partial();\n\nexport type ShutdownConfig = z.infer<typeof ShutdownConfigSchema>;\n\n// Vault Config\nexport const VaultConfigSchema = z.object({\n enabled: z.boolean().default(false),\n address: z.string().default('https://vault.beta.commerceiq.ai'),\n authMethod: z.enum(['kubernetes', 'token']).default('kubernetes'),\n role: z.string().optional(),\n mountPoint: z.string().default('secret'),\n tokenPath: z.string().default('/var/run/secrets/kubernetes.io/serviceaccount/token'),\n k8sAuthMount: z.string().default('kubernetes'),\n namespace: z.string().optional(),\n});\n\nexport type VaultConfig = z.infer<typeof VaultConfigSchema>;\n\n// Main Foundation Config\nexport const FoundationConfigSchema = z.object({\n serviceName: z.string().min(1, 'serviceName is required'),\n serviceVersion: z.string().default(process.env.SERVICE_VERSION || '1.0.0'),\n environment: z\n .enum(['development', 'staging', 'qa', 'production'])\n .default(\n ['development', 'staging', 'qa', 'production'].includes(process.env.NODE_ENV ?? '')\n ? (process.env.NODE_ENV as 'development' | 'staging' | 'qa' | 'production')\n : 'development'\n ),\n features: FeaturesConfigSchema.default({}),\n otel: OtelConfigSchema.default({}),\n logging: LoggingConfigSchema.default({}),\n requestLogging: RequestLoggingConfigSchema.default({}),\n redaction: RedactionConfigSchema.default({}),\n shutdown: ShutdownConfigSchema.default({}),\n vault: VaultConfigSchema.default({}),\n});\n\nexport type FoundationConfig = z.infer<typeof FoundationConfigSchema>;\n\nexport type FoundationConfigInput = z.input<typeof FoundationConfigSchema> & {\n serviceName: string;\n};\n\n/** Parse and validate configuration */\nexport function parseConfig(input: FoundationConfigInput): FoundationConfig {\n const result = FoundationConfigSchema.safeParse(input);\n\n if (!result.success) {\n const errors = result.error.errors\n .map((e) => ` - ${e.path.join('.')}: ${e.message}`)\n .join('\\n');\n throw new Error(`Invalid foundation configuration:\\n${errors}`);\n }\n\n return result.data;\n}\n\n/** Get default OTEL endpoint */\nexport function getDefaultOtelEndpoint(): string {\n return process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTEL_ENDPOINT;\n}\n","/**\n * OpenTelemetry Tracing Setup\n */\n\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';\nimport { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';\nimport {\n trace,\n context,\n propagation,\n SpanStatusCode,\n diag,\n DiagConsoleLogger,\n DiagLogLevel,\n type Tracer,\n} from '@opentelemetry/api';\nimport { type IMetricReader } from '@opentelemetry/sdk-metrics';\nimport { type AutoInstrumentationConfig, getDefaultOtelEndpoint } from '../config';\n\n/**\n * Element type of the NodeSDK instrumentations list. Derived from\n * getNodeAutoInstrumentations so we don't take a direct import on\n * @opentelemetry/instrumentation (only a transitive dependency here).\n */\ntype Instrumentation = ReturnType<typeof getNodeAutoInstrumentations>[number];\n\nexport interface TracingOptions {\n serviceName: string;\n serviceVersion: string;\n environment: string;\n endpoint?: string;\n autoInstrumentation?: AutoInstrumentationConfig;\n /**\n * Metric reader to attach to the NodeSDK so a single SDK owns both traces and\n * metrics (one global MeterProvider, same gRPC collector). When provided, NodeSDK\n * is the sole owner of metrics; when omitted, NodeSDK is opted out of metrics\n * entirely (see below) so it never registers a stray default MeterProvider.\n */\n metricReader?: IMetricReader;\n}\n\nlet sdk: NodeSDK | null = null;\nlet isInitialized = false;\n\nconst MONITORED_MODULES = ['pg', 'mongodb', 'ioredis', 'mysql2', 'express', '@grpc/grpc-js'];\n\nfunction warnPreloadedModules(): void {\n // require.resolve / require.cache are only available in CJS contexts\n if (typeof require === 'undefined' || typeof require.cache === 'undefined') return;\n\n for (const mod of MONITORED_MODULES) {\n try {\n const resolved = require.resolve(mod);\n if (require.cache[resolved]) {\n console.warn(\n `[neoiq-foundation] \"${mod}\" was imported before tracing init. ` +\n 'Auto-instrumentation may not work for this module. ' +\n 'Use node -r @ciq-dev/neoiq-foundation-node/bootstrap or call createFoundation() first.'\n );\n }\n } catch {\n // Module not installed — no warning needed\n }\n }\n}\n\n/** Initialize OpenTelemetry tracing */\nexport function setupTracing(options: TracingOptions): void {\n if (isInitialized) {\n console.warn('[neoiq-foundation] Tracing already initialized');\n return;\n }\n\n // Enable OTEL diagnostic logging when OTEL_DEBUG=true so export failures,\n // connection errors, and dropped spans are visible in application logs.\n if (process.env.OTEL_DEBUG === 'true') {\n diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);\n } else if (process.env.OTEL_LOG_LEVEL) {\n const levelMap: Record<string, DiagLogLevel> = {\n none: DiagLogLevel.NONE,\n error: DiagLogLevel.ERROR,\n warn: DiagLogLevel.WARN,\n info: DiagLogLevel.INFO,\n debug: DiagLogLevel.DEBUG,\n verbose: DiagLogLevel.VERBOSE,\n all: DiagLogLevel.ALL,\n };\n const level = levelMap[process.env.OTEL_LOG_LEVEL.toLowerCase()];\n if (level !== undefined) {\n diag.setLogger(new DiagConsoleLogger(), level);\n }\n }\n\n warnPreloadedModules();\n\n const {\n serviceName,\n serviceVersion,\n environment,\n endpoint = getDefaultOtelEndpoint(),\n autoInstrumentation = {},\n metricReader,\n } = options;\n\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: serviceName,\n [ATTR_SERVICE_VERSION]: serviceVersion,\n 'deployment.environment': environment,\n });\n\n const traceExporter = new OTLPTraceExporter({ url: endpoint });\n\n // Use an explicit BatchSpanProcessor so we control queue sizes and timeouts\n // rather than relying on NodeSDK's internal defaults. Env vars\n // OTEL_BSP_MAX_QUEUE_SIZE, OTEL_BSP_MAX_EXPORT_BATCH_SIZE,\n // OTEL_BSP_SCHEDULE_DELAY, and OTEL_BSP_EXPORT_TIMEOUT override these.\n const spanProcessor = new BatchSpanProcessor(traceExporter, {\n maxQueueSize: intEnv('OTEL_BSP_MAX_QUEUE_SIZE', 4096),\n maxExportBatchSize: intEnv('OTEL_BSP_MAX_EXPORT_BATCH_SIZE', 512),\n scheduledDelayMillis: intEnv('OTEL_BSP_SCHEDULE_DELAY', 2000),\n exportTimeoutMillis: intEnv('OTEL_BSP_EXPORT_TIMEOUT', 30000),\n });\n\n // Foundation owns every OpenTelemetry signal explicitly. Left to its own devices,\n // NodeSDK auto-configures metrics AND logs exporters from env defaults — both OTLP\n // over HTTP to localhost:4318 — which nothing listens on, producing ECONNREFUSED\n // spam every export interval. We therefore configure each signal on NodeSDK directly:\n // - traces: the explicit BatchSpanProcessor above.\n // - metrics: the explicit gRPC reader when provided (single SDK, single global\n // MeterProvider, same collector as traces). When metrics are disabled\n // and the deployer hasn't chosen an exporter, opt NodeSDK out via\n // OTEL_METRICS_EXPORTER=none (the only switch NodeSDK exposes for that).\n // - logs: foundation logs via pino, never OTLP. Passing an empty processor list\n // stops NodeSDK from falling back to its env-default localhost:4318 log\n // exporter (`logRecordProcessors: []` takes the explicit branch and skips\n // configureLoggerProviderFromEnv()).\n if (!metricReader && !process.env.OTEL_METRICS_EXPORTER) {\n process.env.OTEL_METRICS_EXPORTER = 'none';\n }\n\n sdk = new NodeSDK({\n resource,\n spanProcessors: [spanProcessor],\n ...(metricReader ? { metricReaders: [metricReader] } : {}),\n logRecordProcessors: [],\n instrumentations: buildInstrumentations(autoInstrumentation),\n });\n\n sdk.start();\n isInitialized = true;\n\n probeEndpoint(endpoint);\n}\n\nfunction intEnv(name: string, fallback: number): number {\n const v = process.env[name];\n if (v === undefined) return fallback;\n const n = parseInt(v, 10);\n return Number.isNaN(n) ? fallback : n;\n}\n\n/** Probe the OTLP endpoint to surface connectivity issues at startup. */\nfunction probeEndpoint(endpoint: string): void {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const net = require('net') as typeof import('net');\n const url = new URL(endpoint);\n const host = url.hostname;\n const port = parseInt(url.port, 10) || 4317;\n const socket = net.createConnection({ host, port, timeout: 5000 }, () => {\n console.log(`[neoiq-foundation] OTLP endpoint reachable at ${host}:${port}`);\n socket.destroy();\n });\n let resolved = false;\n socket.on('timeout', () => {\n if (resolved) return;\n resolved = true;\n console.error(\n `[neoiq-foundation] WARNING: Cannot reach ${host}:${port} — connection timed out. Traces may NOT be exported.`\n );\n socket.destroy();\n });\n socket.on('error', (err: Error) => {\n if (resolved) return;\n resolved = true;\n console.error(\n `[neoiq-foundation] WARNING: Cannot reach ${host}:${port} — ${err.message}. Traces may NOT be exported.`\n );\n });\n } catch {\n // Don't crash if probe fails\n }\n}\n\n/**\n * Build the full instrumentation list for the NodeSDK.\n *\n * auto-instrumentations-node owns the framework/library instrumentation set. Keep\n * Fastify in the same config map as the other OpenTelemetry instrumentations so we\n * get route/server spans without adding @fastify/otel's lifecycle hook waterfall.\n */\nexport function buildInstrumentations(\n autoInstrumentation: AutoInstrumentationConfig = {}\n): Instrumentation[] {\n return [...getNodeAutoInstrumentations(buildInstrumentationConfig(autoInstrumentation))];\n}\n\nfunction buildInstrumentationConfig(\n config: AutoInstrumentationConfig\n): Record<string, { enabled: boolean }> {\n const mapping: Record<string, string> = {\n http: '@opentelemetry/instrumentation-http',\n fastify: '@opentelemetry/instrumentation-fastify',\n express: '@opentelemetry/instrumentation-express',\n mongodb: '@opentelemetry/instrumentation-mongodb',\n pg: '@opentelemetry/instrumentation-pg',\n mysql: '@opentelemetry/instrumentation-mysql',\n redis: '@opentelemetry/instrumentation-redis',\n ioredis: '@opentelemetry/instrumentation-ioredis',\n grpc: '@opentelemetry/instrumentation-grpc',\n fs: '@opentelemetry/instrumentation-fs',\n dns: '@opentelemetry/instrumentation-dns',\n };\n\n const result: Record<string, { enabled: boolean }> = {};\n for (const [key, instrumentationName] of Object.entries(mapping)) {\n const userSetting = config[key as keyof AutoInstrumentationConfig];\n const defaultValue = key !== 'fs' && key !== 'dns' && key !== 'grpc';\n result[instrumentationName] = { enabled: userSetting ?? defaultValue };\n }\n return result;\n}\n\n/** Shutdown tracing gracefully */\nexport async function shutdownTracing(): Promise<void> {\n if (!sdk) return;\n try {\n await sdk.shutdown();\n isInitialized = false;\n sdk = null;\n } catch (error) {\n console.error('[neoiq-foundation] Error shutting down tracing:', error);\n throw error;\n }\n}\n\nexport function getTracer(name: string): Tracer {\n return trace.getTracer(name);\n}\n\nexport function getActiveSpan(): ReturnType<typeof trace.getActiveSpan> {\n return trace.getActiveSpan();\n}\n\nexport function getTraceContext(): { traceId?: string; spanId?: string } {\n const span = trace.getActiveSpan();\n if (!span) return {};\n const ctx = span.spanContext();\n return { traceId: ctx.traceId, spanId: ctx.spanId };\n}\n\nexport function isTracingEnabled(): boolean {\n return isInitialized;\n}\n\nexport { trace, context, propagation, SpanStatusCode };\nexport type { Tracer };\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ciq-dev/neoiq-foundation-node",
|
|
3
|
-
"version": "1.1.2-beta.
|
|
3
|
+
"version": "1.1.2-beta.14",
|
|
4
4
|
"description": "Node.js observability foundation for CommerceIQ services. Integrates with Groundcover via OpenTelemetry.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"@opentelemetry/resources": ">=1.21.0",
|
|
56
56
|
"@opentelemetry/sdk-metrics": ">=1.21.0",
|
|
57
57
|
"@opentelemetry/sdk-node": ">=0.48.0",
|
|
58
|
+
"@opentelemetry/sdk-trace-base": ">=1.21.0",
|
|
58
59
|
"@opentelemetry/semantic-conventions": "^1.21.0",
|
|
59
60
|
"axios": "^1.6.0",
|
|
60
61
|
"axios-retry": "^4.0.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tracing-BYBASOyy.js","names":["input: FoundationConfigInput","sdk: NodeSDK | null","options: TracingOptions","autoInstrumentation: AutoInstrumentationConfig","config: AutoInstrumentationConfig","mapping: Record<string, string>","result: Record<string, { enabled: boolean }>","name: string"],"sources":["../src/config.ts","../src/features/tracing.ts"],"sourcesContent":["/**\n * Foundation Configuration with Zod Validation\n */\n\nimport { z } from 'zod';\n\n// Auto-Instrumentation Config\nexport const AutoInstrumentationConfigSchema = z\n .object({\n http: z.boolean().default(true),\n fastify: z.boolean().default(true),\n express: z.boolean().default(true),\n mongodb: z.boolean().default(true),\n pg: z.boolean().default(true),\n mysql: z.boolean().default(true),\n redis: z.boolean().default(true),\n ioredis: z.boolean().default(true),\n grpc: z.boolean().default(true),\n fs: z.boolean().default(false),\n dns: z.boolean().default(false),\n })\n .partial();\n\nexport type AutoInstrumentationConfig = z.infer<typeof AutoInstrumentationConfigSchema>;\n\n// Features Config\nexport const FeaturesConfigSchema = z\n .object({\n tracing: z.boolean().default(true),\n metrics: z.boolean().default(true),\n logging: z.boolean().default(true),\n autoInstrumentation: AutoInstrumentationConfigSchema.default({}),\n })\n .partial();\n\nexport type FeaturesConfig = z.infer<typeof FeaturesConfigSchema>;\n\n// OTEL Config\nconst DEFAULT_OTEL_ENDPOINT =\n 'http://otel-stack-deployment-collector.observability.svc.cluster.local:4317';\n\nexport const OtelConfigSchema = z.object({\n endpoint: z\n .string()\n .default(() => process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTEL_ENDPOINT),\n metricsIntervalMs: z.number().min(1000).default(5000),\n});\n\nexport type OtelConfig = z.infer<typeof OtelConfigSchema>;\n\n// Logging Config\nexport const LoggingConfigSchema = z\n .object({\n level: z.enum(['debug', 'info', 'warn', 'error']).default('info'),\n prettyPrint: z.boolean().optional(),\n })\n .partial();\n\nexport type LoggingConfig = z.infer<typeof LoggingConfigSchema>;\n\n// Request Logging Config\nexport const RequestLoggingConfigSchema = z\n .object({\n logHeaders: z.boolean().default(true),\n logBody: z.boolean().default(false),\n logResponseBody: z.boolean().default(false),\n maxBodySize: z.number().default(10 * 1024),\n redactHeaders: z.array(z.string()).optional(),\n })\n .partial();\n\nexport type RequestLoggingConfig = z.infer<typeof RequestLoggingConfigSchema>;\n\n// Redaction Config\nexport const RedactionConfigSchema = z\n .object({\n additionalPaths: z.array(z.string()).default([]),\n })\n .partial();\n\nexport type RedactionConfig = z.infer<typeof RedactionConfigSchema>;\n\n// Shutdown Config\nexport const ShutdownConfigSchema = z\n .object({\n /** Flush telemetry on uncaughtException / unhandledRejection before process exits */\n flushOnCrash: z.boolean().default(false),\n /** Grace period (ms) for flushing before forcing exit */\n flushTimeoutMs: z.number().min(100).default(5000),\n })\n .partial();\n\nexport type ShutdownConfig = z.infer<typeof ShutdownConfigSchema>;\n\n// Vault Config\nexport const VaultConfigSchema = z.object({\n enabled: z.boolean().default(false),\n address: z.string().default('https://vault.beta.commerceiq.ai'),\n authMethod: z.enum(['kubernetes', 'token']).default('kubernetes'),\n role: z.string().optional(),\n mountPoint: z.string().default('secret'),\n tokenPath: z.string().default('/var/run/secrets/kubernetes.io/serviceaccount/token'),\n k8sAuthMount: z.string().default('kubernetes'),\n namespace: z.string().optional(),\n});\n\nexport type VaultConfig = z.infer<typeof VaultConfigSchema>;\n\n// Main Foundation Config\nexport const FoundationConfigSchema = z.object({\n serviceName: z.string().min(1, 'serviceName is required'),\n serviceVersion: z.string().default(process.env.SERVICE_VERSION || '1.0.0'),\n environment: z\n .enum(['development', 'staging', 'qa', 'production'])\n .default(\n ['development', 'staging', 'qa', 'production'].includes(process.env.NODE_ENV ?? '')\n ? (process.env.NODE_ENV as 'development' | 'staging' | 'qa' | 'production')\n : 'development'\n ),\n features: FeaturesConfigSchema.default({}),\n otel: OtelConfigSchema.default({}),\n logging: LoggingConfigSchema.default({}),\n requestLogging: RequestLoggingConfigSchema.default({}),\n redaction: RedactionConfigSchema.default({}),\n shutdown: ShutdownConfigSchema.default({}),\n vault: VaultConfigSchema.default({}),\n});\n\nexport type FoundationConfig = z.infer<typeof FoundationConfigSchema>;\n\nexport type FoundationConfigInput = z.input<typeof FoundationConfigSchema> & {\n serviceName: string;\n};\n\n/** Parse and validate configuration */\nexport function parseConfig(input: FoundationConfigInput): FoundationConfig {\n const result = FoundationConfigSchema.safeParse(input);\n\n if (!result.success) {\n const errors = result.error.errors\n .map((e) => ` - ${e.path.join('.')}: ${e.message}`)\n .join('\\n');\n throw new Error(`Invalid foundation configuration:\\n${errors}`);\n }\n\n return result.data;\n}\n\n/** Get default OTEL endpoint */\nexport function getDefaultOtelEndpoint(): string {\n return process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTEL_ENDPOINT;\n}\n","/**\n * OpenTelemetry Tracing Setup\n */\n\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';\nimport { trace, context, propagation, SpanStatusCode, type Tracer } from '@opentelemetry/api';\nimport { type IMetricReader } from '@opentelemetry/sdk-metrics';\nimport { type AutoInstrumentationConfig, getDefaultOtelEndpoint } from '../config';\n\n/**\n * Element type of the NodeSDK instrumentations list. Derived from\n * getNodeAutoInstrumentations so we don't take a direct import on\n * @opentelemetry/instrumentation (only a transitive dependency here).\n */\ntype Instrumentation = ReturnType<typeof getNodeAutoInstrumentations>[number];\n\nexport interface TracingOptions {\n serviceName: string;\n serviceVersion: string;\n environment: string;\n endpoint?: string;\n autoInstrumentation?: AutoInstrumentationConfig;\n /**\n * Metric reader to attach to the NodeSDK so a single SDK owns both traces and\n * metrics (one global MeterProvider, same gRPC collector). When provided, NodeSDK\n * is the sole owner of metrics; when omitted, NodeSDK is opted out of metrics\n * entirely (see below) so it never registers a stray default MeterProvider.\n */\n metricReader?: IMetricReader;\n}\n\nlet sdk: NodeSDK | null = null;\nlet isInitialized = false;\n\nconst MONITORED_MODULES = ['pg', 'mongodb', 'ioredis', 'mysql2', 'express', '@grpc/grpc-js'];\n\nfunction warnPreloadedModules(): void {\n // require.resolve / require.cache are only available in CJS contexts\n if (typeof require === 'undefined' || typeof require.cache === 'undefined') return;\n\n for (const mod of MONITORED_MODULES) {\n try {\n const resolved = require.resolve(mod);\n if (require.cache[resolved]) {\n console.warn(\n `[neoiq-foundation] \"${mod}\" was imported before tracing init. ` +\n 'Auto-instrumentation may not work for this module. ' +\n 'Use node -r @ciq-dev/neoiq-foundation-node/bootstrap or call createFoundation() first.'\n );\n }\n } catch {\n // Module not installed — no warning needed\n }\n }\n}\n\n/** Initialize OpenTelemetry tracing */\nexport function setupTracing(options: TracingOptions): void {\n if (isInitialized) {\n console.warn('[neoiq-foundation] Tracing already initialized');\n return;\n }\n\n warnPreloadedModules();\n\n const {\n serviceName,\n serviceVersion,\n environment,\n endpoint = getDefaultOtelEndpoint(),\n autoInstrumentation = {},\n metricReader,\n } = options;\n\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: serviceName,\n [ATTR_SERVICE_VERSION]: serviceVersion,\n 'deployment.environment': environment,\n });\n\n const traceExporter = new OTLPTraceExporter({ url: endpoint });\n\n // Foundation owns every OpenTelemetry signal explicitly. Left to its own devices,\n // NodeSDK auto-configures metrics AND logs exporters from env defaults — both OTLP\n // over HTTP to localhost:4318 — which nothing listens on, producing ECONNREFUSED\n // spam every export interval. We therefore configure each signal on NodeSDK directly:\n // - traces: the explicit gRPC exporter above.\n // - metrics: the explicit gRPC reader when provided (single SDK, single global\n // MeterProvider, same collector as traces). When metrics are disabled\n // and the deployer hasn't chosen an exporter, opt NodeSDK out via\n // OTEL_METRICS_EXPORTER=none (the only switch NodeSDK exposes for that).\n // - logs: foundation logs via pino, never OTLP. Passing an empty processor list\n // stops NodeSDK from falling back to its env-default localhost:4318 log\n // exporter (`logRecordProcessors: []` takes the explicit branch and skips\n // configureLoggerProviderFromEnv()).\n if (!metricReader && !process.env.OTEL_METRICS_EXPORTER) {\n process.env.OTEL_METRICS_EXPORTER = 'none';\n }\n\n sdk = new NodeSDK({\n resource,\n traceExporter,\n ...(metricReader ? { metricReaders: [metricReader] } : {}),\n logRecordProcessors: [],\n instrumentations: buildInstrumentations(autoInstrumentation),\n });\n\n sdk.start();\n isInitialized = true;\n}\n\n/**\n * Build the full instrumentation list for the NodeSDK.\n *\n * auto-instrumentations-node owns the framework/library instrumentation set. Keep\n * Fastify in the same config map as the other OpenTelemetry instrumentations so we\n * get route/server spans without adding @fastify/otel's lifecycle hook waterfall.\n */\nexport function buildInstrumentations(\n autoInstrumentation: AutoInstrumentationConfig = {}\n): Instrumentation[] {\n return [...getNodeAutoInstrumentations(buildInstrumentationConfig(autoInstrumentation))];\n}\n\nfunction buildInstrumentationConfig(\n config: AutoInstrumentationConfig\n): Record<string, { enabled: boolean }> {\n const mapping: Record<string, string> = {\n http: '@opentelemetry/instrumentation-http',\n fastify: '@opentelemetry/instrumentation-fastify',\n express: '@opentelemetry/instrumentation-express',\n mongodb: '@opentelemetry/instrumentation-mongodb',\n pg: '@opentelemetry/instrumentation-pg',\n mysql: '@opentelemetry/instrumentation-mysql',\n redis: '@opentelemetry/instrumentation-redis',\n ioredis: '@opentelemetry/instrumentation-ioredis',\n grpc: '@opentelemetry/instrumentation-grpc',\n fs: '@opentelemetry/instrumentation-fs',\n dns: '@opentelemetry/instrumentation-dns',\n };\n\n const result: Record<string, { enabled: boolean }> = {};\n for (const [key, instrumentationName] of Object.entries(mapping)) {\n const userSetting = config[key as keyof AutoInstrumentationConfig];\n const defaultValue = key !== 'fs' && key !== 'dns';\n result[instrumentationName] = { enabled: userSetting ?? defaultValue };\n }\n return result;\n}\n\n/** Shutdown tracing gracefully */\nexport async function shutdownTracing(): Promise<void> {\n if (!sdk) return;\n try {\n await sdk.shutdown();\n isInitialized = false;\n sdk = null;\n } catch (error) {\n console.error('[neoiq-foundation] Error shutting down tracing:', error);\n throw error;\n }\n}\n\nexport function getTracer(name: string): Tracer {\n return trace.getTracer(name);\n}\n\nexport function getActiveSpan(): ReturnType<typeof trace.getActiveSpan> {\n return trace.getActiveSpan();\n}\n\nexport function getTraceContext(): { traceId?: string; spanId?: string } {\n const span = trace.getActiveSpan();\n if (!span) return {};\n const ctx = span.spanContext();\n return { traceId: ctx.traceId, spanId: ctx.spanId };\n}\n\nexport function isTracingEnabled(): boolean {\n return isInitialized;\n}\n\nexport { trace, context, propagation, SpanStatusCode };\nexport type { Tracer };\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tracing-CVXLySRJ.mjs","names":["input: FoundationConfigInput","sdk: NodeSDK | null","options: TracingOptions","autoInstrumentation: AutoInstrumentationConfig","config: AutoInstrumentationConfig","mapping: Record<string, string>","result: Record<string, { enabled: boolean }>","name: string"],"sources":["../src/config.ts","../src/features/tracing.ts"],"sourcesContent":["/**\n * Foundation Configuration with Zod Validation\n */\n\nimport { z } from 'zod';\n\n// Auto-Instrumentation Config\nexport const AutoInstrumentationConfigSchema = z\n .object({\n http: z.boolean().default(true),\n fastify: z.boolean().default(true),\n express: z.boolean().default(true),\n mongodb: z.boolean().default(true),\n pg: z.boolean().default(true),\n mysql: z.boolean().default(true),\n redis: z.boolean().default(true),\n ioredis: z.boolean().default(true),\n grpc: z.boolean().default(true),\n fs: z.boolean().default(false),\n dns: z.boolean().default(false),\n })\n .partial();\n\nexport type AutoInstrumentationConfig = z.infer<typeof AutoInstrumentationConfigSchema>;\n\n// Features Config\nexport const FeaturesConfigSchema = z\n .object({\n tracing: z.boolean().default(true),\n metrics: z.boolean().default(true),\n logging: z.boolean().default(true),\n autoInstrumentation: AutoInstrumentationConfigSchema.default({}),\n })\n .partial();\n\nexport type FeaturesConfig = z.infer<typeof FeaturesConfigSchema>;\n\n// OTEL Config\nconst DEFAULT_OTEL_ENDPOINT =\n 'http://otel-stack-deployment-collector.observability.svc.cluster.local:4317';\n\nexport const OtelConfigSchema = z.object({\n endpoint: z\n .string()\n .default(() => process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTEL_ENDPOINT),\n metricsIntervalMs: z.number().min(1000).default(5000),\n});\n\nexport type OtelConfig = z.infer<typeof OtelConfigSchema>;\n\n// Logging Config\nexport const LoggingConfigSchema = z\n .object({\n level: z.enum(['debug', 'info', 'warn', 'error']).default('info'),\n prettyPrint: z.boolean().optional(),\n })\n .partial();\n\nexport type LoggingConfig = z.infer<typeof LoggingConfigSchema>;\n\n// Request Logging Config\nexport const RequestLoggingConfigSchema = z\n .object({\n logHeaders: z.boolean().default(true),\n logBody: z.boolean().default(false),\n logResponseBody: z.boolean().default(false),\n maxBodySize: z.number().default(10 * 1024),\n redactHeaders: z.array(z.string()).optional(),\n })\n .partial();\n\nexport type RequestLoggingConfig = z.infer<typeof RequestLoggingConfigSchema>;\n\n// Redaction Config\nexport const RedactionConfigSchema = z\n .object({\n additionalPaths: z.array(z.string()).default([]),\n })\n .partial();\n\nexport type RedactionConfig = z.infer<typeof RedactionConfigSchema>;\n\n// Shutdown Config\nexport const ShutdownConfigSchema = z\n .object({\n /** Flush telemetry on uncaughtException / unhandledRejection before process exits */\n flushOnCrash: z.boolean().default(false),\n /** Grace period (ms) for flushing before forcing exit */\n flushTimeoutMs: z.number().min(100).default(5000),\n })\n .partial();\n\nexport type ShutdownConfig = z.infer<typeof ShutdownConfigSchema>;\n\n// Vault Config\nexport const VaultConfigSchema = z.object({\n enabled: z.boolean().default(false),\n address: z.string().default('https://vault.beta.commerceiq.ai'),\n authMethod: z.enum(['kubernetes', 'token']).default('kubernetes'),\n role: z.string().optional(),\n mountPoint: z.string().default('secret'),\n tokenPath: z.string().default('/var/run/secrets/kubernetes.io/serviceaccount/token'),\n k8sAuthMount: z.string().default('kubernetes'),\n namespace: z.string().optional(),\n});\n\nexport type VaultConfig = z.infer<typeof VaultConfigSchema>;\n\n// Main Foundation Config\nexport const FoundationConfigSchema = z.object({\n serviceName: z.string().min(1, 'serviceName is required'),\n serviceVersion: z.string().default(process.env.SERVICE_VERSION || '1.0.0'),\n environment: z\n .enum(['development', 'staging', 'qa', 'production'])\n .default(\n ['development', 'staging', 'qa', 'production'].includes(process.env.NODE_ENV ?? '')\n ? (process.env.NODE_ENV as 'development' | 'staging' | 'qa' | 'production')\n : 'development'\n ),\n features: FeaturesConfigSchema.default({}),\n otel: OtelConfigSchema.default({}),\n logging: LoggingConfigSchema.default({}),\n requestLogging: RequestLoggingConfigSchema.default({}),\n redaction: RedactionConfigSchema.default({}),\n shutdown: ShutdownConfigSchema.default({}),\n vault: VaultConfigSchema.default({}),\n});\n\nexport type FoundationConfig = z.infer<typeof FoundationConfigSchema>;\n\nexport type FoundationConfigInput = z.input<typeof FoundationConfigSchema> & {\n serviceName: string;\n};\n\n/** Parse and validate configuration */\nexport function parseConfig(input: FoundationConfigInput): FoundationConfig {\n const result = FoundationConfigSchema.safeParse(input);\n\n if (!result.success) {\n const errors = result.error.errors\n .map((e) => ` - ${e.path.join('.')}: ${e.message}`)\n .join('\\n');\n throw new Error(`Invalid foundation configuration:\\n${errors}`);\n }\n\n return result.data;\n}\n\n/** Get default OTEL endpoint */\nexport function getDefaultOtelEndpoint(): string {\n return process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTEL_ENDPOINT;\n}\n","/**\n * OpenTelemetry Tracing Setup\n */\n\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';\nimport { trace, context, propagation, SpanStatusCode, type Tracer } from '@opentelemetry/api';\nimport { type IMetricReader } from '@opentelemetry/sdk-metrics';\nimport { type AutoInstrumentationConfig, getDefaultOtelEndpoint } from '../config';\n\n/**\n * Element type of the NodeSDK instrumentations list. Derived from\n * getNodeAutoInstrumentations so we don't take a direct import on\n * @opentelemetry/instrumentation (only a transitive dependency here).\n */\ntype Instrumentation = ReturnType<typeof getNodeAutoInstrumentations>[number];\n\nexport interface TracingOptions {\n serviceName: string;\n serviceVersion: string;\n environment: string;\n endpoint?: string;\n autoInstrumentation?: AutoInstrumentationConfig;\n /**\n * Metric reader to attach to the NodeSDK so a single SDK owns both traces and\n * metrics (one global MeterProvider, same gRPC collector). When provided, NodeSDK\n * is the sole owner of metrics; when omitted, NodeSDK is opted out of metrics\n * entirely (see below) so it never registers a stray default MeterProvider.\n */\n metricReader?: IMetricReader;\n}\n\nlet sdk: NodeSDK | null = null;\nlet isInitialized = false;\n\nconst MONITORED_MODULES = ['pg', 'mongodb', 'ioredis', 'mysql2', 'express', '@grpc/grpc-js'];\n\nfunction warnPreloadedModules(): void {\n // require.resolve / require.cache are only available in CJS contexts\n if (typeof require === 'undefined' || typeof require.cache === 'undefined') return;\n\n for (const mod of MONITORED_MODULES) {\n try {\n const resolved = require.resolve(mod);\n if (require.cache[resolved]) {\n console.warn(\n `[neoiq-foundation] \"${mod}\" was imported before tracing init. ` +\n 'Auto-instrumentation may not work for this module. ' +\n 'Use node -r @ciq-dev/neoiq-foundation-node/bootstrap or call createFoundation() first.'\n );\n }\n } catch {\n // Module not installed — no warning needed\n }\n }\n}\n\n/** Initialize OpenTelemetry tracing */\nexport function setupTracing(options: TracingOptions): void {\n if (isInitialized) {\n console.warn('[neoiq-foundation] Tracing already initialized');\n return;\n }\n\n warnPreloadedModules();\n\n const {\n serviceName,\n serviceVersion,\n environment,\n endpoint = getDefaultOtelEndpoint(),\n autoInstrumentation = {},\n metricReader,\n } = options;\n\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: serviceName,\n [ATTR_SERVICE_VERSION]: serviceVersion,\n 'deployment.environment': environment,\n });\n\n const traceExporter = new OTLPTraceExporter({ url: endpoint });\n\n // Foundation owns every OpenTelemetry signal explicitly. Left to its own devices,\n // NodeSDK auto-configures metrics AND logs exporters from env defaults — both OTLP\n // over HTTP to localhost:4318 — which nothing listens on, producing ECONNREFUSED\n // spam every export interval. We therefore configure each signal on NodeSDK directly:\n // - traces: the explicit gRPC exporter above.\n // - metrics: the explicit gRPC reader when provided (single SDK, single global\n // MeterProvider, same collector as traces). When metrics are disabled\n // and the deployer hasn't chosen an exporter, opt NodeSDK out via\n // OTEL_METRICS_EXPORTER=none (the only switch NodeSDK exposes for that).\n // - logs: foundation logs via pino, never OTLP. Passing an empty processor list\n // stops NodeSDK from falling back to its env-default localhost:4318 log\n // exporter (`logRecordProcessors: []` takes the explicit branch and skips\n // configureLoggerProviderFromEnv()).\n if (!metricReader && !process.env.OTEL_METRICS_EXPORTER) {\n process.env.OTEL_METRICS_EXPORTER = 'none';\n }\n\n sdk = new NodeSDK({\n resource,\n traceExporter,\n ...(metricReader ? { metricReaders: [metricReader] } : {}),\n logRecordProcessors: [],\n instrumentations: buildInstrumentations(autoInstrumentation),\n });\n\n sdk.start();\n isInitialized = true;\n}\n\n/**\n * Build the full instrumentation list for the NodeSDK.\n *\n * auto-instrumentations-node owns the framework/library instrumentation set. Keep\n * Fastify in the same config map as the other OpenTelemetry instrumentations so we\n * get route/server spans without adding @fastify/otel's lifecycle hook waterfall.\n */\nexport function buildInstrumentations(\n autoInstrumentation: AutoInstrumentationConfig = {}\n): Instrumentation[] {\n return [...getNodeAutoInstrumentations(buildInstrumentationConfig(autoInstrumentation))];\n}\n\nfunction buildInstrumentationConfig(\n config: AutoInstrumentationConfig\n): Record<string, { enabled: boolean }> {\n const mapping: Record<string, string> = {\n http: '@opentelemetry/instrumentation-http',\n fastify: '@opentelemetry/instrumentation-fastify',\n express: '@opentelemetry/instrumentation-express',\n mongodb: '@opentelemetry/instrumentation-mongodb',\n pg: '@opentelemetry/instrumentation-pg',\n mysql: '@opentelemetry/instrumentation-mysql',\n redis: '@opentelemetry/instrumentation-redis',\n ioredis: '@opentelemetry/instrumentation-ioredis',\n grpc: '@opentelemetry/instrumentation-grpc',\n fs: '@opentelemetry/instrumentation-fs',\n dns: '@opentelemetry/instrumentation-dns',\n };\n\n const result: Record<string, { enabled: boolean }> = {};\n for (const [key, instrumentationName] of Object.entries(mapping)) {\n const userSetting = config[key as keyof AutoInstrumentationConfig];\n const defaultValue = key !== 'fs' && key !== 'dns';\n result[instrumentationName] = { enabled: userSetting ?? defaultValue };\n }\n return result;\n}\n\n/** Shutdown tracing gracefully */\nexport async function shutdownTracing(): Promise<void> {\n if (!sdk) return;\n try {\n await sdk.shutdown();\n isInitialized = false;\n sdk = null;\n } catch (error) {\n console.error('[neoiq-foundation] Error shutting down tracing:', error);\n throw error;\n }\n}\n\nexport function getTracer(name: string): Tracer {\n return trace.getTracer(name);\n}\n\nexport function getActiveSpan(): ReturnType<typeof trace.getActiveSpan> {\n return trace.getActiveSpan();\n}\n\nexport function getTraceContext(): { traceId?: string; spanId?: string } {\n const span = trace.getActiveSpan();\n if (!span) return {};\n const ctx = span.spanContext();\n return { traceId: ctx.traceId, spanId: ctx.spanId };\n}\n\nexport function isTracingEnabled(): boolean {\n return isInitialized;\n}\n\nexport { trace, context, propagation, SpanStatusCode };\nexport type { Tracer };\n"],"mappings":"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"}
|