@autter/runtime-node 1.6.0 → 1.7.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/dist/index.js CHANGED
@@ -45,10 +45,10 @@ import {
45
45
  splitFingerprint,
46
46
  toClientError,
47
47
  withRuntimeOperation
48
- } from "./chunk-FBKYOHU2.js";
48
+ } from "./chunk-KA4AOZX2.js";
49
49
  import {
50
50
  runDoctor
51
- } from "./chunk-Y4T7SQMO.js";
51
+ } from "./chunk-QUC6OUE7.js";
52
52
  import {
53
53
  COMPAT_MANIFEST,
54
54
  CountingExporter,
@@ -67,9 +67,10 @@ import {
67
67
  observeIngesterResponse,
68
68
  registerFlushTarget,
69
69
  setDebugMode,
70
+ sharedSlot,
70
71
  telemetryStats,
71
72
  unregisterFlushTargets
72
- } from "./chunk-OY2HTUWY.js";
73
+ } from "./chunk-LG52CW3V.js";
73
74
 
74
75
  // src/relay.ts
75
76
  var IpWindow = class {
@@ -440,6 +441,9 @@ function createBrowserRelayHandler(opts) {
440
441
  }
441
442
 
442
443
  // src/server.ts
444
+ import { errorMonitor } from "events";
445
+ import { createRequire } from "module";
446
+ import { join } from "path";
443
447
  import { ServerResponse } from "http";
444
448
  import { randomUUID } from "crypto";
445
449
  import {
@@ -449,7 +453,7 @@ import {
449
453
  SpanStatusCode,
450
454
  TraceFlags
451
455
  } from "@opentelemetry/api";
452
- import { getRPCMetadata as getRPCMetadata2, RPCType as RPCType2 } from "@opentelemetry/core";
456
+ import { getRPCMetadata as getRPCMetadata3, RPCType as RPCType3 } from "@opentelemetry/core";
453
457
  import {
454
458
  AggregationTemporalityPreference,
455
459
  OTLPMetricExporter
@@ -478,6 +482,89 @@ import { Resource } from "@opentelemetry/resources";
478
482
  import {
479
483
  AggregationTemporality
480
484
  } from "@opentelemetry/sdk-metrics";
485
+
486
+ // src/url-query.ts
487
+ var URL_KEYS = ["http.target", "http.url", "url.full", "url.original", "url.path"];
488
+ var QUERY_KEY = "url.query";
489
+ var NAME_KEYS = ["next.span_name"];
490
+ var MASK = "[redacted]";
491
+ var SCHEME = /^[a-z][a-z0-9+.-]*:\/\//i;
492
+ function splitUrl(value) {
493
+ const from = SCHEME.test(value) ? value.indexOf("://") + 3 : 0;
494
+ const question = value.indexOf("?", from);
495
+ const hash = value.indexOf("#", from);
496
+ const at = question < 0 ? hash : hash < 0 ? question : Math.min(question, hash);
497
+ if (at < 0) return null;
498
+ const query = at === question ? value.slice(at + 1, hash > at ? hash : void 0) : null;
499
+ return { base: value.slice(0, at), query };
500
+ }
501
+ function decodeName(raw) {
502
+ try {
503
+ return decodeURIComponent(raw.replace(/\+/g, " "));
504
+ } catch {
505
+ return raw;
506
+ }
507
+ }
508
+ function makeUrlQueryPolicy(keepQueryParams = []) {
509
+ const keep = new Set(keepQueryParams.filter((name) => typeof name === "string"));
510
+ const maskQuery = (query) => {
511
+ const out = [];
512
+ for (const part of query.split("&")) {
513
+ if (!part) continue;
514
+ const eq = part.indexOf("=");
515
+ const name = eq < 0 ? part : part.slice(0, eq);
516
+ if (keep.size && keep.has(decodeName(name))) out.push(part);
517
+ else if (eq < 0) out.push(MASK);
518
+ else out.push(eq === part.length - 1 ? part : `${name}=${MASK}`);
519
+ }
520
+ return out.join("&");
521
+ };
522
+ const stripName = (name) => {
523
+ if (!name.includes("?") && !name.includes("#")) return name;
524
+ return name.split(" ").map((token) => {
525
+ if (!token.startsWith("/") && !SCHEME.test(token)) return token;
526
+ return splitUrl(token)?.base ?? token;
527
+ }).join(" ");
528
+ };
529
+ return {
530
+ attributes(attributes) {
531
+ let out = null;
532
+ const set = (key, value) => {
533
+ out ??= { ...attributes };
534
+ out[key] = value;
535
+ };
536
+ let dropped = null;
537
+ for (const key of URL_KEYS) {
538
+ const value = attributes[key];
539
+ if (typeof value !== "string") continue;
540
+ const split = splitUrl(value);
541
+ if (!split) continue;
542
+ set(key, split.base);
543
+ if (split.query) dropped ??= split.query;
544
+ }
545
+ for (const key of NAME_KEYS) {
546
+ const value = attributes[key];
547
+ if (typeof value !== "string") continue;
548
+ const stripped = stripName(value);
549
+ if (stripped !== value) set(key, stripped);
550
+ }
551
+ const current = attributes[QUERY_KEY];
552
+ if (typeof current === "string") {
553
+ const masked = maskQuery(current.startsWith("?") ? current.slice(1) : current);
554
+ if (masked !== current) set(QUERY_KEY, masked);
555
+ } else if (current !== void 0) {
556
+ set(QUERY_KEY, MASK);
557
+ } else if (dropped !== null) {
558
+ const masked = maskQuery(dropped);
559
+ if (masked) set(QUERY_KEY, masked);
560
+ }
561
+ return out ?? attributes;
562
+ },
563
+ name: stripName
564
+ };
565
+ }
566
+
567
+ // src/redact-exporter.ts
481
568
  var scrubbedResources = /* @__PURE__ */ new WeakMap();
482
569
  function redactResource(resource, redactor) {
483
570
  const cached = scrubbedResources.get(resource);
@@ -486,17 +573,41 @@ function redactResource(resource, redactor) {
486
573
  scrubbedResources.set(resource, scrubbed);
487
574
  return scrubbed;
488
575
  }
489
- function redactSpan(span, redactor) {
490
- const status = span.status.message ? { ...span.status, message: redactor.text(span.status.message) } : span.status;
576
+ var DEFAULT_QUERY_POLICY = makeUrlQueryPolicy();
577
+ function stripQueries(span, query) {
578
+ const name = query.name(span.name);
579
+ const attributes = query.attributes(span.attributes);
580
+ if (name === span.name && attributes === span.attributes) return span;
581
+ return wrap(span, { name, attributes });
582
+ }
583
+ function wrap(span, fields) {
491
584
  return {
492
- name: redactor.text(span.name),
585
+ name: fields.name,
493
586
  kind: span.kind,
494
587
  spanContext: () => span.spanContext(),
495
588
  parentSpanId: span.parentSpanId,
496
589
  startTime: span.startTime,
497
590
  endTime: span.endTime,
591
+ status: fields.status ?? span.status,
592
+ attributes: fields.attributes,
593
+ links: fields.links ?? span.links,
594
+ events: fields.events ?? span.events,
595
+ duration: span.duration,
596
+ ended: span.ended,
597
+ resource: fields.resource ?? span.resource,
598
+ instrumentationLibrary: span.instrumentationLibrary,
599
+ droppedAttributesCount: span.droppedAttributesCount,
600
+ droppedEventsCount: span.droppedEventsCount,
601
+ droppedLinksCount: span.droppedLinksCount
602
+ };
603
+ }
604
+ function redactSpan(span, redactor, query = DEFAULT_QUERY_POLICY) {
605
+ if (!redactor.enabled) return stripQueries(span, query);
606
+ const status = span.status.message ? { ...span.status, message: redactor.text(span.status.message) } : span.status;
607
+ return wrap(span, {
608
+ name: query.name(redactor.text(span.name)),
498
609
  status,
499
- attributes: redactor.exported(span.attributes),
610
+ attributes: query.attributes(redactor.exported(span.attributes)),
500
611
  links: span.links.map(
501
612
  (link) => link.attributes ? { ...link, attributes: redactor.exported(link.attributes) } : link
502
613
  ),
@@ -505,34 +616,27 @@ function redactSpan(span, redactor) {
505
616
  name: redactor.text(event.name),
506
617
  ...event.attributes ? { attributes: redactor.exported(event.attributes) } : {}
507
618
  })),
508
- duration: span.duration,
509
- ended: span.ended,
510
- resource: redactResource(span.resource, redactor),
511
- instrumentationLibrary: span.instrumentationLibrary,
512
- droppedAttributesCount: span.droppedAttributesCount,
513
- droppedEventsCount: span.droppedEventsCount,
514
- droppedLinksCount: span.droppedLinksCount
515
- };
619
+ resource: redactResource(span.resource, redactor)
620
+ });
516
621
  }
517
622
  var RedactingSpanExporter = class {
518
- constructor(inner, redactor) {
623
+ constructor(inner, redactor, query = DEFAULT_QUERY_POLICY) {
519
624
  this.inner = inner;
520
625
  this.redactor = redactor;
626
+ this.query = query;
521
627
  }
522
628
  inner;
523
629
  redactor;
630
+ query;
524
631
  export(spans, resultCallback) {
525
632
  const redactor = this.redactor();
526
- let scrubbed = spans;
633
+ const scrubbed = [];
527
634
  let failed = 0;
528
- if (redactor.enabled) {
529
- scrubbed = [];
530
- for (const span of spans) {
531
- try {
532
- scrubbed.push(redactSpan(span, redactor));
533
- } catch {
534
- failed++;
535
- }
635
+ for (const span of spans) {
636
+ try {
637
+ scrubbed.push(redactSpan(span, redactor, this.query));
638
+ } catch {
639
+ failed++;
536
640
  }
537
641
  }
538
642
  if (!failed) {
@@ -641,7 +745,7 @@ import { context, trace } from "@opentelemetry/api";
641
745
  import { getRPCMetadata, RPCType } from "@opentelemetry/core";
642
746
  var REQUEST_STATE = /* @__PURE__ */ Symbol.for("autter.runtime.request");
643
747
  var BOUND = /* @__PURE__ */ Symbol.for("autter.runtime.bound");
644
- var CAPTURED = /* @__PURE__ */ new WeakSet();
748
+ var CAPTURED = sharedSlot("requests", () => ({ captured: /* @__PURE__ */ new WeakSet() })).captured;
645
749
  var capturer = () => {
646
750
  };
647
751
  function setRequestExceptionCapturer(fn) {
@@ -667,14 +771,14 @@ function defaultShouldCapture(error) {
667
771
  return error instanceof RuntimeError || errorCodeOf(error) !== void 0 || responseStatusOf(error) >= 500;
668
772
  }
669
773
  function onRequestSpan(fn) {
670
- const active2 = context.active();
671
- const metadata = getRPCMetadata(active2);
774
+ const active = context.active();
775
+ const metadata = getRPCMetadata(active);
672
776
  const span = metadata?.type === RPCType.HTTP ? metadata.span : void 0;
673
- if (!span || !span.isRecording() || trace.getSpan(active2) === span) {
777
+ if (!span || !span.isRecording() || trace.getSpan(active) === span) {
674
778
  fn();
675
779
  return;
676
780
  }
677
- context.with(trace.setSpan(active2, span), fn);
781
+ context.with(trace.setSpan(active, span), fn);
678
782
  }
679
783
  function captureAtBoundary(error, state, rule = void 0, attributes) {
680
784
  let wanted;
@@ -713,7 +817,7 @@ function bindEmitter(emitter, state) {
713
817
  if (!target || typeof target !== "object" || target[BOUND]) return;
714
818
  target[BOUND] = true;
715
819
  const wrappers = /* @__PURE__ */ new WeakMap();
716
- const wrap = (listener) => {
820
+ const wrap2 = (listener) => {
717
821
  if (typeof listener !== "function") return listener;
718
822
  let wrapped = wrappers.get(listener);
719
823
  if (!wrapped) {
@@ -737,7 +841,7 @@ function bindEmitter(emitter, state) {
737
841
  const original = target[method];
738
842
  if (typeof original !== "function") continue;
739
843
  target[method] = function(event, listener) {
740
- return original.call(this, event, wrap(listener));
844
+ return original.call(this, event, wrap2(listener));
741
845
  };
742
846
  }
743
847
  for (const method of ["removeListener", "off"]) {
@@ -1175,7 +1279,48 @@ function installUnhandledRejectionCapture(capture, mode = unhandledRejectionsMod
1175
1279
  };
1176
1280
  }
1177
1281
 
1282
+ // src/route-label.ts
1283
+ import { getRPCMetadata as getRPCMetadata2, RPCType as RPCType2 } from "@opentelemetry/core";
1284
+ var ours = /* @__PURE__ */ new WeakMap();
1285
+ var RAW_PATH_SPAN_TYPES = /* @__PURE__ */ new Set(["BaseServer.renderToResponse"]);
1286
+ function noteFrameworkRoute(parent, attributes) {
1287
+ const route = attributes?.["next.route"];
1288
+ if (typeof route !== "string" || !route.startsWith("/")) return;
1289
+ if (RAW_PATH_SPAN_TYPES.has(String(attributes?.["next.span_type"] ?? ""))) return;
1290
+ const rpc = getRPCMetadata2(parent);
1291
+ if (!rpc || rpc.type !== RPCType2.HTTP) return;
1292
+ const mine = ours.get(rpc);
1293
+ if (rpc.route !== void 0 && rpc.route !== mine) return;
1294
+ if (mine !== void 0 && mine.includes("[") && !route.includes("[")) return;
1295
+ if (mine !== void 0 && (route === `${mine}/route` || route === `${mine}/page`)) return;
1296
+ rpc.route = route;
1297
+ ours.set(rpc, route);
1298
+ }
1299
+ var RouteLabelSampler = class {
1300
+ constructor(delegate) {
1301
+ this.delegate = delegate;
1302
+ }
1303
+ delegate;
1304
+ shouldSample(ctx, traceId, spanName, spanKind, attributes, links) {
1305
+ try {
1306
+ noteFrameworkRoute(ctx, attributes);
1307
+ } catch {
1308
+ }
1309
+ return this.delegate.shouldSample(ctx, traceId, spanName, spanKind, attributes, links);
1310
+ }
1311
+ toString() {
1312
+ return `RouteLabel(${this.delegate.toString()})`;
1313
+ }
1314
+ };
1315
+
1178
1316
  // src/server.ts
1317
+ var S = sharedSlot("server", () => ({
1318
+ active: null,
1319
+ alwaysOnProvider: null,
1320
+ redactor: makeRedactor(true),
1321
+ loggerOnlyCapture: null,
1322
+ hookRequests: false
1323
+ }));
1179
1324
  var LLM_MARKER_ATTRIBUTES = [
1180
1325
  "gen_ai.operation.name",
1181
1326
  "gen_ai.system",
@@ -1303,9 +1448,9 @@ var ErrorTraceRetentionProcessor = class {
1303
1448
  }
1304
1449
  /** Promote the active trace, if any — called on captured errors. */
1305
1450
  retainActiveTrace() {
1306
- const active2 = trace2.getActiveSpan()?.spanContext();
1307
- if (!active2 || (active2.traceFlags & TraceFlags.SAMPLED) !== 0) return;
1308
- const entry = this.entryFor(active2.traceId);
1451
+ const active = trace2.getActiveSpan()?.spanContext();
1452
+ if (!active || (active.traceFlags & TraceFlags.SAMPLED) !== 0) return;
1453
+ const entry = this.entryFor(active.traceId);
1309
1454
  if (entry.retained) return;
1310
1455
  entry.retained = true;
1311
1456
  this.flush(entry);
@@ -1372,7 +1517,7 @@ function llmBaseAttributes(info) {
1372
1517
  ...info.sessionId ? { "autter.session_id": info.sessionId } : {},
1373
1518
  // Caller-supplied extras go through redaction like every other
1374
1519
  // custom attribute family (prompts can contain emails/keys).
1375
- ...info.attributes ? activeRedactor(info.attributes) : {}
1520
+ ...info.attributes ? S.redactor(info.attributes) : {}
1376
1521
  };
1377
1522
  }
1378
1523
  function usageAttributes(usage) {
@@ -1413,7 +1558,7 @@ async function runLlmSpan(tracer, info, fn) {
1413
1558
  },
1414
1559
  setResponseModel,
1415
1560
  setModel: setResponseModel,
1416
- setAttributes: (attributes) => span.setAttributes(activeRedactor(attributes))
1561
+ setAttributes: (attributes) => span.setAttributes(S.redactor(attributes))
1417
1562
  };
1418
1563
  try {
1419
1564
  const result = await context2.with(
@@ -1475,67 +1620,82 @@ async function runWithSpan(tracer, name, fn, attributes, links) {
1475
1620
  }
1476
1621
  function recordError(span, err) {
1477
1622
  const isErr = isError(err);
1478
- const message = activeRedactor.text(isErr ? err.message : String(err));
1623
+ const message = S.redactor.text(isErr ? err.message : String(err));
1479
1624
  if (isErr) {
1480
1625
  span.addEvent("exception", {
1481
1626
  "exception.type": err.name,
1482
1627
  "exception.message": message,
1483
- ...err.stack ? { "exception.stacktrace": activeRedactor.text(err.stack) } : {},
1484
- ...breadcrumbEventAttributes(activeRedactor.text, err)
1628
+ ...err.stack ? { "exception.stacktrace": S.redactor.text(err.stack) } : {},
1629
+ ...breadcrumbEventAttributes(S.redactor.text, err)
1485
1630
  });
1486
1631
  }
1487
1632
  span.setStatus({ code: SpanStatusCode.ERROR, message });
1488
1633
  }
1489
1634
  function redactWithActive(attributes) {
1490
- return activeRedactor(attributes);
1635
+ return S.redactor(attributes);
1491
1636
  }
1492
- function captureExpressRoute(_span, response) {
1637
+ function patchLoadedHttpModules() {
1638
+ try {
1639
+ const load = createRequire(join(process.cwd(), "autter-runtime-http.cjs"));
1640
+ load("http");
1641
+ load("https");
1642
+ } catch {
1643
+ }
1644
+ }
1645
+ var METRIC_ROUTE_ATTR = "autter.metric_has_route";
1646
+ function captureRequestRoute(span, response) {
1493
1647
  if (!(response instanceof ServerResponse)) return;
1494
- const rpcMetadata = getRPCMetadata2(context2.active());
1495
- if (rpcMetadata?.type !== RPCType2.HTTP) return;
1648
+ const rpcMetadata = getRPCMetadata3(context2.active());
1649
+ if (rpcMetadata?.type !== RPCType3.HTTP) return;
1650
+ let failed = false;
1651
+ response.on(errorMonitor, () => {
1652
+ failed = true;
1653
+ });
1496
1654
  const setRoute = () => {
1497
1655
  if (rpcMetadata.route) return;
1498
1656
  const route = expressRouteOf(response.req);
1499
1657
  if (route) rpcMetadata.route = route;
1500
1658
  };
1501
1659
  response.prependListener("finish", setRoute);
1502
- response.prependListener("close", setRoute);
1503
- if (hookRequests) startHookRequest(response);
1660
+ response.prependListener("close", () => {
1661
+ setRoute();
1662
+ if (rpcMetadata.route && !failed) span.setAttribute(METRIC_ROUTE_ATTR, true);
1663
+ });
1664
+ if (S.hookRequests) startHookRequest(response);
1504
1665
  }
1505
- var hookRequests = false;
1506
1666
  function structuredErrorAttributes(error) {
1507
1667
  const attrs = { ...errorAttributes(error, { causes: false }) };
1508
1668
  const internal = errorInternal(error);
1509
1669
  if (internal) {
1510
1670
  try {
1511
1671
  attrs["autter.error.internal"] = JSON.stringify(
1512
- activeRedactor(internal)
1672
+ S.redactor(internal)
1513
1673
  ).slice(0, 4e3);
1514
1674
  } catch {
1515
1675
  }
1516
1676
  }
1517
1677
  return attrs;
1518
1678
  }
1519
- var active = null;
1520
- var activeAlwaysOnProvider = null;
1521
- var activeRedactor = makeRedactor(true);
1522
1679
  function initAutterServer(options) {
1523
- if (active) return active;
1680
+ if (S.active) return S.active;
1524
1681
  const endpoint = (options.endpoint ?? "https://otlp.autter.dev").replace(
1525
1682
  /\/$/,
1526
1683
  ""
1527
1684
  );
1528
1685
  const headers = { authorization: `Bearer ${options.apiKey}` };
1686
+ const redactor = makeRedactor(options.redactAttributes ?? true);
1687
+ const queryPolicy = makeUrlQueryPolicy(options.keepQueryParams);
1529
1688
  const traceExporter = () => new CountingExporter(
1530
1689
  new RedactingSpanExporter(
1531
1690
  new OTLPTraceExporter({ url: `${endpoint}/v1/traces`, headers }),
1532
- () => activeRedactor
1691
+ () => redactor,
1692
+ queryPolicy
1533
1693
  )
1534
1694
  );
1535
1695
  const environment = options.environment ?? process.env.NODE_ENV ?? "production";
1536
1696
  if (isDebugEnabled() || options.debug === true) setDebugMode(true);
1537
1697
  debugLog(`initialising service=${options.service} endpoint=${endpoint}`);
1538
- activeRedactor = makeRedactor(options.redactAttributes ?? true);
1698
+ S.redactor = redactor;
1539
1699
  const distro = options.distro ?? SDK_IDENTITY;
1540
1700
  configureCompatCheck({ endpoint, enabled: options.compatCheck !== false, sdk: distro });
1541
1701
  const resource = new Resource2({
@@ -1549,8 +1709,14 @@ function initAutterServer(options) {
1549
1709
  "telemetry.distro.version": distro.version,
1550
1710
  // Tells the ingester request metrics arrive on the metrics pipe, so
1551
1711
  // it must not also fold our server spans into usage rollups (that
1552
- // would double-count every sampled request).
1553
- "autter.metrics_wired": true
1712
+ // would double-count every sampled request)...
1713
+ "autter.metrics_wired": true,
1714
+ // ...for the requests whose metric carries their route: those
1715
+ // request spans say so (`autter.metric_has_route`, see
1716
+ // captureRequestRoute). Ingesters honour metrics_wired only with
1717
+ // this, so an older SDK, whose Next.js metrics have no route, keeps
1718
+ // its span-fed per-route counts.
1719
+ "autter.metrics_routes": true
1554
1720
  });
1555
1721
  const rootSampler = new TraceIdRatioBasedSampler(options.traceSampleRate ?? 0.01);
1556
1722
  const headSampler = new ParentBasedSampler({
@@ -1567,6 +1733,7 @@ function initAutterServer(options) {
1567
1733
  const retainTraces = retainOnError || slowThresholdMs > 0;
1568
1734
  let sampler = retainTraces ? new RecordUnsampledSampler(headSampler) : headSampler;
1569
1735
  if (options.llmTracing !== false) sampler = new LlmAwareSampler(sampler);
1736
+ sampler = new RouteLabelSampler(sampler);
1570
1737
  const errorTraceBuffer = retainTraces ? new ErrorTraceRetentionProcessor(
1571
1738
  new BatchSpanProcessor(traceExporter(), { scheduledDelayMillis: 2e3 }),
1572
1739
  retainOnError,
@@ -1584,7 +1751,7 @@ function initAutterServer(options) {
1584
1751
  // 60 s export.
1585
1752
  temporalityPreference: AggregationTemporalityPreference.DELTA
1586
1753
  }),
1587
- () => activeRedactor
1754
+ () => redactor
1588
1755
  ),
1589
1756
  exportIntervalMillis: options.metricIntervalMs ?? 6e4
1590
1757
  });
@@ -1605,11 +1772,12 @@ function initAutterServer(options) {
1605
1772
  ) })
1606
1773
  ],
1607
1774
  instrumentations: [
1608
- new HttpInstrumentation({ responseHook: captureExpressRoute }),
1775
+ new HttpInstrumentation({ responseHook: captureRequestRoute }),
1609
1776
  ...options.instrumentations ?? []
1610
1777
  ]
1611
1778
  });
1612
1779
  sdk.start();
1780
+ patchLoadedHttpModules();
1613
1781
  const stopMemoryMetrics = options.memoryMetrics === false ? null : startMemoryMetrics();
1614
1782
  noteCompatFeature("endpoint_latency");
1615
1783
  if (stopMemoryMetrics) noteCompatFeature("memory_metrics");
@@ -1628,12 +1796,12 @@ function initAutterServer(options) {
1628
1796
  errorTraceBuffer?.retainActiveTrace();
1629
1797
  telemetryStats.markCaptured();
1630
1798
  const isErr = isError(error);
1631
- const message = activeRedactor.text(isErr ? error.message : String(error));
1632
- const stackText = isErr && error.stack ? activeRedactor.text(error.stack) : "";
1799
+ const message = redactor.text(isErr ? error.message : String(error));
1800
+ const stackText = isErr && error.stack ? redactor.text(error.stack) : "";
1633
1801
  const split = splitFingerprint(attributes);
1634
- const fingerprint = fingerprintEventAttributes(split.fingerprint, activeRedactor.text);
1802
+ const fingerprint = fingerprintEventAttributes(split.fingerprint, redactor.text);
1635
1803
  if (fingerprint["autter.fingerprint"]) noteCompatFeature("fingerprint_override");
1636
- const redacted = activeRedactor(split.attributes);
1804
+ const redacted = redactor(split.attributes);
1637
1805
  const activeSpan = trace2.getActiveSpan();
1638
1806
  const reuseActive = activeSpan?.isRecording() === true;
1639
1807
  const span = reuseActive ? activeSpan : errorTracer.startSpan(isErr ? error.name : "Error");
@@ -1660,17 +1828,17 @@ function initAutterServer(options) {
1660
1828
  "exception.stacktrace": stackText,
1661
1829
  ...redacted["autter.handled"] === true ? { "autter.handled": true, "autter.sampled": redacted["autter.sampled"] === true } : {},
1662
1830
  ...declared,
1663
- ...breadcrumbEventAttributes(activeRedactor.text, error)
1831
+ ...breadcrumbEventAttributes(redactor.text, error)
1664
1832
  });
1665
1833
  } else {
1666
1834
  const stack = new Error().stack?.split("\n").filter((line, i) => i === 0 || !line.includes("captureException")).join("\n");
1667
1835
  span.addEvent("exception", {
1668
1836
  "exception.type": isErr ? error.name : "Error",
1669
1837
  "exception.message": message,
1670
- ...stack ? { "exception.stacktrace": activeRedactor.text(stack) } : {},
1838
+ ...stack ? { "exception.stacktrace": redactor.text(stack) } : {},
1671
1839
  ...redacted["autter.handled"] === true ? { "autter.handled": true, "autter.sampled": redacted["autter.sampled"] === true } : {},
1672
1840
  ...declared,
1673
- ...breadcrumbEventAttributes(activeRedactor.text, error)
1841
+ ...breadcrumbEventAttributes(redactor.text, error)
1674
1842
  });
1675
1843
  }
1676
1844
  span.setStatus({ code: SpanStatusCode.ERROR, message });
@@ -1682,16 +1850,16 @@ function initAutterServer(options) {
1682
1850
  }
1683
1851
  telemetryStats.markCaptured();
1684
1852
  const stack = new Error().stack?.split("\n").filter((line, i) => i === 0 || !line.includes("captureMessage")).join("\n");
1685
- const safeMessage = activeRedactor.text(message);
1853
+ const safeMessage = redactor.text(message);
1686
1854
  const span = errorTracer.startSpan("Message", {
1687
- attributes: { "autter.severity": severity, ...activeRedactor(attributes) }
1855
+ attributes: { "autter.severity": severity, ...redactor(attributes) }
1688
1856
  });
1689
1857
  span.addEvent("exception", {
1690
1858
  "exception.type": "Message",
1691
1859
  "exception.message": safeMessage,
1692
- ...stack ? { "exception.stacktrace": activeRedactor.text(stack) } : {},
1860
+ ...stack ? { "exception.stacktrace": redactor.text(stack) } : {},
1693
1861
  "autter.severity": severity,
1694
- ...breadcrumbEventAttributes(activeRedactor.text)
1862
+ ...breadcrumbEventAttributes(redactor.text)
1695
1863
  });
1696
1864
  span.setStatus({ code: SpanStatusCode.ERROR, message: safeMessage });
1697
1865
  span.end();
@@ -1702,11 +1870,11 @@ function initAutterServer(options) {
1702
1870
  const activeSpan = trace2.getActiveSpan();
1703
1871
  const reuseActive = activeSpan?.isRecording() === true;
1704
1872
  const span = reuseActive ? activeSpan : errorTracer.startSpan("OutcomeFailure");
1705
- const safeOutcome = activeRedactor({ "autter.outcome.name": name.slice(0, 200), "autter.outcome.message": message.slice(0, 1e3) });
1873
+ const safeOutcome = redactor({ "autter.outcome.name": name.slice(0, 200), "autter.outcome.message": message.slice(0, 1e3) });
1706
1874
  const safeName = String(safeOutcome["autter.outcome.name"] ?? "operation");
1707
1875
  const safeMessage = String(safeOutcome["autter.outcome.message"] ?? "failed");
1708
1876
  span.addEvent("autter.outcome", {
1709
- ...activeRedactor(attributes),
1877
+ ...redactor(attributes),
1710
1878
  "autter.outcome.status": "error",
1711
1879
  "autter.outcome.name": safeName,
1712
1880
  "autter.outcome.message": safeMessage
@@ -1722,11 +1890,11 @@ function initAutterServer(options) {
1722
1890
  distro,
1723
1891
  release: options.release,
1724
1892
  options: options.logging,
1725
- redact: activeRedactor,
1726
- run: (name, fn, attributes, links) => runWithSpan(processTracer, name, fn, activeRedactor(attributes), links),
1893
+ redact: redactor,
1894
+ run: (name, fn, attributes, links) => runWithSpan(processTracer, name, fn, redactor(attributes), links),
1727
1895
  reportOutcome: reportOutcome2
1728
1896
  });
1729
- hookRequests = options.logging?.requests === true;
1897
+ S.hookRequests = options.logging?.requests === true;
1730
1898
  let onCrash = null;
1731
1899
  let uninstallRejections = null;
1732
1900
  if (options.captureGlobalErrors !== false) {
@@ -1769,17 +1937,20 @@ function initAutterServer(options) {
1769
1937
  captureException: captureException2,
1770
1938
  captureMessage: captureMessage2,
1771
1939
  reportOutcome: reportOutcome2,
1772
- withProcessSpan: (name, fn, attributes) => runWithSpan(processTracer, name, fn, activeRedactor(attributes)),
1940
+ withProcessSpan: (name, fn, attributes) => runWithSpan(processTracer, name, fn, redactor(attributes)),
1773
1941
  withLlmCall: (info, fn) => runLlmSpan(llmTracer, info, fn),
1774
1942
  trackLlmCall: (call) => recordLlmCall(llmTracer, call),
1775
1943
  shutdown: async () => {
1776
1944
  const logShutdown = await Promise.allSettled([shutdownRuntimeLogger()]);
1777
- hookRequests = false;
1778
1945
  if (onCrash) process.off("uncaughtExceptionMonitor", onCrash);
1779
1946
  uninstallRejections?.();
1780
1947
  stopMemoryMetrics?.();
1781
- active = null;
1782
- activeAlwaysOnProvider = null;
1948
+ if (S.active === server) {
1949
+ S.active = null;
1950
+ S.alwaysOnProvider = null;
1951
+ S.hookRequests = false;
1952
+ S.redactor = makeRedactor(true);
1953
+ }
1783
1954
  autoFlushHandle?.dispose();
1784
1955
  autoFlushHandle = null;
1785
1956
  unregisterFlushTargets();
@@ -1788,82 +1959,81 @@ function initAutterServer(options) {
1788
1959
  if (logShutdown[0]?.status === "rejected") throw logShutdown[0].reason;
1789
1960
  }
1790
1961
  };
1791
- active = server;
1792
- activeAlwaysOnProvider = alwaysOnProvider;
1962
+ S.active = server;
1963
+ S.alwaysOnProvider = alwaysOnProvider;
1793
1964
  return server;
1794
1965
  }
1795
1966
  function captureException(error, attributes) {
1796
- if (active) {
1797
- active.captureException(error, attributes);
1967
+ if (S.active) {
1968
+ S.active.captureException(error, attributes);
1798
1969
  return;
1799
1970
  }
1800
- if (loggerOnlyCapture) {
1801
- loggerOnlyCapture(error, attributes);
1971
+ if (S.loggerOnlyCapture) {
1972
+ S.loggerOnlyCapture(error, attributes);
1802
1973
  return;
1803
1974
  }
1804
1975
  markCaptured(error);
1805
1976
  noteOperationError(error);
1806
1977
  const split = splitFingerprint(attributes);
1807
- const fingerprint = fingerprintEventAttributes(split.fingerprint, activeRedactor.text);
1978
+ const fingerprint = fingerprintEventAttributes(split.fingerprint, S.redactor.text);
1808
1979
  notifyException(error, { ...split.attributes, ...fingerprint });
1809
1980
  const span = trace2.getTracer("autter-errors").startSpan(isError(error) ? error.name : "Error", {
1810
- attributes: { ...activeRedactor(split.attributes), ...fingerprint }
1981
+ attributes: { ...S.redactor(split.attributes), ...fingerprint }
1811
1982
  });
1812
1983
  recordError(span, error);
1813
1984
  span.end();
1814
1985
  }
1815
- var loggerOnlyCapture = null;
1816
1986
  function setLoggerOnlyCapture(fn) {
1817
- loggerOnlyCapture = fn;
1987
+ S.loggerOnlyCapture = fn;
1818
1988
  }
1819
1989
  function serverActive() {
1820
- return active !== null;
1990
+ return S.active !== null;
1821
1991
  }
1822
1992
  setRequestExceptionCapturer((error, attributes) => captureException(error, attributes));
1823
1993
  setRequestSpanAnnotator((attributes) => {
1824
1994
  const span = trace2.getActiveSpan();
1825
- if (span?.isRecording()) span.setAttributes(activeRedactor(splitFingerprint(attributes).attributes));
1995
+ if (span?.isRecording()) span.setAttributes(S.redactor(splitFingerprint(attributes).attributes));
1826
1996
  });
1827
1997
  function withProcessSpan(name, fn, attributes) {
1828
- if (active) return active.withProcessSpan(name, fn, attributes);
1998
+ if (S.active) return S.active.withProcessSpan(name, fn, attributes);
1829
1999
  return runWithSpan(
1830
2000
  trace2.getTracer("autter-processes"),
1831
2001
  name,
1832
2002
  fn,
1833
- activeRedactor(attributes)
2003
+ S.redactor(attributes)
1834
2004
  );
1835
2005
  }
1836
2006
  function withLlmCall(info, fn) {
1837
- if (active) return active.withLlmCall(info, fn);
2007
+ if (S.active) return S.active.withLlmCall(info, fn);
1838
2008
  return runLlmSpan(trace2.getTracer("autter-llm"), info, fn);
1839
2009
  }
1840
2010
  function trackLlmCall(call) {
1841
- if (active) {
1842
- active.trackLlmCall(call);
2011
+ if (S.active) {
2012
+ S.active.trackLlmCall(call);
1843
2013
  return;
1844
2014
  }
1845
2015
  recordLlmCall(trace2.getTracer("autter-llm"), call);
1846
2016
  }
1847
2017
  function captureMessage(message, severity = "warning", attributes) {
1848
- if (active) {
1849
- active.captureMessage(message, severity, attributes);
2018
+ if (S.active) {
2019
+ S.active.captureMessage(message, severity, attributes);
1850
2020
  return;
1851
2021
  }
1852
- const safeMessage = activeRedactor.text(message);
2022
+ const safeMessage = S.redactor.text(message);
1853
2023
  const span = trace2.getTracer("autter-errors").startSpan("Message", {
1854
- attributes: { "autter.severity": severity, ...activeRedactor(attributes) }
2024
+ attributes: { "autter.severity": severity, ...S.redactor(attributes) }
1855
2025
  });
1856
2026
  span.addEvent("exception", {
1857
2027
  "exception.type": "Message",
1858
2028
  "exception.message": safeMessage,
1859
2029
  "autter.severity": severity,
1860
- ...breadcrumbEventAttributes(activeRedactor.text)
2030
+ ...breadcrumbEventAttributes(S.redactor.text)
1861
2031
  });
1862
2032
  span.setStatus({ code: SpanStatusCode.ERROR, message: safeMessage });
1863
2033
  span.end();
1864
2034
  }
1865
2035
  function reportOutcome(name, message, attributes) {
1866
- if (active) return active.reportOutcome(name, message, attributes);
2036
+ if (S.active) return S.active.reportOutcome(name, message, attributes);
1867
2037
  const span = trace2.getTracer("autter-outcomes").startSpan("OutcomeFailure");
1868
2038
  const safeOutcome = redactAttributes({ "autter.outcome.name": name.slice(0, 200), "autter.outcome.message": message.slice(0, 1e3) });
1869
2039
  const safeName = String(safeOutcome["autter.outcome.name"] ?? "operation");
@@ -1885,13 +2055,13 @@ function makeSafeCapture(options) {
1885
2055
  };
1886
2056
  }
1887
2057
  function alwaysOnTracer(name) {
1888
- return activeAlwaysOnProvider ? activeAlwaysOnProvider.getTracer(name) : trace2.getTracer(name);
2058
+ return S.alwaysOnProvider ? S.alwaysOnProvider.getTracer(name) : trace2.getTracer(name);
1889
2059
  }
1890
2060
  function autterLlmTracer() {
1891
2061
  return alwaysOnTracer("autter-llm");
1892
2062
  }
1893
2063
  async function emitLlmSelftestTrace() {
1894
- const provider = activeAlwaysOnProvider;
2064
+ const provider = S.alwaysOnProvider;
1895
2065
  if (!provider) {
1896
2066
  throw new Error(
1897
2067
  "emitLlmSelftestTrace() requires initAutterServer() to have been called"
@@ -2149,14 +2319,14 @@ function startCaughtExceptionSampler(options = {}) {
2149
2319
  const rate = Math.max(0, Math.min(1, options.sampleRate ?? 0.01));
2150
2320
  const cap = Math.max(1, Math.min(100, options.maxPerMinute ?? 10));
2151
2321
  const session = new inspector.Session();
2152
- let active2 = true;
2322
+ let active = true;
2153
2323
  let minute = 0;
2154
2324
  let sent = 0;
2155
2325
  session.connect();
2156
2326
  session.on("Debugger.paused", (message) => {
2157
2327
  session.post("Debugger.resume", () => {
2158
2328
  });
2159
- if (!active2 || message.params.reason !== "exception") return;
2329
+ if (!active || message.params.reason !== "exception") return;
2160
2330
  const nowMinute = Math.floor(Date.now() / 6e4);
2161
2331
  if (nowMinute !== minute) {
2162
2332
  minute = nowMinute;
@@ -2171,16 +2341,16 @@ function startCaughtExceptionSampler(options = {}) {
2171
2341
  error.stack = `${error.name}: ${error.message}
2172
2342
  ${frames.join("\n")}`;
2173
2343
  queueMicrotask(() => {
2174
- if (active2) captureException(error, { "autter.handled": true, "autter.sampled": true });
2344
+ if (active) captureException(error, { "autter.handled": true, "autter.sampled": true });
2175
2345
  });
2176
2346
  });
2177
2347
  session.post("Debugger.enable", (error) => {
2178
- if (!error && active2) session.post("Debugger.setPauseOnExceptions", { state: "all" }, () => {
2348
+ if (!error && active) session.post("Debugger.setPauseOnExceptions", { state: "all" }, () => {
2179
2349
  });
2180
2350
  });
2181
2351
  return { stop() {
2182
- if (!active2) return;
2183
- active2 = false;
2352
+ if (!active) return;
2353
+ active = false;
2184
2354
  session.post("Debugger.setPauseOnExceptions", { state: "none" }, () => {
2185
2355
  session.post("Debugger.disable", () => session.disconnect());
2186
2356
  });
@@ -2228,9 +2398,9 @@ import {
2228
2398
  SpanStatusCode as SpanStatusCode3,
2229
2399
  trace as trace3
2230
2400
  } from "@opentelemetry/api";
2231
- var activeLogging = null;
2401
+ var L = sharedSlot("logging", () => ({ active: null }));
2232
2402
  function initAutterLogging(options) {
2233
- if (activeLogging) return activeLogging;
2403
+ if (L.active) return L.active;
2234
2404
  if (serverActive()) {
2235
2405
  console.warn(
2236
2406
  "[autter-runtime] initAutterLogging() ignored: initAutterServer() is already active and includes logging"
@@ -2332,11 +2502,11 @@ function initAutterLogging(options) {
2332
2502
  autoFlushHandle?.dispose();
2333
2503
  autoFlushHandle = null;
2334
2504
  unregisterFlushTargets();
2335
- activeLogging = null;
2505
+ if (L.active === handle) L.active = null;
2336
2506
  await shutdownRuntimeLogger();
2337
2507
  }
2338
2508
  };
2339
- activeLogging = handle;
2509
+ L.active = handle;
2340
2510
  return handle;
2341
2511
  }
2342
2512