@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/README.md +58 -5
- package/dist/{chunk-ARN26OK6.cjs → chunk-GYTQLITW.cjs} +126 -39
- package/dist/{chunk-UGNPCOIW.cjs → chunk-HKVFURYT.cjs} +11 -11
- package/dist/{chunk-FBKYOHU2.js → chunk-KA4AOZX2.js} +41 -31
- package/dist/{chunk-OY2HTUWY.js → chunk-LG52CW3V.js} +118 -31
- package/dist/{chunk-Y4T7SQMO.js → chunk-QUC6OUE7.js} +1 -1
- package/dist/{chunk-UKYTXZOQ.cjs → chunk-RV4MJNM2.cjs} +65 -55
- package/dist/cli.cjs +8 -8
- package/dist/cli.js +2 -2
- package/dist/index.cjs +465 -295
- package/dist/index.d.cts +15 -2
- package/dist/index.d.ts +15 -2
- package/dist/index.js +284 -114
- package/dist/testing.cjs +8 -8
- package/dist/testing.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -184,6 +184,7 @@ Defaults (cheap by construction):
|
|
|
184
184
|
| Request metrics | exported every 60 s |
|
|
185
185
|
| Logs | not collected |
|
|
186
186
|
| PII in custom attributes | redacted before export (`redactAttributes`) |
|
|
187
|
+
| Query strings in span URLs | parameter names only (`keepQueryParams`) |
|
|
187
188
|
| Forgotten `shutdown()` | exporters still flushed on exit (`autoFlush`) |
|
|
188
189
|
|
|
189
190
|
**Error-linked trace retention.** Errors export at 100% while traces are
|
|
@@ -316,11 +317,21 @@ silent loss. While wiring things up, set `debug: true` (or `AUTTER_DEBUG=1`)
|
|
|
316
317
|
to see `[autter] exported N span(s)` lines on stderr as batches leave.
|
|
317
318
|
|
|
318
319
|
Note on usage rollups: requests are counted from the `http.server.duration`
|
|
319
|
-
metric (100% accurate)
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
`autter.
|
|
320
|
+
metric (100% accurate), under their route template: Express routes and,
|
|
321
|
+
from 1.7.0, Next.js routes (`/blog/[slug]`, from the `next.route` Next puts
|
|
322
|
+
on its own spans). The SDK marks its resource `autter.metrics_wired` and
|
|
323
|
+
`autter.metrics_routes`, and each request span whose metric got a route
|
|
324
|
+
`autter.metric_has_route`; an up-to-date ingester then counts that request
|
|
325
|
+
from the metric alone, whatever `traceSampleRate` is. A request whose
|
|
326
|
+
metric has no route (Koa, Fastify or plain `http` without a router
|
|
327
|
+
instrumentation) is also counted from its sampled spans, which keep its
|
|
328
|
+
path. Ingesters up to 1.7.0 count every sampled server span on top of the
|
|
329
|
+
metric (at `traceSampleRate: 1`, about twice the requests), except
|
|
330
|
+
tail-retained error traces (`autter.tail_retained`). Since 1.7.0 the SDK
|
|
331
|
+
also applies the HTTP instrumentation to servers created before
|
|
332
|
+
`initAutterServer` (Next.js creates its server before `instrumentation.ts`
|
|
333
|
+
runs): until then a Next.js app got request spans and metrics only from
|
|
334
|
+
the first request that happened to load `http` again, if ever.
|
|
324
335
|
|
|
325
336
|
### Privacy: secrets and PII are scrubbed before they leave the process
|
|
326
337
|
|
|
@@ -383,6 +394,48 @@ This closes the server-side gap to match the browser relay's payload
|
|
|
383
394
|
whitelist; it is best-effort scrubbing of obvious PII shapes, not a DLP
|
|
384
395
|
engine — keep secrets out of attributes in the first place.
|
|
385
396
|
|
|
397
|
+
**Query strings (1.6.1+).** Query strings carry what a path never should:
|
|
398
|
+
emails, one-time codes, API keys, session ids, search terms. So no span
|
|
399
|
+
leaves the process with query values, whichever instrumentation made it
|
|
400
|
+
(HTTP server and client spans, undici/fetch, Express, Fastify, Next.js's own
|
|
401
|
+
spans):
|
|
402
|
+
|
|
403
|
+
- `http.target`, `http.url`, `url.full` and `url.original` are cut at the `?`
|
|
404
|
+
(and `#`): `/search?email=jane@example.com&page=2` becomes `/search`;
|
|
405
|
+
- `url.query` keeps only the parameter names, `email=[redacted]&page=[redacted]`.
|
|
406
|
+
It is added from the URL when the instrumentation did not set it, so you
|
|
407
|
+
still see which parameters a request used. A parameter without `=`
|
|
408
|
+
(`/verify?<token>`) may itself be the secret and is masked whole;
|
|
409
|
+
- span names lose their query too (Next.js names a request span
|
|
410
|
+
`GET /search?q=…` until it knows the route).
|
|
411
|
+
|
|
412
|
+
List the parameters whose values are safe to keep, such as pagination or
|
|
413
|
+
sorting (exact names, after percent-decoding; kept values are still scrubbed
|
|
414
|
+
by `redactAttributes`):
|
|
415
|
+
|
|
416
|
+
```ts
|
|
417
|
+
initAutterServer({ ..., keepQueryParams: ["page", "sort"] });
|
|
418
|
+
// url.query: "email=[redacted]&page=2"
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
This runs at export, like the scrubbing above, and also with
|
|
422
|
+
`redactAttributes: false`. Route templates (`http.route`) and request
|
|
423
|
+
summaries never contained a query.
|
|
424
|
+
|
|
425
|
+
### One SDK per process, however many copies are loaded
|
|
426
|
+
|
|
427
|
+
Bundlers can load this package more than once: Next.js bundles one copy
|
|
428
|
+
into `instrumentation.ts` and another into each route (unless it is in
|
|
429
|
+
`serverExternalPackages`), and a process can load both the ESM and the CJS
|
|
430
|
+
build. Since 1.6.1 every copy shares one process-wide state
|
|
431
|
+
(`globalThis[Symbol.for("@autter/runtime-node")]`): the copy that ran
|
|
432
|
+
`initAutterServer` (or `initAutterLogging`) is the SDK for all of them, so
|
|
433
|
+
`withRuntimeRequest`, `captureException`, `runtimeLogger` and the rest export
|
|
434
|
+
through it from any copy, and an error one copy reported is never reported
|
|
435
|
+
again by another. Before any copy starts, and after `shutdown()`, every copy
|
|
436
|
+
behaves as before: records go to the console. The shared object is
|
|
437
|
+
versioned; a copy that finds a layout it does not know keeps its own state.
|
|
438
|
+
|
|
386
439
|
## 3. LLM tracing
|
|
387
440
|
|
|
388
441
|
For the field contract, manual reporting patterns, and provider-agnostic
|
|
@@ -119,6 +119,43 @@ var compat_manifest_default = {
|
|
|
119
119
|
ingester: "1.6.0",
|
|
120
120
|
migrations: [],
|
|
121
121
|
sdks: { "@autter/runtime-browser": "1.6.0", "@autter/runtime-next": "1.6.0", "@autter/runtime-node": "1.6.0" }
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: "sourcemap_upload_keys",
|
|
125
|
+
title: "Upload-only keys for source maps (autter_rtu_)",
|
|
126
|
+
ingester: "1.7.0",
|
|
127
|
+
migrations: [],
|
|
128
|
+
route: "/v1/sourcemaps",
|
|
129
|
+
sdks: {}
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
id: "source_map_verify",
|
|
133
|
+
title: "Source map check by debug id (autter-runtime sourcemaps verify)",
|
|
134
|
+
ingester: "1.7.0",
|
|
135
|
+
migrations: ["0017-source-maps-debug-id"],
|
|
136
|
+
route: "/v1/sourcemaps/check",
|
|
137
|
+
sdks: {}
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: "ingest_drop_counts",
|
|
141
|
+
title: "Rejected and dropped records per repository",
|
|
142
|
+
ingester: "1.8.0",
|
|
143
|
+
migrations: ["0018-runtime-ingest-drops"],
|
|
144
|
+
sdks: {}
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: "telemetry_sender_via",
|
|
148
|
+
title: "How each service's telemetry arrives (SDK, OpenTelemetry Collector or browser relay)",
|
|
149
|
+
ingester: "1.8.0",
|
|
150
|
+
migrations: ["0019-sdk-versions-via"],
|
|
151
|
+
sdks: {}
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: "frame_resolution_counts",
|
|
155
|
+
title: "Resolved and unresolved browser frames per occurrence",
|
|
156
|
+
ingester: "1.8.0",
|
|
157
|
+
migrations: ["0020-occurrence-frame-resolution"],
|
|
158
|
+
sdks: {}
|
|
122
159
|
}
|
|
123
160
|
]
|
|
124
161
|
};
|
|
@@ -464,7 +501,7 @@ var _https = require('https');
|
|
|
464
501
|
// package.json
|
|
465
502
|
var package_default = {
|
|
466
503
|
name: "@autter/runtime-node",
|
|
467
|
-
version: "1.
|
|
504
|
+
version: "1.7.0",
|
|
468
505
|
description: "Autter Runtime for Node.js: request wide events, coded errors, operation logging, same-origin browser relay and a curated OpenTelemetry server tracker",
|
|
469
506
|
license: "MIT",
|
|
470
507
|
type: "module",
|
|
@@ -539,6 +576,55 @@ var package_default = {
|
|
|
539
576
|
]
|
|
540
577
|
};
|
|
541
578
|
|
|
579
|
+
// src/shared.ts
|
|
580
|
+
var SHARED_STATE_KEY = /* @__PURE__ */ Symbol.for("@autter/runtime-node");
|
|
581
|
+
var SHARED_STATE_LAYOUT = 1;
|
|
582
|
+
function isObject(value) {
|
|
583
|
+
return typeof value === "object" && value !== null;
|
|
584
|
+
}
|
|
585
|
+
var VERSION = package_default.version;
|
|
586
|
+
function resolveShared() {
|
|
587
|
+
const fresh = { v: SHARED_STATE_LAYOUT, sdk: VERSION };
|
|
588
|
+
const host = globalThis;
|
|
589
|
+
let existing;
|
|
590
|
+
try {
|
|
591
|
+
existing = host[SHARED_STATE_KEY];
|
|
592
|
+
if (existing === void 0) {
|
|
593
|
+
Object.defineProperty(host, SHARED_STATE_KEY, {
|
|
594
|
+
value: fresh,
|
|
595
|
+
configurable: true,
|
|
596
|
+
enumerable: false,
|
|
597
|
+
writable: false
|
|
598
|
+
});
|
|
599
|
+
return fresh;
|
|
600
|
+
}
|
|
601
|
+
} catch (e) {
|
|
602
|
+
return fresh;
|
|
603
|
+
}
|
|
604
|
+
if (isObject(existing) && existing.v === SHARED_STATE_LAYOUT) return existing;
|
|
605
|
+
debugLog(
|
|
606
|
+
`another copy of @autter/runtime-node uses shared state layout ${String(isObject(existing) ? existing.v : typeof existing)}; this copy (${VERSION}, layout ${SHARED_STATE_LAYOUT}) keeps its own state`
|
|
607
|
+
);
|
|
608
|
+
return fresh;
|
|
609
|
+
}
|
|
610
|
+
var shared = resolveShared();
|
|
611
|
+
function sharedSlot(name, init) {
|
|
612
|
+
const fresh = init();
|
|
613
|
+
try {
|
|
614
|
+
const existing = shared[name];
|
|
615
|
+
if (!isObject(existing)) {
|
|
616
|
+
shared[name] = fresh;
|
|
617
|
+
return fresh;
|
|
618
|
+
}
|
|
619
|
+
for (const [key, value] of Object.entries(fresh)) {
|
|
620
|
+
if (existing[key] === void 0) existing[key] = value;
|
|
621
|
+
}
|
|
622
|
+
return existing;
|
|
623
|
+
} catch (e2) {
|
|
624
|
+
return fresh;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
542
628
|
// src/compat.ts
|
|
543
629
|
var SDK_IDENTITY = {
|
|
544
630
|
name: "@autter/runtime-node",
|
|
@@ -587,7 +673,7 @@ function compatRequest(method, url, options = {}) {
|
|
|
587
673
|
if (options.unref !== false) req.on("socket", (socket) => socket.unref());
|
|
588
674
|
req.on("error", () => done(null));
|
|
589
675
|
req.end(options.body);
|
|
590
|
-
} catch (
|
|
676
|
+
} catch (e3) {
|
|
591
677
|
done(null);
|
|
592
678
|
}
|
|
593
679
|
});
|
|
@@ -600,7 +686,7 @@ async function fetchIngesterCompat(endpoint, options = {}) {
|
|
|
600
686
|
let report = null;
|
|
601
687
|
try {
|
|
602
688
|
report = JSON.parse(res.body);
|
|
603
|
-
} catch (
|
|
689
|
+
} catch (e4) {
|
|
604
690
|
}
|
|
605
691
|
const info = _nullishCoalesce(ingesterInfoFromReport(report), () => ( headerInfo(res.headers)));
|
|
606
692
|
return info ? { info, report } : { info: null, report, error: "unrecognised /v1/compat response" };
|
|
@@ -631,7 +717,7 @@ function envDisabled() {
|
|
|
631
717
|
return value === "0" || value === "false" || value === "off";
|
|
632
718
|
}
|
|
633
719
|
var defaultWarn = (message) => console.warn(`[autter-runtime] ${message}`);
|
|
634
|
-
var
|
|
720
|
+
var compat = sharedSlot("compat", () => ({ state: freshState(null, false) }));
|
|
635
721
|
function freshState(endpoint, enabled, sdk = SDK_IDENTITY, warn = defaultWarn) {
|
|
636
722
|
return {
|
|
637
723
|
endpoint,
|
|
@@ -646,7 +732,7 @@ function freshState(endpoint, enabled, sdk = SDK_IDENTITY, warn = defaultWarn) {
|
|
|
646
732
|
};
|
|
647
733
|
}
|
|
648
734
|
function configureCompatCheck(options) {
|
|
649
|
-
state = freshState(
|
|
735
|
+
compat.state = freshState(
|
|
650
736
|
options.endpoint.replace(/\/$/, ""),
|
|
651
737
|
options.enabled !== false && !envDisabled(),
|
|
652
738
|
_nullishCoalesce(options.sdk, () => ( SDK_IDENTITY)),
|
|
@@ -654,23 +740,23 @@ function configureCompatCheck(options) {
|
|
|
654
740
|
);
|
|
655
741
|
}
|
|
656
742
|
function ensureCompatConfigured(endpoint) {
|
|
657
|
-
if (state.endpoint === null && !envDisabled()) {
|
|
658
|
-
state = freshState(endpoint.replace(/\/$/, ""), true);
|
|
743
|
+
if (compat.state.endpoint === null && !envDisabled()) {
|
|
744
|
+
compat.state = freshState(endpoint.replace(/\/$/, ""), true);
|
|
659
745
|
}
|
|
660
746
|
}
|
|
661
747
|
function noteCompatFeature(id) {
|
|
662
748
|
try {
|
|
663
|
-
if (!state.enabled || state.features.has(id) || !featureById(id)) return;
|
|
664
|
-
state.features.add(id);
|
|
665
|
-
if (state.info) {
|
|
749
|
+
if (!compat.state.enabled || compat.state.features.has(id) || !featureById(id)) return;
|
|
750
|
+
compat.state.features.add(id);
|
|
751
|
+
if (compat.state.info) {
|
|
666
752
|
evaluateAndWarn();
|
|
667
753
|
return;
|
|
668
754
|
}
|
|
669
|
-
if (state.started) return;
|
|
670
|
-
state.started = true;
|
|
671
|
-
const current = state;
|
|
755
|
+
if (compat.state.started) return;
|
|
756
|
+
compat.state.started = true;
|
|
757
|
+
const current = compat.state;
|
|
672
758
|
setTimeout(() => void runCheck(current), 0).unref();
|
|
673
|
-
} catch (
|
|
759
|
+
} catch (e5) {
|
|
674
760
|
}
|
|
675
761
|
}
|
|
676
762
|
async function runCheck(current) {
|
|
@@ -679,7 +765,7 @@ async function runCheck(current) {
|
|
|
679
765
|
const { info, error } = await fetchIngesterCompat(current.endpoint, {
|
|
680
766
|
probeRoutes: routesFor(current.features)
|
|
681
767
|
});
|
|
682
|
-
if (state !== current) return;
|
|
768
|
+
if (compat.state !== current) return;
|
|
683
769
|
if (!info) {
|
|
684
770
|
debugLog(`compat check skipped: ${_nullishCoalesce(error, () => ( "ingester version unknown"))}`);
|
|
685
771
|
return;
|
|
@@ -703,8 +789,8 @@ function routesFor(features) {
|
|
|
703
789
|
return routes;
|
|
704
790
|
}
|
|
705
791
|
function mergeInfo(next) {
|
|
706
|
-
const prev = state.info;
|
|
707
|
-
state.info = {
|
|
792
|
+
const prev = compat.state.info;
|
|
793
|
+
compat.state.info = {
|
|
708
794
|
version: _nullishCoalesce(_nullishCoalesce(next.version, () => ( _optionalChain([prev, 'optionalAccess', _21 => _21.version]))), () => ( null)),
|
|
709
795
|
...next.legacy || _optionalChain([prev, 'optionalAccess', _22 => _22.legacy]) ? { legacy: true } : {},
|
|
710
796
|
..._nullishCoalesce(next.schema, () => ( _optionalChain([prev, 'optionalAccess', _23 => _23.schema]))) ? { schema: _nullishCoalesce(next.schema, () => ( _optionalChain([prev, 'optionalAccess', _24 => _24.schema]))) } : {},
|
|
@@ -712,17 +798,17 @@ function mergeInfo(next) {
|
|
|
712
798
|
};
|
|
713
799
|
}
|
|
714
800
|
function evaluateAndWarn() {
|
|
715
|
-
const info = state.info;
|
|
801
|
+
const info = compat.state.info;
|
|
716
802
|
if (!info) return;
|
|
717
|
-
if (info.version === null && state.endpoint) {
|
|
718
|
-
const unprobed = routesFor(state.features).filter(
|
|
719
|
-
(route) => _optionalChain([info, 'access', _26 => _26.routes, 'optionalAccess', _27 => _27[route]]) === void 0 && !state.pendingProbes.has(route)
|
|
803
|
+
if (info.version === null && compat.state.endpoint) {
|
|
804
|
+
const unprobed = routesFor(compat.state.features).filter(
|
|
805
|
+
(route) => _optionalChain([info, 'access', _26 => _26.routes, 'optionalAccess', _27 => _27[route]]) === void 0 && !compat.state.pendingProbes.has(route)
|
|
720
806
|
);
|
|
721
807
|
if (unprobed.length) {
|
|
722
|
-
const current = state;
|
|
808
|
+
const current = compat.state;
|
|
723
809
|
for (const route of unprobed) current.pendingProbes.add(route);
|
|
724
810
|
void Promise.all(unprobed.map((route) => probeRoute(current.endpoint, route, {}).then((exists) => [route, exists]))).then((results) => {
|
|
725
|
-
if (state !== current) return;
|
|
811
|
+
if (compat.state !== current) return;
|
|
726
812
|
const routes = {};
|
|
727
813
|
for (const [route, exists] of results) if (exists !== null) routes[route] = exists;
|
|
728
814
|
mergeInfo({ version: null, legacy: true, routes });
|
|
@@ -731,56 +817,56 @@ function evaluateAndWarn() {
|
|
|
731
817
|
});
|
|
732
818
|
}
|
|
733
819
|
}
|
|
734
|
-
for (const issue of evaluateCompat({ features: state.features, ingester: info })) {
|
|
820
|
+
for (const issue of evaluateCompat({ features: compat.state.features, ingester: info })) {
|
|
735
821
|
warnOnce(issue);
|
|
736
822
|
}
|
|
737
823
|
}
|
|
738
824
|
function warnOnce(issue) {
|
|
739
825
|
const key = `${issue.feature}:${issue.kind}`;
|
|
740
|
-
if (state.warned.has(key)) return;
|
|
741
|
-
state.warned.add(key);
|
|
826
|
+
if (compat.state.warned.has(key)) return;
|
|
827
|
+
compat.state.warned.add(key);
|
|
742
828
|
try {
|
|
743
|
-
state.warn(issue.message);
|
|
744
|
-
} catch (
|
|
829
|
+
compat.state.warn(issue.message);
|
|
830
|
+
} catch (e6) {
|
|
745
831
|
}
|
|
746
832
|
}
|
|
747
833
|
function observeIngesterResponse(response) {
|
|
748
834
|
try {
|
|
749
|
-
if (!state.enabled) return;
|
|
835
|
+
if (!compat.state.enabled) return;
|
|
750
836
|
const fromHeader = headerInfo(response.headers);
|
|
751
837
|
if (fromHeader) {
|
|
752
|
-
if (_optionalChain([
|
|
838
|
+
if (_optionalChain([compat, 'access', _28 => _28.state, 'access', _29 => _29.info, 'optionalAccess', _30 => _30.version]) !== fromHeader.version) {
|
|
753
839
|
mergeInfo(fromHeader);
|
|
754
840
|
evaluateAndWarn();
|
|
755
841
|
}
|
|
756
842
|
return;
|
|
757
843
|
}
|
|
758
|
-
if (response.status === 404 && response.route && !_optionalChain([state, 'access',
|
|
844
|
+
if (response.status === 404 && response.route && !_optionalChain([compat, 'access', _31 => _31.state, 'access', _32 => _32.info, 'optionalAccess', _33 => _33.version])) {
|
|
759
845
|
mergeInfo({ version: null, legacy: true, routes: { [response.route]: false } });
|
|
760
846
|
evaluateAndWarn();
|
|
761
847
|
}
|
|
762
|
-
} catch (
|
|
848
|
+
} catch (e7) {
|
|
763
849
|
}
|
|
764
850
|
}
|
|
765
851
|
function noteRelayRejection(eventTypes, status, headers) {
|
|
766
852
|
try {
|
|
767
|
-
if (!state.enabled || status !== 400 || headerInfo(headers)) return [];
|
|
853
|
+
if (!compat.state.enabled || status !== 400 || headerInfo(headers)) return [];
|
|
768
854
|
const named = [];
|
|
769
855
|
for (const id of featuresForBrowserEvents(eventTypes)) {
|
|
770
856
|
const feature = featureById(id);
|
|
771
857
|
const key = `${id}:relay_rejected`;
|
|
772
|
-
if (state.warned.has(key)) continue;
|
|
773
|
-
state.warned.add(key);
|
|
858
|
+
if (compat.state.warned.has(key)) continue;
|
|
859
|
+
compat.state.warned.add(key);
|
|
774
860
|
named.push(id);
|
|
775
861
|
try {
|
|
776
|
-
state.warn(
|
|
862
|
+
compat.state.warn(
|
|
777
863
|
`The ingester rejected a browser batch containing ${feature.title.toLowerCase()} events. ${feature.title} needs ingester >= ${feature.ingester}; yours is 1.4.0 or older (it predates version reporting), so those batches are dropped. ${ingesterUpgradeHint(COMPAT_MANIFEST)}`
|
|
778
864
|
);
|
|
779
|
-
} catch (
|
|
865
|
+
} catch (e8) {
|
|
780
866
|
}
|
|
781
867
|
}
|
|
782
868
|
return named;
|
|
783
|
-
} catch (
|
|
869
|
+
} catch (e9) {
|
|
784
870
|
return [];
|
|
785
871
|
}
|
|
786
872
|
}
|
|
@@ -807,4 +893,5 @@ function noteRelayRejection(eventTypes, status, headers) {
|
|
|
807
893
|
|
|
808
894
|
|
|
809
895
|
|
|
810
|
-
|
|
896
|
+
|
|
897
|
+
exports.COMPAT_MANIFEST = COMPAT_MANIFEST; exports.INGESTER_VERSION_HEADER = INGESTER_VERSION_HEADER; exports.versionAtLeast = versionAtLeast; exports.featuresForBrowserEvents = featuresForBrowserEvents; exports.evaluateCompat = evaluateCompat; exports.setDebugMode = setDebugMode; exports.isDebugEnabled = isDebugEnabled; exports.debugLog = debugLog; exports.telemetryStats = telemetryStats; exports.CountingExporter = CountingExporter; exports.registerFlushTarget = registerFlushTarget; exports.unregisterFlushTargets = unregisterFlushTargets; exports.installAutterAutoFlush = installAutterAutoFlush; exports.sharedSlot = sharedSlot; exports.SDK_IDENTITY = SDK_IDENTITY; exports.compatRequest = compatRequest; exports.fetchIngesterCompat = fetchIngesterCompat; exports.configureCompatCheck = configureCompatCheck; exports.ensureCompatConfigured = ensureCompatConfigured; exports.noteCompatFeature = noteCompatFeature; exports.observeIngesterResponse = observeIngesterResponse; exports.noteRelayRejection = noteRelayRejection;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
var
|
|
8
|
+
var _chunkGYTQLITWcjs = require('./chunk-GYTQLITW.cjs');
|
|
9
9
|
|
|
10
10
|
// src/doctor.ts
|
|
11
11
|
var _fs = require('fs');
|
|
@@ -30,19 +30,19 @@ function installedSdks(cwd) {
|
|
|
30
30
|
}
|
|
31
31
|
if (_path.dirname.call(void 0, dir) === dir) break;
|
|
32
32
|
}
|
|
33
|
-
if (!found.has(
|
|
33
|
+
if (!found.has(_chunkGYTQLITWcjs.SDK_IDENTITY.name)) found.set(_chunkGYTQLITWcjs.SDK_IDENTITY.name, _chunkGYTQLITWcjs.SDK_IDENTITY.version);
|
|
34
34
|
return [...found].map(([name, version]) => ({ name, version }));
|
|
35
35
|
}
|
|
36
36
|
function featuresInUse(sdks) {
|
|
37
|
-
return
|
|
37
|
+
return _chunkGYTQLITWcjs.COMPAT_MANIFEST.features.filter(
|
|
38
38
|
(feature) => sdks.some((sdk) => {
|
|
39
39
|
const min = feature.sdks[sdk.name];
|
|
40
|
-
return min !== void 0 &&
|
|
40
|
+
return min !== void 0 && _chunkGYTQLITWcjs.versionAtLeast.call(void 0, sdk.version, min) === true;
|
|
41
41
|
})
|
|
42
42
|
).map((feature) => feature.id);
|
|
43
43
|
}
|
|
44
44
|
async function checkKey(endpoint, key, timeoutMs) {
|
|
45
|
-
const res = await
|
|
45
|
+
const res = await _chunkGYTQLITWcjs.compatRequest.call(void 0, "POST", `${endpoint}/v1/traces`, {
|
|
46
46
|
body: JSON.stringify({ resourceSpans: [] }),
|
|
47
47
|
headers: { authorization: `Bearer ${key}` },
|
|
48
48
|
unref: false,
|
|
@@ -62,10 +62,10 @@ async function runDoctor(options = {}) {
|
|
|
62
62
|
const features = _optionalChain([options, 'access', _ => _.features, 'optionalAccess', _2 => _2.length]) ? options.features : featuresInUse(sdks);
|
|
63
63
|
const routes = [
|
|
64
64
|
...new Set(
|
|
65
|
-
features.map((id) => _optionalChain([
|
|
65
|
+
features.map((id) => _optionalChain([_chunkGYTQLITWcjs.COMPAT_MANIFEST, 'access', _3 => _3.features, 'access', _4 => _4.find, 'call', _5 => _5((f) => f.id === id), 'optionalAccess', _6 => _6.route])).filter((route) => Boolean(route))
|
|
66
66
|
)
|
|
67
67
|
];
|
|
68
|
-
const { info, error } = await
|
|
68
|
+
const { info, error } = await _chunkGYTQLITWcjs.fetchIngesterCompat.call(void 0, endpoint, { probeRoutes: routes, unref: false, timeoutMs });
|
|
69
69
|
const issues = [];
|
|
70
70
|
const seen = /* @__PURE__ */ new Set();
|
|
71
71
|
const add = (issue) => {
|
|
@@ -75,15 +75,15 @@ async function runDoctor(options = {}) {
|
|
|
75
75
|
issues.push(issue);
|
|
76
76
|
}
|
|
77
77
|
};
|
|
78
|
-
|
|
78
|
+
_chunkGYTQLITWcjs.evaluateCompat.call(void 0, { features, ingester: info }).forEach(add);
|
|
79
79
|
for (const sdk of sdks) {
|
|
80
80
|
if (_optionalChain([options, 'access', _7 => _7.features, 'optionalAccess', _8 => _8.length])) {
|
|
81
|
-
|
|
81
|
+
_chunkGYTQLITWcjs.evaluateCompat.call(void 0, { features: options.features, ingester: null, sdk }).forEach(add);
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
const key = options.key ? await checkKey(endpoint, options.key, timeoutMs) : void 0;
|
|
85
85
|
const featureRows = features.flatMap((id) => {
|
|
86
|
-
const feature =
|
|
86
|
+
const feature = _chunkGYTQLITWcjs.COMPAT_MANIFEST.features.find((f) => f.id === id);
|
|
87
87
|
if (!feature) return [];
|
|
88
88
|
const bad = issues.some((issue) => issue.feature === id);
|
|
89
89
|
const known = _optionalChain([info, 'optionalAccess', _9 => _9.version]) != null || feature.route !== void 0 && _optionalChain([info, 'optionalAccess', _10 => _10.routes, 'optionalAccess', _11 => _11[feature.route]]) !== void 0;
|
|
@@ -153,7 +153,7 @@ Checks that your Autter SDKs, ingester and ClickHouse schema versions match.
|
|
|
153
153
|
Options:
|
|
154
154
|
--endpoint <url> Ingester base URL (default: $AUTTER_ENDPOINT, $OTEL_EXPORTER_OTLP_ENDPOINT, or ${DEFAULT_ENDPOINT})
|
|
155
155
|
--key <key> Also verify an ingest key (default: $AUTTER_RUNTIME_KEY)
|
|
156
|
-
--features <a,b> Only check these features (${
|
|
156
|
+
--features <a,b> Only check these features (${_chunkGYTQLITWcjs.COMPAT_MANIFEST.features.map((f) => f.id).join(", ")})
|
|
157
157
|
--json Machine-readable output
|
|
158
158
|
--timeout <ms> Per-request timeout (default 5000)
|
|
159
159
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
noteCompatFeature,
|
|
3
|
-
observeIngesterResponse
|
|
4
|
-
|
|
3
|
+
observeIngesterResponse,
|
|
4
|
+
sharedSlot
|
|
5
|
+
} from "./chunk-LG52CW3V.js";
|
|
5
6
|
|
|
6
7
|
// ../runtime-core/dist/index.js
|
|
7
8
|
var SENSITIVE_KEY_PATTERNS = [
|
|
@@ -993,7 +994,7 @@ import {
|
|
|
993
994
|
unlinkSync
|
|
994
995
|
} from "fs";
|
|
995
996
|
import { join, resolve } from "path";
|
|
996
|
-
var sinkStats = { dropped: 0 };
|
|
997
|
+
var sinkStats = sharedSlot("sinks", () => ({ dropped: 0 }));
|
|
997
998
|
function otlpSink(options = {}) {
|
|
998
999
|
let context = null;
|
|
999
1000
|
let queue = [];
|
|
@@ -1362,18 +1363,22 @@ function memorySink() {
|
|
|
1362
1363
|
}
|
|
1363
1364
|
|
|
1364
1365
|
// src/logger.ts
|
|
1365
|
-
var
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1366
|
+
var shared = sharedSlot("logger", () => ({
|
|
1367
|
+
local: new AsyncLocalStorage(),
|
|
1368
|
+
config: null,
|
|
1369
|
+
sinks: [],
|
|
1370
|
+
extraSinks: /* @__PURE__ */ new Set()
|
|
1371
|
+
}));
|
|
1372
|
+
var local = shared.local;
|
|
1373
|
+
var extraSinks = shared.extraSinks;
|
|
1369
1374
|
var fallbackConsole = null;
|
|
1370
1375
|
function redact(attributes) {
|
|
1371
|
-
return config?.redact(attributes) ?? redactAttributes(attributes);
|
|
1376
|
+
return shared.config?.redact(attributes) ?? redactAttributes(attributes);
|
|
1372
1377
|
}
|
|
1373
1378
|
var safe = (attributes = {}) => boundContext(attributes, redact);
|
|
1374
1379
|
var merge = (current, next) => mergeContext(current, next, redact);
|
|
1375
1380
|
function activeSinks() {
|
|
1376
|
-
if (config) return extraSinks.size ? [...sinks, ...extraSinks] : sinks;
|
|
1381
|
+
if (shared.config) return extraSinks.size ? [...shared.sinks, ...extraSinks] : shared.sinks;
|
|
1377
1382
|
if (extraSinks.size) return [...extraSinks];
|
|
1378
1383
|
fallbackConsole ??= consoleSink();
|
|
1379
1384
|
return [fallbackConsole];
|
|
@@ -1410,9 +1415,9 @@ function emit(level, message, attributes, extra = {}, options = {}) {
|
|
|
1410
1415
|
if (state?.sealed) return;
|
|
1411
1416
|
const summary = extra["autter.event.type"] === "operation";
|
|
1412
1417
|
if (!summary) recordLogBreadcrumb(level, message, attributes, state);
|
|
1413
|
-
if (!summary && LEVEL_SEVERITY[level] < LEVEL_SEVERITY[config?.options?.minLevel ?? "debug"])
|
|
1418
|
+
if (!summary && LEVEL_SEVERITY[level] < LEVEL_SEVERITY[shared.config?.options?.minLevel ?? "debug"])
|
|
1414
1419
|
return;
|
|
1415
|
-
if (!summary && state && config?.options?.inline !== false) {
|
|
1420
|
+
if (!summary && state && shared.config?.options?.inline !== false) {
|
|
1416
1421
|
const { "exception.stacktrace": _stack, ...inlineAttrs } = userContext(
|
|
1417
1422
|
attributes,
|
|
1418
1423
|
redact
|
|
@@ -1432,7 +1437,7 @@ function emit(level, message, attributes, extra = {}, options = {}) {
|
|
|
1432
1437
|
...operationAttributes(state),
|
|
1433
1438
|
...user
|
|
1434
1439
|
};
|
|
1435
|
-
const enrichers = config?.options?.enrich;
|
|
1440
|
+
const enrichers = shared.config?.options?.enrich;
|
|
1436
1441
|
if (summary && state && enrichers?.length) {
|
|
1437
1442
|
const event2 = { level, message, attributes: raw };
|
|
1438
1443
|
for (const enrich of enrichers) {
|
|
@@ -1460,9 +1465,9 @@ function emit(level, message, attributes, extra = {}, options = {}) {
|
|
|
1460
1465
|
}
|
|
1461
1466
|
}
|
|
1462
1467
|
function configureRuntimeLogger(next) {
|
|
1463
|
-
config = next;
|
|
1464
|
-
sinks = defaultSinks(next);
|
|
1465
|
-
for (const sink of sinks) {
|
|
1468
|
+
shared.config = next;
|
|
1469
|
+
shared.sinks = defaultSinks(next);
|
|
1470
|
+
for (const sink of shared.sinks) {
|
|
1466
1471
|
try {
|
|
1467
1472
|
sink.start?.({
|
|
1468
1473
|
endpoint: next.endpoint,
|
|
@@ -1477,14 +1482,14 @@ function configureRuntimeLogger(next) {
|
|
|
1477
1482
|
}
|
|
1478
1483
|
}
|
|
1479
1484
|
function loggerConfigured() {
|
|
1480
|
-
return config;
|
|
1485
|
+
return shared.config;
|
|
1481
1486
|
}
|
|
1482
1487
|
function addRuntimeSink(sink) {
|
|
1483
1488
|
extraSinks.add(sink);
|
|
1484
1489
|
return () => extraSinks.delete(sink);
|
|
1485
1490
|
}
|
|
1486
1491
|
function notifyException(error, attributes = {}) {
|
|
1487
|
-
if (!extraSinks.size && !sinks.some((sink) => sink.exception)) return;
|
|
1492
|
+
if (!extraSinks.size && !shared.sinks.some((sink) => sink.exception)) return;
|
|
1488
1493
|
const state = local.getStore();
|
|
1489
1494
|
const record = {
|
|
1490
1495
|
time: Date.now(),
|
|
@@ -1495,7 +1500,7 @@ function notifyException(error, attributes = {}) {
|
|
|
1495
1500
|
...state ? { operationId: state.id } : {},
|
|
1496
1501
|
...state?.requestId ? { requestId: state.requestId } : {}
|
|
1497
1502
|
};
|
|
1498
|
-
for (const sink of [...sinks, ...extraSinks]) {
|
|
1503
|
+
for (const sink of [...shared.sinks, ...extraSinks]) {
|
|
1499
1504
|
try {
|
|
1500
1505
|
sink.exception?.(record);
|
|
1501
1506
|
} catch {
|
|
@@ -1504,25 +1509,25 @@ function notifyException(error, attributes = {}) {
|
|
|
1504
1509
|
}
|
|
1505
1510
|
function runtimeLogStats() {
|
|
1506
1511
|
return {
|
|
1507
|
-
buffered: sinks.reduce((sum, sink) => sum + (sink.buffered?.() ?? 0), 0),
|
|
1512
|
+
buffered: shared.sinks.reduce((sum, sink) => sum + (sink.buffered?.() ?? 0), 0),
|
|
1508
1513
|
dropped: sinkStats.dropped
|
|
1509
1514
|
};
|
|
1510
1515
|
}
|
|
1511
1516
|
async function flushRuntimeLogs() {
|
|
1512
|
-
if (!config) return;
|
|
1517
|
+
if (!shared.config) return;
|
|
1513
1518
|
const results = await Promise.allSettled(
|
|
1514
|
-
sinks.map((sink) => Promise.resolve().then(() => sink.flush?.()))
|
|
1519
|
+
shared.sinks.map((sink) => Promise.resolve().then(() => sink.flush?.()))
|
|
1515
1520
|
);
|
|
1516
1521
|
const failed = results.find((result) => result.status === "rejected");
|
|
1517
1522
|
if (failed) throw failed.reason;
|
|
1518
1523
|
}
|
|
1519
1524
|
async function shutdownRuntimeLogger() {
|
|
1520
|
-
const active = sinks;
|
|
1525
|
+
const active = shared.sinks;
|
|
1521
1526
|
const results = await Promise.allSettled(
|
|
1522
1527
|
active.map((sink) => Promise.resolve().then(() => sink.shutdown?.()))
|
|
1523
1528
|
);
|
|
1524
|
-
config = null;
|
|
1525
|
-
sinks = [];
|
|
1529
|
+
shared.config = null;
|
|
1530
|
+
shared.sinks = [];
|
|
1526
1531
|
const failed = results.find((result) => result.status === "rejected");
|
|
1527
1532
|
if (failed) throw failed.reason;
|
|
1528
1533
|
}
|
|
@@ -1674,7 +1679,7 @@ function emitSummary(state, options = {}) {
|
|
|
1674
1679
|
}
|
|
1675
1680
|
function reportExplicitFailure(state) {
|
|
1676
1681
|
if (state.outcome === "failed" && state.explicitOutcome && !state.errorThrown)
|
|
1677
|
-
config?.reportOutcome(state.name, state.message ?? "Operation failed", {
|
|
1682
|
+
shared.config?.reportOutcome(state.name, state.message ?? "Operation failed", {
|
|
1678
1683
|
...traceAttributes(state),
|
|
1679
1684
|
"autter.outcome.stack": state.reportingStack ?? ""
|
|
1680
1685
|
});
|
|
@@ -1708,7 +1713,7 @@ function startOperation(name, fn, attributes, options, internal = {}) {
|
|
|
1708
1713
|
thrown = error;
|
|
1709
1714
|
noteOperationError(error, true, state);
|
|
1710
1715
|
rememberThrownBreadcrumbs(error, state);
|
|
1711
|
-
config?.captureThrown?.(error);
|
|
1716
|
+
shared.config?.captureThrown?.(error);
|
|
1712
1717
|
throw error;
|
|
1713
1718
|
} finally {
|
|
1714
1719
|
trace.getActiveSpan()?.setAttributes({
|
|
@@ -1717,7 +1722,7 @@ function startOperation(name, fn, attributes, options, internal = {}) {
|
|
|
1717
1722
|
"autter.operation.kind": state.kind
|
|
1718
1723
|
});
|
|
1719
1724
|
if (state.outcome === "failed" && !thrown)
|
|
1720
|
-
config?.reportOutcome(
|
|
1725
|
+
shared.config?.reportOutcome(
|
|
1721
1726
|
state.name,
|
|
1722
1727
|
state.message ?? "Operation failed",
|
|
1723
1728
|
{
|
|
@@ -1738,7 +1743,7 @@ function startOperation(name, fn, attributes, options, internal = {}) {
|
|
|
1738
1743
|
}
|
|
1739
1744
|
}
|
|
1740
1745
|
] : [];
|
|
1741
|
-
const promise = config?.run ? config.run(state.name, run, traceAttributes(state), links) : run();
|
|
1746
|
+
const promise = shared.config?.run ? shared.config.run(state.name, run, traceAttributes(state), links) : run();
|
|
1742
1747
|
if (!options.waitUntil) return promise;
|
|
1743
1748
|
const waitUntil = options.waitUntil;
|
|
1744
1749
|
return promise.finally(() => {
|
|
@@ -1815,9 +1820,14 @@ var runtimeContext = {
|
|
|
1815
1820
|
// src/breadcrumbs.ts
|
|
1816
1821
|
var INVALID_TRACE_ID = "00000000000000000000000000000000";
|
|
1817
1822
|
var MAX_TRACE_TRAILS = 200;
|
|
1818
|
-
var
|
|
1819
|
-
|
|
1820
|
-
|
|
1823
|
+
var trails = sharedSlot("breadcrumbs", () => ({
|
|
1824
|
+
byTrace: /* @__PURE__ */ new Map(),
|
|
1825
|
+
process: new BreadcrumbTrail(),
|
|
1826
|
+
thrown: /* @__PURE__ */ new WeakMap()
|
|
1827
|
+
}));
|
|
1828
|
+
var byTrace = trails.byTrace;
|
|
1829
|
+
var processTrail = trails.process;
|
|
1830
|
+
var thrownTrails = trails.thrown;
|
|
1821
1831
|
function redact2(attributes) {
|
|
1822
1832
|
return loggerConfigured()?.redact(attributes) ?? redactAttributes(attributes);
|
|
1823
1833
|
}
|