autotel 6.2.1 → 6.4.0
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 +50 -1
- package/dist/auto.cjs +1 -1
- package/dist/auto.js +1 -1
- package/dist/correlation-id.cjs +1 -1
- package/dist/correlation-id.js +1 -1
- package/dist/decorators.cjs +1 -1
- package/dist/decorators.d.cts +1 -1
- package/dist/decorators.d.ts +1 -1
- package/dist/decorators.js +1 -1
- package/dist/event.cjs +1 -1
- package/dist/event.js +1 -1
- package/dist/functional-AT22vCw6.d.ts +389 -0
- package/dist/{functional-CogDARqC.cjs → functional-BEEhlVKD.cjs} +221 -283
- package/dist/{functional-2AZsH_gQ.js → functional-DbCVvGfT.js} +221 -283
- package/dist/functional-e6jw5zQj.d.cts +389 -0
- package/dist/functional.cjs +1 -1
- package/dist/functional.d.cts +2 -432
- package/dist/functional.d.ts +2 -432
- package/dist/functional.js +1 -1
- package/dist/http.cjs +1 -1
- package/dist/http.js +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/{init-Bu6wR-k_.js → init-C2fjPVkW.js} +458 -358
- package/dist/{init-BzVuFs5a.cjs → init-C5V4z7S5.cjs} +458 -358
- package/dist/{init-BoIYyB6Q.d.cts → init-Cc7-vvzj.d.cts} +42 -2
- package/dist/{init-BHp-kqLJ.d.ts → init-CsODvlau.d.ts} +42 -2
- package/dist/instrumentation.cjs +2 -2
- package/dist/instrumentation.js +2 -2
- package/dist/messaging.cjs +1 -1
- package/dist/messaging.js +1 -1
- package/dist/policy-CbpP4tFH.d.cts +147 -0
- package/dist/policy-CbpP4tFH.d.ts +147 -0
- package/dist/policy.cjs +0 -0
- package/dist/policy.d.cts +2 -0
- package/dist/policy.d.ts +2 -0
- package/dist/policy.js +0 -0
- package/dist/semantic-helpers.cjs +1 -1
- package/dist/semantic-helpers.js +1 -1
- package/dist/webhook.cjs +1 -1
- package/dist/webhook.js +1 -1
- package/dist/workflow-distributed.cjs +1 -1
- package/dist/workflow-distributed.js +1 -1
- package/dist/workflow.cjs +1 -1
- package/dist/workflow.js +1 -1
- package/dist/yaml-config.cjs +1 -0
- package/dist/yaml-config.d.cts +3 -1
- package/dist/yaml-config.d.ts +3 -1
- package/dist/yaml-config.js +1 -0
- package/package.json +6 -1
|
@@ -5,6 +5,7 @@ const require_sampling = require('./sampling.cjs');
|
|
|
5
5
|
const require_node_require = require('./node-require-PFqDihze.cjs');
|
|
6
6
|
const require_tail_sampling_processor = require('./tail-sampling-processor.cjs');
|
|
7
7
|
const require_filtering_span_processor = require('./filtering-span-processor.cjs');
|
|
8
|
+
const require_policy = require('./policy.cjs');
|
|
8
9
|
const require_span_name_normalizer = require('./span-name-normalizer.cjs');
|
|
9
10
|
const require_attribute_redacting_processor = require('./attribute-redacting-processor.cjs');
|
|
10
11
|
const require_pretty_console_exporter = require('./pretty-console-exporter-CMzlrRNg.cjs');
|
|
@@ -18,13 +19,13 @@ let _opentelemetry_resources = require("@opentelemetry/resources");
|
|
|
18
19
|
let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
|
|
19
20
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
20
21
|
let _opentelemetry_sdk_metrics = require("@opentelemetry/sdk-metrics");
|
|
21
|
-
let _opentelemetry_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http");
|
|
22
|
-
let _opentelemetry_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http");
|
|
23
|
-
let _opentelemetry_exporter_logs_otlp_http = require("@opentelemetry/exporter-logs-otlp-http");
|
|
24
22
|
let _opentelemetry_sdk_logs = require("@opentelemetry/sdk-logs");
|
|
25
23
|
let node_os = require("node:os");
|
|
26
24
|
let node_async_hooks = require("node:async_hooks");
|
|
27
25
|
node_async_hooks = require_rolldown_runtime.__toESM(node_async_hooks, 1);
|
|
26
|
+
let _opentelemetry_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http");
|
|
27
|
+
let _opentelemetry_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http");
|
|
28
|
+
let _opentelemetry_exporter_logs_otlp_http = require("@opentelemetry/exporter-logs-otlp-http");
|
|
28
29
|
|
|
29
30
|
//#region src/posthog-logs.ts
|
|
30
31
|
var RedactingLogRecordProcessor = class {
|
|
@@ -327,6 +328,39 @@ function resolveDevtoolsConfig(config) {
|
|
|
327
328
|
//#endregion
|
|
328
329
|
//#region src/process-handlers.ts
|
|
329
330
|
let removeOwnedHandlers = [];
|
|
331
|
+
/**
|
|
332
|
+
* Tracked separately from `removeOwnedHandlers` because the exit flush outlives
|
|
333
|
+
* a call to `installProcessHandlers`, which clears that list before installing
|
|
334
|
+
* its own signal listeners.
|
|
335
|
+
*/
|
|
336
|
+
let removeExitFlush;
|
|
337
|
+
/**
|
|
338
|
+
* Shared across every path that can end the process: signals, fatal errors and
|
|
339
|
+
* a clean exit. They can overlap — a container stopping a job that has just
|
|
340
|
+
* finished its work sends SIGTERM while the exit flush is still draining — and
|
|
341
|
+
* a second shutdown would tear down queues the first is still using.
|
|
342
|
+
*/
|
|
343
|
+
let shutdownInFlight;
|
|
344
|
+
/**
|
|
345
|
+
* The clean-exit flush, while it runs. Doubles as the latch that keeps a
|
|
346
|
+
* re-emitted `beforeExit` from flushing twice, and as the thing a shutdown
|
|
347
|
+
* waits on rather than tearing down queues mid-drain.
|
|
348
|
+
*/
|
|
349
|
+
let exitFlushInFlight;
|
|
350
|
+
const DEFAULT_SHUTDOWN_TIMEOUT_MS = 2e3;
|
|
351
|
+
function runShutdownOnce(shutdown, timeoutMs) {
|
|
352
|
+
if (shutdownInFlight) return shutdownInFlight;
|
|
353
|
+
let timeoutHandle;
|
|
354
|
+
const shutdownAttempt = Promise.resolve(exitFlushInFlight).then(shutdown).catch(() => void 0);
|
|
355
|
+
const timeout = new Promise((resolve) => {
|
|
356
|
+
timeoutHandle = setTimeout(resolve, timeoutMs);
|
|
357
|
+
timeoutHandle.unref();
|
|
358
|
+
});
|
|
359
|
+
shutdownInFlight = Promise.race([shutdownAttempt, timeout]).then(() => {
|
|
360
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
361
|
+
});
|
|
362
|
+
return shutdownInFlight;
|
|
363
|
+
}
|
|
330
364
|
const DEFAULT_SIGNALS = ["SIGTERM", "SIGINT"];
|
|
331
365
|
function signalExitCode(signal) {
|
|
332
366
|
return 128 + node_os.constants.signals[signal];
|
|
@@ -345,21 +379,7 @@ function reportFatalError(error, event) {
|
|
|
345
379
|
}
|
|
346
380
|
function installProcessHandlers(config, shutdown) {
|
|
347
381
|
uninstallProcessHandlers();
|
|
348
|
-
|
|
349
|
-
const runShutdownOnce = () => {
|
|
350
|
-
if (shutdownInFlight) return shutdownInFlight;
|
|
351
|
-
const timeoutMs = config.shutdownTimeoutMs ?? 2e3;
|
|
352
|
-
let timeoutHandle;
|
|
353
|
-
const shutdownAttempt = Promise.resolve().then(shutdown).catch(() => void 0);
|
|
354
|
-
const timeout = new Promise((resolve) => {
|
|
355
|
-
timeoutHandle = setTimeout(resolve, timeoutMs);
|
|
356
|
-
timeoutHandle.unref();
|
|
357
|
-
});
|
|
358
|
-
shutdownInFlight = Promise.race([shutdownAttempt, timeout]).then(() => {
|
|
359
|
-
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
360
|
-
});
|
|
361
|
-
return shutdownInFlight;
|
|
362
|
-
};
|
|
382
|
+
const timeoutMs = config.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS;
|
|
363
383
|
let exitScheduled = false;
|
|
364
384
|
let resolvedExitCode = 0;
|
|
365
385
|
let fatalLatched = false;
|
|
@@ -372,7 +392,7 @@ function installProcessHandlers(config, shutdown) {
|
|
|
372
392
|
} else if (!exitScheduled && !fatalLatched) resolvedExitCode = exitCode;
|
|
373
393
|
if (exitScheduled) return;
|
|
374
394
|
exitScheduled = true;
|
|
375
|
-
runShutdownOnce().then(() => {
|
|
395
|
+
runShutdownOnce(shutdown, timeoutMs).then(() => {
|
|
376
396
|
process.exit(resolvedExitCode);
|
|
377
397
|
});
|
|
378
398
|
};
|
|
@@ -403,9 +423,43 @@ function installProcessHandlers(config, shutdown) {
|
|
|
403
423
|
});
|
|
404
424
|
}
|
|
405
425
|
}
|
|
426
|
+
/**
|
|
427
|
+
* Flush telemetry when the process runs to completion.
|
|
428
|
+
*
|
|
429
|
+
* The signal and fatal-error handlers above cover a process that is stopped or
|
|
430
|
+
* that crashes. Neither fires when a script simply finishes: the event loop
|
|
431
|
+
* drains and Node exits, taking whatever the batch span processor was still
|
|
432
|
+
* holding with it. `beforeExit` is the only hook for that case, and it is the
|
|
433
|
+
* one that matters for CLIs, cron jobs, CI steps and serverless handlers.
|
|
434
|
+
*
|
|
435
|
+
* A flush, never a shutdown: `beforeExit` fires on *any* event-loop drain, not
|
|
436
|
+
* only the final one, so tearing the SDK down here would silently kill
|
|
437
|
+
* telemetry in a process that goes on to do more work.
|
|
438
|
+
*/
|
|
439
|
+
function installExitFlush(flushTelemetry, timeoutMs = DEFAULT_SHUTDOWN_TIMEOUT_MS) {
|
|
440
|
+
removeExitFlush?.();
|
|
441
|
+
exitFlushInFlight = void 0;
|
|
442
|
+
const listener = (code) => {
|
|
443
|
+
if (exitFlushInFlight || shutdownInFlight) return;
|
|
444
|
+
const deadline = setTimeout(() => {
|
|
445
|
+
process.exit(process.exitCode ?? code);
|
|
446
|
+
}, timeoutMs);
|
|
447
|
+
exitFlushInFlight = flushTelemetry().catch(() => void 0).finally(() => {
|
|
448
|
+
clearTimeout(deadline);
|
|
449
|
+
});
|
|
450
|
+
};
|
|
451
|
+
process.on("beforeExit", listener);
|
|
452
|
+
removeExitFlush = () => {
|
|
453
|
+
process.removeListener("beforeExit", listener);
|
|
454
|
+
removeExitFlush = void 0;
|
|
455
|
+
};
|
|
456
|
+
}
|
|
406
457
|
function uninstallProcessHandlers() {
|
|
407
458
|
for (const removeHandler of removeOwnedHandlers) removeHandler();
|
|
408
459
|
removeOwnedHandlers = [];
|
|
460
|
+
removeExitFlush?.();
|
|
461
|
+
shutdownInFlight = void 0;
|
|
462
|
+
exitFlushInFlight = void 0;
|
|
409
463
|
}
|
|
410
464
|
|
|
411
465
|
//#endregion
|
|
@@ -493,11 +547,15 @@ function getContextStorage() {
|
|
|
493
547
|
return contextStorage;
|
|
494
548
|
}
|
|
495
549
|
/**
|
|
496
|
-
* Get the active context
|
|
497
|
-
*
|
|
550
|
+
* Get the active OTel context with the latest stored baggage overlaid.
|
|
551
|
+
* Span identity always comes from the active OTel scope.
|
|
498
552
|
*/
|
|
499
553
|
function getActiveContextWithBaggage() {
|
|
500
|
-
|
|
554
|
+
const activeContext = _opentelemetry_api.context.active();
|
|
555
|
+
const stored = contextStorage.getStore()?.value;
|
|
556
|
+
if (!stored) return activeContext;
|
|
557
|
+
const storedBaggage = _opentelemetry_api.propagation.getBaggage(stored);
|
|
558
|
+
return storedBaggage ? _opentelemetry_api.propagation.setBaggage(activeContext, storedBaggage) : activeContext;
|
|
501
559
|
}
|
|
502
560
|
/**
|
|
503
561
|
* Set a value in AsyncLocalStorage, preferring enterWith() when available
|
|
@@ -577,15 +635,13 @@ function createTraceContext(span) {
|
|
|
577
635
|
return baggage?.getEntry(key)?.value;
|
|
578
636
|
},
|
|
579
637
|
setBaggage(key, value) {
|
|
580
|
-
const
|
|
581
|
-
const currentContext = contextStorage.getStore()?.value ?? activeCtx;
|
|
638
|
+
const currentContext = getActiveContextWithBaggage();
|
|
582
639
|
const updated = (_opentelemetry_api.propagation.getBaggage(currentContext) ?? _opentelemetry_api.propagation.createBaggage()).setEntry(key, { value });
|
|
583
640
|
updateActiveContext(_opentelemetry_api.propagation.setBaggage(currentContext, updated));
|
|
584
641
|
return value;
|
|
585
642
|
},
|
|
586
643
|
deleteBaggage(key) {
|
|
587
|
-
const
|
|
588
|
-
const currentContext = contextStorage.getStore()?.value ?? activeCtx;
|
|
644
|
+
const currentContext = getActiveContextWithBaggage();
|
|
589
645
|
const baggage = _opentelemetry_api.propagation.getBaggage(currentContext);
|
|
590
646
|
if (baggage) {
|
|
591
647
|
const updated = baggage.removeEntry(key);
|
|
@@ -621,8 +677,7 @@ function createTraceContext(span) {
|
|
|
621
677
|
return Object.keys(result).length > 0 ? result : void 0;
|
|
622
678
|
}),
|
|
623
679
|
setTypedBaggage: ((namespace, value) => {
|
|
624
|
-
const
|
|
625
|
-
const currentContext = contextStorage.getStore()?.value ?? activeCtx;
|
|
680
|
+
const currentContext = getActiveContextWithBaggage();
|
|
626
681
|
let baggage = _opentelemetry_api.propagation.getBaggage(currentContext) ?? _opentelemetry_api.propagation.createBaggage();
|
|
627
682
|
const prefix = namespace ? `${namespace}.` : "";
|
|
628
683
|
for (const [key, val] of Object.entries(value)) if (val !== void 0) {
|
|
@@ -913,6 +968,11 @@ function getSubscriberName(subscriber) {
|
|
|
913
968
|
return (subscriber.constructor?.name || "unknown").replace(/Subscriber$/i, "").toLowerCase();
|
|
914
969
|
}
|
|
915
970
|
/**
|
|
971
|
+
* Subscribers whose `shutdown()` has run. Terminal for the client they wrap, so
|
|
972
|
+
* a queue rebuilt from the same config must not reuse them.
|
|
973
|
+
*/
|
|
974
|
+
const shutDownSubscribers = /* @__PURE__ */ new WeakSet();
|
|
975
|
+
/**
|
|
916
976
|
* Events queue with batching and backpressure
|
|
917
977
|
*
|
|
918
978
|
* Features:
|
|
@@ -934,13 +994,15 @@ var EventQueue = class {
|
|
|
934
994
|
observableCleanups = [];
|
|
935
995
|
subscriberHealthy = /* @__PURE__ */ new Map();
|
|
936
996
|
constructor(subscribers, config) {
|
|
937
|
-
|
|
997
|
+
const live = subscribers.filter((s) => !shutDownSubscribers.has(s));
|
|
998
|
+
if (live.length < subscribers.length) getLogger().warn({ subscribers: subscribers.filter((s) => shutDownSubscribers.has(s)).map((s) => getSubscriberName(s)) }, "[autotel] Subscriber already shut down, events will not be delivered; pass new subscriber instances to init()");
|
|
999
|
+
this.subscribers = live;
|
|
938
1000
|
this.config = {
|
|
939
1001
|
...DEFAULT_CONFIG$2,
|
|
940
1002
|
...config
|
|
941
1003
|
};
|
|
942
1004
|
this.rateLimiter = this.config.rateLimit ? new TokenBucketRateLimiter(this.config.rateLimit) : null;
|
|
943
|
-
for (const subscriber of subscribers) {
|
|
1005
|
+
for (const subscriber of this.subscribers) {
|
|
944
1006
|
const name = getSubscriberName(subscriber);
|
|
945
1007
|
this.subscriberHealthy.set(name, true);
|
|
946
1008
|
}
|
|
@@ -1234,6 +1296,20 @@ var EventQueue = class {
|
|
|
1234
1296
|
async shutdown() {
|
|
1235
1297
|
this.isShuttingDown = true;
|
|
1236
1298
|
await this.flush();
|
|
1299
|
+
const drains = await Promise.allSettled(this.subscribers.map(async (subscriber) => {
|
|
1300
|
+
if (!subscriber.shutdown) return;
|
|
1301
|
+
shutDownSubscribers.add(subscriber);
|
|
1302
|
+
return subscriber.shutdown();
|
|
1303
|
+
}));
|
|
1304
|
+
for (const [index, drain] of drains.entries()) {
|
|
1305
|
+
if (drain.status !== "rejected") continue;
|
|
1306
|
+
const subscriberName = getSubscriberName(this.subscribers[index]);
|
|
1307
|
+
this.subscriberHealthy.set(subscriberName, false);
|
|
1308
|
+
getLogger().error({
|
|
1309
|
+
subscriber: subscriberName,
|
|
1310
|
+
err: drain.reason
|
|
1311
|
+
}, "[autotel] Subscriber shutdown failed, buffered events may be lost");
|
|
1312
|
+
}
|
|
1237
1313
|
}
|
|
1238
1314
|
/**
|
|
1239
1315
|
* Cleanup observable metric callbacks to prevent memory leaks
|
|
@@ -2463,47 +2539,7 @@ async function shutdown() {
|
|
|
2463
2539
|
}
|
|
2464
2540
|
|
|
2465
2541
|
//#endregion
|
|
2466
|
-
//#region src/
|
|
2467
|
-
/**
|
|
2468
|
-
* Simplified initialization for autotel
|
|
2469
|
-
*
|
|
2470
|
-
* Single init() function with sensible defaults.
|
|
2471
|
-
* Replaces initInstrumentation() and separate events config.
|
|
2472
|
-
*/
|
|
2473
|
-
/**
|
|
2474
|
-
* Silent logger (no-op) - used as default when user doesn't provide one.
|
|
2475
|
-
* Internal autotel logs are silent by default to avoid spam.
|
|
2476
|
-
* Users can import { autotelLogger } from 'autotel/logger' to create their own.
|
|
2477
|
-
*/
|
|
2478
|
-
const silentLogger = {
|
|
2479
|
-
info: () => {},
|
|
2480
|
-
warn: () => {},
|
|
2481
|
-
error: () => {},
|
|
2482
|
-
debug: () => {}
|
|
2483
|
-
};
|
|
2484
|
-
/**
|
|
2485
|
-
* Adapts an Autotel Sampler to the OTel SDK Sampler interface.
|
|
2486
|
-
*/
|
|
2487
|
-
function toOtelSampler(sampler) {
|
|
2488
|
-
return {
|
|
2489
|
-
shouldSample(_context, _traceId, spanName, _spanKind, _attributes, links) {
|
|
2490
|
-
const samplingContext = {
|
|
2491
|
-
operationName: spanName,
|
|
2492
|
-
args: [],
|
|
2493
|
-
links
|
|
2494
|
-
};
|
|
2495
|
-
const shouldTrace = sampler.shouldSample(samplingContext);
|
|
2496
|
-
const rate = sampler.sampleRate?.(samplingContext);
|
|
2497
|
-
return {
|
|
2498
|
-
decision: shouldTrace ? _opentelemetry_sdk_trace_base.SamplingDecision.RECORD_AND_SAMPLED : _opentelemetry_sdk_trace_base.SamplingDecision.NOT_RECORD,
|
|
2499
|
-
...shouldTrace && rate !== void 0 && rate > 1 ? { attributes: { [require_sampling.AUTOTEL_SAMPLING_RATE]: rate } } : {}
|
|
2500
|
-
};
|
|
2501
|
-
},
|
|
2502
|
-
toString() {
|
|
2503
|
-
return `AutotelSamplerAdapter`;
|
|
2504
|
-
}
|
|
2505
|
-
};
|
|
2506
|
-
}
|
|
2542
|
+
//#region src/otlp-exporters.ts
|
|
2507
2543
|
let OTLPTraceExporterPROTO;
|
|
2508
2544
|
let OTLPMetricExporterPROTO;
|
|
2509
2545
|
let OTLPLogExporterPROTO;
|
|
@@ -2617,62 +2653,9 @@ function formatEndpointUrl(endpoint, signal, protocol) {
|
|
|
2617
2653
|
if (!endpoint.endsWith(`/v1/${signal}`)) return `${endpoint}/v1/${signal}`;
|
|
2618
2654
|
return endpoint;
|
|
2619
2655
|
}
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
let sdk = null;
|
|
2624
|
-
let warnedOnce = false;
|
|
2625
|
-
let logger = silentLogger;
|
|
2626
|
-
let validationConfig = null;
|
|
2627
|
-
let eventsConfig = null;
|
|
2628
|
-
let _stringRedactor = null;
|
|
2629
|
-
function acceptsStringRedactor(subscriber) {
|
|
2630
|
-
return typeof subscriber === "object" && subscriber !== null && "setStringRedactor" in subscriber && typeof subscriber.setStringRedactor === "function";
|
|
2631
|
-
}
|
|
2632
|
-
let _optionalRequire = require_node_require.safeRequire;
|
|
2633
|
-
let _devtoolsClose = null;
|
|
2634
|
-
const LOG_LEVELS = {
|
|
2635
|
-
debug: 0,
|
|
2636
|
-
info: 1,
|
|
2637
|
-
warn: 2,
|
|
2638
|
-
error: 3
|
|
2639
|
-
};
|
|
2640
|
-
/**
|
|
2641
|
-
* Lock the logger to prevent further `init()` calls.
|
|
2642
|
-
* Use this when framework plugins set up instrumentation and you want
|
|
2643
|
-
* to prevent accidental re-initialization from user code.
|
|
2644
|
-
*/
|
|
2645
|
-
function lockLogger() {
|
|
2646
|
-
locked = true;
|
|
2647
|
-
}
|
|
2648
|
-
/**
|
|
2649
|
-
* Check if the logger has been locked.
|
|
2650
|
-
*/
|
|
2651
|
-
function isLoggerLocked() {
|
|
2652
|
-
return locked;
|
|
2653
|
-
}
|
|
2654
|
-
function createSilentLogger() {
|
|
2655
|
-
return {
|
|
2656
|
-
info: () => {},
|
|
2657
|
-
warn: () => {},
|
|
2658
|
-
error: () => {},
|
|
2659
|
-
debug: () => {}
|
|
2660
|
-
};
|
|
2661
|
-
}
|
|
2662
|
-
function wrapLogger(base, silent, minLevel) {
|
|
2663
|
-
if (silent) return createSilentLogger();
|
|
2664
|
-
const threshold = LOG_LEVELS[minLevel];
|
|
2665
|
-
const wrap = (fn, level) => {
|
|
2666
|
-
if (LOG_LEVELS[level] < threshold) return (() => {});
|
|
2667
|
-
return ((...args) => fn(...args));
|
|
2668
|
-
};
|
|
2669
|
-
return {
|
|
2670
|
-
debug: wrap(base.debug, "debug"),
|
|
2671
|
-
info: wrap(base.info, "info"),
|
|
2672
|
-
warn: wrap(base.warn, "warn"),
|
|
2673
|
-
error: wrap(base.error, "error")
|
|
2674
|
-
};
|
|
2675
|
-
}
|
|
2656
|
+
|
|
2657
|
+
//#endregion
|
|
2658
|
+
//#region src/config-resolution.ts
|
|
2676
2659
|
/**
|
|
2677
2660
|
* Resolve the effective attribute redactor. Explicit config wins (`false`
|
|
2678
2661
|
* disables). Otherwise the `AUTOTEL_REDACT_PII` env var controls it, and as a
|
|
@@ -2787,102 +2770,343 @@ function destinationSupportsSignal(destination, signal) {
|
|
|
2787
2770
|
return destination.signals ? destination.signals.has(signal) : true;
|
|
2788
2771
|
}
|
|
2789
2772
|
/**
|
|
2790
|
-
*
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
*
|
|
2802
|
-
*
|
|
2803
|
-
*
|
|
2804
|
-
* init({
|
|
2805
|
-
* service: 'my-app',
|
|
2806
|
-
* subscribers: [new PostHogSubscriber({ apiKey: '...' })]
|
|
2807
|
-
* })
|
|
2808
|
-
* ```
|
|
2809
|
-
*
|
|
2810
|
-
* @example Observe in Jaeger
|
|
2811
|
-
* ```typescript
|
|
2812
|
-
* import { JaegerExporter } from '@opentelemetry/exporter-jaeger'
|
|
2813
|
-
*
|
|
2814
|
-
* init({
|
|
2815
|
-
* service: 'my-app',
|
|
2816
|
-
* spanExporter: new JaegerExporter({ endpoint: 'http://localhost:14268/api/traces' })
|
|
2817
|
-
* })
|
|
2818
|
-
* ```
|
|
2819
|
-
*
|
|
2820
|
-
* @example Observe in Zipkin
|
|
2821
|
-
* ```typescript
|
|
2822
|
-
* import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'
|
|
2823
|
-
*
|
|
2824
|
-
* init({
|
|
2825
|
-
* service: 'my-app',
|
|
2826
|
-
* spanExporter: new ZipkinExporter({ url: 'http://localhost:9411/api/v2/spans' })
|
|
2827
|
-
* })
|
|
2828
|
-
* ```
|
|
2829
|
-
*
|
|
2830
|
-
* @example Observe in Datadog
|
|
2831
|
-
* ```typescript
|
|
2832
|
-
* import { DatadogSpanProcessor } from '@opentelemetry/exporter-datadog'
|
|
2833
|
-
*
|
|
2834
|
-
* init({
|
|
2835
|
-
* service: 'my-app',
|
|
2836
|
-
* spanProcessor: new DatadogSpanProcessor({ ... })
|
|
2837
|
-
* })
|
|
2838
|
-
* ```
|
|
2773
|
+
* Auto-detect version from package.json
|
|
2774
|
+
*/
|
|
2775
|
+
function detectVersion() {
|
|
2776
|
+
try {
|
|
2777
|
+
const fs = require_node_require.requireModule("node:fs");
|
|
2778
|
+
return JSON.parse(fs.readFileSync(`${process.cwd()}/package.json`, "utf8")).version || "1.0.0";
|
|
2779
|
+
} catch {
|
|
2780
|
+
return "1.0.0";
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
/**
|
|
2784
|
+
* Detect hostname for resource attributes.
|
|
2785
|
+
* Supports Datadog conventions (DD_HOSTNAME) and falls back to system hostname.
|
|
2839
2786
|
*
|
|
2840
|
-
*
|
|
2841
|
-
*
|
|
2842
|
-
*
|
|
2787
|
+
* Priority order:
|
|
2788
|
+
* 1. DD_HOSTNAME environment variable (Datadog convention)
|
|
2789
|
+
* 2. HOSTNAME environment variable (common Unix convention)
|
|
2790
|
+
* 3. os.hostname() (system hostname)
|
|
2843
2791
|
*
|
|
2844
|
-
*
|
|
2845
|
-
* service: 'my-app',
|
|
2846
|
-
* spanProcessor: new SimpleSpanProcessor(new ConsoleSpanExporter())
|
|
2847
|
-
* })
|
|
2848
|
-
* ```
|
|
2792
|
+
* @returns hostname string or undefined if detection fails
|
|
2849
2793
|
*/
|
|
2850
|
-
function
|
|
2851
|
-
if (
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
...cfg,
|
|
2858
|
-
resourceAttributes: {
|
|
2859
|
-
...envConfig.resourceAttributes,
|
|
2860
|
-
...yamlConfig.resourceAttributes,
|
|
2861
|
-
...detectEnvironmentAttributes(),
|
|
2862
|
-
...cfg.resourceAttributes
|
|
2863
|
-
},
|
|
2864
|
-
headers: cfg.headers ?? yamlConfig.headers ?? envConfig.headers
|
|
2865
|
-
};
|
|
2866
|
-
const resolvedRedactor = resolveAttributeRedactor(mergedConfig.attributeRedactor, mergedConfig.environment || process.env.NODE_ENV || "development");
|
|
2867
|
-
if (resolvedRedactor === void 0) mergedConfig.attributeRedactor = void 0;
|
|
2868
|
-
else {
|
|
2869
|
-
const normalizedRedactor = require_attribute_redacting_processor.normalizeAttributeRedactorConfig(resolvedRedactor);
|
|
2870
|
-
if (!normalizedRedactor) throw new Error("Invalid attributeRedactor config");
|
|
2871
|
-
mergedConfig.attributeRedactor = normalizedRedactor;
|
|
2794
|
+
function detectHostname() {
|
|
2795
|
+
if (process.env.DD_HOSTNAME) return process.env.DD_HOSTNAME;
|
|
2796
|
+
if (process.env.HOSTNAME) return process.env.HOSTNAME;
|
|
2797
|
+
try {
|
|
2798
|
+
return require_node_require.requireModule("node:os").hostname();
|
|
2799
|
+
} catch {
|
|
2800
|
+
return;
|
|
2872
2801
|
}
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
const
|
|
2885
|
-
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
//#endregion
|
|
2805
|
+
//#region src/auto-instrumentations.ts
|
|
2806
|
+
/**
|
|
2807
|
+
* Extract instrumentation class names from instrumentation instances
|
|
2808
|
+
* Used to detect duplicates between manual and auto instrumentations
|
|
2809
|
+
*/
|
|
2810
|
+
function getInstrumentationNames(instrumentations) {
|
|
2811
|
+
const names = /* @__PURE__ */ new Set();
|
|
2812
|
+
if (!instrumentations) return names;
|
|
2813
|
+
for (const instrumentation of instrumentations) if (instrumentation && typeof instrumentation === "object") names.add(instrumentation.constructor.name);
|
|
2814
|
+
return names;
|
|
2815
|
+
}
|
|
2816
|
+
/**
|
|
2817
|
+
* Map common instrumentation class names to their package names
|
|
2818
|
+
* Used to disable auto-instrumentations when user provides manual configs
|
|
2819
|
+
*/
|
|
2820
|
+
const INSTRUMENTATION_CLASS_TO_PACKAGE = {
|
|
2821
|
+
HttpInstrumentation: "@opentelemetry/instrumentation-http",
|
|
2822
|
+
HttpsInstrumentation: "@opentelemetry/instrumentation-http",
|
|
2823
|
+
ExpressInstrumentation: "@opentelemetry/instrumentation-express",
|
|
2824
|
+
FastifyInstrumentation: "@opentelemetry/instrumentation-fastify",
|
|
2825
|
+
MongoDBInstrumentation: "@opentelemetry/instrumentation-mongodb",
|
|
2826
|
+
MongooseInstrumentation: "@opentelemetry/instrumentation-mongoose",
|
|
2827
|
+
PrismaInstrumentation: "@opentelemetry/instrumentation-prisma",
|
|
2828
|
+
PinoInstrumentation: "@opentelemetry/instrumentation-pino",
|
|
2829
|
+
WinstonInstrumentation: "@opentelemetry/instrumentation-winston",
|
|
2830
|
+
RedisInstrumentation: "@opentelemetry/instrumentation-redis",
|
|
2831
|
+
GraphQLInstrumentation: "@opentelemetry/instrumentation-graphql",
|
|
2832
|
+
GrpcInstrumentation: "@opentelemetry/instrumentation-grpc",
|
|
2833
|
+
IORedisInstrumentation: "@opentelemetry/instrumentation-ioredis",
|
|
2834
|
+
KnexInstrumentation: "@opentelemetry/instrumentation-knex",
|
|
2835
|
+
NestJsInstrumentation: "@opentelemetry/instrumentation-nestjs-core",
|
|
2836
|
+
PgInstrumentation: "@opentelemetry/instrumentation-pg",
|
|
2837
|
+
MySQLInstrumentation: "@opentelemetry/instrumentation-mysql",
|
|
2838
|
+
MySQL2Instrumentation: "@opentelemetry/instrumentation-mysql2"
|
|
2839
|
+
};
|
|
2840
|
+
/**
|
|
2841
|
+
* Detect if we're running in ESM mode
|
|
2842
|
+
*/
|
|
2843
|
+
function isESMMode() {
|
|
2844
|
+
try {
|
|
2845
|
+
const fs = require_node_require.requireModule("node:fs");
|
|
2846
|
+
try {
|
|
2847
|
+
return JSON.parse(fs.readFileSync(`${process.cwd()}/package.json`, "utf8")).type === "module";
|
|
2848
|
+
} catch {
|
|
2849
|
+
return false;
|
|
2850
|
+
}
|
|
2851
|
+
} catch {
|
|
2852
|
+
return false;
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
/**
|
|
2856
|
+
* Lazy-load auto-instrumentations (optional peer dependency)
|
|
2857
|
+
* Only loads when integrations config is truthy, avoiding ~40+ package imports at startup.
|
|
2858
|
+
*/
|
|
2859
|
+
function loadNodeAutoInstrumentations() {
|
|
2860
|
+
try {
|
|
2861
|
+
return require_node_require.requireModule("@opentelemetry/auto-instrumentations-node").getNodeAutoInstrumentations;
|
|
2862
|
+
} catch {
|
|
2863
|
+
const isESM = isESMMode();
|
|
2864
|
+
const baseMessage = "@opentelemetry/auto-instrumentations-node not found.";
|
|
2865
|
+
if (isESM) throw new Error(`${baseMessage}\n\nESM Setup Required:
|
|
2866
|
+
1. Install as a direct dependency: pnpm add @opentelemetry/auto-instrumentations-node
|
|
2867
|
+
2. Create instrumentation.mjs with:
|
|
2868
|
+
import 'autotel/register'; // MUST be first!
|
|
2869
|
+
import { init } from 'autotel';
|
|
2870
|
+
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
|
|
2871
|
+
init({ service: "my-app", instrumentations: getNodeAutoInstrumentations() });
|
|
2872
|
+
3. Run with: tsx --import ./instrumentation.mjs src/index.ts
|
|
2873
|
+
|
|
2874
|
+
See: https://github.com/jagreehal/autotel#esm-setup`);
|
|
2875
|
+
throw new Error(`${baseMessage} Install it: pnpm add @opentelemetry/auto-instrumentations-node`);
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
/**
|
|
2879
|
+
* Injectable loader for testing. Set to override the default loader.
|
|
2880
|
+
* @internal
|
|
2881
|
+
*/
|
|
2882
|
+
let _autoInstrumentationsLoader = null;
|
|
2883
|
+
/**
|
|
2884
|
+
* Get auto-instrumentations based on simple integration names
|
|
2885
|
+
* Excludes instrumentations that are manually provided to avoid conflicts
|
|
2886
|
+
*/
|
|
2887
|
+
function getAutoInstrumentations(integrations, manualInstrumentationNames = /* @__PURE__ */ new Set()) {
|
|
2888
|
+
if (integrations === false) return [];
|
|
2889
|
+
const getNodeAutoInstrumentations = _autoInstrumentationsLoader ? _autoInstrumentationsLoader() : loadNodeAutoInstrumentations();
|
|
2890
|
+
const exclusionConfig = {};
|
|
2891
|
+
for (const className of manualInstrumentationNames) {
|
|
2892
|
+
const packageName = INSTRUMENTATION_CLASS_TO_PACKAGE[className];
|
|
2893
|
+
if (packageName) exclusionConfig[packageName] = { enabled: false };
|
|
2894
|
+
}
|
|
2895
|
+
if (integrations === true) {
|
|
2896
|
+
if (Object.keys(exclusionConfig).length > 0) return getNodeAutoInstrumentations(exclusionConfig);
|
|
2897
|
+
return getNodeAutoInstrumentations();
|
|
2898
|
+
}
|
|
2899
|
+
if (Array.isArray(integrations)) {
|
|
2900
|
+
const config = { ...exclusionConfig };
|
|
2901
|
+
for (const name of integrations) {
|
|
2902
|
+
const packageName = `@opentelemetry/instrumentation-${name}`;
|
|
2903
|
+
if (!exclusionConfig[packageName]) config[packageName] = { enabled: true };
|
|
2904
|
+
}
|
|
2905
|
+
return getNodeAutoInstrumentations(config);
|
|
2906
|
+
}
|
|
2907
|
+
const config = {
|
|
2908
|
+
...exclusionConfig,
|
|
2909
|
+
...integrations
|
|
2910
|
+
};
|
|
2911
|
+
for (const packageName of Object.keys(exclusionConfig)) if (Object.keys(integrations).find((key) => packageName.includes(key))) config[packageName] = { enabled: false };
|
|
2912
|
+
return getNodeAutoInstrumentations(config);
|
|
2913
|
+
}
|
|
2914
|
+
|
|
2915
|
+
//#endregion
|
|
2916
|
+
//#region src/init.ts
|
|
2917
|
+
/**
|
|
2918
|
+
* Simplified initialization for autotel
|
|
2919
|
+
*
|
|
2920
|
+
* Single init() function with sensible defaults.
|
|
2921
|
+
* Replaces initInstrumentation() and separate events config.
|
|
2922
|
+
*/
|
|
2923
|
+
/**
|
|
2924
|
+
* Silent logger (no-op) - used as default when user doesn't provide one.
|
|
2925
|
+
* Internal autotel logs are silent by default to avoid spam.
|
|
2926
|
+
* Users can import { autotelLogger } from 'autotel/logger' to create their own.
|
|
2927
|
+
*/
|
|
2928
|
+
const silentLogger = {
|
|
2929
|
+
info: () => {},
|
|
2930
|
+
warn: () => {},
|
|
2931
|
+
error: () => {},
|
|
2932
|
+
debug: () => {}
|
|
2933
|
+
};
|
|
2934
|
+
/**
|
|
2935
|
+
* Adapts an Autotel Sampler to the OTel SDK Sampler interface.
|
|
2936
|
+
*/
|
|
2937
|
+
function toOtelSampler(sampler) {
|
|
2938
|
+
return {
|
|
2939
|
+
shouldSample(_context, _traceId, spanName, _spanKind, _attributes, links) {
|
|
2940
|
+
const samplingContext = {
|
|
2941
|
+
operationName: spanName,
|
|
2942
|
+
args: [],
|
|
2943
|
+
links
|
|
2944
|
+
};
|
|
2945
|
+
const shouldTrace = sampler.shouldSample(samplingContext);
|
|
2946
|
+
const rate = sampler.sampleRate?.(samplingContext);
|
|
2947
|
+
return {
|
|
2948
|
+
decision: shouldTrace ? _opentelemetry_sdk_trace_base.SamplingDecision.RECORD_AND_SAMPLED : _opentelemetry_sdk_trace_base.SamplingDecision.NOT_RECORD,
|
|
2949
|
+
...shouldTrace && rate !== void 0 && rate > 1 ? { attributes: { [require_sampling.AUTOTEL_SAMPLING_RATE]: rate } } : {}
|
|
2950
|
+
};
|
|
2951
|
+
},
|
|
2952
|
+
toString() {
|
|
2953
|
+
return `AutotelSamplerAdapter`;
|
|
2954
|
+
}
|
|
2955
|
+
};
|
|
2956
|
+
}
|
|
2957
|
+
let initialized = false;
|
|
2958
|
+
let locked = false;
|
|
2959
|
+
let config = null;
|
|
2960
|
+
let sdk = null;
|
|
2961
|
+
let warnedOnce = false;
|
|
2962
|
+
let logger = silentLogger;
|
|
2963
|
+
let validationConfig = null;
|
|
2964
|
+
let eventsConfig = null;
|
|
2965
|
+
let _stringRedactor = null;
|
|
2966
|
+
function acceptsStringRedactor(subscriber) {
|
|
2967
|
+
return typeof subscriber === "object" && subscriber !== null && "setStringRedactor" in subscriber && typeof subscriber.setStringRedactor === "function";
|
|
2968
|
+
}
|
|
2969
|
+
let _optionalRequire = require_node_require.safeRequire;
|
|
2970
|
+
let _devtoolsClose = null;
|
|
2971
|
+
const LOG_LEVELS = {
|
|
2972
|
+
debug: 0,
|
|
2973
|
+
info: 1,
|
|
2974
|
+
warn: 2,
|
|
2975
|
+
error: 3
|
|
2976
|
+
};
|
|
2977
|
+
/**
|
|
2978
|
+
* Lock the logger to prevent further `init()` calls.
|
|
2979
|
+
* Use this when framework plugins set up instrumentation and you want
|
|
2980
|
+
* to prevent accidental re-initialization from user code.
|
|
2981
|
+
*/
|
|
2982
|
+
function lockLogger() {
|
|
2983
|
+
locked = true;
|
|
2984
|
+
}
|
|
2985
|
+
/**
|
|
2986
|
+
* Check if the logger has been locked.
|
|
2987
|
+
*/
|
|
2988
|
+
function isLoggerLocked() {
|
|
2989
|
+
return locked;
|
|
2990
|
+
}
|
|
2991
|
+
function createSilentLogger() {
|
|
2992
|
+
return {
|
|
2993
|
+
info: () => {},
|
|
2994
|
+
warn: () => {},
|
|
2995
|
+
error: () => {},
|
|
2996
|
+
debug: () => {}
|
|
2997
|
+
};
|
|
2998
|
+
}
|
|
2999
|
+
function wrapLogger(base, silent, minLevel) {
|
|
3000
|
+
if (silent) return createSilentLogger();
|
|
3001
|
+
const threshold = LOG_LEVELS[minLevel];
|
|
3002
|
+
const wrap = (fn, level) => {
|
|
3003
|
+
if (LOG_LEVELS[level] < threshold) return (() => {});
|
|
3004
|
+
return ((...args) => fn(...args));
|
|
3005
|
+
};
|
|
3006
|
+
return {
|
|
3007
|
+
debug: wrap(base.debug, "debug"),
|
|
3008
|
+
info: wrap(base.info, "info"),
|
|
3009
|
+
warn: wrap(base.warn, "warn"),
|
|
3010
|
+
error: wrap(base.error, "error")
|
|
3011
|
+
};
|
|
3012
|
+
}
|
|
3013
|
+
/**
|
|
3014
|
+
* Initialize autotel - Write Once, Observe Everywhere
|
|
3015
|
+
*
|
|
3016
|
+
* Follows OpenTelemetry standards: opinionated defaults with full flexibility
|
|
3017
|
+
* Idempotent: multiple calls are safe, last one wins
|
|
3018
|
+
*
|
|
3019
|
+
* @example Minimal setup (OTLP default)
|
|
3020
|
+
* ```typescript
|
|
3021
|
+
* init({ service: 'my-app' })
|
|
3022
|
+
* ```
|
|
3023
|
+
*
|
|
3024
|
+
* @example With events (observe in PostHog, Mixpanel, etc.)
|
|
3025
|
+
* ```typescript
|
|
3026
|
+
* import { PostHogSubscriber } from 'autotel-subscribers/posthog';
|
|
3027
|
+
*
|
|
3028
|
+
* init({
|
|
3029
|
+
* service: 'my-app',
|
|
3030
|
+
* subscribers: [new PostHogSubscriber({ apiKey: '...' })]
|
|
3031
|
+
* })
|
|
3032
|
+
* ```
|
|
3033
|
+
*
|
|
3034
|
+
* @example Observe in Jaeger
|
|
3035
|
+
* ```typescript
|
|
3036
|
+
* import { JaegerExporter } from '@opentelemetry/exporter-jaeger'
|
|
3037
|
+
*
|
|
3038
|
+
* init({
|
|
3039
|
+
* service: 'my-app',
|
|
3040
|
+
* spanExporter: new JaegerExporter({ endpoint: 'http://localhost:14268/api/traces' })
|
|
3041
|
+
* })
|
|
3042
|
+
* ```
|
|
3043
|
+
*
|
|
3044
|
+
* @example Observe in Zipkin
|
|
3045
|
+
* ```typescript
|
|
3046
|
+
* import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'
|
|
3047
|
+
*
|
|
3048
|
+
* init({
|
|
3049
|
+
* service: 'my-app',
|
|
3050
|
+
* spanExporter: new ZipkinExporter({ url: 'http://localhost:9411/api/v2/spans' })
|
|
3051
|
+
* })
|
|
3052
|
+
* ```
|
|
3053
|
+
*
|
|
3054
|
+
* @example Observe in Datadog
|
|
3055
|
+
* ```typescript
|
|
3056
|
+
* import { DatadogSpanProcessor } from '@opentelemetry/exporter-datadog'
|
|
3057
|
+
*
|
|
3058
|
+
* init({
|
|
3059
|
+
* service: 'my-app',
|
|
3060
|
+
* spanProcessor: new DatadogSpanProcessor({ ... })
|
|
3061
|
+
* })
|
|
3062
|
+
* ```
|
|
3063
|
+
*
|
|
3064
|
+
* @example Console output (dev)
|
|
3065
|
+
* ```typescript
|
|
3066
|
+
* import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'
|
|
3067
|
+
*
|
|
3068
|
+
* init({
|
|
3069
|
+
* service: 'my-app',
|
|
3070
|
+
* spanProcessor: new SimpleSpanProcessor(new ConsoleSpanExporter())
|
|
3071
|
+
* })
|
|
3072
|
+
* ```
|
|
3073
|
+
*/
|
|
3074
|
+
function init(cfg) {
|
|
3075
|
+
if (locked) return;
|
|
3076
|
+
const envConfig = resolveConfigFromEnv();
|
|
3077
|
+
const yamlConfig = require_yaml_config.loadYamlConfig() ?? {};
|
|
3078
|
+
const mergedConfig = {
|
|
3079
|
+
...envConfig,
|
|
3080
|
+
...yamlConfig,
|
|
3081
|
+
...cfg,
|
|
3082
|
+
resourceAttributes: {
|
|
3083
|
+
...envConfig.resourceAttributes,
|
|
3084
|
+
...yamlConfig.resourceAttributes,
|
|
3085
|
+
...detectEnvironmentAttributes(),
|
|
3086
|
+
...cfg.resourceAttributes
|
|
3087
|
+
},
|
|
3088
|
+
headers: cfg.headers ?? yamlConfig.headers ?? envConfig.headers
|
|
3089
|
+
};
|
|
3090
|
+
const resolvedRedactor = resolveAttributeRedactor(mergedConfig.attributeRedactor, mergedConfig.environment || process.env.NODE_ENV || "development");
|
|
3091
|
+
if (resolvedRedactor === void 0) mergedConfig.attributeRedactor = void 0;
|
|
3092
|
+
else {
|
|
3093
|
+
const normalizedRedactor = require_attribute_redacting_processor.normalizeAttributeRedactorConfig(resolvedRedactor);
|
|
3094
|
+
if (!normalizedRedactor) throw new Error("Invalid attributeRedactor config");
|
|
3095
|
+
mergedConfig.attributeRedactor = normalizedRedactor;
|
|
3096
|
+
}
|
|
3097
|
+
const devtoolsConfig = resolveDevtoolsConfig(mergedConfig.devtools);
|
|
3098
|
+
if (devtoolsConfig.enabled && mergedConfig.logs === void 0) mergedConfig.logs = true;
|
|
3099
|
+
const silent = mergedConfig.silent ?? false;
|
|
3100
|
+
const minLevel = mergedConfig.minLevel ?? "info";
|
|
3101
|
+
logger = wrapLogger(mergedConfig.logger || silentLogger, silent, minLevel);
|
|
3102
|
+
if (initialized) logger.warn({}, "[autotel] init() called again - last config wins. This may cause unexpected behavior.");
|
|
3103
|
+
config = mergedConfig;
|
|
3104
|
+
validationConfig = mergedConfig.validation || null;
|
|
3105
|
+
eventsConfig = mergedConfig.events || null;
|
|
3106
|
+
let endpoint = mergedConfig.endpoint ?? devtoolsConfig.endpoint;
|
|
3107
|
+
const version = mergedConfig.version || detectVersion();
|
|
3108
|
+
const environment = mergedConfig.environment || process.env.NODE_ENV || "development";
|
|
3109
|
+
const metricsEnabled = resolveMetricsFlag(mergedConfig.metrics);
|
|
2886
3110
|
const logsEnabled = resolveLogsFlag(mergedConfig.logs);
|
|
2887
3111
|
if (devtoolsConfig.enabled && devtoolsConfig.embedded) {
|
|
2888
3112
|
const devtoolsModule = _optionalRequire("autotel-devtools");
|
|
@@ -2957,6 +3181,12 @@ function init(cfg) {
|
|
|
2957
3181
|
for (const subscriber of mergedConfig.subscribers) if (acceptsStringRedactor(subscriber)) subscriber.setStringRedactor(_stringRedactor);
|
|
2958
3182
|
}
|
|
2959
3183
|
if (mergedConfig.spanNameNormalizer && spanProcessors.length > 0) spanProcessors = spanProcessors.map((processor) => new require_span_name_normalizer.SpanNameNormalizingProcessor(processor, { normalizer: mergedConfig.spanNameNormalizer }));
|
|
3184
|
+
if (mergedConfig.policies) {
|
|
3185
|
+
if (typeof mergedConfig.policies === "string") require_policy.watchPolicyFile(mergedConfig.policies);
|
|
3186
|
+
else require_policy.setPolicies(mergedConfig.policies);
|
|
3187
|
+
const userSpanFilter = mergedConfig.spanFilter;
|
|
3188
|
+
mergedConfig.spanFilter = userSpanFilter ? (span) => userSpanFilter(span) && require_policy.policySpanFilter(span) : require_policy.policySpanFilter;
|
|
3189
|
+
}
|
|
2960
3190
|
if (mergedConfig.spanFilter && spanProcessors.length > 0) spanProcessors = spanProcessors.map((processor) => new require_filtering_span_processor.FilteringSpanProcessor(processor, { filter: mergedConfig.spanFilter }));
|
|
2961
3191
|
const metricReaders = [];
|
|
2962
3192
|
if (configuredMetricReaders && configuredMetricReaders.length > 0) metricReaders.push(...configuredMetricReaders);
|
|
@@ -2995,6 +3225,7 @@ function init(cfg) {
|
|
|
2995
3225
|
logRecordProcessors.push(...posthogProcessors);
|
|
2996
3226
|
logger.info({}, "[autotel] PostHog OTLP logs configured");
|
|
2997
3227
|
}
|
|
3228
|
+
if (mergedConfig.policies && logRecordProcessors) logRecordProcessors = logRecordProcessors.map((processor) => new require_policy.PolicyLogRecordProcessor(processor));
|
|
2998
3229
|
let finalInstrumentations = mergedConfig.instrumentations ? [...mergedConfig.instrumentations] : [];
|
|
2999
3230
|
if (mergedConfig.autoInstrumentations !== void 0 && mergedConfig.autoInstrumentations !== false) {
|
|
3000
3231
|
if (isESMMode()) logger.info({}, "[autotel] ESM mode detected. For auto-instrumentation to work:\n 1. Install @opentelemetry/auto-instrumentations-node as a direct dependency\n 2. Import autotel/register FIRST in your instrumentation file\n 3. Use getNodeAutoInstrumentations() directly instead of autoInstrumentations\n See: https://github.com/jagreehal/autotel#esm-setup");
|
|
@@ -3041,118 +3272,18 @@ function init(cfg) {
|
|
|
3041
3272
|
}
|
|
3042
3273
|
initialized = true;
|
|
3043
3274
|
const processHandlers = mergedConfig.processHandlers;
|
|
3044
|
-
|
|
3275
|
+
const handlersConfig = processHandlers && processHandlers !== true ? processHandlers : {};
|
|
3276
|
+
if (processHandlers) installProcessHandlers(handlersConfig, shutdown);
|
|
3045
3277
|
else uninstallProcessHandlers();
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
const names = /* @__PURE__ */ new Set();
|
|
3053
|
-
if (!instrumentations) return names;
|
|
3054
|
-
for (const instrumentation of instrumentations) if (instrumentation && typeof instrumentation === "object") names.add(instrumentation.constructor.name);
|
|
3055
|
-
return names;
|
|
3056
|
-
}
|
|
3057
|
-
/**
|
|
3058
|
-
* Map common instrumentation class names to their package names
|
|
3059
|
-
* Used to disable auto-instrumentations when user provides manual configs
|
|
3060
|
-
*/
|
|
3061
|
-
const INSTRUMENTATION_CLASS_TO_PACKAGE = {
|
|
3062
|
-
HttpInstrumentation: "@opentelemetry/instrumentation-http",
|
|
3063
|
-
HttpsInstrumentation: "@opentelemetry/instrumentation-http",
|
|
3064
|
-
ExpressInstrumentation: "@opentelemetry/instrumentation-express",
|
|
3065
|
-
FastifyInstrumentation: "@opentelemetry/instrumentation-fastify",
|
|
3066
|
-
MongoDBInstrumentation: "@opentelemetry/instrumentation-mongodb",
|
|
3067
|
-
MongooseInstrumentation: "@opentelemetry/instrumentation-mongoose",
|
|
3068
|
-
PrismaInstrumentation: "@opentelemetry/instrumentation-prisma",
|
|
3069
|
-
PinoInstrumentation: "@opentelemetry/instrumentation-pino",
|
|
3070
|
-
WinstonInstrumentation: "@opentelemetry/instrumentation-winston",
|
|
3071
|
-
RedisInstrumentation: "@opentelemetry/instrumentation-redis",
|
|
3072
|
-
GraphQLInstrumentation: "@opentelemetry/instrumentation-graphql",
|
|
3073
|
-
GrpcInstrumentation: "@opentelemetry/instrumentation-grpc",
|
|
3074
|
-
IORedisInstrumentation: "@opentelemetry/instrumentation-ioredis",
|
|
3075
|
-
KnexInstrumentation: "@opentelemetry/instrumentation-knex",
|
|
3076
|
-
NestJsInstrumentation: "@opentelemetry/instrumentation-nestjs-core",
|
|
3077
|
-
PgInstrumentation: "@opentelemetry/instrumentation-pg",
|
|
3078
|
-
MySQLInstrumentation: "@opentelemetry/instrumentation-mysql",
|
|
3079
|
-
MySQL2Instrumentation: "@opentelemetry/instrumentation-mysql2"
|
|
3080
|
-
};
|
|
3081
|
-
/**
|
|
3082
|
-
* Detect if we're running in ESM mode
|
|
3083
|
-
*/
|
|
3084
|
-
function isESMMode() {
|
|
3085
|
-
try {
|
|
3086
|
-
const fs = require_node_require.requireModule("node:fs");
|
|
3087
|
-
try {
|
|
3088
|
-
return JSON.parse(fs.readFileSync(`${process.cwd()}/package.json`, "utf8")).type === "module";
|
|
3089
|
-
} catch {
|
|
3090
|
-
return false;
|
|
3091
|
-
}
|
|
3092
|
-
} catch {
|
|
3093
|
-
return false;
|
|
3094
|
-
}
|
|
3095
|
-
}
|
|
3096
|
-
/**
|
|
3097
|
-
* Lazy-load auto-instrumentations (optional peer dependency)
|
|
3098
|
-
* Only loads when integrations config is truthy, avoiding ~40+ package imports at startup.
|
|
3099
|
-
*/
|
|
3100
|
-
function loadNodeAutoInstrumentations() {
|
|
3101
|
-
try {
|
|
3102
|
-
return require_node_require.requireModule("@opentelemetry/auto-instrumentations-node").getNodeAutoInstrumentations;
|
|
3103
|
-
} catch {
|
|
3104
|
-
const isESM = isESMMode();
|
|
3105
|
-
const baseMessage = "@opentelemetry/auto-instrumentations-node not found.";
|
|
3106
|
-
if (isESM) throw new Error(`${baseMessage}\n\nESM Setup Required:
|
|
3107
|
-
1. Install as a direct dependency: pnpm add @opentelemetry/auto-instrumentations-node
|
|
3108
|
-
2. Create instrumentation.mjs with:
|
|
3109
|
-
import 'autotel/register'; // MUST be first!
|
|
3110
|
-
import { init } from 'autotel';
|
|
3111
|
-
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
|
|
3112
|
-
init({ service: "my-app", instrumentations: getNodeAutoInstrumentations() });
|
|
3113
|
-
3. Run with: tsx --import ./instrumentation.mjs src/index.ts
|
|
3114
|
-
|
|
3115
|
-
See: https://github.com/jagreehal/autotel#esm-setup`);
|
|
3116
|
-
throw new Error(`${baseMessage} Install it: pnpm add @opentelemetry/auto-instrumentations-node`);
|
|
3278
|
+
if (mergedConfig.flushOnExit !== false) {
|
|
3279
|
+
const timeoutMs = handlersConfig.shutdownTimeoutMs;
|
|
3280
|
+
installExitFlush(() => flush({
|
|
3281
|
+
forShutdown: true,
|
|
3282
|
+
timeout: timeoutMs
|
|
3283
|
+
}), timeoutMs);
|
|
3117
3284
|
}
|
|
3118
3285
|
}
|
|
3119
3286
|
/**
|
|
3120
|
-
* Injectable loader for testing. Set to override the default loader.
|
|
3121
|
-
* @internal
|
|
3122
|
-
*/
|
|
3123
|
-
let _autoInstrumentationsLoader = null;
|
|
3124
|
-
/**
|
|
3125
|
-
* Get auto-instrumentations based on simple integration names
|
|
3126
|
-
* Excludes instrumentations that are manually provided to avoid conflicts
|
|
3127
|
-
*/
|
|
3128
|
-
function getAutoInstrumentations(integrations, manualInstrumentationNames = /* @__PURE__ */ new Set()) {
|
|
3129
|
-
if (integrations === false) return [];
|
|
3130
|
-
const getNodeAutoInstrumentations = _autoInstrumentationsLoader ? _autoInstrumentationsLoader() : loadNodeAutoInstrumentations();
|
|
3131
|
-
const exclusionConfig = {};
|
|
3132
|
-
for (const className of manualInstrumentationNames) {
|
|
3133
|
-
const packageName = INSTRUMENTATION_CLASS_TO_PACKAGE[className];
|
|
3134
|
-
if (packageName) exclusionConfig[packageName] = { enabled: false };
|
|
3135
|
-
}
|
|
3136
|
-
if (integrations === true) {
|
|
3137
|
-
if (Object.keys(exclusionConfig).length > 0) return getNodeAutoInstrumentations(exclusionConfig);
|
|
3138
|
-
return getNodeAutoInstrumentations();
|
|
3139
|
-
}
|
|
3140
|
-
if (Array.isArray(integrations)) {
|
|
3141
|
-
const config = { ...exclusionConfig };
|
|
3142
|
-
for (const name of integrations) {
|
|
3143
|
-
const packageName = `@opentelemetry/instrumentation-${name}`;
|
|
3144
|
-
if (!exclusionConfig[packageName]) config[packageName] = { enabled: true };
|
|
3145
|
-
}
|
|
3146
|
-
return getNodeAutoInstrumentations(config);
|
|
3147
|
-
}
|
|
3148
|
-
const config = {
|
|
3149
|
-
...exclusionConfig,
|
|
3150
|
-
...integrations
|
|
3151
|
-
};
|
|
3152
|
-
for (const packageName of Object.keys(exclusionConfig)) if (Object.keys(integrations).find((key) => packageName.includes(key))) config[packageName] = { enabled: false };
|
|
3153
|
-
return getNodeAutoInstrumentations(config);
|
|
3154
|
-
}
|
|
3155
|
-
/**
|
|
3156
3287
|
* Check if autotel has been initialized
|
|
3157
3288
|
*/
|
|
3158
3289
|
function isInitialized() {
|
|
@@ -3192,37 +3323,6 @@ function warnIfNotInitialized(context) {
|
|
|
3192
3323
|
}
|
|
3193
3324
|
}
|
|
3194
3325
|
/**
|
|
3195
|
-
* Auto-detect version from package.json
|
|
3196
|
-
*/
|
|
3197
|
-
function detectVersion() {
|
|
3198
|
-
try {
|
|
3199
|
-
const fs = require_node_require.requireModule("node:fs");
|
|
3200
|
-
return JSON.parse(fs.readFileSync(`${process.cwd()}/package.json`, "utf8")).version || "1.0.0";
|
|
3201
|
-
} catch {
|
|
3202
|
-
return "1.0.0";
|
|
3203
|
-
}
|
|
3204
|
-
}
|
|
3205
|
-
/**
|
|
3206
|
-
* Detect hostname for resource attributes.
|
|
3207
|
-
* Supports Datadog conventions (DD_HOSTNAME) and falls back to system hostname.
|
|
3208
|
-
*
|
|
3209
|
-
* Priority order:
|
|
3210
|
-
* 1. DD_HOSTNAME environment variable (Datadog convention)
|
|
3211
|
-
* 2. HOSTNAME environment variable (common Unix convention)
|
|
3212
|
-
* 3. os.hostname() (system hostname)
|
|
3213
|
-
*
|
|
3214
|
-
* @returns hostname string or undefined if detection fails
|
|
3215
|
-
*/
|
|
3216
|
-
function detectHostname() {
|
|
3217
|
-
if (process.env.DD_HOSTNAME) return process.env.DD_HOSTNAME;
|
|
3218
|
-
if (process.env.HOSTNAME) return process.env.HOSTNAME;
|
|
3219
|
-
try {
|
|
3220
|
-
return require_node_require.requireModule("node:os").hostname();
|
|
3221
|
-
} catch {
|
|
3222
|
-
return;
|
|
3223
|
-
}
|
|
3224
|
-
}
|
|
3225
|
-
/**
|
|
3226
3326
|
* @internal Close embedded devtools if running.
|
|
3227
3327
|
*/
|
|
3228
3328
|
async function _closeEmbeddedDevtools() {
|