tracebeam-sdk 0.1.1 → 0.2.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/dist/index.mjs CHANGED
@@ -1,3 +1,613 @@
1
+ // src/sdk.ts
2
+ import * as api from "@opentelemetry/api";
3
+
4
+ // node_modules/@opentelemetry/core/build/esm/trace/suppress-tracing.js
5
+ import { createContextKey } from "@opentelemetry/api";
6
+ var SUPPRESS_TRACING_KEY = createContextKey("OpenTelemetry SDK Context Key SUPPRESS_TRACING");
7
+ function suppressTracing(context3) {
8
+ return context3.setValue(SUPPRESS_TRACING_KEY, true);
9
+ }
10
+
11
+ // node_modules/@opentelemetry/core/build/esm/common/logging-error-handler.js
12
+ import { diag } from "@opentelemetry/api";
13
+ function loggingErrorHandler() {
14
+ return (ex) => {
15
+ diag.error(stringifyException(ex));
16
+ };
17
+ }
18
+ function stringifyException(ex) {
19
+ if (typeof ex === "string") {
20
+ return ex;
21
+ } else {
22
+ return JSON.stringify(flattenException(ex));
23
+ }
24
+ }
25
+ function flattenException(ex) {
26
+ const result = {};
27
+ let current = ex;
28
+ while (current !== null) {
29
+ Object.getOwnPropertyNames(current).forEach((propertyName) => {
30
+ if (result[propertyName])
31
+ return;
32
+ const value = current[propertyName];
33
+ if (value) {
34
+ result[propertyName] = String(value);
35
+ }
36
+ });
37
+ current = Object.getPrototypeOf(current);
38
+ }
39
+ return result;
40
+ }
41
+
42
+ // node_modules/@opentelemetry/core/build/esm/common/global-error-handler.js
43
+ var delegateHandler = loggingErrorHandler();
44
+ function globalErrorHandler(ex) {
45
+ try {
46
+ delegateHandler(ex);
47
+ } catch {
48
+ }
49
+ }
50
+
51
+ // node_modules/@opentelemetry/core/build/esm/platform/node/environment.js
52
+ import { diag as diag2 } from "@opentelemetry/api";
53
+ import { inspect } from "util";
54
+ function getNumberFromEnv(key) {
55
+ const raw = process.env[key];
56
+ if (raw == null || raw.trim() === "") {
57
+ return void 0;
58
+ }
59
+ const value = Number(raw);
60
+ if (isNaN(value)) {
61
+ diag2.warn(`Unknown value ${inspect(raw)} for ${key}, expected a number, using defaults`);
62
+ return void 0;
63
+ }
64
+ return value;
65
+ }
66
+
67
+ // node_modules/@opentelemetry/core/build/esm/common/time.js
68
+ var NANOSECOND_DIGITS = 9;
69
+ var NANOSECOND_DIGITS_IN_MILLIS = 6;
70
+ var MILLISECONDS_TO_NANOSECONDS = Math.pow(10, NANOSECOND_DIGITS_IN_MILLIS);
71
+ var SECOND_TO_NANOSECONDS = Math.pow(10, NANOSECOND_DIGITS);
72
+ function hrTimeToMicroseconds(time) {
73
+ return time[0] * 1e6 + time[1] / 1e3;
74
+ }
75
+
76
+ // node_modules/@opentelemetry/core/build/esm/ExportResult.js
77
+ var ExportResultCode;
78
+ (function(ExportResultCode2) {
79
+ ExportResultCode2[ExportResultCode2["SUCCESS"] = 0] = "SUCCESS";
80
+ ExportResultCode2[ExportResultCode2["FAILED"] = 1] = "FAILED";
81
+ })(ExportResultCode || (ExportResultCode = {}));
82
+
83
+ // node_modules/@opentelemetry/core/build/esm/utils/promise.js
84
+ var Deferred = class {
85
+ _promise;
86
+ _resolve;
87
+ _reject;
88
+ constructor() {
89
+ this._promise = new Promise((resolve, reject) => {
90
+ this._resolve = resolve;
91
+ this._reject = reject;
92
+ });
93
+ }
94
+ get promise() {
95
+ return this._promise;
96
+ }
97
+ resolve(val) {
98
+ this._resolve(val);
99
+ }
100
+ reject(err) {
101
+ this._reject(err);
102
+ }
103
+ };
104
+
105
+ // node_modules/@opentelemetry/core/build/esm/utils/callback.js
106
+ var BindOnceFuture = class {
107
+ _isCalled = false;
108
+ _deferred = new Deferred();
109
+ _callback;
110
+ _that;
111
+ constructor(callback, that) {
112
+ this._callback = callback;
113
+ this._that = that;
114
+ }
115
+ get isCalled() {
116
+ return this._isCalled;
117
+ }
118
+ get promise() {
119
+ return this._deferred.promise;
120
+ }
121
+ call(...args) {
122
+ if (!this._isCalled) {
123
+ this._isCalled = true;
124
+ try {
125
+ Promise.resolve(this._callback.call(this._that, ...args)).then((val) => this._deferred.resolve(val), (err) => this._deferred.reject(err));
126
+ } catch (err) {
127
+ this._deferred.reject(err);
128
+ }
129
+ }
130
+ return this._deferred.promise;
131
+ }
132
+ };
133
+
134
+ // node_modules/@opentelemetry/core/build/esm/internal/exporter.js
135
+ import { context } from "@opentelemetry/api";
136
+ function _export(exporter, arg) {
137
+ return new Promise((resolve) => {
138
+ context.with(suppressTracing(context.active()), () => {
139
+ exporter.export(arg, (result) => {
140
+ resolve(result);
141
+ });
142
+ });
143
+ });
144
+ }
145
+
146
+ // node_modules/@opentelemetry/core/build/esm/index.js
147
+ var internal = {
148
+ _export
149
+ };
150
+
151
+ // node_modules/@opentelemetry/sdk-trace-base/build/esm/export/BatchSpanProcessorBase.js
152
+ import { context as context2, diag as diag3, TraceFlags } from "@opentelemetry/api";
153
+ var BatchSpanProcessorBase = class {
154
+ _maxExportBatchSize;
155
+ _maxQueueSize;
156
+ _scheduledDelayMillis;
157
+ _exportTimeoutMillis;
158
+ _exporter;
159
+ _isExporting = false;
160
+ _finishedSpans = [];
161
+ _timer;
162
+ _shutdownOnce;
163
+ _droppedSpansCount = 0;
164
+ constructor(exporter, config) {
165
+ this._exporter = exporter;
166
+ this._maxExportBatchSize = typeof config?.maxExportBatchSize === "number" ? config.maxExportBatchSize : getNumberFromEnv("OTEL_BSP_MAX_EXPORT_BATCH_SIZE") ?? 512;
167
+ this._maxQueueSize = typeof config?.maxQueueSize === "number" ? config.maxQueueSize : getNumberFromEnv("OTEL_BSP_MAX_QUEUE_SIZE") ?? 2048;
168
+ this._scheduledDelayMillis = typeof config?.scheduledDelayMillis === "number" ? config.scheduledDelayMillis : getNumberFromEnv("OTEL_BSP_SCHEDULE_DELAY") ?? 5e3;
169
+ this._exportTimeoutMillis = typeof config?.exportTimeoutMillis === "number" ? config.exportTimeoutMillis : getNumberFromEnv("OTEL_BSP_EXPORT_TIMEOUT") ?? 3e4;
170
+ this._shutdownOnce = new BindOnceFuture(this._shutdown, this);
171
+ if (this._maxExportBatchSize > this._maxQueueSize) {
172
+ diag3.warn("BatchSpanProcessor: maxExportBatchSize must be smaller or equal to maxQueueSize, setting maxExportBatchSize to match maxQueueSize");
173
+ this._maxExportBatchSize = this._maxQueueSize;
174
+ }
175
+ }
176
+ forceFlush() {
177
+ if (this._shutdownOnce.isCalled) {
178
+ return this._shutdownOnce.promise;
179
+ }
180
+ return this._flushAll();
181
+ }
182
+ // does nothing.
183
+ onStart(_span, _parentContext) {
184
+ }
185
+ onEnd(span) {
186
+ if (this._shutdownOnce.isCalled) {
187
+ return;
188
+ }
189
+ if ((span.spanContext().traceFlags & TraceFlags.SAMPLED) === 0) {
190
+ return;
191
+ }
192
+ this._addToBuffer(span);
193
+ }
194
+ shutdown() {
195
+ return this._shutdownOnce.call();
196
+ }
197
+ _shutdown() {
198
+ return Promise.resolve().then(() => {
199
+ return this.onShutdown();
200
+ }).then(() => {
201
+ return this._flushAll();
202
+ }).then(() => {
203
+ return this._exporter.shutdown();
204
+ });
205
+ }
206
+ /** Add a span in the buffer. */
207
+ _addToBuffer(span) {
208
+ if (this._finishedSpans.length >= this._maxQueueSize) {
209
+ if (this._droppedSpansCount === 0) {
210
+ diag3.debug("maxQueueSize reached, dropping spans");
211
+ }
212
+ this._droppedSpansCount++;
213
+ return;
214
+ }
215
+ if (this._droppedSpansCount > 0) {
216
+ diag3.warn(`Dropped ${this._droppedSpansCount} spans because maxQueueSize reached`);
217
+ this._droppedSpansCount = 0;
218
+ }
219
+ this._finishedSpans.push(span);
220
+ this._maybeStartTimer();
221
+ }
222
+ /**
223
+ * Send all spans to the exporter respecting the batch size limit
224
+ * This function is used only on forceFlush or shutdown,
225
+ * for all other cases _flush should be used
226
+ * */
227
+ _flushAll() {
228
+ return new Promise((resolve, reject) => {
229
+ const promises = [];
230
+ const count = Math.ceil(this._finishedSpans.length / this._maxExportBatchSize);
231
+ for (let i = 0, j = count; i < j; i++) {
232
+ promises.push(this._flushOneBatch());
233
+ }
234
+ Promise.all(promises).then(() => {
235
+ resolve();
236
+ }).catch(reject);
237
+ });
238
+ }
239
+ _flushOneBatch() {
240
+ this._clearTimer();
241
+ if (this._finishedSpans.length === 0) {
242
+ return Promise.resolve();
243
+ }
244
+ return new Promise((resolve, reject) => {
245
+ const timer = setTimeout(() => {
246
+ reject(new Error("Timeout"));
247
+ }, this._exportTimeoutMillis);
248
+ context2.with(suppressTracing(context2.active()), () => {
249
+ let spans;
250
+ if (this._finishedSpans.length <= this._maxExportBatchSize) {
251
+ spans = this._finishedSpans;
252
+ this._finishedSpans = [];
253
+ } else {
254
+ spans = this._finishedSpans.splice(0, this._maxExportBatchSize);
255
+ }
256
+ const doExport = () => this._exporter.export(spans, (result) => {
257
+ clearTimeout(timer);
258
+ if (result.code === ExportResultCode.SUCCESS) {
259
+ resolve();
260
+ } else {
261
+ reject(result.error ?? new Error("BatchSpanProcessor: span export failed"));
262
+ }
263
+ });
264
+ let pendingResources = null;
265
+ for (let i = 0, len = spans.length; i < len; i++) {
266
+ const span = spans[i];
267
+ if (span.resource.asyncAttributesPending && span.resource.waitForAsyncAttributes) {
268
+ pendingResources ??= [];
269
+ pendingResources.push(span.resource.waitForAsyncAttributes());
270
+ }
271
+ }
272
+ if (pendingResources === null) {
273
+ doExport();
274
+ } else {
275
+ Promise.all(pendingResources).then(doExport, (err) => {
276
+ globalErrorHandler(err);
277
+ reject(err);
278
+ });
279
+ }
280
+ });
281
+ });
282
+ }
283
+ _maybeStartTimer() {
284
+ if (this._isExporting)
285
+ return;
286
+ const flush = () => {
287
+ this._isExporting = true;
288
+ this._flushOneBatch().finally(() => {
289
+ this._isExporting = false;
290
+ if (this._finishedSpans.length > 0) {
291
+ this._clearTimer();
292
+ this._maybeStartTimer();
293
+ }
294
+ }).catch((e) => {
295
+ this._isExporting = false;
296
+ globalErrorHandler(e);
297
+ });
298
+ };
299
+ if (this._finishedSpans.length >= this._maxExportBatchSize) {
300
+ return flush();
301
+ }
302
+ if (this._timer !== void 0)
303
+ return;
304
+ this._timer = setTimeout(() => flush(), this._scheduledDelayMillis);
305
+ if (typeof this._timer !== "number") {
306
+ this._timer.unref();
307
+ }
308
+ }
309
+ _clearTimer() {
310
+ if (this._timer !== void 0) {
311
+ clearTimeout(this._timer);
312
+ this._timer = void 0;
313
+ }
314
+ }
315
+ };
316
+
317
+ // node_modules/@opentelemetry/sdk-trace-base/build/esm/platform/node/export/BatchSpanProcessor.js
318
+ var BatchSpanProcessor = class extends BatchSpanProcessorBase {
319
+ onShutdown() {
320
+ }
321
+ };
322
+
323
+ // node_modules/@opentelemetry/sdk-trace-base/build/esm/export/ConsoleSpanExporter.js
324
+ var ConsoleSpanExporter = class {
325
+ /**
326
+ * Export spans.
327
+ * @param spans
328
+ * @param resultCallback
329
+ */
330
+ export(spans, resultCallback) {
331
+ return this._sendSpans(spans, resultCallback);
332
+ }
333
+ /**
334
+ * Shutdown the exporter.
335
+ */
336
+ shutdown() {
337
+ this._sendSpans([]);
338
+ return this.forceFlush();
339
+ }
340
+ /**
341
+ * Exports any pending spans in exporter
342
+ */
343
+ forceFlush() {
344
+ return Promise.resolve();
345
+ }
346
+ /**
347
+ * converts span info into more readable format
348
+ * @param span
349
+ */
350
+ _exportInfo(span) {
351
+ return {
352
+ resource: {
353
+ attributes: span.resource.attributes
354
+ },
355
+ instrumentationScope: span.instrumentationScope,
356
+ traceId: span.spanContext().traceId,
357
+ parentSpanContext: span.parentSpanContext,
358
+ traceState: span.spanContext().traceState?.serialize(),
359
+ name: span.name,
360
+ id: span.spanContext().spanId,
361
+ kind: span.kind,
362
+ timestamp: hrTimeToMicroseconds(span.startTime),
363
+ duration: hrTimeToMicroseconds(span.duration),
364
+ attributes: span.attributes,
365
+ status: span.status,
366
+ events: span.events,
367
+ links: span.links
368
+ };
369
+ }
370
+ /**
371
+ * Showing spans in console
372
+ * @param spans
373
+ * @param done
374
+ */
375
+ _sendSpans(spans, done) {
376
+ for (const span of spans) {
377
+ console.dir(this._exportInfo(span), { depth: 3 });
378
+ }
379
+ if (done) {
380
+ return done({ code: ExportResultCode.SUCCESS });
381
+ }
382
+ }
383
+ };
384
+
385
+ // node_modules/@opentelemetry/sdk-trace-base/build/esm/export/SimpleSpanProcessor.js
386
+ import { TraceFlags as TraceFlags2 } from "@opentelemetry/api";
387
+ var SimpleSpanProcessor = class {
388
+ _exporter;
389
+ _shutdownOnce;
390
+ _pendingExports;
391
+ constructor(exporter) {
392
+ this._exporter = exporter;
393
+ this._shutdownOnce = new BindOnceFuture(this._shutdown, this);
394
+ this._pendingExports = /* @__PURE__ */ new Set();
395
+ }
396
+ async forceFlush() {
397
+ await Promise.all(Array.from(this._pendingExports));
398
+ if (this._exporter.forceFlush) {
399
+ await this._exporter.forceFlush();
400
+ }
401
+ }
402
+ onStart(_span, _parentContext) {
403
+ }
404
+ onEnd(span) {
405
+ if (this._shutdownOnce.isCalled) {
406
+ return;
407
+ }
408
+ if ((span.spanContext().traceFlags & TraceFlags2.SAMPLED) === 0) {
409
+ return;
410
+ }
411
+ const pendingExport = this._doExport(span).catch((err) => globalErrorHandler(err));
412
+ this._pendingExports.add(pendingExport);
413
+ void pendingExport.finally(() => this._pendingExports.delete(pendingExport));
414
+ }
415
+ async _doExport(span) {
416
+ if (span.resource.asyncAttributesPending) {
417
+ await span.resource.waitForAsyncAttributes?.();
418
+ }
419
+ const result = await internal._export(this._exporter, [span]);
420
+ if (result.code !== ExportResultCode.SUCCESS) {
421
+ throw result.error ?? new Error(`SimpleSpanProcessor: span export failed (status ${result})`);
422
+ }
423
+ }
424
+ shutdown() {
425
+ return this._shutdownOnce.call();
426
+ }
427
+ _shutdown() {
428
+ return this._exporter.shutdown();
429
+ }
430
+ };
431
+
432
+ // src/sdk.ts
433
+ import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
434
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
435
+ import { resourceFromAttributes } from "@opentelemetry/resources";
436
+ import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";
437
+
438
+ // node_modules/@opentelemetry/instrumentation/build/esm/autoLoader.js
439
+ import { trace, metrics } from "@opentelemetry/api";
440
+
441
+ // node_modules/@opentelemetry/api-logs/build/esm/NoopLogger.js
442
+ var NoopLogger = class {
443
+ emit(_logRecord) {
444
+ }
445
+ };
446
+ var NOOP_LOGGER = new NoopLogger();
447
+
448
+ // node_modules/@opentelemetry/api-logs/build/esm/NoopLoggerProvider.js
449
+ var NoopLoggerProvider = class {
450
+ getLogger(_name, _version, _options) {
451
+ return new NoopLogger();
452
+ }
453
+ };
454
+ var NOOP_LOGGER_PROVIDER = new NoopLoggerProvider();
455
+
456
+ // node_modules/@opentelemetry/api-logs/build/esm/ProxyLogger.js
457
+ var ProxyLogger = class {
458
+ constructor(provider, name, version, options) {
459
+ this._provider = provider;
460
+ this.name = name;
461
+ this.version = version;
462
+ this.options = options;
463
+ }
464
+ /**
465
+ * Emit a log record. This method should only be used by log appenders.
466
+ *
467
+ * @param logRecord
468
+ */
469
+ emit(logRecord) {
470
+ this._getLogger().emit(logRecord);
471
+ }
472
+ /**
473
+ * Try to get a logger from the proxy logger provider.
474
+ * If the proxy logger provider has no delegate, return a noop logger.
475
+ */
476
+ _getLogger() {
477
+ if (this._delegate) {
478
+ return this._delegate;
479
+ }
480
+ const logger = this._provider._getDelegateLogger(this.name, this.version, this.options);
481
+ if (!logger) {
482
+ return NOOP_LOGGER;
483
+ }
484
+ this._delegate = logger;
485
+ return this._delegate;
486
+ }
487
+ };
488
+
489
+ // node_modules/@opentelemetry/api-logs/build/esm/ProxyLoggerProvider.js
490
+ var ProxyLoggerProvider = class {
491
+ getLogger(name, version, options) {
492
+ var _a;
493
+ return (_a = this._getDelegateLogger(name, version, options)) !== null && _a !== void 0 ? _a : new ProxyLogger(this, name, version, options);
494
+ }
495
+ /**
496
+ * Get the delegate logger provider.
497
+ * Used by tests only.
498
+ * @internal
499
+ */
500
+ _getDelegate() {
501
+ var _a;
502
+ return (_a = this._delegate) !== null && _a !== void 0 ? _a : NOOP_LOGGER_PROVIDER;
503
+ }
504
+ /**
505
+ * Set the delegate logger provider
506
+ * @internal
507
+ */
508
+ _setDelegate(delegate) {
509
+ this._delegate = delegate;
510
+ }
511
+ /**
512
+ * @internal
513
+ */
514
+ _getDelegateLogger(name, version, options) {
515
+ var _a;
516
+ return (_a = this._delegate) === null || _a === void 0 ? void 0 : _a.getLogger(name, version, options);
517
+ }
518
+ };
519
+
520
+ // node_modules/@opentelemetry/api-logs/build/esm/internal/global-utils.js
521
+ var GLOBAL_LOGS_API_KEY = /* @__PURE__ */ Symbol.for("io.opentelemetry.js.api.logs");
522
+ var _global = globalThis;
523
+ function makeGetter(requiredVersion, instance, fallback) {
524
+ return (version) => version === requiredVersion ? instance : fallback;
525
+ }
526
+ var API_BACKWARDS_COMPATIBILITY_VERSION = 1;
527
+
528
+ // node_modules/@opentelemetry/api-logs/build/esm/api/logs.js
529
+ var LogsAPI = class _LogsAPI {
530
+ constructor() {
531
+ this._proxyLoggerProvider = new ProxyLoggerProvider();
532
+ }
533
+ static getInstance() {
534
+ if (!this._instance) {
535
+ this._instance = new _LogsAPI();
536
+ }
537
+ return this._instance;
538
+ }
539
+ setGlobalLoggerProvider(provider) {
540
+ if (_global[GLOBAL_LOGS_API_KEY]) {
541
+ return this.getLoggerProvider();
542
+ }
543
+ _global[GLOBAL_LOGS_API_KEY] = makeGetter(API_BACKWARDS_COMPATIBILITY_VERSION, provider, NOOP_LOGGER_PROVIDER);
544
+ this._proxyLoggerProvider._setDelegate(provider);
545
+ return provider;
546
+ }
547
+ /**
548
+ * Returns the global logger provider.
549
+ *
550
+ * @returns LoggerProvider
551
+ */
552
+ getLoggerProvider() {
553
+ var _a, _b;
554
+ return (_b = (_a = _global[GLOBAL_LOGS_API_KEY]) === null || _a === void 0 ? void 0 : _a.call(_global, API_BACKWARDS_COMPATIBILITY_VERSION)) !== null && _b !== void 0 ? _b : this._proxyLoggerProvider;
555
+ }
556
+ /**
557
+ * Returns a logger from the global logger provider.
558
+ *
559
+ * @returns Logger
560
+ */
561
+ getLogger(name, version, options) {
562
+ return this.getLoggerProvider().getLogger(name, version, options);
563
+ }
564
+ /** Remove the global logger provider */
565
+ disable() {
566
+ delete _global[GLOBAL_LOGS_API_KEY];
567
+ this._proxyLoggerProvider = new ProxyLoggerProvider();
568
+ }
569
+ };
570
+
571
+ // node_modules/@opentelemetry/api-logs/build/esm/index.js
572
+ var logs = LogsAPI.getInstance();
573
+
574
+ // node_modules/@opentelemetry/instrumentation/build/esm/autoLoaderUtils.js
575
+ function enableInstrumentations(instrumentations, tracerProvider, meterProvider, loggerProvider) {
576
+ for (let i = 0, j = instrumentations.length; i < j; i++) {
577
+ const instrumentation = instrumentations[i];
578
+ if (tracerProvider) {
579
+ instrumentation.setTracerProvider(tracerProvider);
580
+ }
581
+ if (meterProvider) {
582
+ instrumentation.setMeterProvider(meterProvider);
583
+ }
584
+ if (loggerProvider && instrumentation.setLoggerProvider) {
585
+ instrumentation.setLoggerProvider(loggerProvider);
586
+ }
587
+ if (!instrumentation.getConfig().enabled) {
588
+ instrumentation.enable();
589
+ }
590
+ }
591
+ }
592
+ function disableInstrumentations(instrumentations) {
593
+ instrumentations.forEach((instrumentation) => instrumentation.disable());
594
+ }
595
+
596
+ // node_modules/@opentelemetry/instrumentation/build/esm/autoLoader.js
597
+ function registerInstrumentations(options) {
598
+ const tracerProvider = options.tracerProvider || trace.getTracerProvider();
599
+ const meterProvider = options.meterProvider || metrics.getMeterProvider();
600
+ const loggerProvider = options.loggerProvider || logs.getLoggerProvider();
601
+ const instrumentations = options.instrumentations?.flat() ?? [];
602
+ enableInstrumentations(instrumentations, tracerProvider, meterProvider, loggerProvider);
603
+ return () => {
604
+ disableInstrumentations(instrumentations);
605
+ };
606
+ }
607
+
608
+ // src/sdk.ts
609
+ import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
610
+
1
611
  // src/config.ts
2
612
  var DEFAULT_CONFIG = {
3
613
  environment: "development",
@@ -98,357 +708,143 @@ function parseBoolEnv(name, defaultValue) {
98
708
  return value.toLowerCase() === "true" || value === "1";
99
709
  }
100
710
 
101
- // src/transport.ts
102
- var defaultLogger = {
103
- debug: (msg, ...args) => console.debug(`[tracebeam] ${msg}`, ...args),
104
- warn: (msg, ...args) => console.warn(`[tracebeam] ${msg}`, ...args),
105
- error: (msg, ...args) => console.error(`[tracebeam] ${msg}`, ...args)
106
- };
107
- var Transport = class {
108
- endpoint;
109
- apiKey;
110
- timeout;
111
- logger;
112
- debug;
113
- state = "active";
114
- constructor(options) {
115
- this.endpoint = options.endpoint.replace(/\/$/, "");
116
- this.apiKey = options.apiKey;
117
- this.timeout = options.timeout;
118
- this.debug = options.debug;
119
- this.logger = options.logger ?? defaultLogger;
120
- }
121
- /**
122
- * Get current transport state
123
- */
124
- getState() {
125
- return this.state;
126
- }
127
- /**
128
- * Check if transport is active
129
- */
130
- isActive() {
131
- return this.state === "active";
132
- }
133
- /**
134
- * Send a batch of events to the Ingest API
135
- * Implements retry with exponential backoff
136
- */
137
- async send(events) {
138
- if (!this.isActive()) {
139
- return {
140
- success: false,
141
- error: "Transport disabled",
142
- message: `Transport is ${this.state}`,
143
- code: 0
144
- };
145
- }
146
- const batch = { events };
147
- const url = `${this.endpoint}/api/v1/events`;
148
- let lastError = null;
149
- const maxRetries = 3;
150
- const baseDelay = 1e3;
151
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
152
- try {
153
- const response = await this.executeRequest(url, batch);
154
- if (response.success) {
155
- if (this.debug) {
156
- this.logger.debug(`Sent ${events.length} events successfully`);
157
- }
158
- return response;
159
- }
160
- const errorResponse = response;
161
- lastError = errorResponse;
162
- switch (errorResponse.code) {
163
- case 401:
164
- this.state = "disabled";
165
- this.logger.error("Invalid API key. SDK disabled.");
166
- return errorResponse;
167
- case 402:
168
- this.state = "quota_exceeded";
169
- this.logger.warn("Event quota exceeded. SDK paused.");
170
- return errorResponse;
171
- case 429:
172
- const retryAfter = errorResponse.retry_after ?? 1;
173
- if (attempt < maxRetries) {
174
- await this.sleep(retryAfter * 1e3);
175
- continue;
176
- }
177
- return errorResponse;
178
- case 400:
179
- this.logger.warn("Bad request:", errorResponse.message);
180
- return errorResponse;
181
- default:
182
- if (errorResponse.code >= 500 && attempt < maxRetries) {
183
- const delay = this.calculateBackoff(attempt, baseDelay);
184
- if (this.debug) {
185
- this.logger.debug(`Retry ${attempt + 1}/${maxRetries} after ${delay}ms`);
186
- }
187
- await this.sleep(delay);
188
- continue;
189
- }
190
- return errorResponse;
191
- }
192
- } catch (error) {
193
- if (attempt < maxRetries) {
194
- const delay = this.calculateBackoff(attempt, baseDelay);
195
- if (this.debug) {
196
- this.logger.debug(`Network error, retry ${attempt + 1}/${maxRetries} after ${delay}ms`);
197
- }
198
- await this.sleep(delay);
199
- continue;
200
- }
201
- const networkError = {
202
- success: false,
203
- error: "Network Error",
204
- message: error instanceof Error ? error.message : "Unknown error",
205
- code: 0
206
- };
207
- return networkError;
208
- }
209
- }
210
- return lastError ?? {
211
- success: false,
212
- error: "Max retries exceeded",
213
- message: "Failed after maximum retry attempts",
214
- code: 0
215
- };
216
- }
217
- /**
218
- * Execute HTTP request
219
- */
220
- async executeRequest(url, batch) {
221
- const controller = new AbortController();
222
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
223
- try {
224
- const response = await fetch(url, {
225
- method: "POST",
226
- headers: {
227
- "Content-Type": "application/json",
228
- "Authorization": `Bearer ${this.apiKey}`
229
- },
230
- body: JSON.stringify(batch),
231
- signal: controller.signal
232
- });
233
- const data = await response.json();
234
- return data;
235
- } finally {
236
- clearTimeout(timeoutId);
237
- }
238
- }
239
- /**
240
- * Calculate backoff delay with jitter
241
- */
242
- calculateBackoff(attempt, baseDelay) {
243
- const exponentialDelay = baseDelay * Math.pow(2, attempt);
244
- const jitter = exponentialDelay * 0.1 * (Math.random() * 2 - 1);
245
- return Math.round(exponentialDelay + jitter);
246
- }
247
- /**
248
- * Sleep for specified milliseconds
249
- */
250
- sleep(ms) {
251
- return new Promise((resolve) => setTimeout(resolve, ms));
252
- }
253
- };
254
-
255
- // src/queue.ts
256
- var EventQueue = class {
257
- queue = [];
258
- options;
259
- onFlush;
260
- flushTimer = null;
261
- isFlushing = false;
262
- constructor(options, onFlush) {
263
- this.options = options;
264
- this.onFlush = onFlush;
265
- }
266
- /**
267
- * Start the background flush timer
268
- */
269
- start() {
270
- if (this.flushTimer) return;
271
- this.flushTimer = setInterval(() => {
272
- this.flush().catch(() => {
273
- });
274
- }, this.options.flushInterval);
275
- if (this.flushTimer.unref) {
276
- this.flushTimer.unref();
277
- }
278
- }
279
- /**
280
- * Stop the background flush timer
281
- */
282
- stop() {
283
- if (this.flushTimer) {
284
- clearInterval(this.flushTimer);
285
- this.flushTimer = null;
286
- }
287
- }
288
- /**
289
- * Add an event to the queue
290
- * Returns immediately (non-blocking)
291
- */
292
- add(event) {
293
- if (this.queue.length >= this.options.maxQueueSize) {
294
- this.queue.shift();
295
- if (this.options.debug) {
296
- console.warn("[tracebeam] Queue full, dropping oldest event");
297
- }
298
- }
299
- this.queue.push(event);
300
- if (this.queue.length >= this.options.batchSize) {
301
- this.flush().catch(() => {
302
- });
303
- }
304
- }
305
- /**
306
- * Flush all queued events
307
- */
308
- async flush() {
309
- if (this.isFlushing || this.queue.length === 0) {
310
- return;
311
- }
312
- this.isFlushing = true;
313
- try {
314
- const events = this.queue.splice(0, this.queue.length);
315
- await this.onFlush(events);
316
- } finally {
317
- this.isFlushing = false;
318
- }
319
- }
320
- /**
321
- * Get current queue size
322
- */
323
- size() {
324
- return this.queue.length;
325
- }
326
- /**
327
- * Check if queue is empty
328
- */
329
- isEmpty() {
330
- return this.queue.length === 0;
331
- }
332
- };
333
- function createEvent(projectId, environment, error, options = {}, globalTags = {}, globalExtra = {}) {
334
- if (!projectId || projectId.trim() === "") {
335
- throw new ConfigError('createEvent: "projectId" must be a non-empty string.');
336
- }
337
- if (!environment || environment.trim() === "") {
338
- throw new ConfigError('createEvent: "environment" must be a non-empty string.');
339
- }
340
- const level = options.level ?? "error";
341
- const timestamp = options.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
342
- const tags = {
343
- ...globalTags,
344
- ...options.tags
345
- };
346
- const extra = {
347
- ...globalExtra,
348
- ...options.extra
349
- };
350
- const truncatedError = error.length > 1e4 ? error.slice(0, 1e4) : error;
351
- return {
352
- project_id: projectId,
353
- error: truncatedError,
354
- level,
355
- environment,
356
- timestamp,
357
- tags,
358
- extra
359
- };
360
- }
361
-
362
- // src/client.ts
711
+ // src/sdk.ts
363
712
  var TraceBeamSDK = class _TraceBeamSDK {
364
713
  config;
365
- transport;
366
- queue;
714
+ provider;
715
+ tracer;
716
+ isInitialized = true;
367
717
  globalTags = {};
368
718
  globalExtra = {};
369
719
  userId = null;
370
- isInitialized = true;
371
720
  /**
372
721
  * Create SDK instance from configuration
373
722
  */
374
723
  constructor(config) {
375
724
  validateConfig(config);
376
725
  this.config = mergeConfig(config);
377
- this.transport = new Transport({
378
- endpoint: this.config.endpoint,
379
- apiKey: this.config.apiKey,
380
- timeout: this.config.timeout,
381
- debug: this.config.debug
726
+ const baseUrl = this.config.endpoint.replace(/\/$/, "");
727
+ const exporterUrl = `${baseUrl}/v1/traces`;
728
+ const exporter = new OTLPTraceExporter({
729
+ url: exporterUrl,
730
+ headers: {
731
+ "Authorization": `Bearer ${this.config.apiKey}`
732
+ }
382
733
  });
383
- this.queue = new EventQueue(
384
- {
385
- batchSize: this.config.batchSize,
386
- flushInterval: this.config.flushInterval,
387
- maxQueueSize: this.config.maxQueueSize,
388
- debug: this.config.debug
389
- },
390
- this.handleFlush.bind(this)
391
- );
392
- this.queue.start();
734
+ const spanProcessors = [];
735
+ spanProcessors.push(new BatchSpanProcessor(exporter, {
736
+ maxExportBatchSize: this.config.batchSize,
737
+ scheduledDelayMillis: this.config.flushInterval
738
+ }));
393
739
  if (this.config.debug) {
394
- console.debug("[tracebeam] Initialized", {
395
- projectId: this.config.projectId,
396
- environment: this.config.environment,
397
- endpoint: this.config.endpoint
740
+ spanProcessors.push(new SimpleSpanProcessor(new ConsoleSpanExporter()));
741
+ console.debug("[tracebeam] Initialized OTLP", {
742
+ url: exporterUrl,
743
+ projectId: this.config.projectId
398
744
  });
399
745
  }
746
+ this.provider = new NodeTracerProvider({
747
+ resource: resourceFromAttributes({
748
+ [SemanticResourceAttributes.SERVICE_NAME]: this.config.projectId,
749
+ [SemanticResourceAttributes.SERVICE_VERSION]: this.config.environment,
750
+ [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: this.config.environment,
751
+ "telemetry.sdk.language": "nodejs",
752
+ "telemetry.sdk.name": "tracebeam-sdk",
753
+ "telemetry.sdk.version": "0.2.1"
754
+ }),
755
+ spanProcessors
756
+ });
757
+ this.provider.register();
758
+ registerInstrumentations({
759
+ tracerProvider: this.provider,
760
+ instrumentations: [
761
+ getNodeAutoInstrumentations({
762
+ // Disable some noisy instrumentations
763
+ "@opentelemetry/instrumentation-fs": { enabled: false },
764
+ "@opentelemetry/instrumentation-dns": { enabled: false }
765
+ })
766
+ ]
767
+ });
768
+ this.tracer = api.trace.getTracer("tracebeam-sdk");
400
769
  }
401
- /**
402
- * Create SDK instance from environment variables
403
- *
404
- * Required env vars:
405
- * - TRACEBEAM_API_KEY
406
- * - TRACEBEAM_PROJECT_ID
407
- *
408
- * Optional env vars:
409
- * - TRACEBEAM_ENVIRONMENT (default: 'development')
410
- * - TRACEBEAM_ENDPOINT (default: 'https://ingest.tracebeam.io')
411
- * - TRACEBEAM_BATCH_SIZE (default: 100)
412
- * - TRACEBEAM_FLUSH_INTERVAL (default: 5000)
413
- * - TRACEBEAM_HTTP_TIMEOUT (default: 10000)
414
- * - TRACEBEAM_MAX_QUEUE_SIZE (default: 10000)
415
- * - TRACEBEAM_SAMPLE_RATE (default: 1.0)
416
- * - TRACEBEAM_DEBUG (default: false)
417
- */
418
770
  static fromEnv() {
419
771
  const config = loadConfigFromEnv();
420
772
  return new _TraceBeamSDK(config);
421
773
  }
422
774
  /**
423
775
  * Capture an exception/error
424
- * Returns immediately (non-blocking)
425
776
  */
426
777
  captureException(error, options = {}) {
427
778
  if (!this.canCapture()) return;
428
- const errorMessage = error instanceof Error ? `${error.name}: ${error.message}
429
- ${error.stack ?? ""}` : String(error);
430
- const event = this.createEventWithContext(errorMessage, {
431
- level: "error",
432
- ...options
779
+ const timestamp = options.timestamp ? new Date(options.timestamp).getTime() : Date.now();
780
+ let startTime = timestamp;
781
+ if (options.latency) {
782
+ startTime = timestamp - options.latency;
783
+ }
784
+ let spanName = "exception";
785
+ let spanStatus = api.SpanStatusCode.ERROR;
786
+ let errorMessage = "";
787
+ if (error instanceof Error) {
788
+ spanName = error.name || "Error";
789
+ errorMessage = error.message;
790
+ } else if (typeof error === "string") {
791
+ if (error === "") {
792
+ spanName = options.tags?.["endpoint"] || "request";
793
+ spanStatus = api.SpanStatusCode.OK;
794
+ } else {
795
+ spanName = "Error";
796
+ errorMessage = error;
797
+ }
798
+ } else {
799
+ spanName = "Error";
800
+ errorMessage = String(error);
801
+ }
802
+ const span = this.tracer.startSpan(spanName, {
803
+ startTime,
804
+ kind: api.SpanKind.CLIENT
433
805
  });
434
- this.queue.add(event);
806
+ const attributes = {};
807
+ Object.entries(this.globalTags).forEach(([k, v]) => attributes[k] = v);
808
+ if (options.tags) {
809
+ Object.entries(options.tags).forEach(([k, v]) => attributes[k] = v);
810
+ }
811
+ Object.entries(this.globalExtra).forEach(([k, v]) => {
812
+ if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
813
+ attributes[k] = v;
814
+ } else {
815
+ attributes[k] = JSON.stringify(v);
816
+ }
817
+ });
818
+ if (options.extra) {
819
+ Object.entries(options.extra).forEach(([k, v]) => {
820
+ if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
821
+ attributes[k] = v;
822
+ } else {
823
+ attributes[k] = JSON.stringify(v);
824
+ }
825
+ });
826
+ }
827
+ if (options.latency) {
828
+ attributes["latency_ms"] = options.latency;
829
+ attributes["latency"] = options.latency;
830
+ }
831
+ span.setAttributes(attributes);
832
+ span.setStatus({ code: spanStatus });
833
+ if (error && error !== "") {
834
+ if (error instanceof Error) {
835
+ span.recordException(error);
836
+ } else {
837
+ span.recordException(new Error(String(error)));
838
+ }
839
+ }
840
+ span.end(timestamp);
435
841
  }
436
842
  /**
437
- * Capture a message/event
438
- * Returns immediately (non-blocking)
843
+ * Capture a message
439
844
  */
440
845
  captureMessage(message, options = {}) {
441
- if (!this.canCapture()) return;
442
- const event = this.createEventWithContext(message, {
443
- level: "info",
444
- ...options
445
- });
446
- this.queue.add(event);
846
+ this.captureException(message, { ...options, level: options.level || "info" });
447
847
  }
448
- /**
449
- * Set the current user ID
450
- * Will be included in all subsequent events
451
- */
452
848
  setUser(userId) {
453
849
  this.userId = userId;
454
850
  if (userId) {
@@ -457,62 +853,30 @@ ${error.stack ?? ""}` : String(error);
457
853
  delete this.globalExtra["user_id"];
458
854
  }
459
855
  }
460
- /**
461
- * Set a global tag
462
- * Will be included in all subsequent events
463
- */
464
856
  setTag(key, value) {
465
857
  this.globalTags[key] = value;
466
858
  }
467
- /**
468
- * Set multiple global tags
469
- * Will be included in all subsequent events
470
- */
471
859
  setTags(tags) {
472
860
  Object.assign(this.globalTags, tags);
473
861
  }
474
- /**
475
- * Set global extra context
476
- * Will be included in all subsequent events
477
- */
478
862
  setExtra(key, value) {
479
863
  this.globalExtra[key] = value;
480
864
  }
481
- /**
482
- * Set multiple global extra context values
483
- */
484
865
  setExtras(extras) {
485
866
  Object.assign(this.globalExtra, extras);
486
867
  }
487
- /**
488
- * Manually flush queued events
489
- * Call this to ensure events are sent before process exit
490
- */
491
868
  async flush() {
492
- await this.queue.flush();
869
+ await this.provider.forceFlush();
493
870
  }
494
- /**
495
- * Graceful shutdown
496
- * Flushes remaining events and stops background workers
497
- */
498
871
  async close() {
499
872
  this.isInitialized = false;
500
- this.queue.stop();
501
- await this.flush();
502
- if (this.config.debug) {
503
- console.debug("[tracebeam] Closed");
504
- }
873
+ await this.provider.shutdown();
505
874
  }
506
- /**
507
- * Check if SDK is active and can capture events
508
- */
509
875
  isActive() {
510
- return this.isInitialized && this.transport.isActive();
876
+ return this.isInitialized;
511
877
  }
512
- // Private methods
513
878
  canCapture() {
514
879
  if (!this.isInitialized) return false;
515
- if (!this.transport.isActive()) return false;
516
880
  if (this.config.sampleRate < 1) {
517
881
  if (Math.random() > this.config.sampleRate) {
518
882
  return false;
@@ -520,31 +884,13 @@ ${error.stack ?? ""}` : String(error);
520
884
  }
521
885
  return true;
522
886
  }
523
- createEventWithContext(error, options) {
524
- return createEvent(
525
- this.config.projectId,
526
- this.config.environment,
527
- error,
528
- options,
529
- this.globalTags,
530
- this.globalExtra
531
- );
532
- }
533
- async handleFlush(events) {
534
- if (events.length === 0) return;
535
- try {
536
- await this.transport.send(events);
537
- } catch (error) {
538
- if (this.config.debug) {
539
- console.error("[tracebeam] Failed to flush events:", error);
540
- }
541
- }
542
- }
543
887
  };
544
888
  export {
545
889
  ConfigError,
546
890
  DEFAULT_CONFIG,
547
891
  TraceBeamSDK,
548
- loadConfigFromEnv
892
+ loadConfigFromEnv,
893
+ mergeConfig,
894
+ validateConfig
549
895
  };
550
896
  //# sourceMappingURL=index.mjs.map