@iii-dev/observability 0.19.7 → 0.20.0-alpha.1

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/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @iii-dev/observability
2
2
 
3
- OpenTelemetry + Logger primitives shared across the iii SDKs.
3
+ > **Deprecated.** This package is a thin shim. Import from `@iii-dev/helpers/observability` (public API) or `@iii-dev/helpers/observability/internal` (internal API) instead.
4
+
5
+ Re-exports the public API from `@iii-dev/helpers/observability`. This package is a deprecated shim; use `@iii-dev/helpers/observability` for new code.
4
6
 
5
7
  See https://github.com/iii-hq/iii for the full project.
package/dist/index.cjs CHANGED
@@ -1,490 +1,9 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_telemetry_system = require('./telemetry-system-BFMV9aXn.cjs');
3
- let _opentelemetry_api_logs = require("@opentelemetry/api-logs");
4
- let _opentelemetry_api = require("@opentelemetry/api");
5
- let node_perf_hooks = require("node:perf_hooks");
6
1
 
7
- //#region src/logger.ts
8
- /**
9
- * Structured logger that emits logs as OpenTelemetry LogRecords.
10
- *
11
- * Every log call automatically captures the active trace and span context,
12
- * correlating your logs with distributed traces without any manual wiring.
13
- * When OTel is not initialized, Logger gracefully falls back to `console.*`.
14
- *
15
- * Pass structured data as the second argument to any log method. Using an
16
- * object of key-value pairs (instead of string interpolation) lets you
17
- * filter, aggregate, and build dashboards in your observability backend.
18
- *
19
- * @example
20
- * ```typescript
21
- * import { Logger } from 'iii-sdk'
22
- *
23
- * const logger = new Logger()
24
- *
25
- * // Basic logging — trace context is injected automatically
26
- * logger.info('Worker connected')
27
- *
28
- * // Structured context for dashboards and alerting
29
- * logger.info('Order processed', { orderId: 'ord_123', amount: 49.99, currency: 'USD' })
30
- * logger.warn('Retry attempt', { attempt: 3, maxRetries: 5, endpoint: '/api/charge' })
31
- * logger.error('Payment failed', { orderId: 'ord_123', gateway: 'stripe', errorCode: 'card_declined' })
32
- * ```
33
- */
34
- var Logger = class {
35
- _otelLogger = null;
36
- get otelLogger() {
37
- if (!this._otelLogger) this._otelLogger = require_telemetry_system.getLogger();
38
- return this._otelLogger;
39
- }
40
- constructor(traceId, serviceName, spanId) {
41
- this.traceId = traceId;
42
- this.serviceName = serviceName;
43
- this.spanId = spanId;
44
- }
45
- emit(message, severity, data) {
46
- const attributes = {};
47
- const traceId = this.traceId ?? require_telemetry_system.currentTraceId();
48
- const spanId = this.spanId ?? require_telemetry_system.currentSpanId();
49
- if (traceId) attributes.trace_id = traceId;
50
- if (spanId) attributes.span_id = spanId;
51
- if (this.serviceName) attributes["service.name"] = this.serviceName;
52
- if (data !== void 0) attributes["log.data"] = data;
53
- if (this.otelLogger) this.otelLogger.emit({
54
- severityNumber: severity,
55
- body: message,
56
- attributes: Object.keys(attributes).length > 0 ? attributes : void 0
57
- });
58
- else switch (severity) {
59
- case _opentelemetry_api_logs.SeverityNumber.DEBUG:
60
- console.debug(message, data);
61
- break;
62
- case _opentelemetry_api_logs.SeverityNumber.INFO:
63
- console.info(message, data);
64
- break;
65
- case _opentelemetry_api_logs.SeverityNumber.WARN:
66
- console.warn(message, data);
67
- break;
68
- case _opentelemetry_api_logs.SeverityNumber.ERROR:
69
- console.error(message, data);
70
- break;
71
- default: console.log(message, data);
72
- }
73
- }
74
- /**
75
- * Log an info-level message.
76
- *
77
- * @param message - Human-readable log message.
78
- * @param data - Structured context attached as OTel log attributes.
79
- * Use key-value objects to enable filtering and aggregation in your
80
- * observability backend (e.g. Grafana, Datadog, New Relic).
81
- *
82
- * @example
83
- * ```typescript
84
- * logger.info('Order processed', { orderId: 'ord_123', status: 'completed' })
85
- * ```
86
- */
87
- info(message, data) {
88
- this.emit(message, _opentelemetry_api_logs.SeverityNumber.INFO, data);
89
- }
90
- /**
91
- * Log a warning-level message.
92
- *
93
- * @param message - Human-readable log message.
94
- * @param data - Structured context attached as OTel log attributes.
95
- * Use key-value objects to enable filtering and aggregation in your
96
- * observability backend (e.g. Grafana, Datadog, New Relic).
97
- *
98
- * @example
99
- * ```typescript
100
- * logger.warn('Retry attempt', { attempt: 3, maxRetries: 5, endpoint: '/api/charge' })
101
- * ```
102
- */
103
- warn(message, data) {
104
- this.emit(message, _opentelemetry_api_logs.SeverityNumber.WARN, data);
105
- }
106
- /**
107
- * Log an error-level message.
108
- *
109
- * @param message - Human-readable log message.
110
- * @param data - Structured context attached as OTel log attributes.
111
- * Use key-value objects to enable filtering and aggregation in your
112
- * observability backend (e.g. Grafana, Datadog, New Relic).
113
- *
114
- * @example
115
- * ```typescript
116
- * logger.error('Payment failed', { orderId: 'ord_123', gateway: 'stripe', errorCode: 'card_declined' })
117
- * ```
118
- */
119
- error(message, data) {
120
- this.emit(message, _opentelemetry_api_logs.SeverityNumber.ERROR, data);
121
- }
122
- /**
123
- * Log a debug-level message.
124
- *
125
- * @param message - Human-readable log message.
126
- * @param data - Structured context attached as OTel log attributes.
127
- * Use key-value objects to enable filtering and aggregation in your
128
- * observability backend (e.g. Grafana, Datadog, New Relic).
129
- *
130
- * @example
131
- * ```typescript
132
- * logger.debug('Cache lookup', { key: 'user:42', hit: false })
133
- * ```
134
- */
135
- debug(message, data) {
136
- this.emit(message, _opentelemetry_api_logs.SeverityNumber.DEBUG, data);
137
- }
138
- };
139
2
 
140
- //#endregion
141
- //#region src/http-instrumentation.ts
142
- const SAFE_REQUEST_HEADERS = ["content-type", "accept"];
143
- const SAFE_RESPONSE_HEADERS = ["content-type"];
144
- /**
145
- * Execute a fetch request inside an OTel CLIENT span.
146
- *
147
- * Mirrors the Rust execute_traced_request shape: injects W3C traceparent into
148
- * outgoing headers, records HTTP semantic-convention attributes, and sets
149
- * ERROR span status for HTTP responses with status >= 400 or network errors.
150
- */
151
- async function executeTracedRequest(input, init) {
152
- const tracer = init?.tracer ?? _opentelemetry_api.trace.getTracer("iii-node-sdk");
153
- const rawUrl = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
154
- let url;
155
- try {
156
- url = new URL(rawUrl);
157
- } catch {
158
- url = null;
159
- }
160
- const method = (init?.method ?? (typeof input === "object" && "method" in input ? input.method : "GET") ?? "GET").toUpperCase();
161
- const name = url?.pathname ? `${method} ${url.pathname}` : method;
162
- return tracer.startActiveSpan(name, {
163
- kind: _opentelemetry_api.SpanKind.CLIENT,
164
- attributes: {
165
- "http.request.method": method,
166
- "url.full": url?.toString() ?? rawUrl,
167
- "network.protocol.name": "http",
168
- ...url ? {
169
- "server.address": url.hostname,
170
- "url.scheme": url.protocol.replace(":", ""),
171
- "url.path": url.pathname
172
- } : {},
173
- ...url?.port ? { "server.port": Number(url.port) } : {},
174
- ...url?.search ? { "url.query": url.search.slice(1) } : {}
175
- }
176
- }, async (span) => {
177
- try {
178
- const baseHeaders = typeof input === "object" && "headers" in input ? input.headers : void 0;
179
- const headers = new Headers(baseHeaders);
180
- if (init?.headers) for (const [k, v] of new Headers(init.headers).entries()) headers.set(k, v);
181
- const carrier = {};
182
- _opentelemetry_api.propagation.inject(_opentelemetry_api.context.active(), carrier);
183
- for (const [k, v] of Object.entries(carrier)) headers.set(k, v);
184
- for (const h of SAFE_REQUEST_HEADERS) {
185
- const v = headers.get(h);
186
- if (v) span.setAttribute(`http.request.header.${h}`, v);
187
- }
188
- const response = await fetch(input, {
189
- ...init,
190
- headers
191
- });
192
- span.setAttribute("http.response.status_code", response.status);
193
- const cl = response.headers.get("content-length");
194
- if (cl) span.setAttribute("http.response.body.size", Number(cl));
195
- for (const h of SAFE_RESPONSE_HEADERS) {
196
- const v = response.headers.get(h);
197
- if (v) span.setAttribute(`http.response.header.${h}`, v);
198
- }
199
- if (response.status >= 400) {
200
- span.setStatus({
201
- code: _opentelemetry_api.SpanStatusCode.ERROR,
202
- message: String(response.status)
203
- });
204
- span.setAttribute("error.type", String(response.status));
205
- } else span.setStatus({ code: _opentelemetry_api.SpanStatusCode.OK });
206
- return response;
207
- } catch (err) {
208
- const error = err;
209
- span.recordException(error);
210
- span.setStatus({
211
- code: _opentelemetry_api.SpanStatusCode.ERROR,
212
- message: error.message
213
- });
214
- span.setAttribute("error.type", error.name);
215
- throw err;
216
- } finally {
217
- span.end();
218
- }
219
- });
220
- }
221
-
222
- //#endregion
223
- //#region src/worker-metrics.ts
224
- /**
225
- * Worker metrics collection for the III Node SDK.
226
- *
227
- * Collects CPU, memory, and event loop metrics for worker health monitoring.
228
- * Uses the Node.js built-in `monitorEventLoopDelay` API for accurate
229
- * event loop lag measurements.
230
- */
231
- /**
232
- * Collects worker resource metrics including CPU, memory, and event loop lag.
233
- *
234
- * Uses the Node.js `monitorEventLoopDelay` API for high-precision event loop
235
- * delay measurements instead of manual `setImmediate` timing.
236
- *
237
- * @example
238
- * ```typescript
239
- * const collector = new WorkerMetricsCollector()
240
- *
241
- * // Collect metrics periodically
242
- * setInterval(() => {
243
- * const metrics = collector.collect()
244
- * console.log('CPU:', metrics.cpu_percent, '%')
245
- * console.log('Event Loop Lag:', metrics.event_loop_lag_ms, 'ms')
246
- * }, 5000)
247
- *
248
- * // Clean up when done
249
- * collector.stopMonitoring()
250
- * ```
251
- */
252
- var WorkerMetricsCollector = class {
253
- startTime;
254
- lastCpuUsage;
255
- lastCpuTime;
256
- eventLoopHistogram = null;
257
- /**
258
- * Creates a new WorkerMetricsCollector instance.
259
- *
260
- * @param options - Configuration options
261
- */
262
- constructor(options = {}) {
263
- this.startTime = Date.now();
264
- this.lastCpuUsage = process.cpuUsage();
265
- this.lastCpuTime = node_perf_hooks.performance.now();
266
- this.startEventLoopMonitoring(options.eventLoopResolutionMs ?? 20);
267
- }
268
- /**
269
- * Starts the event loop delay histogram monitoring.
270
- *
271
- * @param resolutionMs - Histogram resolution in milliseconds
272
- */
273
- startEventLoopMonitoring(resolutionMs) {
274
- this.eventLoopHistogram = (0, node_perf_hooks.monitorEventLoopDelay)({ resolution: Number.isFinite(resolutionMs) && resolutionMs > 0 ? Math.max(1, Math.floor(resolutionMs)) : 20 });
275
- this.eventLoopHistogram.enable();
276
- }
277
- /**
278
- * Stops the event loop monitoring and releases resources.
279
- * Should be called when the collector is no longer needed.
280
- */
281
- stopMonitoring() {
282
- if (this.eventLoopHistogram) {
283
- this.eventLoopHistogram.disable();
284
- this.eventLoopHistogram = null;
285
- }
286
- }
287
- /**
288
- * Collects current worker metrics.
289
- *
290
- * This method calculates CPU usage since the last collection,
291
- * reads memory usage, and gets event loop delay statistics.
292
- * The event loop histogram is reset after each collection for
293
- * accurate per-interval measurements.
294
- *
295
- * @returns Current worker metrics snapshot
296
- */
297
- collect() {
298
- const memoryUsage = process.memoryUsage();
299
- const cpuUsage = process.cpuUsage();
300
- const now = node_perf_hooks.performance.now();
301
- const cpuDelta = {
302
- user: cpuUsage.user - this.lastCpuUsage.user,
303
- system: cpuUsage.system - this.lastCpuUsage.system
304
- };
305
- const timeDelta = (now - this.lastCpuTime) * 1e3;
306
- const cpuPercent = timeDelta > 0 ? (cpuDelta.user + cpuDelta.system) / timeDelta * 100 : 0;
307
- this.lastCpuUsage = cpuUsage;
308
- this.lastCpuTime = now;
309
- let eventLoopLagMs = 0;
310
- if (this.eventLoopHistogram) {
311
- eventLoopLagMs = this.eventLoopHistogram.mean / 1e6;
312
- this.eventLoopHistogram.reset();
313
- }
314
- return {
315
- memory_heap_used: memoryUsage.heapUsed,
316
- memory_heap_total: memoryUsage.heapTotal,
317
- memory_rss: memoryUsage.rss,
318
- memory_external: memoryUsage.external,
319
- cpu_user_micros: cpuUsage.user,
320
- cpu_system_micros: cpuUsage.system,
321
- cpu_percent: Math.min(cpuPercent, 100),
322
- event_loop_lag_ms: eventLoopLagMs,
323
- uptime_seconds: Math.floor((Date.now() - this.startTime) / 1e3),
324
- timestamp_ms: Date.now(),
325
- runtime: "node"
326
- };
327
- }
328
- };
329
-
330
- //#endregion
331
- //#region src/otel-worker-gauges.ts
332
- let registeredGauges = false;
333
- let metricsCollector = null;
334
- let registeredMeter = null;
335
- let registeredBatchCallback = null;
336
- let registeredObservables = [];
337
- function registerWorkerGauges(meter, options) {
338
- if (registeredGauges) return;
339
- const { workerId, workerName } = options;
340
- const baseAttributes = {
341
- "worker.id": workerId,
342
- ...workerName && { "worker.name": workerName }
343
- };
344
- metricsCollector = new WorkerMetricsCollector();
345
- const memoryHeapUsed = meter.createObservableGauge("iii.worker.memory.heap_used", {
346
- description: "Worker heap memory used in bytes",
347
- unit: "bytes"
348
- });
349
- const memoryHeapTotal = meter.createObservableGauge("iii.worker.memory.heap_total", {
350
- description: "Worker total heap memory in bytes",
351
- unit: "bytes"
352
- });
353
- const memoryRss = meter.createObservableGauge("iii.worker.memory.rss", {
354
- description: "Worker resident set size in bytes",
355
- unit: "bytes"
356
- });
357
- const memoryExternal = meter.createObservableGauge("iii.worker.memory.external", {
358
- description: "Worker external memory in bytes",
359
- unit: "bytes"
360
- });
361
- const cpuPercent = meter.createObservableGauge("iii.worker.cpu.percent", {
362
- description: "Worker CPU usage percentage",
363
- unit: "%"
364
- });
365
- const cpuUserMicros = meter.createObservableGauge("iii.worker.cpu.user_micros", {
366
- description: "Worker CPU user time in microseconds",
367
- unit: "us"
368
- });
369
- const cpuSystemMicros = meter.createObservableGauge("iii.worker.cpu.system_micros", {
370
- description: "Worker CPU system time in microseconds",
371
- unit: "us"
372
- });
373
- const eventLoopLag = meter.createObservableGauge("iii.worker.event_loop.lag_ms", {
374
- description: "Worker event loop lag in milliseconds",
375
- unit: "ms"
376
- });
377
- const uptimeSeconds = meter.createObservableGauge("iii.worker.uptime_seconds", {
378
- description: "Worker uptime in seconds",
379
- unit: "s"
380
- });
381
- const batchCallback = (observableResult) => {
382
- if (!metricsCollector) return;
383
- const metrics = metricsCollector.collect();
384
- if (metrics.memory_heap_used !== void 0) observableResult.observe(memoryHeapUsed, metrics.memory_heap_used, baseAttributes);
385
- if (metrics.memory_heap_total !== void 0) observableResult.observe(memoryHeapTotal, metrics.memory_heap_total, baseAttributes);
386
- if (metrics.memory_rss !== void 0) observableResult.observe(memoryRss, metrics.memory_rss, baseAttributes);
387
- if (metrics.memory_external !== void 0) observableResult.observe(memoryExternal, metrics.memory_external, baseAttributes);
388
- if (metrics.cpu_percent !== void 0) observableResult.observe(cpuPercent, metrics.cpu_percent, baseAttributes);
389
- if (metrics.cpu_user_micros !== void 0) observableResult.observe(cpuUserMicros, metrics.cpu_user_micros, baseAttributes);
390
- if (metrics.cpu_system_micros !== void 0) observableResult.observe(cpuSystemMicros, metrics.cpu_system_micros, baseAttributes);
391
- if (metrics.event_loop_lag_ms !== void 0) observableResult.observe(eventLoopLag, metrics.event_loop_lag_ms, baseAttributes);
392
- if (metrics.uptime_seconds !== void 0) observableResult.observe(uptimeSeconds, metrics.uptime_seconds, baseAttributes);
393
- };
394
- meter.addBatchObservableCallback(batchCallback, [
395
- memoryHeapUsed,
396
- memoryHeapTotal,
397
- memoryRss,
398
- memoryExternal,
399
- cpuPercent,
400
- cpuUserMicros,
401
- cpuSystemMicros,
402
- eventLoopLag,
403
- uptimeSeconds
404
- ]);
405
- registeredMeter = meter;
406
- registeredBatchCallback = batchCallback;
407
- registeredObservables = [
408
- memoryHeapUsed,
409
- memoryHeapTotal,
410
- memoryRss,
411
- memoryExternal,
412
- cpuPercent,
413
- cpuUserMicros,
414
- cpuSystemMicros,
415
- eventLoopLag,
416
- uptimeSeconds
417
- ];
418
- registeredGauges = true;
419
- }
420
- function stopWorkerGauges() {
421
- if (registeredMeter && registeredBatchCallback) registeredMeter.removeBatchObservableCallback(registeredBatchCallback, registeredObservables);
422
- if (metricsCollector) {
423
- metricsCollector.stopMonitoring();
424
- metricsCollector = null;
425
- }
426
- registeredMeter = null;
427
- registeredBatchCallback = null;
428
- registeredObservables = [];
429
- registeredGauges = false;
430
- }
431
-
432
- //#endregion
433
- //#region src/utils.ts
434
- /**
435
- * Safely stringify a value, handling circular references, BigInt, and other edge cases.
436
- * Returns "[unserializable]" if serialization fails for any reason.
437
- */
438
- function safeStringify(value) {
439
- const seen = /* @__PURE__ */ new WeakSet();
440
- try {
441
- return JSON.stringify(value, (_key, val) => {
442
- if (typeof val === "bigint") return val.toString();
443
- if (val !== null && typeof val === "object") {
444
- if (seen.has(val)) return "[Circular]";
445
- seen.add(val);
446
- }
447
- return val;
448
- }) ?? "[unserializable]";
449
- } catch {
450
- return "[unserializable]";
451
- }
452
- }
453
-
454
- //#endregion
455
- exports.BaggageSpanProcessor = require_telemetry_system.BaggageSpanProcessor;
456
- exports.DEFAULT_ALLOWLIST = require_telemetry_system.DEFAULT_ALLOWLIST;
457
- exports.Logger = Logger;
458
- exports.REDACTED_PLACEHOLDER = require_telemetry_system.REDACTED_PLACEHOLDER;
459
- exports.SeverityNumber = _opentelemetry_api_logs.SeverityNumber;
460
- exports.WorkerMetricsCollector = WorkerMetricsCollector;
461
- exports.currentSpanId = require_telemetry_system.currentSpanId;
462
- exports.currentSpanIsRecording = require_telemetry_system.currentSpanIsRecording;
463
- exports.currentTraceId = require_telemetry_system.currentTraceId;
464
- exports.executeTracedRequest = executeTracedRequest;
465
- exports.extractBaggage = require_telemetry_system.extractBaggage;
466
- exports.extractContext = require_telemetry_system.extractContext;
467
- exports.extractTraceparent = require_telemetry_system.extractTraceparent;
468
- exports.flushOtel = require_telemetry_system.flushOtel;
469
- exports.getAllBaggage = require_telemetry_system.getAllBaggage;
470
- exports.getBaggageEntry = require_telemetry_system.getBaggageEntry;
471
- exports.getLogger = require_telemetry_system.getLogger;
472
- exports.initOtel = require_telemetry_system.initOtel;
473
- exports.injectBaggage = require_telemetry_system.injectBaggage;
474
- exports.injectTraceparent = require_telemetry_system.injectTraceparent;
475
- exports.patchGlobalFetch = require_telemetry_system.patchGlobalFetch;
476
- exports.recordSpanEvent = require_telemetry_system.recordSpanEvent;
477
- exports.redact = require_telemetry_system.redact;
478
- exports.redactAndTruncate = require_telemetry_system.redactAndTruncate;
479
- exports.registerWorkerGauges = registerWorkerGauges;
480
- exports.removeBaggageEntry = require_telemetry_system.removeBaggageEntry;
481
- exports.resolveMaxBytesFromEnv = require_telemetry_system.resolveMaxBytesFromEnv;
482
- exports.safeStringify = safeStringify;
483
- exports.setBaggageEntry = require_telemetry_system.setBaggageEntry;
484
- exports.setCurrentSpanAttribute = require_telemetry_system.setCurrentSpanAttribute;
485
- exports.setCurrentSpanError = require_telemetry_system.setCurrentSpanError;
486
- exports.shutdownOtel = require_telemetry_system.shutdownOtel;
487
- exports.stopWorkerGauges = stopWorkerGauges;
488
- exports.unpatchGlobalFetch = require_telemetry_system.unpatchGlobalFetch;
489
- exports.withSpan = require_telemetry_system.withSpan;
490
- //# sourceMappingURL=index.cjs.map
3
+ var _iii_dev_helpers_observability = require("@iii-dev/helpers/observability");
4
+ Object.keys(_iii_dev_helpers_observability).forEach(function (k) {
5
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
6
+ enumerable: true,
7
+ get: function () { return _iii_dev_helpers_observability[k]; }
8
+ });
9
+ });