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