brass-runtime 1.15.0 → 1.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +673 -136
- package/dist/agent/cli/main.cjs +40 -35
- package/dist/agent/cli/main.js +9 -4
- package/dist/agent/cli/main.mjs +9 -4
- package/dist/agent/index.cjs +8 -4
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/index.js +7 -3
- package/dist/agent/index.mjs +7 -3
- package/dist/chunk-2HQTDLHF.mjs +683 -0
- package/dist/chunk-36I3M4UC.mjs +370 -0
- package/dist/chunk-3AYM6WPJ.js +1629 -0
- package/dist/chunk-3LOYJFRR.cjs +300 -0
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/chunk-3Y2RIUMM.js +300 -0
- package/dist/{chunk-VEZNF5GZ.cjs → chunk-4ROBZFL6.cjs} +130 -126
- package/dist/{chunk-3QMOKAS5.js → chunk-52OB2ROS.js} +9 -5
- package/dist/chunk-52PPNNI4.cjs +416 -0
- package/dist/chunk-5EC274J5.cjs +2874 -0
- package/dist/chunk-5QC7LRZ3.js +229 -0
- package/dist/chunk-5VRJNBLZ.mjs +2874 -0
- package/dist/chunk-62AZW6UT.cjs +313 -0
- package/dist/chunk-6IXXWIUM.js +683 -0
- package/dist/chunk-74ZTY6CP.js +2871 -0
- package/dist/chunk-76YMRMH2.cjs +777 -0
- package/dist/chunk-7CMJS3QE.mjs +2871 -0
- package/dist/{chunk-4NHES7VK.mjs → chunk-7JIJOVCT.js} +27 -13
- package/dist/chunk-A2OM6NEH.mjs +194 -0
- package/dist/chunk-AGR5B2BC.cjs +683 -0
- package/dist/chunk-AVNQLJ5V.js +777 -0
- package/dist/chunk-B33ICAKP.js +313 -0
- package/dist/{chunk-ELOOF35R.mjs → chunk-B5JD23U7.mjs} +1 -1
- package/dist/chunk-BABBZK4Y.js +2024 -0
- package/dist/chunk-C3MDXTRZ.js +354 -0
- package/dist/chunk-CIZFIMK5.js +2193 -0
- package/dist/chunk-CZIVE6NT.cjs +354 -0
- package/dist/chunk-DNFJLJMW.mjs +354 -0
- package/dist/chunk-DNFO2EIZ.mjs +777 -0
- package/dist/chunk-EJ6BPYVR.mjs +416 -0
- package/dist/chunk-ENKODRU3.cjs +2193 -0
- package/dist/chunk-EOC4UHBS.mjs +229 -0
- package/dist/{chunk-BMH5AV44.js → chunk-FH2X7BVP.js} +756 -440
- package/dist/{chunk-PPUXIH5R.js → chunk-FHQGHPMO.mjs} +27 -13
- package/dist/{chunk-TGIFUAK4.cjs → chunk-GLE2WY7Z.cjs} +951 -635
- package/dist/{chunk-BDF4AMWX.mjs → chunk-GYM3LLGS.mjs} +756 -440
- package/dist/chunk-HLWLMW2F.mjs +2024 -0
- package/dist/chunk-JF5WGYJJ.cjs +194 -0
- package/dist/chunk-KH4SYAOS.mjs +1629 -0
- package/dist/chunk-KN32XNTH.mjs +313 -0
- package/dist/chunk-KQLYONSE.cjs +2871 -0
- package/dist/{chunk-STVLQ3XD.cjs → chunk-KZJQ723N.cjs} +92 -78
- package/dist/chunk-L2SYFEBS.js +194 -0
- package/dist/chunk-L6VB5N7Q.cjs +104 -0
- package/dist/{chunk-K6M7MDZ4.mjs → chunk-MBEJI5HF.mjs} +9 -5
- package/dist/chunk-MIIYDLGM.js +2874 -0
- package/dist/chunk-MOO4L7F4.mjs +104 -0
- package/dist/chunk-MT3OWDPC.mjs +2193 -0
- package/dist/chunk-MVGUEJ5Z.cjs +370 -0
- package/dist/chunk-OBGZSXTJ.cjs +10 -0
- package/dist/chunk-PD4EJTQC.cjs +229 -0
- package/dist/chunk-PWC3RBQE.mjs +300 -0
- package/dist/chunk-Q2I37RP3.cjs +1629 -0
- package/dist/chunk-RKGKFN2A.js +416 -0
- package/dist/{chunk-R3R2FVLG.cjs → chunk-SA6HUJVI.cjs} +5 -5
- package/dist/chunk-TRM4JUZQ.js +104 -0
- package/dist/chunk-UB4B6OFY.js +370 -0
- package/dist/{chunk-TO7IKXYT.js → chunk-UCUBNWM2.js} +1 -1
- package/dist/chunk-VN44DYYT.cjs +2024 -0
- package/dist/chunk-Y6FXYEAI.mjs +10 -0
- package/dist/client-CZHU674n.d.ts +820 -0
- package/dist/core/index.cjs +198 -4
- package/dist/core/index.d.ts +311 -212
- package/dist/core/index.js +237 -43
- package/dist/core/index.mjs +237 -43
- package/dist/{effect-CMOQKX8y.d.ts → effect-DIUHZ9IN.d.ts} +195 -1
- package/dist/effectRunner-CFLC32IK.cjs +8 -0
- package/dist/effectRunner-L4S7IPT3.js +8 -0
- package/dist/effectRunner-NNGG75QA.mjs +8 -0
- package/dist/http/index.cjs +1227 -2971
- package/dist/http/index.d.ts +826 -280
- package/dist/http/index.js +1089 -2833
- package/dist/http/index.mjs +1089 -2833
- package/dist/http/testing.cjs +161 -0
- package/dist/http/testing.d.ts +43 -0
- package/dist/http/testing.js +161 -0
- package/dist/http/testing.mjs +161 -0
- package/dist/index.cjs +486 -250
- package/dist/index.d.ts +87 -95
- package/dist/index.js +391 -155
- package/dist/index.mjs +391 -155
- package/dist/observability/index.cjs +162 -0
- package/dist/observability/index.d.ts +152 -0
- package/dist/observability/index.js +162 -0
- package/dist/observability/index.mjs +162 -0
- package/dist/perf/cli.cjs +401 -0
- package/dist/perf/cli.d.ts +1 -0
- package/dist/perf/cli.js +401 -0
- package/dist/perf/cli.mjs +401 -0
- package/dist/perf/index.cjs +141 -0
- package/dist/perf/index.d.ts +483 -0
- package/dist/perf/index.js +141 -0
- package/dist/perf/index.mjs +141 -0
- package/dist/schedule-CK3Ml_7p.d.ts +259 -0
- package/dist/schema/index.cjs +29 -0
- package/dist/schema/index.d.ts +179 -0
- package/dist/schema/index.js +29 -0
- package/dist/schema/index.mjs +29 -0
- package/dist/server-GJPg8ZSG.d.ts +675 -0
- package/dist/{stream-FQm9h4Mg.d.ts → stream-B4oK9JFP.d.ts} +1 -1
- package/dist/tracer-Hwt1cl7h.d.ts +189 -0
- package/dist/tracing-DqbTKGcf.d.ts +148 -0
- package/docs/ARCHITECTURE.md +292 -0
- package/docs/README.md +63 -0
- package/docs/adr/0001-ai-context-pack.md +32 -0
- package/docs/agent-apply-mode.md +104 -0
- package/docs/agent-approvals.md +110 -0
- package/docs/agent-batch.md +185 -0
- package/docs/agent-boundaries.md +112 -0
- package/docs/agent-chat-sessions.md +160 -0
- package/docs/agent-ci.md +17 -0
- package/docs/agent-cli.md +405 -0
- package/docs/agent-config.md +480 -0
- package/docs/agent-context-discovery.md +159 -0
- package/docs/agent-copilot-like-dx.md +126 -0
- package/docs/agent-declarative-optimized-planning.md +138 -0
- package/docs/agent-dx.md +224 -0
- package/docs/agent-env-files.md +126 -0
- package/docs/agent-follow-up-context.md +43 -0
- package/docs/agent-global-usage.md +180 -0
- package/docs/agent-init.md +109 -0
- package/docs/agent-install-and-configure.md +516 -0
- package/docs/agent-language-workspace-ux.md +99 -0
- package/docs/agent-llm-adapters.md +123 -0
- package/docs/agent-local-install.md +190 -0
- package/docs/agent-local-tests.md +51 -0
- package/docs/agent-observability.md +155 -0
- package/docs/agent-patch-quality-loop.md +162 -0
- package/docs/agent-presets.md +22 -0
- package/docs/agent-project-commands.md +237 -0
- package/docs/agent-project-intelligence.md +156 -0
- package/docs/agent-redaction.md +18 -0
- package/docs/agent-release-readiness.md +76 -0
- package/docs/agent-rollback-safety.md +162 -0
- package/docs/agent-rollback.md +23 -0
- package/docs/agent-run-artifacts.md +16 -0
- package/docs/agent-vscode-auto-discovery.md +137 -0
- package/docs/agent-vscode-batch-runner.md +100 -0
- package/docs/agent-vscode-chat-layout.md +90 -0
- package/docs/agent-vscode-clean-install.md +147 -0
- package/docs/agent-vscode-code-actions.md +70 -0
- package/docs/agent-vscode-diff-preview.md +45 -0
- package/docs/agent-vscode-inline-assist.md +56 -0
- package/docs/agent-vscode-install.md +186 -0
- package/docs/agent-vscode-model-setup.md +97 -0
- package/docs/agent-vscode-patch-preview.md +92 -0
- package/docs/agent-vscode-problems.md +79 -0
- package/docs/agent-vscode-project-dashboard.md +106 -0
- package/docs/agent-vscode-run-history.md +92 -0
- package/docs/agent-vscode-ux.md +73 -0
- package/docs/ai/INVARIANTS.md +84 -0
- package/docs/ai/PROJECT_MAP.md +338 -0
- package/docs/ai/PUBLIC_API.md +336 -0
- package/docs/ai/VALIDATION_MATRIX.md +67 -0
- package/docs/api-polish.md +37 -0
- package/docs/cancellation.md +162 -0
- package/docs/coverage.md +46 -0
- package/docs/getting-started.md +159 -0
- package/docs/guides/README.md +40 -0
- package/docs/guides/circuit-breaker.md +89 -0
- package/docs/guides/error-handling.md +91 -0
- package/docs/guides/getting-started.md +107 -0
- package/docs/guides/layers.md +189 -0
- package/docs/guides/metrics.md +101 -0
- package/docs/guides/resource-management.md +141 -0
- package/docs/guides/retry.md +215 -0
- package/docs/guides/semaphore.md +66 -0
- package/docs/guides/streams.md +117 -0
- package/docs/guides/supervisors.md +98 -0
- package/docs/guides/testing.md +162 -0
- package/docs/guides/tracing.md +71 -0
- package/docs/http-recipes.md +399 -0
- package/docs/http.md +749 -0
- package/docs/modules.md +285 -0
- package/docs/observability-collector-smoke.md +31 -0
- package/docs/observability-framework-examples.md +98 -0
- package/docs/observability.md +542 -0
- package/docs/otel-collector-smoke.yaml +27 -0
- package/docs/performance-profiler.md +199 -0
- package/docs/production-readiness.md +73 -0
- package/docs/recipes/README.md +12 -0
- package/docs/recipes/http-server.md +45 -0
- package/docs/recipes/layers.md +44 -0
- package/docs/recipes/performance.md +47 -0
- package/docs/recipes/runtime.md +41 -0
- package/docs/recipes/testing.md +41 -0
- package/docs/release.md +53 -0
- package/docs/wasm-bounded-queues.md +44 -0
- package/docs/wasm-engine-observability-benchmarks.md +85 -0
- package/docs/wasm-fiber-engine.md +117 -0
- package/docs/wasm-scheduler-state-machine.md +122 -0
- package/docs/wasm-stream-chunks.md +54 -0
- package/package.json +48 -2
- package/dist/chunk-AR22SXML.js +0 -1043
- package/dist/chunk-BDYEENHT.js +0 -224
- package/dist/chunk-JFPU5GQI.mjs +0 -1043
- package/dist/chunk-MS34J5LY.cjs +0 -224
- package/dist/chunk-UMAZLXAB.mjs +0 -224
- package/dist/chunk-XPZNXSVN.cjs +0 -1043
- package/dist/tracing-DNT9jEbr.d.ts +0 -106
|
@@ -0,0 +1,2193 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
var _chunk52PPNNI4cjs = require('./chunk-52PPNNI4.cjs');
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
var _chunkGLE2WY7Zcjs = require('./chunk-GLE2WY7Z.cjs');
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
var _chunkMVGUEJ5Zcjs = require('./chunk-MVGUEJ5Z.cjs');
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
var _chunkCZIVE6NTcjs = require('./chunk-CZIVE6NT.cjs');
|
|
24
|
+
|
|
25
|
+
// src/observability/health.ts
|
|
26
|
+
function snapshotRuntimeHealth(options = {}) {
|
|
27
|
+
const checkedAtMs = _nullishCoalesce(_optionalChain([options, 'access', _2 => _2.clock, 'optionalCall', _3 => _3()]), () => ( Date.now()));
|
|
28
|
+
const checks = runChecks(options.checks);
|
|
29
|
+
const circuitBreakers = collectCircuitBreakers(options.circuitBreakers);
|
|
30
|
+
const adaptiveLimiters = collectAdaptiveLimiters(options.adaptiveLimiters);
|
|
31
|
+
const fibers = options.registry ? fiberSummary(options.registry) : void 0;
|
|
32
|
+
const scopes = options.registry ? scopeSummary(options.registry) : void 0;
|
|
33
|
+
const metrics = options.metrics ? metricsSummary(options.metrics) : void 0;
|
|
34
|
+
const runtime = options.runtime ? runtimeSummary(options.runtime) : void 0;
|
|
35
|
+
const status = aggregateStatus([
|
|
36
|
+
...Object.values(checks).map((check) => check.status),
|
|
37
|
+
...Object.values(circuitBreakers).map((breaker) => breaker.status),
|
|
38
|
+
...Object.values(adaptiveLimiters).map((limiter) => limiter.status),
|
|
39
|
+
...runtimeSchedulerDropped(runtime) > 0 ? ["degraded"] : []
|
|
40
|
+
]);
|
|
41
|
+
return {
|
|
42
|
+
status,
|
|
43
|
+
ready: status === "ok" || status === "degraded" && _optionalChain([options, 'access', _4 => _4.readiness, 'optionalAccess', _5 => _5.failOnDegraded]) !== true,
|
|
44
|
+
checkedAt: new Date(checkedAtMs).toISOString(),
|
|
45
|
+
...fibers ? { fibers } : {},
|
|
46
|
+
...scopes ? { scopes } : {},
|
|
47
|
+
...runtime ? { runtime } : {},
|
|
48
|
+
...metrics ? { metrics } : {},
|
|
49
|
+
circuitBreakers,
|
|
50
|
+
adaptiveLimiters,
|
|
51
|
+
checks
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function makeRuntimeHealth(options = {}) {
|
|
55
|
+
return _chunkMVGUEJ5Zcjs.asyncSync.call(void 0, () => snapshotRuntimeHealth(options));
|
|
56
|
+
}
|
|
57
|
+
var runtimeHealth = makeRuntimeHealth;
|
|
58
|
+
function readiness(options = {}) {
|
|
59
|
+
return _chunkMVGUEJ5Zcjs.asyncSync.call(void 0, () => snapshotRuntimeHealth(options).ready);
|
|
60
|
+
}
|
|
61
|
+
function healthToHttpResponse(report) {
|
|
62
|
+
return {
|
|
63
|
+
status: report.ready ? 200 : 503,
|
|
64
|
+
headers: { "content-type": "application/json" },
|
|
65
|
+
body: JSON.stringify(report)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function runChecks(checks) {
|
|
69
|
+
const out = {};
|
|
70
|
+
for (const [name, check] of Object.entries(_nullishCoalesce(checks, () => ( {})))) {
|
|
71
|
+
try {
|
|
72
|
+
out[name] = normalizeHealthCheckResult(check());
|
|
73
|
+
} catch (error) {
|
|
74
|
+
out[name] = {
|
|
75
|
+
status: "down",
|
|
76
|
+
message: error instanceof Error ? error.message : String(error)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
function collectCircuitBreakers(breakers) {
|
|
83
|
+
const out = {};
|
|
84
|
+
for (const [name, breaker] of Object.entries(_nullishCoalesce(breakers, () => ( {})))) {
|
|
85
|
+
const stats = breaker.stats();
|
|
86
|
+
out[name] = {
|
|
87
|
+
...stats,
|
|
88
|
+
status: stats.state === "open" ? "down" : stats.state === "half-open" ? "degraded" : "ok"
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
function collectAdaptiveLimiters(limiters) {
|
|
94
|
+
const out = {};
|
|
95
|
+
for (const [name, limiter] of Object.entries(_nullishCoalesce(limiters, () => ( {})))) {
|
|
96
|
+
const stats = limiter.stats();
|
|
97
|
+
out[name] = {
|
|
98
|
+
...stats,
|
|
99
|
+
status: adaptiveLimiterStatus(stats)
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
function adaptiveLimiterStatus(stats) {
|
|
105
|
+
const rejectionRate = _nullishCoalesce(stats.rejectionRate, () => ( 0));
|
|
106
|
+
const errorRate = _nullishCoalesce(stats.errorRate, () => ( 0));
|
|
107
|
+
if (rejectionRate >= 0.5) return "down";
|
|
108
|
+
if (rejectionRate > 0 || stats.queueDepth > stats.limit || errorRate >= 0.25) return "degraded";
|
|
109
|
+
return "ok";
|
|
110
|
+
}
|
|
111
|
+
function fiberSummary(registry) {
|
|
112
|
+
const fibers = Array.from(registry.fibers.values());
|
|
113
|
+
return {
|
|
114
|
+
active: fibers.filter((fiber) => fiber.status === "Running").length,
|
|
115
|
+
suspended: fibers.filter((fiber) => fiber.runState === "Suspended").length,
|
|
116
|
+
done: fibers.filter((fiber) => fiber.runState === "Done").length
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function scopeSummary(registry) {
|
|
120
|
+
const scopes = Array.from(registry.scopes.values());
|
|
121
|
+
return {
|
|
122
|
+
open: scopes.filter((scope) => scope.closedAt === void 0).length,
|
|
123
|
+
closed: scopes.filter((scope) => scope.closedAt !== void 0).length
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function metricsSummary(metrics) {
|
|
127
|
+
const snapshot = metrics.snapshot();
|
|
128
|
+
const gaugeValue = (name) => _optionalChain([snapshot, 'access', _6 => _6.gauges, 'access', _7 => _7.find, 'call', _8 => _8((gauge) => gauge.name === name), 'optionalAccess', _9 => _9.value]);
|
|
129
|
+
return {
|
|
130
|
+
counters: snapshot.counters.length,
|
|
131
|
+
gauges: snapshot.gauges.length,
|
|
132
|
+
histograms: snapshot.histograms.length,
|
|
133
|
+
activeFibers: gaugeValue("brass_runtime_fibers_active"),
|
|
134
|
+
activeScopes: gaugeValue("brass_runtime_scopes_active"),
|
|
135
|
+
activeSpans: gaugeValue("brass_runtime_spans_active")
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function runtimeSummary(runtime) {
|
|
139
|
+
const stats = _optionalChain([runtime, 'access', _10 => _10.stats, 'optionalCall', _11 => _11()]);
|
|
140
|
+
const scheduler = _optionalChain([runtime, 'access', _12 => _12.scheduler, 'optionalAccess', _13 => _13.stats, 'optionalCall', _14 => _14()]);
|
|
141
|
+
return {
|
|
142
|
+
engine: _optionalChain([stats, 'optionalAccess', _15 => _15.engine]),
|
|
143
|
+
fiberStats: _optionalChain([stats, 'optionalAccess', _16 => _16.data]),
|
|
144
|
+
scheduler
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function runtimeSchedulerDropped(runtime) {
|
|
148
|
+
const data = _optionalChain([runtime, 'optionalAccess', _17 => _17.scheduler, 'optionalAccess', _18 => _18.data]);
|
|
149
|
+
return typeof _optionalChain([data, 'optionalAccess', _19 => _19.droppedTasks]) === "number" ? data.droppedTasks : 0;
|
|
150
|
+
}
|
|
151
|
+
function aggregateStatus(statuses) {
|
|
152
|
+
if (statuses.includes("down")) return "down";
|
|
153
|
+
if (statuses.includes("degraded")) return "degraded";
|
|
154
|
+
return "ok";
|
|
155
|
+
}
|
|
156
|
+
function normalizeHealthCheckResult(result) {
|
|
157
|
+
return {
|
|
158
|
+
status: result.status,
|
|
159
|
+
...result.message ? { message: result.message } : {},
|
|
160
|
+
...result.details ? { details: result.details } : {}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/observability/redaction.ts
|
|
165
|
+
var DEFAULT_REPLACEMENT = "[REDACTED]";
|
|
166
|
+
var DEFAULT_MAX_DEPTH = 6;
|
|
167
|
+
var DEFAULT_MAX_STRING_LENGTH = 8192;
|
|
168
|
+
var DEFAULT_KEY_PATTERNS = [
|
|
169
|
+
"authorization",
|
|
170
|
+
"cookie",
|
|
171
|
+
"set-cookie",
|
|
172
|
+
"password",
|
|
173
|
+
"passwd",
|
|
174
|
+
"secret",
|
|
175
|
+
"token",
|
|
176
|
+
"access_token",
|
|
177
|
+
"refresh_token",
|
|
178
|
+
"api_key",
|
|
179
|
+
"apikey",
|
|
180
|
+
"private_key",
|
|
181
|
+
/credential/i
|
|
182
|
+
];
|
|
183
|
+
var DEFAULT_HEADER_PATTERNS = [
|
|
184
|
+
"authorization",
|
|
185
|
+
"cookie",
|
|
186
|
+
"set-cookie",
|
|
187
|
+
"x-api-key",
|
|
188
|
+
"proxy-authorization"
|
|
189
|
+
];
|
|
190
|
+
function makeObservabilityRedactor(config) {
|
|
191
|
+
if (isRedactor(config)) return config;
|
|
192
|
+
const options = config === false ? { keys: [], headers: [], redactUrlQuery: false } : _nullishCoalesce(config, () => ( {}));
|
|
193
|
+
const replacement = _nullishCoalesce(options.replacement, () => ( DEFAULT_REPLACEMENT));
|
|
194
|
+
const keys = _nullishCoalesce(options.keys, () => ( DEFAULT_KEY_PATTERNS));
|
|
195
|
+
const headers = _nullishCoalesce(options.headers, () => ( DEFAULT_HEADER_PATTERNS));
|
|
196
|
+
const maxDepth = Math.max(1, Math.floor(_nullishCoalesce(options.maxDepth, () => ( DEFAULT_MAX_DEPTH))));
|
|
197
|
+
const maxStringLength = Math.max(0, Math.floor(_nullishCoalesce(options.maxStringLength, () => ( DEFAULT_MAX_STRING_LENGTH))));
|
|
198
|
+
const redactUrlQuery = _nullishCoalesce(options.redactUrlQuery, () => ( true));
|
|
199
|
+
const redactByKey = (key, value, depth) => {
|
|
200
|
+
if (matchesAny(key, keys)) return replacement;
|
|
201
|
+
return redactValue(value, depth);
|
|
202
|
+
};
|
|
203
|
+
const redactValue = (value, depth) => {
|
|
204
|
+
if (value == null || typeof value === "number" || typeof value === "boolean") return value;
|
|
205
|
+
if (typeof value === "string") return maxStringLength > 0 && value.length > maxStringLength ? `${value.slice(0, maxStringLength)}...` : value;
|
|
206
|
+
if (depth >= maxDepth) return "[MaxDepth]";
|
|
207
|
+
if (Array.isArray(value)) return value.map((item) => redactValue(item, depth + 1));
|
|
208
|
+
if (value instanceof Error) {
|
|
209
|
+
return {
|
|
210
|
+
name: value.name,
|
|
211
|
+
message: value.message
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
if (typeof value === "object") {
|
|
215
|
+
const out = {};
|
|
216
|
+
for (const [key, item] of Object.entries(value)) {
|
|
217
|
+
out[key] = redactByKey(key, item, depth + 1);
|
|
218
|
+
}
|
|
219
|
+
return out;
|
|
220
|
+
}
|
|
221
|
+
return String(value);
|
|
222
|
+
};
|
|
223
|
+
return {
|
|
224
|
+
value: (value) => redactValue(value, 0),
|
|
225
|
+
fields: (fields) => redactRecord(fields, redactByKey),
|
|
226
|
+
attributes: (fields) => redactRecord(fields, redactByKey),
|
|
227
|
+
headers: (input) => {
|
|
228
|
+
const out = {};
|
|
229
|
+
for (const [key, value] of Object.entries(input)) {
|
|
230
|
+
out[key] = matchesAny(key, headers) ? replacement : value;
|
|
231
|
+
}
|
|
232
|
+
return out;
|
|
233
|
+
},
|
|
234
|
+
url: (url) => redactUrl(url, redactUrlQuery)
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function redactRecord(fields, redactByKey) {
|
|
238
|
+
const out = {};
|
|
239
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
240
|
+
out[key] = redactByKey(key, value, 0);
|
|
241
|
+
}
|
|
242
|
+
return out;
|
|
243
|
+
}
|
|
244
|
+
function redactUrl(url, redactQuery) {
|
|
245
|
+
if (!redactQuery) return url;
|
|
246
|
+
try {
|
|
247
|
+
const parsed = new URL(url);
|
|
248
|
+
if (parsed.search) parsed.search = "?[REDACTED]";
|
|
249
|
+
return parsed.toString();
|
|
250
|
+
} catch (e) {
|
|
251
|
+
const queryIndex = url.indexOf("?");
|
|
252
|
+
if (queryIndex < 0) return url;
|
|
253
|
+
return `${url.slice(0, queryIndex)}?[REDACTED]`;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
function matchesAny(key, patterns) {
|
|
257
|
+
const lower = key.toLowerCase();
|
|
258
|
+
return patterns.some(
|
|
259
|
+
(pattern) => typeof pattern === "string" ? lower === pattern.toLowerCase() : pattern.test(key)
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
function isRedactor(value) {
|
|
263
|
+
return typeof value === "object" && value !== null && typeof value.fields === "function" && typeof value.attributes === "function";
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// src/observability/traceContext.ts
|
|
267
|
+
var TRACEPARENT_HEADER = "traceparent";
|
|
268
|
+
var TRACESTATE_HEADER = "tracestate";
|
|
269
|
+
var BAGGAGE_HEADER = "baggage";
|
|
270
|
+
var HEX = /^[0-9a-f]+$/;
|
|
271
|
+
var BAGGAGE_KEY = /^[!#$%&'*+\-.^_`|~0-9a-zA-Z]+$/;
|
|
272
|
+
function parseTraceparent(value) {
|
|
273
|
+
if (!value) return void 0;
|
|
274
|
+
const parts = value.trim().split("-");
|
|
275
|
+
if (parts.length < 4) return void 0;
|
|
276
|
+
const [version, traceIdRaw, spanIdRaw, flagsRaw] = parts;
|
|
277
|
+
if (!isFixedHex(version, 2) || version.toLowerCase() === "ff") return void 0;
|
|
278
|
+
if (version.toLowerCase() === "00" && parts.length !== 4) return void 0;
|
|
279
|
+
if (!isFixedHex(traceIdRaw, 32) || isAllZero(traceIdRaw)) return void 0;
|
|
280
|
+
if (!isFixedHex(spanIdRaw, 16) || isAllZero(spanIdRaw)) return void 0;
|
|
281
|
+
if (!isFixedHex(flagsRaw, 2)) return void 0;
|
|
282
|
+
const flags = Number.parseInt(flagsRaw, 16);
|
|
283
|
+
return {
|
|
284
|
+
traceId: traceIdRaw.toLowerCase(),
|
|
285
|
+
spanId: spanIdRaw.toLowerCase(),
|
|
286
|
+
sampled: (flags & 1) === 1
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function formatTraceparent(trace) {
|
|
290
|
+
return `00-${normalizeTraceId(trace.traceId)}-${normalizeSpanId(trace.spanId)}-${trace.sampled === false ? "00" : "01"}`;
|
|
291
|
+
}
|
|
292
|
+
function extractTraceContext(headers) {
|
|
293
|
+
const parsed = parseTraceparent(readHeader(headers, TRACEPARENT_HEADER));
|
|
294
|
+
if (!parsed) return void 0;
|
|
295
|
+
const traceState = readHeader(headers, TRACESTATE_HEADER);
|
|
296
|
+
const baggage = extractBaggage(headers);
|
|
297
|
+
return {
|
|
298
|
+
...parsed,
|
|
299
|
+
...traceState ? { traceState } : {},
|
|
300
|
+
...baggage ? { baggage } : {}
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
function injectTraceContext(headers, trace, options = {}) {
|
|
304
|
+
const out = { ..._nullishCoalesce(headers, () => ( {})) };
|
|
305
|
+
const hasTraceparent = hasHeader(out, TRACEPARENT_HEADER);
|
|
306
|
+
const shouldInjectTraceContext = options.overwrite || !hasTraceparent;
|
|
307
|
+
if (!shouldInjectTraceContext) return out;
|
|
308
|
+
if (shouldInjectTraceContext) {
|
|
309
|
+
setHeader(out, TRACEPARENT_HEADER, formatTraceparent(trace));
|
|
310
|
+
}
|
|
311
|
+
if (trace.traceState && (options.overwrite || !hasHeader(out, TRACESTATE_HEADER))) {
|
|
312
|
+
setHeader(out, TRACESTATE_HEADER, trace.traceState);
|
|
313
|
+
}
|
|
314
|
+
if (trace.baggage && Object.keys(trace.baggage).length > 0 && (options.overwrite || !hasHeader(out, BAGGAGE_HEADER))) {
|
|
315
|
+
setHeader(out, BAGGAGE_HEADER, formatBaggage(trace.baggage));
|
|
316
|
+
}
|
|
317
|
+
return out;
|
|
318
|
+
}
|
|
319
|
+
function parseBaggage(value) {
|
|
320
|
+
if (!value) return void 0;
|
|
321
|
+
const entries = {};
|
|
322
|
+
for (const rawMember of value.split(",")) {
|
|
323
|
+
const member = rawMember.trim();
|
|
324
|
+
if (!member) continue;
|
|
325
|
+
const [pair] = member.split(";", 1);
|
|
326
|
+
const eq = pair.indexOf("=");
|
|
327
|
+
if (eq <= 0) continue;
|
|
328
|
+
const key = pair.slice(0, eq).trim();
|
|
329
|
+
const rawValue = pair.slice(eq + 1).trim();
|
|
330
|
+
if (!BAGGAGE_KEY.test(key)) continue;
|
|
331
|
+
entries[key] = decodeBaggageValue(rawValue);
|
|
332
|
+
}
|
|
333
|
+
return Object.keys(entries).length > 0 ? entries : void 0;
|
|
334
|
+
}
|
|
335
|
+
function formatBaggage(baggage) {
|
|
336
|
+
if (!baggage) return "";
|
|
337
|
+
return Object.entries(baggage).filter(([key, value]) => BAGGAGE_KEY.test(key) && value !== void 0).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => `${key}=${encodeBaggageValue(value)}`).join(",");
|
|
338
|
+
}
|
|
339
|
+
function extractBaggage(headers) {
|
|
340
|
+
return parseBaggage(readHeader(headers, BAGGAGE_HEADER));
|
|
341
|
+
}
|
|
342
|
+
function injectBaggage(headers, baggage, options = {}) {
|
|
343
|
+
const out = { ..._nullishCoalesce(headers, () => ( {})) };
|
|
344
|
+
if (!baggage || Object.keys(baggage).length === 0) return out;
|
|
345
|
+
if (!options.overwrite && hasHeader(out, BAGGAGE_HEADER)) return out;
|
|
346
|
+
setHeader(out, BAGGAGE_HEADER, formatBaggage(baggage));
|
|
347
|
+
return out;
|
|
348
|
+
}
|
|
349
|
+
function normalizeTraceId(id) {
|
|
350
|
+
return normalizeHexId(id, 32);
|
|
351
|
+
}
|
|
352
|
+
function normalizeSpanId(id) {
|
|
353
|
+
return normalizeHexId(id, 16);
|
|
354
|
+
}
|
|
355
|
+
function readHeader(headers, name) {
|
|
356
|
+
if (!headers) return void 0;
|
|
357
|
+
const getter = headers.get;
|
|
358
|
+
if (typeof getter === "function") {
|
|
359
|
+
return _nullishCoalesce(getter.call(headers, name), () => ( void 0));
|
|
360
|
+
}
|
|
361
|
+
if (isIterableHeaders(headers)) {
|
|
362
|
+
for (const [key, value] of headers) {
|
|
363
|
+
if (key.toLowerCase() === name) return value;
|
|
364
|
+
}
|
|
365
|
+
return void 0;
|
|
366
|
+
}
|
|
367
|
+
const record = headers;
|
|
368
|
+
for (const [key, value] of Object.entries(record)) {
|
|
369
|
+
if (key.toLowerCase() !== name) continue;
|
|
370
|
+
if (typeof value === "string") return value;
|
|
371
|
+
if (Array.isArray(value)) return value[0];
|
|
372
|
+
return void 0;
|
|
373
|
+
}
|
|
374
|
+
return void 0;
|
|
375
|
+
}
|
|
376
|
+
function isIterableHeaders(value) {
|
|
377
|
+
return typeof _optionalChain([value, 'optionalAccess', _20 => _20[Symbol.iterator]]) === "function";
|
|
378
|
+
}
|
|
379
|
+
function hasHeader(headers, name) {
|
|
380
|
+
const lower = name.toLowerCase();
|
|
381
|
+
return Object.keys(headers).some((key) => key.toLowerCase() === lower);
|
|
382
|
+
}
|
|
383
|
+
function setHeader(headers, name, value) {
|
|
384
|
+
const lower = name.toLowerCase();
|
|
385
|
+
const existing = Object.keys(headers).find((key) => key.toLowerCase() === lower);
|
|
386
|
+
headers[_nullishCoalesce(existing, () => ( name))] = value;
|
|
387
|
+
}
|
|
388
|
+
function isFixedHex(value, length) {
|
|
389
|
+
return typeof value === "string" && value.length === length && HEX.test(value.toLowerCase());
|
|
390
|
+
}
|
|
391
|
+
function isAllZero(value) {
|
|
392
|
+
for (let i = 0; i < value.length; i++) {
|
|
393
|
+
if (value[i] !== "0") return false;
|
|
394
|
+
}
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
function normalizeHexId(id, length) {
|
|
398
|
+
const hex = String(id).toLowerCase().replace(/[^0-9a-f]/g, "");
|
|
399
|
+
if (hex.length >= length) return avoidAllZero(hex.slice(0, length));
|
|
400
|
+
if (hex.length > 0) return avoidAllZero(hex.padStart(length, "0"));
|
|
401
|
+
return stableHex(String(id), length);
|
|
402
|
+
}
|
|
403
|
+
function decodeBaggageValue(value) {
|
|
404
|
+
try {
|
|
405
|
+
return decodeURIComponent(value);
|
|
406
|
+
} catch (e2) {
|
|
407
|
+
return value;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
function encodeBaggageValue(value) {
|
|
411
|
+
return encodeURIComponent(String(value));
|
|
412
|
+
}
|
|
413
|
+
function avoidAllZero(hex) {
|
|
414
|
+
return isAllZero(hex) ? `${hex.slice(0, -1)}1` : hex;
|
|
415
|
+
}
|
|
416
|
+
function stableHex(input, length) {
|
|
417
|
+
let hash = 2166136261;
|
|
418
|
+
let out = "";
|
|
419
|
+
while (out.length < length) {
|
|
420
|
+
for (let i = 0; i < input.length; i++) {
|
|
421
|
+
hash ^= input.charCodeAt(i);
|
|
422
|
+
hash = Math.imul(hash, 16777619);
|
|
423
|
+
}
|
|
424
|
+
out += (hash >>> 0).toString(16).padStart(8, "0");
|
|
425
|
+
input = `${input}:${out.length}`;
|
|
426
|
+
}
|
|
427
|
+
return avoidAllZero(out.slice(0, length));
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// src/observability/metrics.ts
|
|
431
|
+
var PROMETHEUS_CONTENT_TYPE = "text/plain; version=0.0.4; charset=utf-8";
|
|
432
|
+
var OTLP_JSON_CONTENT_TYPE = "application/json";
|
|
433
|
+
var DEFAULT_DURATION_BUCKETS = [1, 5, 10, 25, 50, 100, 250, 500, 1e3, 2500, 5e3, 1e4];
|
|
434
|
+
function makePrometheusMetricsExporter(registry, options = {}) {
|
|
435
|
+
return {
|
|
436
|
+
contentType: PROMETHEUS_CONTENT_TYPE,
|
|
437
|
+
export: () => formatPrometheusMetrics(registry.snapshot(), options)
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
function formatPrometheusMetrics(snapshot, options = {}) {
|
|
441
|
+
const lines = [];
|
|
442
|
+
const emittedHeaders = /* @__PURE__ */ new Set();
|
|
443
|
+
const now = Math.trunc(_nullishCoalesce(_optionalChain([options, 'access', _21 => _21.now, 'optionalCall', _22 => _22()]), () => ( Date.now())));
|
|
444
|
+
const metricName = (name) => sanitizeMetricName(`${_nullishCoalesce(options.prefix, () => ( ""))}${name}`);
|
|
445
|
+
const emitHeader = (name, type) => {
|
|
446
|
+
const safeName = metricName(name);
|
|
447
|
+
const key = `${safeName}:${type}`;
|
|
448
|
+
if (emittedHeaders.has(key)) return;
|
|
449
|
+
emittedHeaders.add(key);
|
|
450
|
+
if (_optionalChain([options, 'access', _23 => _23.help, 'optionalAccess', _24 => _24[name]])) lines.push(`# HELP ${safeName} ${escapeHelp(options.help[name])}`);
|
|
451
|
+
lines.push(`# TYPE ${safeName} ${type}`);
|
|
452
|
+
};
|
|
453
|
+
const emitSample = (name, labels, value, exemplar) => {
|
|
454
|
+
const exemplarSuffix = options.includeExemplars && exemplar ? formatExemplar(exemplar) : "";
|
|
455
|
+
const suffix = exemplarSuffix || !options.includeTimestamp ? exemplarSuffix : ` ${now}`;
|
|
456
|
+
lines.push(`${metricName(name)}${formatLabels(labels)} ${formatNumber(value)}${suffix}`);
|
|
457
|
+
};
|
|
458
|
+
for (const counter of snapshot.counters) {
|
|
459
|
+
emitHeader(counter.name, "counter");
|
|
460
|
+
emitSample(counter.name, counter.labels, counter.value);
|
|
461
|
+
}
|
|
462
|
+
for (const gauge of snapshot.gauges) {
|
|
463
|
+
emitHeader(gauge.name, "gauge");
|
|
464
|
+
emitSample(gauge.name, gauge.labels, gauge.value);
|
|
465
|
+
}
|
|
466
|
+
for (const histogram of snapshot.histograms) {
|
|
467
|
+
emitHeader(histogram.name, "histogram");
|
|
468
|
+
const buckets = histogram.buckets;
|
|
469
|
+
let cumulative = 0;
|
|
470
|
+
for (let i = 0; i < buckets.boundaries.length; i++) {
|
|
471
|
+
cumulative += _nullishCoalesce(buckets.counts[i], () => ( 0));
|
|
472
|
+
emitSample(`${histogram.name}_bucket`, { ...histogram.labels, le: String(buckets.boundaries[i]) }, cumulative, _optionalChain([buckets, 'access', _25 => _25.exemplars, 'optionalAccess', _26 => _26[i]]));
|
|
473
|
+
}
|
|
474
|
+
cumulative += _nullishCoalesce(buckets.counts[buckets.boundaries.length], () => ( 0));
|
|
475
|
+
emitSample(`${histogram.name}_bucket`, { ...histogram.labels, le: "+Inf" }, cumulative, _optionalChain([buckets, 'access', _27 => _27.exemplars, 'optionalAccess', _28 => _28[buckets.boundaries.length]]));
|
|
476
|
+
emitSample(`${histogram.name}_sum`, histogram.labels, buckets.sum);
|
|
477
|
+
emitSample(`${histogram.name}_count`, histogram.labels, buckets.count);
|
|
478
|
+
}
|
|
479
|
+
return lines.length === 0 ? "" : `${lines.join("\n")}
|
|
480
|
+
`;
|
|
481
|
+
}
|
|
482
|
+
function metricsSnapshotToOtlp(snapshot, options = {}) {
|
|
483
|
+
const timeUnixNano = unixNanoFromMs(_nullishCoalesce(_optionalChain([options, 'access', _29 => _29.now, 'optionalCall', _30 => _30()]), () => ( Date.now())));
|
|
484
|
+
const scope = { name: _nullishCoalesce(options.scopeName, () => ( "brass-runtime")) };
|
|
485
|
+
if (options.scopeVersion) scope.version = options.scopeVersion;
|
|
486
|
+
const metrics = [];
|
|
487
|
+
for (const counter of snapshot.counters) {
|
|
488
|
+
metrics.push({
|
|
489
|
+
name: counter.name,
|
|
490
|
+
sum: {
|
|
491
|
+
aggregationTemporality: 2,
|
|
492
|
+
isMonotonic: true,
|
|
493
|
+
dataPoints: [numberDataPoint(counter.labels, counter.value, timeUnixNano)]
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
for (const gauge of snapshot.gauges) {
|
|
498
|
+
metrics.push({
|
|
499
|
+
name: gauge.name,
|
|
500
|
+
gauge: {
|
|
501
|
+
dataPoints: [numberDataPoint(gauge.labels, gauge.value, timeUnixNano)]
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
for (const histogram of snapshot.histograms) {
|
|
506
|
+
const buckets = histogram.buckets;
|
|
507
|
+
metrics.push({
|
|
508
|
+
name: histogram.name,
|
|
509
|
+
histogram: {
|
|
510
|
+
aggregationTemporality: 2,
|
|
511
|
+
dataPoints: [{
|
|
512
|
+
attributes: toOtlpAttributes(histogram.labels),
|
|
513
|
+
timeUnixNano,
|
|
514
|
+
count: String(buckets.count),
|
|
515
|
+
sum: finiteOrZero(buckets.sum),
|
|
516
|
+
min: finiteOrZero(buckets.min),
|
|
517
|
+
max: finiteOrZero(buckets.max),
|
|
518
|
+
explicitBounds: buckets.boundaries,
|
|
519
|
+
bucketCounts: buckets.counts.map((count) => String(count)),
|
|
520
|
+
...buckets.exemplars ? { exemplars: buckets.exemplars.filter(isMetricExemplar).map(exemplarToOtlp) } : {}
|
|
521
|
+
}]
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
return {
|
|
526
|
+
resourceMetrics: [{
|
|
527
|
+
resource: { attributes: toOtlpAttributes(_nullishCoalesce(options.resource, () => ( {}))) },
|
|
528
|
+
scopeMetrics: [{ scope, metrics }]
|
|
529
|
+
}]
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
function makeOtlpHttpMetricsExporter(registry, options) {
|
|
533
|
+
return {
|
|
534
|
+
export: async () => {
|
|
535
|
+
const body = JSON.stringify(metricsSnapshotToOtlp(registry.snapshot(), options));
|
|
536
|
+
const response = await postOtlpJson(options, body);
|
|
537
|
+
return { status: response.status, body };
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
function makeRuntimeMetricsSink(metrics, options = {}) {
|
|
542
|
+
const clock = _nullishCoalesce(options.clock, () => ( Date.now));
|
|
543
|
+
const durationBuckets = [..._nullishCoalesce(options.durationBuckets, () => ( DEFAULT_DURATION_BUCKETS))];
|
|
544
|
+
const includeEventTotals = _nullishCoalesce(options.includeEventTotals, () => ( true));
|
|
545
|
+
const includeSpanNameLabel = _nullishCoalesce(options.includeSpanNameLabel, () => ( false));
|
|
546
|
+
const fiberStarts = /* @__PURE__ */ new Map();
|
|
547
|
+
const scopeStarts = /* @__PURE__ */ new Map();
|
|
548
|
+
const spanStarts = /* @__PURE__ */ new Map();
|
|
549
|
+
const activeFibers = metrics.gauge("brass_runtime_fibers_active");
|
|
550
|
+
const activeScopes = metrics.gauge("brass_runtime_scopes_active");
|
|
551
|
+
const activeSpans = metrics.gauge("brass_runtime_spans_active");
|
|
552
|
+
return {
|
|
553
|
+
emit(ev, ctx) {
|
|
554
|
+
try {
|
|
555
|
+
if (includeEventTotals) metrics.counter("brass_runtime_events_total", { type: ev.type }).increment();
|
|
556
|
+
recordRuntimeMetricEvent(ev, ctx, clock(), durationBuckets, {
|
|
557
|
+
metrics,
|
|
558
|
+
fiberStarts,
|
|
559
|
+
scopeStarts,
|
|
560
|
+
spanStarts,
|
|
561
|
+
activeFibers,
|
|
562
|
+
activeScopes,
|
|
563
|
+
activeSpans,
|
|
564
|
+
includeSpanNameLabel
|
|
565
|
+
});
|
|
566
|
+
} catch (e3) {
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
function recordRuntimeMetricEvent(ev, ctx, now, durationBuckets, state) {
|
|
572
|
+
switch (ev.type) {
|
|
573
|
+
case "fiber.start":
|
|
574
|
+
state.fiberStarts.set(ev.fiberId, now);
|
|
575
|
+
state.metrics.counter("brass_runtime_fibers_started_total").increment();
|
|
576
|
+
state.activeFibers.increment();
|
|
577
|
+
return;
|
|
578
|
+
case "fiber.end": {
|
|
579
|
+
state.metrics.counter("brass_runtime_fibers_finished_total", { status: ev.status }).increment();
|
|
580
|
+
decrementIfPositive(state.activeFibers);
|
|
581
|
+
const started = state.fiberStarts.get(ev.fiberId);
|
|
582
|
+
state.fiberStarts.delete(ev.fiberId);
|
|
583
|
+
if (started !== void 0) {
|
|
584
|
+
const durationMs = now - started;
|
|
585
|
+
state.metrics.histogram("brass_runtime_fiber_duration_ms", [...durationBuckets], { status: ev.status }).observe(durationMs, exemplarFromTraceContext(ctx, durationMs, now));
|
|
586
|
+
}
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
case "scope.open":
|
|
590
|
+
state.scopeStarts.set(ev.scopeId, now);
|
|
591
|
+
state.metrics.counter("brass_runtime_scopes_opened_total").increment();
|
|
592
|
+
state.activeScopes.increment();
|
|
593
|
+
return;
|
|
594
|
+
case "scope.close": {
|
|
595
|
+
state.metrics.counter("brass_runtime_scopes_closed_total", { status: ev.status }).increment();
|
|
596
|
+
decrementIfPositive(state.activeScopes);
|
|
597
|
+
const started = state.scopeStarts.get(ev.scopeId);
|
|
598
|
+
state.scopeStarts.delete(ev.scopeId);
|
|
599
|
+
if (started !== void 0) {
|
|
600
|
+
const durationMs = now - started;
|
|
601
|
+
state.metrics.histogram("brass_runtime_scope_duration_ms", [...durationBuckets], { status: ev.status }).observe(durationMs, exemplarFromTraceContext(ctx, durationMs, now));
|
|
602
|
+
}
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
case "span.start":
|
|
606
|
+
if (ctx.spanId) state.spanStarts.set(ctx.spanId, now);
|
|
607
|
+
state.metrics.counter("brass_runtime_spans_started_total", state.includeSpanNameLabel ? { name: ev.name } : {}).increment();
|
|
608
|
+
state.activeSpans.increment();
|
|
609
|
+
return;
|
|
610
|
+
case "span.end": {
|
|
611
|
+
const status = ev.status;
|
|
612
|
+
state.metrics.counter("brass_runtime_spans_finished_total", { status }).increment();
|
|
613
|
+
decrementIfPositive(state.activeSpans);
|
|
614
|
+
if (ctx.spanId) {
|
|
615
|
+
const started = state.spanStarts.get(ctx.spanId);
|
|
616
|
+
state.spanStarts.delete(ctx.spanId);
|
|
617
|
+
if (started !== void 0) {
|
|
618
|
+
const durationMs = now - started;
|
|
619
|
+
state.metrics.histogram("brass_runtime_span_duration_ms", [...durationBuckets], { status }).observe(durationMs, exemplarFromTraceContext(ctx, durationMs, now));
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
case "log":
|
|
625
|
+
state.metrics.counter("brass_runtime_logs_total", { level: ev.level }).increment();
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
function exemplarFromTraceContext(trace, value, timestamp = Date.now(), labels) {
|
|
630
|
+
if (!_optionalChain([trace, 'optionalAccess', _31 => _31.traceId]) || !_optionalChain([trace, 'optionalAccess', _32 => _32.spanId])) return void 0;
|
|
631
|
+
return {
|
|
632
|
+
value,
|
|
633
|
+
timestamp,
|
|
634
|
+
traceId: normalizeTraceId(trace.traceId),
|
|
635
|
+
spanId: normalizeSpanId(trace.spanId),
|
|
636
|
+
...labels ? { labels } : {}
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
function decrementIfPositive(gauge) {
|
|
640
|
+
if (gauge.value() > 0) gauge.decrement();
|
|
641
|
+
}
|
|
642
|
+
function numberDataPoint(labels, value, timeUnixNano) {
|
|
643
|
+
return {
|
|
644
|
+
attributes: toOtlpAttributes(labels),
|
|
645
|
+
timeUnixNano,
|
|
646
|
+
asDouble: finiteOrZero(value)
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
function exemplarToOtlp(exemplar) {
|
|
650
|
+
return {
|
|
651
|
+
timeUnixNano: unixNanoFromMs(exemplar.timestamp),
|
|
652
|
+
asDouble: finiteOrZero(exemplar.value),
|
|
653
|
+
...exemplar.traceId ? { traceId: normalizeTraceId(exemplar.traceId) } : {},
|
|
654
|
+
...exemplar.spanId ? { spanId: normalizeSpanId(exemplar.spanId) } : {},
|
|
655
|
+
filteredAttributes: toOtlpAttributes(_nullishCoalesce(exemplar.labels, () => ( {})))
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
function isMetricExemplar(value) {
|
|
659
|
+
return value !== void 0;
|
|
660
|
+
}
|
|
661
|
+
function toOtlpAttributes(labels) {
|
|
662
|
+
return Object.entries(labels).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => ({
|
|
663
|
+
key,
|
|
664
|
+
value: otlpAnyValue(value)
|
|
665
|
+
}));
|
|
666
|
+
}
|
|
667
|
+
function otlpAnyValue(value) {
|
|
668
|
+
if (typeof value === "boolean") return { boolValue: value };
|
|
669
|
+
if (typeof value === "number") return { doubleValue: finiteOrZero(value) };
|
|
670
|
+
return { stringValue: value };
|
|
671
|
+
}
|
|
672
|
+
async function postOtlpJson(options, body) {
|
|
673
|
+
const fetchImpl = _nullishCoalesce(options.fetch, () => ( globalThis.fetch));
|
|
674
|
+
if (typeof fetchImpl !== "function") {
|
|
675
|
+
throw new Error("No fetch implementation available for OTLP export");
|
|
676
|
+
}
|
|
677
|
+
const response = await fetchImpl(options.url, {
|
|
678
|
+
method: "POST",
|
|
679
|
+
headers: {
|
|
680
|
+
"content-type": OTLP_JSON_CONTENT_TYPE,
|
|
681
|
+
..._nullishCoalesce(options.headers, () => ( {}))
|
|
682
|
+
},
|
|
683
|
+
body
|
|
684
|
+
});
|
|
685
|
+
if (_optionalChain([response, 'optionalAccess', _33 => _33.ok]) === false) {
|
|
686
|
+
let detail = "";
|
|
687
|
+
try {
|
|
688
|
+
detail = response.text ? `: ${await response.text()}` : "";
|
|
689
|
+
} catch (e4) {
|
|
690
|
+
detail = "";
|
|
691
|
+
}
|
|
692
|
+
throw new Error(`OTLP export failed with status ${_nullishCoalesce(response.status, () => ( "unknown"))}${detail}`);
|
|
693
|
+
}
|
|
694
|
+
return _nullishCoalesce(response, () => ( {}));
|
|
695
|
+
}
|
|
696
|
+
function unixNanoFromMs(ms) {
|
|
697
|
+
const wholeMs = Math.max(0, Math.trunc(ms));
|
|
698
|
+
return (BigInt(wholeMs) * 1000000n).toString();
|
|
699
|
+
}
|
|
700
|
+
function formatLabels(labels) {
|
|
701
|
+
const entries = Object.entries(labels).sort(([a], [b]) => a.localeCompare(b));
|
|
702
|
+
if (entries.length === 0) return "";
|
|
703
|
+
return `{${entries.map(([key, value]) => `${sanitizeLabelName(key)}="${escapeLabelValue(value)}"`).join(",")}}`;
|
|
704
|
+
}
|
|
705
|
+
function formatExemplar(exemplar) {
|
|
706
|
+
const labels = {
|
|
707
|
+
..._nullishCoalesce(exemplar.labels, () => ( {})),
|
|
708
|
+
...exemplar.traceId ? { trace_id: normalizeTraceId(exemplar.traceId) } : {},
|
|
709
|
+
...exemplar.spanId ? { span_id: normalizeSpanId(exemplar.spanId) } : {}
|
|
710
|
+
};
|
|
711
|
+
if (Object.keys(labels).length === 0) return "";
|
|
712
|
+
return ` # ${formatLabels(labels)} ${formatNumber(exemplar.value)} ${formatNumber(exemplar.timestamp / 1e3)}`;
|
|
713
|
+
}
|
|
714
|
+
function sanitizeMetricName(name) {
|
|
715
|
+
const safe = name.replace(/[^a-zA-Z0-9_:]/g, "_");
|
|
716
|
+
return /^[a-zA-Z_:]/.test(safe) ? safe : `_${safe}`;
|
|
717
|
+
}
|
|
718
|
+
function sanitizeLabelName(name) {
|
|
719
|
+
const safe = name.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
720
|
+
return /^[a-zA-Z_]/.test(safe) ? safe : `_${safe}`;
|
|
721
|
+
}
|
|
722
|
+
function escapeLabelValue(value) {
|
|
723
|
+
return String(value).replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/"/g, '\\"');
|
|
724
|
+
}
|
|
725
|
+
function escapeHelp(value) {
|
|
726
|
+
return String(value).replace(/\\/g, "\\\\").replace(/\n/g, "\\n");
|
|
727
|
+
}
|
|
728
|
+
function formatNumber(value) {
|
|
729
|
+
return Number.isFinite(value) ? String(value) : "0";
|
|
730
|
+
}
|
|
731
|
+
function finiteOrZero(value) {
|
|
732
|
+
return Number.isFinite(value) ? value : 0;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// src/observability/logs.ts
|
|
736
|
+
var LEVEL_WEIGHT = {
|
|
737
|
+
debug: 10,
|
|
738
|
+
info: 20,
|
|
739
|
+
warn: 30,
|
|
740
|
+
error: 40
|
|
741
|
+
};
|
|
742
|
+
function makeStructuredLogSink(options = {}) {
|
|
743
|
+
const minLevel = _nullishCoalesce(options.minLevel, () => ( "debug"));
|
|
744
|
+
const clock = _nullishCoalesce(options.clock, () => ( Date.now));
|
|
745
|
+
const write = _nullishCoalesce(options.write, () => ( defaultStructuredLogWriter));
|
|
746
|
+
const redactor = makeObservabilityRedactor(options.redact);
|
|
747
|
+
return {
|
|
748
|
+
emit(ev, ctx) {
|
|
749
|
+
if (ev.type !== "log") return;
|
|
750
|
+
if (LEVEL_WEIGHT[ev.level] < LEVEL_WEIGHT[minLevel]) return;
|
|
751
|
+
const wallTs = clock();
|
|
752
|
+
try {
|
|
753
|
+
write({
|
|
754
|
+
ts: new Date(wallTs).toISOString(),
|
|
755
|
+
wallTs,
|
|
756
|
+
level: ev.level,
|
|
757
|
+
message: ev.message,
|
|
758
|
+
fields: redactor.fields(_nullishCoalesce(ev.fields, () => ( {}))),
|
|
759
|
+
fiberId: ctx.fiberId,
|
|
760
|
+
scopeId: ctx.scopeId,
|
|
761
|
+
traceId: ctx.traceId,
|
|
762
|
+
spanId: ctx.spanId,
|
|
763
|
+
parentSpanId: ctx.parentSpanId,
|
|
764
|
+
traceState: ctx.traceState
|
|
765
|
+
});
|
|
766
|
+
} catch (e5) {
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
function formatStructuredLog(record) {
|
|
772
|
+
return JSON.stringify(record);
|
|
773
|
+
}
|
|
774
|
+
function structuredLogsToOtlp(records, options = {}) {
|
|
775
|
+
const scope = { name: _nullishCoalesce(options.scopeName, () => ( "brass-runtime")) };
|
|
776
|
+
if (options.scopeVersion) scope.version = options.scopeVersion;
|
|
777
|
+
return {
|
|
778
|
+
resourceLogs: [{
|
|
779
|
+
resource: { attributes: toOtlpAttributes(_nullishCoalesce(options.resource, () => ( {}))) },
|
|
780
|
+
scopeLogs: [{
|
|
781
|
+
scope,
|
|
782
|
+
logRecords: records.map(logRecordToOtlp)
|
|
783
|
+
}]
|
|
784
|
+
}]
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
function makeOtlpHttpLogExporter(source, options) {
|
|
788
|
+
return {
|
|
789
|
+
export: async () => {
|
|
790
|
+
const records = typeof source === "function" ? source() : source.exportRecords();
|
|
791
|
+
const body = JSON.stringify(structuredLogsToOtlp(records, options));
|
|
792
|
+
const response = await postOtlpJson(options, body);
|
|
793
|
+
return { status: response.status, body, logCount: records.length };
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
function logEffect(level, message, fields = {}) {
|
|
798
|
+
return _chunkMVGUEJ5Zcjs.asyncSync.call(void 0, () => {
|
|
799
|
+
const fiber = _chunkGLE2WY7Zcjs.getCurrentFiber.call(void 0, );
|
|
800
|
+
const runtime = _optionalChain([fiber, 'optionalAccess', _34 => _34.runtime]);
|
|
801
|
+
if (!runtime) return;
|
|
802
|
+
const inherited = _optionalChain([fiber, 'access', _35 => _35.fiberContext, 'optionalAccess', _36 => _36.log]) ? _chunkGLE2WY7Zcjs.ctxToObject.call(void 0, fiber.fiberContext.log) : {};
|
|
803
|
+
runtime.log(level, message, { ...inherited, ...fields });
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
function withLogContext(patch, effect) {
|
|
807
|
+
return _chunkMVGUEJ5Zcjs.asyncFlatMap.call(void 0,
|
|
808
|
+
startLogContext(patch),
|
|
809
|
+
(state) => _chunkMVGUEJ5Zcjs.asyncFold.call(void 0,
|
|
810
|
+
effect,
|
|
811
|
+
(error) => _chunkMVGUEJ5Zcjs.asyncFlatMap.call(void 0, endLogContext(state), () => _chunkMVGUEJ5Zcjs.asyncFail.call(void 0, error)),
|
|
812
|
+
(value) => _chunkMVGUEJ5Zcjs.asyncFlatMap.call(void 0, endLogContext(state), () => _chunkMVGUEJ5Zcjs.asyncSucceed.call(void 0, value))
|
|
813
|
+
)
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
function startLogContext(patch) {
|
|
817
|
+
return _chunkMVGUEJ5Zcjs.asyncSync.call(void 0, () => {
|
|
818
|
+
const fiber = _chunkGLE2WY7Zcjs.getCurrentFiber.call(void 0, );
|
|
819
|
+
if (!_optionalChain([fiber, 'optionalAccess', _37 => _37.fiberContext])) return void 0;
|
|
820
|
+
const previous = _nullishCoalesce(fiber.fiberContext.log, () => ( _chunkGLE2WY7Zcjs.emptyContext));
|
|
821
|
+
const state = { fiber, previous, ended: false };
|
|
822
|
+
fiber.fiberContext = { ...fiber.fiberContext, log: _chunkGLE2WY7Zcjs.ctxExtend.call(void 0, previous, patch) };
|
|
823
|
+
_optionalChain([fiber, 'access', _38 => _38.addFinalizer, 'optionalCall', _39 => _39(() => restoreLogContext(state))]);
|
|
824
|
+
return state;
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
function endLogContext(state) {
|
|
828
|
+
return _chunkMVGUEJ5Zcjs.asyncSync.call(void 0, () => restoreLogContext(state));
|
|
829
|
+
}
|
|
830
|
+
function restoreLogContext(state) {
|
|
831
|
+
if (!state || state.ended) return;
|
|
832
|
+
state.ended = true;
|
|
833
|
+
if (_optionalChain([state, 'access', _40 => _40.fiber, 'optionalAccess', _41 => _41.fiberContext])) {
|
|
834
|
+
state.fiber.fiberContext = { ...state.fiber.fiberContext, log: state.previous };
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
function defaultStructuredLogWriter(record) {
|
|
838
|
+
const line = formatStructuredLog(record);
|
|
839
|
+
if (record.level === "error") console.error(line);
|
|
840
|
+
else console.log(line);
|
|
841
|
+
}
|
|
842
|
+
function logRecordToOtlp(record) {
|
|
843
|
+
return {
|
|
844
|
+
timeUnixNano: unixNanoFromMs(record.wallTs),
|
|
845
|
+
severityText: record.level.toUpperCase(),
|
|
846
|
+
severityNumber: severityNumber(record.level),
|
|
847
|
+
body: { stringValue: record.message },
|
|
848
|
+
...record.traceId ? { traceId: record.traceId } : {},
|
|
849
|
+
...record.spanId ? { spanId: record.spanId } : {},
|
|
850
|
+
attributes: toOtlpAttributes(normalizeLogAttributes({
|
|
851
|
+
...record.fields,
|
|
852
|
+
...record.fiberId !== void 0 ? { "brass.fiber_id": record.fiberId } : {},
|
|
853
|
+
...record.scopeId !== void 0 ? { "brass.scope_id": record.scopeId } : {},
|
|
854
|
+
...record.parentSpanId ? { "brass.parent_span_id": record.parentSpanId } : {},
|
|
855
|
+
...record.traceState ? { "w3c.tracestate": record.traceState } : {}
|
|
856
|
+
}))
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
function severityNumber(level) {
|
|
860
|
+
switch (level) {
|
|
861
|
+
case "debug":
|
|
862
|
+
return 5;
|
|
863
|
+
case "info":
|
|
864
|
+
return 9;
|
|
865
|
+
case "warn":
|
|
866
|
+
return 13;
|
|
867
|
+
case "error":
|
|
868
|
+
return 17;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
function normalizeLogAttributes(fields) {
|
|
872
|
+
const out = {};
|
|
873
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
874
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
875
|
+
out[key] = value;
|
|
876
|
+
} else if (value != null) {
|
|
877
|
+
out[key] = JSON.stringify(value);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
return out;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// src/observability/sampling.ts
|
|
884
|
+
var alwaysOnSampler = {
|
|
885
|
+
shouldSample: () => true
|
|
886
|
+
};
|
|
887
|
+
var alwaysOffSampler = {
|
|
888
|
+
shouldSample: () => false
|
|
889
|
+
};
|
|
890
|
+
function ratioSampler(ratio2) {
|
|
891
|
+
const bounded = clampRatio(ratio2);
|
|
892
|
+
if (bounded >= 1) return alwaysOnSampler;
|
|
893
|
+
if (bounded <= 0) return alwaysOffSampler;
|
|
894
|
+
return {
|
|
895
|
+
shouldSample: (input) => traceRatio(input.traceId) < bounded
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
function makeTraceSampler(options = {}) {
|
|
899
|
+
const fallback = _nullishCoalesce(options.sampler, () => ( ratioSampler(_nullishCoalesce(options.ratio, () => ( 1)))));
|
|
900
|
+
const rules = _nullishCoalesce(options.rules, () => ( []));
|
|
901
|
+
return {
|
|
902
|
+
shouldSample(input) {
|
|
903
|
+
for (const rule of rules) {
|
|
904
|
+
if (!samplingRuleMatches(rule, input)) continue;
|
|
905
|
+
if (rule.sampled !== void 0) return rule.sampled;
|
|
906
|
+
if (rule.ratio !== void 0) return shouldSampleWith(ratioSampler(rule.ratio), input);
|
|
907
|
+
}
|
|
908
|
+
return shouldSampleWith(fallback, input);
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
function resolveTraceSampling(config) {
|
|
913
|
+
if (config === false) {
|
|
914
|
+
return { sampler: alwaysOffSampler, respectRemoteSampled: true, forceSampleOnError: false };
|
|
915
|
+
}
|
|
916
|
+
if (typeof config === "number") {
|
|
917
|
+
return { sampler: ratioSampler(config), respectRemoteSampled: true, forceSampleOnError: false };
|
|
918
|
+
}
|
|
919
|
+
if (isTraceSampler(config)) {
|
|
920
|
+
return { sampler: config, respectRemoteSampled: true, forceSampleOnError: false };
|
|
921
|
+
}
|
|
922
|
+
return {
|
|
923
|
+
sampler: makeTraceSampler(_nullishCoalesce(config, () => ( {}))),
|
|
924
|
+
respectRemoteSampled: _nullishCoalesce(_optionalChain([config, 'optionalAccess', _42 => _42.respectRemoteSampled]), () => ( true)),
|
|
925
|
+
forceSampleOnError: _nullishCoalesce(_optionalChain([config, 'optionalAccess', _43 => _43.forceSampleOnError]), () => ( false))
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
function shouldSampleWith(sampler, input) {
|
|
929
|
+
if (!sampler) return true;
|
|
930
|
+
if (typeof sampler === "function") return sampler(input);
|
|
931
|
+
return sampler.shouldSample(input);
|
|
932
|
+
}
|
|
933
|
+
function samplingRuleMatches(rule, input) {
|
|
934
|
+
if (rule.name && !matchText(rule.name, input.spanName)) return false;
|
|
935
|
+
if (rule.route && !matchText(rule.route, _optionalChain([input, 'access', _44 => _44.attributes, 'optionalAccess', _45 => _45["http.route"]]))) return false;
|
|
936
|
+
return true;
|
|
937
|
+
}
|
|
938
|
+
function matchText(pattern, value) {
|
|
939
|
+
if (typeof value !== "string") return false;
|
|
940
|
+
return typeof pattern === "string" ? pattern === value : pattern.test(value);
|
|
941
|
+
}
|
|
942
|
+
function isTraceSampler(value) {
|
|
943
|
+
return typeof value === "function" || typeof value === "object" && value !== null && typeof value.shouldSample === "function";
|
|
944
|
+
}
|
|
945
|
+
function traceRatio(traceId) {
|
|
946
|
+
const normalized = normalizeTraceId(traceId);
|
|
947
|
+
const head = normalized.slice(0, 8);
|
|
948
|
+
const value = Number.parseInt(head, 16);
|
|
949
|
+
if (!Number.isFinite(value)) return 1;
|
|
950
|
+
return value / 4294967295;
|
|
951
|
+
}
|
|
952
|
+
function clampRatio(value) {
|
|
953
|
+
if (!Number.isFinite(value)) return 1;
|
|
954
|
+
return Math.max(0, Math.min(1, value));
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// src/observability/traces.ts
|
|
958
|
+
function withSpan(name, effect, attributesOrOptions) {
|
|
959
|
+
const options = resolveSpanOptions(attributesOrOptions);
|
|
960
|
+
return _chunkMVGUEJ5Zcjs.asyncFlatMap.call(void 0,
|
|
961
|
+
startSpan(name, options),
|
|
962
|
+
(state) => _chunkMVGUEJ5Zcjs.asyncFold.call(void 0,
|
|
963
|
+
effect,
|
|
964
|
+
(error) => _chunkMVGUEJ5Zcjs.asyncFlatMap.call(void 0, endSpan(state, "failure", error), () => _chunkMVGUEJ5Zcjs.asyncFail.call(void 0, error)),
|
|
965
|
+
(value) => _chunkMVGUEJ5Zcjs.asyncFlatMap.call(void 0, endSpan(state, "success"), () => _chunkMVGUEJ5Zcjs.asyncSucceed.call(void 0, value))
|
|
966
|
+
)
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
function spanLink(trace, attributes) {
|
|
970
|
+
return {
|
|
971
|
+
traceId: trace.traceId,
|
|
972
|
+
spanId: trace.spanId,
|
|
973
|
+
...trace.traceState ? { traceState: trace.traceState } : {},
|
|
974
|
+
...attributes ? { attributes } : {}
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
function currentSpanLink(attributes) {
|
|
978
|
+
const fiber = _chunkGLE2WY7Zcjs.getCurrentFiber.call(void 0, );
|
|
979
|
+
const trace = _optionalChain([fiber, 'optionalAccess', _46 => _46.fiberContext, 'optionalAccess', _47 => _47.trace]);
|
|
980
|
+
return _optionalChain([trace, 'optionalAccess', _48 => _48.traceId]) && _optionalChain([trace, 'optionalAccess', _49 => _49.spanId]) ? spanLink(trace, attributes) : void 0;
|
|
981
|
+
}
|
|
982
|
+
function withBaggage(baggage, effect) {
|
|
983
|
+
return _chunkMVGUEJ5Zcjs.asyncFlatMap.call(void 0,
|
|
984
|
+
startBaggage(baggage),
|
|
985
|
+
(state) => _chunkMVGUEJ5Zcjs.asyncFold.call(void 0,
|
|
986
|
+
effect,
|
|
987
|
+
(error) => _chunkMVGUEJ5Zcjs.asyncFlatMap.call(void 0, endBaggage(state), () => _chunkMVGUEJ5Zcjs.asyncFail.call(void 0, error)),
|
|
988
|
+
(value) => _chunkMVGUEJ5Zcjs.asyncFlatMap.call(void 0, endBaggage(state), () => _chunkMVGUEJ5Zcjs.asyncSucceed.call(void 0, value))
|
|
989
|
+
)
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
function currentBaggage() {
|
|
993
|
+
const fiber = _chunkGLE2WY7Zcjs.getCurrentFiber.call(void 0, );
|
|
994
|
+
const baggage = _optionalChain([fiber, 'optionalAccess', _50 => _50.fiberContext, 'optionalAccess', _51 => _51.trace, 'optionalAccess', _52 => _52.baggage]);
|
|
995
|
+
return baggage ? { ...baggage } : void 0;
|
|
996
|
+
}
|
|
997
|
+
function spanEvent(name, attributes = {}) {
|
|
998
|
+
return _chunkMVGUEJ5Zcjs.asyncSync.call(void 0, () => {
|
|
999
|
+
const fiber = _chunkGLE2WY7Zcjs.getCurrentFiber.call(void 0, );
|
|
1000
|
+
const trace = _optionalChain([fiber, 'optionalAccess', _53 => _53.fiberContext, 'optionalAccess', _54 => _54.trace]);
|
|
1001
|
+
if (!_optionalChain([fiber, 'optionalAccess', _55 => _55.runtime]) || !trace || trace.sampled === false) return;
|
|
1002
|
+
fiber.runtime.hooks.emit(
|
|
1003
|
+
{ type: "span.event", name, attributes },
|
|
1004
|
+
spanContext(fiber, trace)
|
|
1005
|
+
);
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
function spansToOtlp(spans, options = {}) {
|
|
1009
|
+
const scope = { name: _nullishCoalesce(options.scopeName, () => ( "brass-runtime")) };
|
|
1010
|
+
if (options.scopeVersion) scope.version = options.scopeVersion;
|
|
1011
|
+
return {
|
|
1012
|
+
resourceSpans: [{
|
|
1013
|
+
resource: { attributes: toOtlpAttributes(_nullishCoalesce(options.resource, () => ( {}))) },
|
|
1014
|
+
scopeSpans: [{
|
|
1015
|
+
scope,
|
|
1016
|
+
spans: spans.map(spanToOtlp)
|
|
1017
|
+
}]
|
|
1018
|
+
}]
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
function makeOtlpHttpSpanExporter(source, options) {
|
|
1022
|
+
return {
|
|
1023
|
+
export: async () => {
|
|
1024
|
+
const spans = typeof source === "function" ? source() : source.exportFinished();
|
|
1025
|
+
const body = JSON.stringify(spansToOtlp(spans, options));
|
|
1026
|
+
const response = await postOtlpJson(options, body);
|
|
1027
|
+
return { status: response.status, body };
|
|
1028
|
+
}
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
function startSpan(name, options) {
|
|
1032
|
+
return _chunkMVGUEJ5Zcjs.asyncSync.call(void 0, () => {
|
|
1033
|
+
const fiber = _chunkGLE2WY7Zcjs.getCurrentFiber.call(void 0, );
|
|
1034
|
+
const runtime = _optionalChain([fiber, 'optionalAccess', _56 => _56.runtime]);
|
|
1035
|
+
if (!_optionalChain([fiber, 'optionalAccess', _57 => _57.fiberContext]) || !runtime) return void 0;
|
|
1036
|
+
const previousTrace = _nullishCoalesce(fiber.fiberContext.trace, () => ( null));
|
|
1037
|
+
const tracer = resolveTracer(runtime);
|
|
1038
|
+
const traceId = _nullishCoalesce(_optionalChain([previousTrace, 'optionalAccess', _58 => _58.traceId]), () => ( tracer.newTraceId()));
|
|
1039
|
+
const sampled = decideSampling(runtime, {
|
|
1040
|
+
traceId,
|
|
1041
|
+
spanName: name,
|
|
1042
|
+
parentSampled: _optionalChain([previousTrace, 'optionalAccess', _59 => _59.sampled]),
|
|
1043
|
+
attributes: options.attributes
|
|
1044
|
+
});
|
|
1045
|
+
const trace = {
|
|
1046
|
+
traceId,
|
|
1047
|
+
spanId: tracer.newSpanId(),
|
|
1048
|
+
parentSpanId: _optionalChain([previousTrace, 'optionalAccess', _60 => _60.spanId]),
|
|
1049
|
+
sampled,
|
|
1050
|
+
traceState: _optionalChain([previousTrace, 'optionalAccess', _61 => _61.traceState]),
|
|
1051
|
+
..._optionalChain([previousTrace, 'optionalAccess', _62 => _62.baggage]) ? { baggage: previousTrace.baggage } : {}
|
|
1052
|
+
};
|
|
1053
|
+
const state = {
|
|
1054
|
+
fiber,
|
|
1055
|
+
runtime,
|
|
1056
|
+
previousTrace,
|
|
1057
|
+
trace,
|
|
1058
|
+
name,
|
|
1059
|
+
options,
|
|
1060
|
+
forceSampleOnError: _optionalChain([runtime, 'optionalAccess', _63 => _63.env, 'optionalAccess', _64 => _64.brass, 'optionalAccess', _65 => _65.forceSampleOnError]) === true,
|
|
1061
|
+
startEmitted: sampled !== false,
|
|
1062
|
+
ended: false
|
|
1063
|
+
};
|
|
1064
|
+
fiber.fiberContext = { ...fiber.fiberContext, trace };
|
|
1065
|
+
_optionalChain([fiber, 'access', _66 => _66.addFinalizer, 'optionalCall', _67 => _67((exit) => {
|
|
1066
|
+
const status = _optionalChain([exit, 'optionalAccess', _68 => _68._tag]) === "Success" ? "success" : _optionalChain([exit, 'optionalAccess', _69 => _69.cause, 'optionalAccess', _70 => _70._tag]) === "Interrupt" ? "interrupted" : "failure";
|
|
1067
|
+
finishSpan(state, status, _optionalChain([exit, 'optionalAccess', _71 => _71._tag]) === "Failure" ? exit.cause : void 0);
|
|
1068
|
+
})]);
|
|
1069
|
+
if (state.startEmitted) {
|
|
1070
|
+
runtime.hooks.emit(
|
|
1071
|
+
{ type: "span.start", name, attributes: options.attributes, links: options.links.map(normalizeSpanLink) },
|
|
1072
|
+
spanContext(fiber, trace)
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
return state;
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
function endSpan(state, status, error) {
|
|
1079
|
+
return _chunkMVGUEJ5Zcjs.asyncSync.call(void 0, () => finishSpan(state, status, error));
|
|
1080
|
+
}
|
|
1081
|
+
function finishSpan(state, status, error) {
|
|
1082
|
+
if (!state || state.ended) return;
|
|
1083
|
+
state.ended = true;
|
|
1084
|
+
if (!state.startEmitted && status === "failure" && state.forceSampleOnError) {
|
|
1085
|
+
state.trace.sampled = true;
|
|
1086
|
+
state.startEmitted = true;
|
|
1087
|
+
state.runtime.hooks.emit(
|
|
1088
|
+
{ type: "span.start", name: state.name, attributes: state.options.attributes, links: state.options.links.map(normalizeSpanLink) },
|
|
1089
|
+
spanContext(state.fiber, state.trace)
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
if (state.startEmitted) {
|
|
1093
|
+
state.runtime.hooks.emit(
|
|
1094
|
+
{ type: "span.end", name: state.name, status, error },
|
|
1095
|
+
spanContext(state.fiber, state.trace)
|
|
1096
|
+
);
|
|
1097
|
+
}
|
|
1098
|
+
if (_optionalChain([state, 'access', _72 => _72.fiber, 'optionalAccess', _73 => _73.fiberContext])) {
|
|
1099
|
+
state.fiber.fiberContext = { ...state.fiber.fiberContext, trace: state.previousTrace };
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
function spanContext(fiber, trace) {
|
|
1103
|
+
return {
|
|
1104
|
+
fiberId: _optionalChain([fiber, 'optionalAccess', _74 => _74.id]),
|
|
1105
|
+
scopeId: _optionalChain([fiber, 'optionalAccess', _75 => _75.scopeId]),
|
|
1106
|
+
traceId: trace.traceId,
|
|
1107
|
+
spanId: trace.spanId,
|
|
1108
|
+
parentSpanId: trace.parentSpanId,
|
|
1109
|
+
traceState: trace.traceState,
|
|
1110
|
+
baggage: trace.baggage,
|
|
1111
|
+
sampled: trace.sampled
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
function startBaggage(baggage) {
|
|
1115
|
+
return _chunkMVGUEJ5Zcjs.asyncSync.call(void 0, () => {
|
|
1116
|
+
const fiber = _chunkGLE2WY7Zcjs.getCurrentFiber.call(void 0, );
|
|
1117
|
+
if (!_optionalChain([fiber, 'optionalAccess', _76 => _76.fiberContext])) return void 0;
|
|
1118
|
+
const previousTrace = _nullishCoalesce(fiber.fiberContext.trace, () => ( null));
|
|
1119
|
+
if (!previousTrace) return void 0;
|
|
1120
|
+
const state = { fiber, previousTrace, ended: false };
|
|
1121
|
+
fiber.fiberContext = {
|
|
1122
|
+
...fiber.fiberContext,
|
|
1123
|
+
trace: {
|
|
1124
|
+
...previousTrace,
|
|
1125
|
+
baggage: {
|
|
1126
|
+
..._nullishCoalesce(previousTrace.baggage, () => ( {})),
|
|
1127
|
+
...baggage
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1131
|
+
_optionalChain([fiber, 'access', _77 => _77.addFinalizer, 'optionalCall', _78 => _78(() => restoreBaggage(state))]);
|
|
1132
|
+
return state;
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
function endBaggage(state) {
|
|
1136
|
+
return _chunkMVGUEJ5Zcjs.asyncSync.call(void 0, () => restoreBaggage(state));
|
|
1137
|
+
}
|
|
1138
|
+
function restoreBaggage(state) {
|
|
1139
|
+
if (!state || state.ended) return;
|
|
1140
|
+
state.ended = true;
|
|
1141
|
+
if (_optionalChain([state, 'access', _79 => _79.fiber, 'optionalAccess', _80 => _80.fiberContext])) {
|
|
1142
|
+
state.fiber.fiberContext = { ...state.fiber.fiberContext, trace: state.previousTrace };
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
function decideSampling(runtime, input) {
|
|
1146
|
+
const brass = _optionalChain([runtime, 'optionalAccess', _81 => _81.env, 'optionalAccess', _82 => _82.brass]);
|
|
1147
|
+
if (input.parentSampled === false && _optionalChain([brass, 'optionalAccess', _83 => _83.respectRemoteSampled]) !== false) return false;
|
|
1148
|
+
return shouldSampleWith(_optionalChain([brass, 'optionalAccess', _84 => _84.sampler]), input);
|
|
1149
|
+
}
|
|
1150
|
+
function resolveTracer(runtime) {
|
|
1151
|
+
const tracer = _optionalChain([runtime, 'optionalAccess', _85 => _85.env, 'optionalAccess', _86 => _86.brass, 'optionalAccess', _87 => _87.tracer]);
|
|
1152
|
+
if (_optionalChain([tracer, 'optionalAccess', _88 => _88.newTraceId]) && _optionalChain([tracer, 'optionalAccess', _89 => _89.newSpanId])) return tracer;
|
|
1153
|
+
return {
|
|
1154
|
+
newTraceId: () => randomRuntimeId("trace"),
|
|
1155
|
+
newSpanId: () => randomRuntimeId("span")
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
function spanToOtlp(span) {
|
|
1159
|
+
const status = inferOtlpStatus(span);
|
|
1160
|
+
return {
|
|
1161
|
+
traceId: normalizeTraceId(span.traceId),
|
|
1162
|
+
spanId: normalizeSpanId(span.spanId),
|
|
1163
|
+
...span.parentSpanId ? { parentSpanId: normalizeSpanId(span.parentSpanId) } : {},
|
|
1164
|
+
...span.traceState ? { traceState: span.traceState } : {},
|
|
1165
|
+
name: span.name,
|
|
1166
|
+
kind: spanKind(span),
|
|
1167
|
+
startTimeUnixNano: unixNanoFromMs(span.startWallTs),
|
|
1168
|
+
endTimeUnixNano: unixNanoFromMs(_nullishCoalesce(span.endWallTs, () => ( span.startWallTs))),
|
|
1169
|
+
attributes: toOtlpAttributes(normalizeAttributes(span.attrs)),
|
|
1170
|
+
events: span.events.map((event) => ({
|
|
1171
|
+
name: event.name,
|
|
1172
|
+
timeUnixNano: unixNanoFromMs(event.wallTs),
|
|
1173
|
+
attributes: toOtlpAttributes(normalizeAttributes(event.attrs))
|
|
1174
|
+
})),
|
|
1175
|
+
links: (_nullishCoalesce(span.links, () => ( []))).map((link) => ({
|
|
1176
|
+
traceId: normalizeTraceId(link.traceId),
|
|
1177
|
+
spanId: normalizeSpanId(link.spanId),
|
|
1178
|
+
...link.traceState ? { traceState: link.traceState } : {},
|
|
1179
|
+
attributes: toOtlpAttributes(normalizeAttributes(link.attributes))
|
|
1180
|
+
})),
|
|
1181
|
+
status
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
function spanKind(span) {
|
|
1185
|
+
const kind = span.attrs["span.kind"];
|
|
1186
|
+
if (kind === "server") return 2;
|
|
1187
|
+
if (kind === "client") return 3;
|
|
1188
|
+
if (kind === "producer") return 4;
|
|
1189
|
+
if (kind === "consumer") return 5;
|
|
1190
|
+
if (typeof kind === "number" && Number.isInteger(kind) && kind >= 1 && kind <= 5) return kind;
|
|
1191
|
+
return 1;
|
|
1192
|
+
}
|
|
1193
|
+
function resolveSpanOptions(input) {
|
|
1194
|
+
if (!input) return { attributes: {}, links: [] };
|
|
1195
|
+
if ("attributes" in input || "links" in input) {
|
|
1196
|
+
const options = input;
|
|
1197
|
+
return {
|
|
1198
|
+
attributes: _nullishCoalesce(options.attributes, () => ( {})),
|
|
1199
|
+
links: _nullishCoalesce(options.links, () => ( []))
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
return { attributes: input, links: [] };
|
|
1203
|
+
}
|
|
1204
|
+
function normalizeSpanLink(link) {
|
|
1205
|
+
return {
|
|
1206
|
+
traceId: link.traceId,
|
|
1207
|
+
spanId: link.spanId,
|
|
1208
|
+
...link.traceState ? { traceState: link.traceState } : {},
|
|
1209
|
+
...link.attributes ? { attributes: link.attributes } : {}
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
function inferOtlpStatus(span) {
|
|
1213
|
+
const end = [...span.events].reverse().find((event) => event.name === "span.end" || event.name === "fiber.end");
|
|
1214
|
+
const status = typeof _optionalChain([end, 'optionalAccess', _90 => _90.attrs]) === "object" && end.attrs != null ? end.attrs.status : void 0;
|
|
1215
|
+
if (status === "failure" || status === "interrupted") {
|
|
1216
|
+
return { code: 2, message: status };
|
|
1217
|
+
}
|
|
1218
|
+
if (status === "success") return { code: 1 };
|
|
1219
|
+
return { code: 0 };
|
|
1220
|
+
}
|
|
1221
|
+
function normalizeAttributes(attrs) {
|
|
1222
|
+
if (!attrs || typeof attrs !== "object") return {};
|
|
1223
|
+
const out = {};
|
|
1224
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
1225
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
1226
|
+
out[key] = value;
|
|
1227
|
+
} else if (value != null) {
|
|
1228
|
+
out[key] = String(value);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
return out;
|
|
1232
|
+
}
|
|
1233
|
+
function randomRuntimeId(prefix) {
|
|
1234
|
+
const cryptoLike = globalThis.crypto;
|
|
1235
|
+
if (typeof _optionalChain([cryptoLike, 'optionalAccess', _91 => _91.randomUUID]) === "function") return cryptoLike.randomUUID();
|
|
1236
|
+
return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
// src/observability/exportPipeline.ts
|
|
1240
|
+
var DEFAULT_BATCH_SIZE = 512;
|
|
1241
|
+
var DEFAULT_MAX_QUEUE_SIZE = 1e4;
|
|
1242
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
1243
|
+
var DEFAULT_SHUTDOWN_TIMEOUT_MS = 1e4;
|
|
1244
|
+
function makeExportPipeline(options) {
|
|
1245
|
+
const signal = options.signal;
|
|
1246
|
+
const clock = _nullishCoalesce(options.clock, () => ( Date.now));
|
|
1247
|
+
const batchSize = Math.max(1, Math.floor(_nullishCoalesce(options.batchSize, () => ( DEFAULT_BATCH_SIZE))));
|
|
1248
|
+
const maxQueueSize = Math.max(0, Math.floor(_nullishCoalesce(options.maxQueueSize, () => ( DEFAULT_MAX_QUEUE_SIZE))));
|
|
1249
|
+
const timeoutMs = Math.max(0, Math.floor(_nullishCoalesce(options.timeoutMs, () => ( DEFAULT_TIMEOUT_MS))));
|
|
1250
|
+
const dropPolicy = _nullishCoalesce(options.dropPolicy, () => ( "drop-oldest"));
|
|
1251
|
+
const queueGauge = _optionalChain([options, 'access', _92 => _92.metrics, 'optionalAccess', _93 => _93.gauge, 'call', _94 => _94("brass_export_queue_size", { signal })]);
|
|
1252
|
+
let queue = [];
|
|
1253
|
+
let droppedTotal = 0;
|
|
1254
|
+
const setQueueSize = () => _optionalChain([queueGauge, 'optionalAccess', _95 => _95.set, 'call', _96 => _96(queue.length)]);
|
|
1255
|
+
const dropItems = (items) => {
|
|
1256
|
+
if (items.length === 0) return;
|
|
1257
|
+
droppedTotal += items.length;
|
|
1258
|
+
_optionalChain([options, 'access', _97 => _97.metrics, 'optionalAccess', _98 => _98.counter, 'call', _99 => _99("brass_export_dropped_total", { signal }), 'access', _100 => _100.increment, 'call', _101 => _101(items.length)]);
|
|
1259
|
+
_optionalChain([options, 'access', _102 => _102.onDrop, 'optionalCall', _103 => _103(items)]);
|
|
1260
|
+
};
|
|
1261
|
+
const enqueue = (items) => {
|
|
1262
|
+
if (items.length === 0 || maxQueueSize === 0) {
|
|
1263
|
+
dropItems([...items]);
|
|
1264
|
+
setQueueSize();
|
|
1265
|
+
return 0;
|
|
1266
|
+
}
|
|
1267
|
+
const incoming = [...items];
|
|
1268
|
+
const available = maxQueueSize - queue.length;
|
|
1269
|
+
if (incoming.length <= available) {
|
|
1270
|
+
queue.push(...incoming);
|
|
1271
|
+
_optionalChain([options, 'access', _104 => _104.metrics, 'optionalAccess', _105 => _105.counter, 'call', _106 => _106("brass_export_enqueued_total", { signal }), 'access', _107 => _107.increment, 'call', _108 => _108(incoming.length)]);
|
|
1272
|
+
setQueueSize();
|
|
1273
|
+
return incoming.length;
|
|
1274
|
+
}
|
|
1275
|
+
if (dropPolicy === "drop-newest") {
|
|
1276
|
+
const accepted = incoming.slice(0, Math.max(0, available));
|
|
1277
|
+
const dropped2 = incoming.slice(accepted.length);
|
|
1278
|
+
queue.push(...accepted);
|
|
1279
|
+
_optionalChain([options, 'access', _109 => _109.metrics, 'optionalAccess', _110 => _110.counter, 'call', _111 => _111("brass_export_enqueued_total", { signal }), 'access', _112 => _112.increment, 'call', _113 => _113(accepted.length)]);
|
|
1280
|
+
dropItems(dropped2);
|
|
1281
|
+
setQueueSize();
|
|
1282
|
+
return accepted.length;
|
|
1283
|
+
}
|
|
1284
|
+
const total = queue.length + incoming.length;
|
|
1285
|
+
const dropCount = Math.max(0, total - maxQueueSize);
|
|
1286
|
+
const dropped = queue.splice(0, Math.min(dropCount, queue.length));
|
|
1287
|
+
const remainingDrop = dropCount - dropped.length;
|
|
1288
|
+
if (remainingDrop > 0) dropped.push(...incoming.splice(0, remainingDrop));
|
|
1289
|
+
queue.push(...incoming);
|
|
1290
|
+
_optionalChain([options, 'access', _114 => _114.metrics, 'optionalAccess', _115 => _115.counter, 'call', _116 => _116("brass_export_enqueued_total", { signal }), 'access', _117 => _117.increment, 'call', _118 => _118(incoming.length)]);
|
|
1291
|
+
dropItems(dropped);
|
|
1292
|
+
setQueueSize();
|
|
1293
|
+
return incoming.length;
|
|
1294
|
+
};
|
|
1295
|
+
const flush = async (flushOptions = {}) => {
|
|
1296
|
+
const startedAt = clock();
|
|
1297
|
+
const deadlineMs = flushOptions.deadlineMs;
|
|
1298
|
+
const errors = [];
|
|
1299
|
+
let exported = 0;
|
|
1300
|
+
let failed = 0;
|
|
1301
|
+
let batchCount = 0;
|
|
1302
|
+
let attempts = 0;
|
|
1303
|
+
let lastStatus;
|
|
1304
|
+
let lastBody;
|
|
1305
|
+
while (queue.length > 0) {
|
|
1306
|
+
if (deadlineMs !== void 0 && clock() - startedAt >= deadlineMs) break;
|
|
1307
|
+
const batch = queue.splice(0, batchSize);
|
|
1308
|
+
const batchStartedAt = clock();
|
|
1309
|
+
try {
|
|
1310
|
+
const result = await exportWithRetry(
|
|
1311
|
+
() => withTimeout(() => options.exportBatch(batch), timeoutMs),
|
|
1312
|
+
{
|
|
1313
|
+
signal,
|
|
1314
|
+
metrics: options.metrics,
|
|
1315
|
+
retry: options.retry
|
|
1316
|
+
}
|
|
1317
|
+
);
|
|
1318
|
+
exported += batch.length;
|
|
1319
|
+
batchCount++;
|
|
1320
|
+
attempts += result.attempts;
|
|
1321
|
+
lastStatus = result.value.status;
|
|
1322
|
+
lastBody = result.value.body;
|
|
1323
|
+
_optionalChain([options, 'access', _119 => _119.metrics, 'optionalAccess', _120 => _120.counter, 'call', _121 => _121("brass_export_batches_total", { signal, status: "success" }), 'access', _122 => _122.increment, 'call', _123 => _123()]);
|
|
1324
|
+
_optionalChain([options, 'access', _124 => _124.metrics, 'optionalAccess', _125 => _125.counter, 'call', _126 => _126("brass_export_items_total", { signal, status: "success" }), 'access', _127 => _127.increment, 'call', _128 => _128(batch.length)]);
|
|
1325
|
+
_optionalChain([options, 'access', _129 => _129.metrics, 'optionalAccess', _130 => _130.histogram, 'call', _131 => _131("brass_export_batch_duration_ms", void 0, { signal, status: "success" }), 'access', _132 => _132.observe, 'call', _133 => _133(clock() - batchStartedAt)]);
|
|
1326
|
+
} catch (error) {
|
|
1327
|
+
failed += batch.length;
|
|
1328
|
+
errors.push(error);
|
|
1329
|
+
queue = [...batch, ...queue];
|
|
1330
|
+
_optionalChain([options, 'access', _134 => _134.metrics, 'optionalAccess', _135 => _135.counter, 'call', _136 => _136("brass_export_batches_total", { signal, status: "failure" }), 'access', _137 => _137.increment, 'call', _138 => _138()]);
|
|
1331
|
+
_optionalChain([options, 'access', _139 => _139.metrics, 'optionalAccess', _140 => _140.counter, 'call', _141 => _141("brass_export_items_total", { signal, status: "failure" }), 'access', _142 => _142.increment, 'call', _143 => _143(batch.length)]);
|
|
1332
|
+
_optionalChain([options, 'access', _144 => _144.metrics, 'optionalAccess', _145 => _145.histogram, 'call', _146 => _146("brass_export_batch_duration_ms", void 0, { signal, status: "failure" }), 'access', _147 => _147.observe, 'call', _148 => _148(clock() - batchStartedAt)]);
|
|
1333
|
+
break;
|
|
1334
|
+
} finally {
|
|
1335
|
+
setQueueSize();
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
_optionalChain([options, 'access', _149 => _149.metrics, 'optionalAccess', _150 => _150.histogram, 'call', _151 => _151("brass_export_flush_duration_ms", void 0, { signal }), 'access', _152 => _152.observe, 'call', _153 => _153(clock() - startedAt)]);
|
|
1339
|
+
return {
|
|
1340
|
+
exported,
|
|
1341
|
+
dropped: droppedTotal,
|
|
1342
|
+
failed,
|
|
1343
|
+
batchCount,
|
|
1344
|
+
attempts,
|
|
1345
|
+
queueSize: queue.length,
|
|
1346
|
+
status: lastStatus,
|
|
1347
|
+
body: lastBody,
|
|
1348
|
+
errors
|
|
1349
|
+
};
|
|
1350
|
+
};
|
|
1351
|
+
return {
|
|
1352
|
+
enqueue,
|
|
1353
|
+
flush,
|
|
1354
|
+
shutdown: (deadline = _nullishCoalesce(options.shutdownTimeoutMs, () => ( DEFAULT_SHUTDOWN_TIMEOUT_MS))) => flush({ deadlineMs: deadline }),
|
|
1355
|
+
stats: () => ({ queueSize: queue.length, dropped: droppedTotal })
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
async function exportWithRetry(operation, options) {
|
|
1359
|
+
const attempts = Math.max(1, Math.floor(_nullishCoalesce(_optionalChain([options, 'access', _154 => _154.retry, 'optionalAccess', _155 => _155.attempts]), () => ( 1))));
|
|
1360
|
+
const initialDelayMs = Math.max(0, _nullishCoalesce(_optionalChain([options, 'access', _156 => _156.retry, 'optionalAccess', _157 => _157.initialDelayMs]), () => ( 100)));
|
|
1361
|
+
const maxDelayMs = Math.max(initialDelayMs, _nullishCoalesce(_optionalChain([options, 'access', _158 => _158.retry, 'optionalAccess', _159 => _159.maxDelayMs]), () => ( 2e3)));
|
|
1362
|
+
const jitterRatio = Math.max(0, Math.min(1, _nullishCoalesce(_optionalChain([options, 'access', _160 => _160.retry, 'optionalAccess', _161 => _161.jitterRatio]), () => ( 0.2))));
|
|
1363
|
+
const sleep = _nullishCoalesce(_optionalChain([options, 'access', _162 => _162.retry, 'optionalAccess', _163 => _163.sleep]), () => ( defaultSleep));
|
|
1364
|
+
let lastError;
|
|
1365
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
1366
|
+
try {
|
|
1367
|
+
return { value: await operation(), attempts: attempt };
|
|
1368
|
+
} catch (error) {
|
|
1369
|
+
lastError = error;
|
|
1370
|
+
if (attempt >= attempts) break;
|
|
1371
|
+
_optionalChain([options, 'access', _164 => _164.metrics, 'optionalAccess', _165 => _165.counter, 'call', _166 => _166("brass_export_retries_total", { signal: options.signal }), 'access', _167 => _167.increment, 'call', _168 => _168()]);
|
|
1372
|
+
await sleep(jitteredDelay(initialDelayMs, maxDelayMs, jitterRatio, attempt));
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
throw lastError;
|
|
1376
|
+
}
|
|
1377
|
+
async function withTimeout(operation, timeoutMs) {
|
|
1378
|
+
if (!timeoutMs || timeoutMs <= 0) return operation();
|
|
1379
|
+
let handle;
|
|
1380
|
+
try {
|
|
1381
|
+
return await Promise.race([
|
|
1382
|
+
operation(),
|
|
1383
|
+
new Promise((_resolve, reject) => {
|
|
1384
|
+
handle = setTimeout(() => reject(new Error(`Observability export timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
1385
|
+
_optionalChain([handle, 'access', _169 => _169.unref, 'optionalCall', _170 => _170()]);
|
|
1386
|
+
})
|
|
1387
|
+
]);
|
|
1388
|
+
} finally {
|
|
1389
|
+
if (handle) clearTimeout(handle);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
function jitteredDelay(initialDelayMs, maxDelayMs, jitterRatio, attempt) {
|
|
1393
|
+
const base = Math.min(maxDelayMs, initialDelayMs * 2 ** Math.max(0, attempt - 1));
|
|
1394
|
+
if (base <= 0 || jitterRatio <= 0) return base;
|
|
1395
|
+
const spread = base * jitterRatio;
|
|
1396
|
+
return Math.max(0, Math.round(base - spread + Math.random() * spread * 2));
|
|
1397
|
+
}
|
|
1398
|
+
function defaultSleep(ms) {
|
|
1399
|
+
return new Promise((resolve) => {
|
|
1400
|
+
const handle = setTimeout(resolve, ms);
|
|
1401
|
+
_optionalChain([handle, 'access', _171 => _171.unref, 'optionalCall', _172 => _172()]);
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
// src/observability/cardinality.ts
|
|
1406
|
+
function makeCardinalityLimitedMetrics(registry, options = {}) {
|
|
1407
|
+
const maxValuesPerLabel = Math.max(1, Math.floor(_nullishCoalesce(options.maxValuesPerLabel, () => ( 100))));
|
|
1408
|
+
const overflowValue = _nullishCoalesce(options.overflowValue, () => ( "__overflow__"));
|
|
1409
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1410
|
+
const limitLabels = (metricName, labels = {}) => {
|
|
1411
|
+
const out = {};
|
|
1412
|
+
for (const [label, value] of Object.entries(labels)) {
|
|
1413
|
+
const key = `${metricName}:${label}`;
|
|
1414
|
+
let values = seen.get(key);
|
|
1415
|
+
if (!values) {
|
|
1416
|
+
values = /* @__PURE__ */ new Set();
|
|
1417
|
+
seen.set(key, values);
|
|
1418
|
+
}
|
|
1419
|
+
if (values.has(value) || values.size < maxValuesPerLabel) {
|
|
1420
|
+
values.add(value);
|
|
1421
|
+
out[label] = value;
|
|
1422
|
+
} else {
|
|
1423
|
+
out[label] = overflowValue;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
return out;
|
|
1427
|
+
};
|
|
1428
|
+
return {
|
|
1429
|
+
counter: (name, labels) => registry.counter(name, limitLabels(name, labels)),
|
|
1430
|
+
gauge: (name, labels) => registry.gauge(name, limitLabels(name, labels)),
|
|
1431
|
+
histogram: (name, boundaries, labels) => registry.histogram(name, boundaries, limitLabels(name, labels)),
|
|
1432
|
+
snapshot: () => registry.snapshot(),
|
|
1433
|
+
reset: () => {
|
|
1434
|
+
seen.clear();
|
|
1435
|
+
registry.reset();
|
|
1436
|
+
}
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
function normalizeHttpRoute(path) {
|
|
1440
|
+
if (!path) return void 0;
|
|
1441
|
+
const withoutQuery = _nullishCoalesce(path.split("?", 1)[0], () => ( path));
|
|
1442
|
+
const normalized = withoutQuery.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/gi, ":id").replace(/\b\d+\b/g, ":id");
|
|
1443
|
+
return normalized || "/";
|
|
1444
|
+
}
|
|
1445
|
+
function sanitizeHttpTarget(url) {
|
|
1446
|
+
if (!url) return void 0;
|
|
1447
|
+
try {
|
|
1448
|
+
const parsed = new URL(url, "http://local");
|
|
1449
|
+
return `${parsed.pathname}${parsed.search ? "?[REDACTED]" : ""}`;
|
|
1450
|
+
} catch (e6) {
|
|
1451
|
+
const [withoutHash] = url.split("#", 1);
|
|
1452
|
+
const queryIndex = withoutHash.indexOf("?");
|
|
1453
|
+
return queryIndex < 0 ? withoutHash : `${withoutHash.slice(0, queryIndex)}?[REDACTED]`;
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
// src/observability/configValidation.ts
|
|
1458
|
+
var fn = _chunkCZIVE6NTcjs.Schema.custom((value) => typeof value === "function", "function");
|
|
1459
|
+
var object = _chunkCZIVE6NTcjs.Schema.custom(
|
|
1460
|
+
(value) => typeof value === "object" && value !== null && !Array.isArray(value),
|
|
1461
|
+
"object"
|
|
1462
|
+
);
|
|
1463
|
+
var falseOrObject = _chunkCZIVE6NTcjs.Schema.union([_chunkCZIVE6NTcjs.Schema.literal(false), object]);
|
|
1464
|
+
var ratio = _chunkCZIVE6NTcjs.Schema.number({ min: 0, max: 1 });
|
|
1465
|
+
var logLevel = _chunkCZIVE6NTcjs.Schema.enum(["debug", "info", "warn", "error"]);
|
|
1466
|
+
var retryOptions = _chunkCZIVE6NTcjs.Schema.object({
|
|
1467
|
+
attempts: _chunkCZIVE6NTcjs.Schema.number({ min: 0, int: true }).optional(),
|
|
1468
|
+
initialDelayMs: _chunkCZIVE6NTcjs.Schema.number({ min: 0, int: true }).optional(),
|
|
1469
|
+
maxDelayMs: _chunkCZIVE6NTcjs.Schema.number({ min: 0, int: true }).optional(),
|
|
1470
|
+
jitterRatio: _chunkCZIVE6NTcjs.Schema.number({ min: 0, max: 1 }).optional(),
|
|
1471
|
+
sleep: fn.optional()
|
|
1472
|
+
}, { unknownKeys: "passthrough" });
|
|
1473
|
+
var pipelineOptions = _chunkCZIVE6NTcjs.Schema.object({
|
|
1474
|
+
maxQueueSize: _chunkCZIVE6NTcjs.Schema.number({ min: 0, int: true }).optional(),
|
|
1475
|
+
batchSize: _chunkCZIVE6NTcjs.Schema.number({ min: 1, int: true }).optional(),
|
|
1476
|
+
timeoutMs: _chunkCZIVE6NTcjs.Schema.number({ min: 0, int: true }).optional(),
|
|
1477
|
+
retry: retryOptions.optional(),
|
|
1478
|
+
dropPolicy: _chunkCZIVE6NTcjs.Schema.enum(["drop-oldest", "drop-newest"]).optional(),
|
|
1479
|
+
shutdownTimeoutMs: _chunkCZIVE6NTcjs.Schema.number({ min: 0, int: true }).optional()
|
|
1480
|
+
}, { unknownKeys: "passthrough" });
|
|
1481
|
+
var otlpOptions = _chunkCZIVE6NTcjs.Schema.object({
|
|
1482
|
+
metricsUrl: _chunkCZIVE6NTcjs.Schema.string().optional(),
|
|
1483
|
+
tracesUrl: _chunkCZIVE6NTcjs.Schema.string().optional(),
|
|
1484
|
+
logsUrl: _chunkCZIVE6NTcjs.Schema.string().optional(),
|
|
1485
|
+
headers: _chunkCZIVE6NTcjs.Schema.record(_chunkCZIVE6NTcjs.Schema.string()).optional(),
|
|
1486
|
+
fetch: fn.optional(),
|
|
1487
|
+
timeoutMs: _chunkCZIVE6NTcjs.Schema.number({ min: 1, int: true }).optional(),
|
|
1488
|
+
retry: retryOptions.optional(),
|
|
1489
|
+
pipeline: pipelineOptions.optional()
|
|
1490
|
+
}, { unknownKeys: "passthrough" });
|
|
1491
|
+
var samplingOptions = _chunkCZIVE6NTcjs.Schema.union([
|
|
1492
|
+
_chunkCZIVE6NTcjs.Schema.literal(false),
|
|
1493
|
+
ratio,
|
|
1494
|
+
fn,
|
|
1495
|
+
_chunkCZIVE6NTcjs.Schema.object({
|
|
1496
|
+
ratio: ratio.optional(),
|
|
1497
|
+
rules: _chunkCZIVE6NTcjs.Schema.array(object).optional(),
|
|
1498
|
+
sampler: _chunkCZIVE6NTcjs.Schema.union([fn, object]).optional(),
|
|
1499
|
+
respectRemoteSampled: _chunkCZIVE6NTcjs.Schema.boolean().optional(),
|
|
1500
|
+
forceSampleOnError: _chunkCZIVE6NTcjs.Schema.boolean().optional()
|
|
1501
|
+
}, { unknownKeys: "passthrough" })
|
|
1502
|
+
]);
|
|
1503
|
+
var observabilityOptions = _chunkCZIVE6NTcjs.Schema.object({
|
|
1504
|
+
serviceName: _chunkCZIVE6NTcjs.Schema.string({ minLength: 1 }).optional(),
|
|
1505
|
+
serviceVersion: _chunkCZIVE6NTcjs.Schema.string({ minLength: 1 }).optional(),
|
|
1506
|
+
resource: object.optional(),
|
|
1507
|
+
eventBus: object.optional(),
|
|
1508
|
+
metrics: falseOrObject.optional(),
|
|
1509
|
+
logs: falseOrObject.optional(),
|
|
1510
|
+
traces: falseOrObject.optional(),
|
|
1511
|
+
sampling: samplingOptions.optional(),
|
|
1512
|
+
redaction: falseOrObject.optional(),
|
|
1513
|
+
cardinality: _chunkCZIVE6NTcjs.Schema.union([
|
|
1514
|
+
_chunkCZIVE6NTcjs.Schema.literal(false),
|
|
1515
|
+
_chunkCZIVE6NTcjs.Schema.object({
|
|
1516
|
+
maxValuesPerLabel: _chunkCZIVE6NTcjs.Schema.number({ min: 1, int: true }).optional(),
|
|
1517
|
+
overflowValue: _chunkCZIVE6NTcjs.Schema.string({ minLength: 1 }).optional()
|
|
1518
|
+
}, { unknownKeys: "passthrough" })
|
|
1519
|
+
]).optional(),
|
|
1520
|
+
otlp: otlpOptions.optional(),
|
|
1521
|
+
flushIntervalMs: _chunkCZIVE6NTcjs.Schema.number({ min: 1, int: true }).optional(),
|
|
1522
|
+
autoStart: _chunkCZIVE6NTcjs.Schema.boolean().optional(),
|
|
1523
|
+
traceSeed: object.optional(),
|
|
1524
|
+
childName: fn.optional(),
|
|
1525
|
+
onFlushError: fn.optional()
|
|
1526
|
+
}, { unknownKeys: "passthrough" });
|
|
1527
|
+
var httpObservabilityOptions = _chunkCZIVE6NTcjs.Schema.object({
|
|
1528
|
+
metrics: falseOrObject.optional(),
|
|
1529
|
+
logs: _chunkCZIVE6NTcjs.Schema.union([
|
|
1530
|
+
_chunkCZIVE6NTcjs.Schema.literal(false),
|
|
1531
|
+
_chunkCZIVE6NTcjs.Schema.object({
|
|
1532
|
+
requestLevel: _chunkCZIVE6NTcjs.Schema.union([logLevel, _chunkCZIVE6NTcjs.Schema.literal(false)]).optional(),
|
|
1533
|
+
responseLevel: _chunkCZIVE6NTcjs.Schema.union([logLevel, _chunkCZIVE6NTcjs.Schema.literal(false)]).optional(),
|
|
1534
|
+
errorLevel: _chunkCZIVE6NTcjs.Schema.union([logLevel, _chunkCZIVE6NTcjs.Schema.literal(false)]).optional()
|
|
1535
|
+
}, { unknownKeys: "passthrough" })
|
|
1536
|
+
]).optional(),
|
|
1537
|
+
spans: _chunkCZIVE6NTcjs.Schema.union([
|
|
1538
|
+
_chunkCZIVE6NTcjs.Schema.literal(false),
|
|
1539
|
+
_chunkCZIVE6NTcjs.Schema.object({
|
|
1540
|
+
name: _chunkCZIVE6NTcjs.Schema.union([_chunkCZIVE6NTcjs.Schema.string({ minLength: 1 }), fn]).optional(),
|
|
1541
|
+
attributes: _chunkCZIVE6NTcjs.Schema.union([object, fn]).optional()
|
|
1542
|
+
}, { unknownKeys: "passthrough" })
|
|
1543
|
+
]).optional(),
|
|
1544
|
+
adaptiveLimiter: _chunkCZIVE6NTcjs.Schema.union([
|
|
1545
|
+
_chunkCZIVE6NTcjs.Schema.boolean(),
|
|
1546
|
+
_chunkCZIVE6NTcjs.Schema.object({
|
|
1547
|
+
enabled: _chunkCZIVE6NTcjs.Schema.boolean().optional(),
|
|
1548
|
+
includeKeyLabel: _chunkCZIVE6NTcjs.Schema.boolean().optional()
|
|
1549
|
+
}, { unknownKeys: "passthrough" })
|
|
1550
|
+
]).optional(),
|
|
1551
|
+
policy: _chunkCZIVE6NTcjs.Schema.union([
|
|
1552
|
+
_chunkCZIVE6NTcjs.Schema.boolean(),
|
|
1553
|
+
_chunkCZIVE6NTcjs.Schema.object({
|
|
1554
|
+
enabled: _chunkCZIVE6NTcjs.Schema.boolean().optional(),
|
|
1555
|
+
labelKeys: _chunkCZIVE6NTcjs.Schema.array(_chunkCZIVE6NTcjs.Schema.enum(["preset", "lane", "poolKey", "dedupKey", "priority", "retry"])).optional()
|
|
1556
|
+
}, { unknownKeys: "passthrough" })
|
|
1557
|
+
]).optional(),
|
|
1558
|
+
injectTraceHeaders: _chunkCZIVE6NTcjs.Schema.boolean().optional(),
|
|
1559
|
+
includeHostLabel: _chunkCZIVE6NTcjs.Schema.boolean().optional(),
|
|
1560
|
+
route: _chunkCZIVE6NTcjs.Schema.union([_chunkCZIVE6NTcjs.Schema.string({ minLength: 1 }), fn]).optional(),
|
|
1561
|
+
clock: fn.optional(),
|
|
1562
|
+
durationBuckets: _chunkCZIVE6NTcjs.Schema.array(_chunkCZIVE6NTcjs.Schema.number({ min: 0 }).refine((n) => n > 0, "duration bucket must be > 0")).optional()
|
|
1563
|
+
}, { unknownKeys: "passthrough" });
|
|
1564
|
+
function validateObservabilityOptions(options) {
|
|
1565
|
+
_chunkCZIVE6NTcjs.parseConfig.call(void 0, "ObservabilityOptions", observabilityOptions, options);
|
|
1566
|
+
}
|
|
1567
|
+
function validateHttpObservabilityOptions(options) {
|
|
1568
|
+
_chunkCZIVE6NTcjs.parseConfig.call(void 0, "HttpObservabilityOptions", httpObservabilityOptions, options);
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
// src/observability/setup.ts
|
|
1572
|
+
function makeObservability(options = {}) {
|
|
1573
|
+
validateObservabilityOptions(options);
|
|
1574
|
+
const serviceName = _nullishCoalesce(options.serviceName, () => ( "brass-runtime"));
|
|
1575
|
+
const eventBus = _nullishCoalesce(options.eventBus, () => ( new (0, _chunk52PPNNI4cjs.EventBus)()));
|
|
1576
|
+
const rawMetrics = _chunk52PPNNI4cjs.makeMetrics.call(void 0, );
|
|
1577
|
+
const metrics = options.cardinality === false ? rawMetrics : makeCardinalityLimitedMetrics(rawMetrics, _nullishCoalesce(options.cardinality, () => ( {})));
|
|
1578
|
+
const redactor = makeObservabilityRedactor(options.redaction);
|
|
1579
|
+
const traceOptions = options.traces && typeof options.traces === "object" ? options.traces : {};
|
|
1580
|
+
const tracer = new (0, _chunk52PPNNI4cjs.InMemoryTracer)({
|
|
1581
|
+
maxFinishedSpans: _nullishCoalesce(traceOptions.maxFinishedSpans, () => ( 1e4)),
|
|
1582
|
+
maxSpanAgeMs: _nullishCoalesce(traceOptions.maxSpanAgeMs, () => ( 10 * 6e4)),
|
|
1583
|
+
clock: traceOptions.clock,
|
|
1584
|
+
sanitizeAttributes: redactor.attributes,
|
|
1585
|
+
sanitizeError: redactor.value
|
|
1586
|
+
});
|
|
1587
|
+
const traceIdGenerator = makeRuntimeTraceIdGenerator();
|
|
1588
|
+
const sampling = resolveTraceSampling(options.traces === false ? false : options.sampling);
|
|
1589
|
+
const makeEnv = (traceSeed, baggage) => ({
|
|
1590
|
+
brass: {
|
|
1591
|
+
tracer: traceIdGenerator,
|
|
1592
|
+
...traceSeed ? { traceSeed: baggage ? { ...traceSeed, baggage: { ..._nullishCoalesce(traceSeed.baggage, () => ( {})), ...baggage } } : traceSeed } : {},
|
|
1593
|
+
...!traceSeed && baggage ? { baggage } : {},
|
|
1594
|
+
sampler: sampling.sampler,
|
|
1595
|
+
respectRemoteSampled: sampling.respectRemoteSampled,
|
|
1596
|
+
forceSampleOnError: sampling.forceSampleOnError,
|
|
1597
|
+
childName: options.childName
|
|
1598
|
+
}
|
|
1599
|
+
});
|
|
1600
|
+
const env = makeEnv(options.traceSeed, _optionalChain([options, 'access', _173 => _173.traceSeed, 'optionalAccess', _174 => _174.baggage]));
|
|
1601
|
+
const envForRequest = (input) => makeEnv(_nullishCoalesce(resolveRequestTraceSeed(input), () => ( options.traceSeed)), resolveRequestBaggage(input));
|
|
1602
|
+
const resource = {
|
|
1603
|
+
"service.name": serviceName,
|
|
1604
|
+
...options.serviceVersion ? { "service.version": options.serviceVersion } : {},
|
|
1605
|
+
..._nullishCoalesce(options.resource, () => ( {}))
|
|
1606
|
+
};
|
|
1607
|
+
if (options.metrics !== false) {
|
|
1608
|
+
const { prometheus: _prometheus, ...metricsOptions } = _nullishCoalesce(options.metrics, () => ( {}));
|
|
1609
|
+
eventBus.subscribeHooks(makeRuntimeMetricsSink(metrics, metricsOptions));
|
|
1610
|
+
}
|
|
1611
|
+
const logPipeline = options.logs !== false && _optionalChain([options, 'access', _175 => _175.otlp, 'optionalAccess', _176 => _176.logsUrl]) ? makeExportPipeline({
|
|
1612
|
+
signal: "logs",
|
|
1613
|
+
metrics,
|
|
1614
|
+
..._nullishCoalesce(options.otlp.pipeline, () => ( {})),
|
|
1615
|
+
timeoutMs: _nullishCoalesce(options.otlp.timeoutMs, () => ( _optionalChain([options, 'access', _177 => _177.otlp, 'access', _178 => _178.pipeline, 'optionalAccess', _179 => _179.timeoutMs]))),
|
|
1616
|
+
retry: _nullishCoalesce(options.otlp.retry, () => ( _optionalChain([options, 'access', _180 => _180.otlp, 'access', _181 => _181.pipeline, 'optionalAccess', _182 => _182.retry]))),
|
|
1617
|
+
exportBatch: async (records) => {
|
|
1618
|
+
const body = JSON.stringify(structuredLogsToOtlp(records, {
|
|
1619
|
+
resource,
|
|
1620
|
+
scopeName: serviceName,
|
|
1621
|
+
scopeVersion: options.serviceVersion
|
|
1622
|
+
}));
|
|
1623
|
+
const response = await postOtlpJson({
|
|
1624
|
+
url: options.otlp.logsUrl,
|
|
1625
|
+
headers: options.otlp.headers,
|
|
1626
|
+
fetch: options.otlp.fetch
|
|
1627
|
+
}, body);
|
|
1628
|
+
return { status: response.status, body };
|
|
1629
|
+
}
|
|
1630
|
+
}) : void 0;
|
|
1631
|
+
if (options.logs !== false) {
|
|
1632
|
+
const configuredLogs = _nullishCoalesce(options.logs, () => ( {}));
|
|
1633
|
+
const configuredWrite = configuredLogs.write;
|
|
1634
|
+
eventBus.subscribeHooks(makeStructuredLogSink({
|
|
1635
|
+
...configuredLogs,
|
|
1636
|
+
redact: options.logs && options.logs.redact !== void 0 ? options.logs.redact : redactor,
|
|
1637
|
+
write: (record) => {
|
|
1638
|
+
if (configuredWrite) configuredWrite(record);
|
|
1639
|
+
else defaultStructuredLogWriter(record);
|
|
1640
|
+
_optionalChain([logPipeline, 'optionalAccess', _183 => _183.enqueue, 'call', _184 => _184([record])]);
|
|
1641
|
+
}
|
|
1642
|
+
}));
|
|
1643
|
+
}
|
|
1644
|
+
if (options.traces !== false) {
|
|
1645
|
+
eventBus.subscribeHooks(tracer);
|
|
1646
|
+
}
|
|
1647
|
+
const prometheus = makePrometheusMetricsExporter(metrics, options.metrics !== false ? _optionalChain([options, 'access', _185 => _185.metrics, 'optionalAccess', _186 => _186.prometheus]) : void 0);
|
|
1648
|
+
const otlpMetrics = _optionalChain([options, 'access', _187 => _187.otlp, 'optionalAccess', _188 => _188.metricsUrl]) ? makeOtlpHttpMetricsExporter(metrics, {
|
|
1649
|
+
url: options.otlp.metricsUrl,
|
|
1650
|
+
headers: options.otlp.headers,
|
|
1651
|
+
fetch: options.otlp.fetch,
|
|
1652
|
+
resource,
|
|
1653
|
+
scopeName: serviceName,
|
|
1654
|
+
scopeVersion: options.serviceVersion
|
|
1655
|
+
}) : void 0;
|
|
1656
|
+
const otlpMetricsExporter = otlpMetrics ? {
|
|
1657
|
+
export: async () => {
|
|
1658
|
+
const result = await exportWithRetry(
|
|
1659
|
+
() => withTimeout(() => otlpMetrics.export(), _nullishCoalesce(_optionalChain([options, 'access', _189 => _189.otlp, 'optionalAccess', _190 => _190.timeoutMs]), () => ( 1e4))),
|
|
1660
|
+
{
|
|
1661
|
+
signal: "metrics",
|
|
1662
|
+
metrics,
|
|
1663
|
+
retry: _optionalChain([options, 'access', _191 => _191.otlp, 'optionalAccess', _192 => _192.retry])
|
|
1664
|
+
}
|
|
1665
|
+
);
|
|
1666
|
+
return result.value;
|
|
1667
|
+
}
|
|
1668
|
+
} : void 0;
|
|
1669
|
+
const exportedSpanIds = /* @__PURE__ */ new Set();
|
|
1670
|
+
const queuedSpanIds = /* @__PURE__ */ new Set();
|
|
1671
|
+
const pendingSpans = () => tracer.exportFinished().filter((span) => !exportedSpanIds.has(span.spanId) && !queuedSpanIds.has(span.spanId));
|
|
1672
|
+
const tracePipeline = _optionalChain([options, 'access', _193 => _193.otlp, 'optionalAccess', _194 => _194.tracesUrl]) ? makeExportPipeline({
|
|
1673
|
+
signal: "traces",
|
|
1674
|
+
metrics,
|
|
1675
|
+
..._nullishCoalesce(options.otlp.pipeline, () => ( {})),
|
|
1676
|
+
timeoutMs: _nullishCoalesce(options.otlp.timeoutMs, () => ( _optionalChain([options, 'access', _195 => _195.otlp, 'access', _196 => _196.pipeline, 'optionalAccess', _197 => _197.timeoutMs]))),
|
|
1677
|
+
retry: _nullishCoalesce(options.otlp.retry, () => ( _optionalChain([options, 'access', _198 => _198.otlp, 'access', _199 => _199.pipeline, 'optionalAccess', _200 => _200.retry]))),
|
|
1678
|
+
exportBatch: async (spans) => {
|
|
1679
|
+
const body = JSON.stringify(spansToOtlp(spans, {
|
|
1680
|
+
resource,
|
|
1681
|
+
scopeName: serviceName,
|
|
1682
|
+
scopeVersion: options.serviceVersion
|
|
1683
|
+
}));
|
|
1684
|
+
const response = await postOtlpJson({
|
|
1685
|
+
url: options.otlp.tracesUrl,
|
|
1686
|
+
headers: options.otlp.headers,
|
|
1687
|
+
fetch: options.otlp.fetch
|
|
1688
|
+
}, body);
|
|
1689
|
+
const spanIds = spans.map((span) => span.spanId);
|
|
1690
|
+
for (const spanId of spanIds) {
|
|
1691
|
+
exportedSpanIds.add(spanId);
|
|
1692
|
+
queuedSpanIds.delete(spanId);
|
|
1693
|
+
}
|
|
1694
|
+
tracer.pruneFinished(spanIds);
|
|
1695
|
+
for (const spanId of spanIds) exportedSpanIds.delete(spanId);
|
|
1696
|
+
return { status: response.status, body };
|
|
1697
|
+
},
|
|
1698
|
+
onDrop: (spans) => {
|
|
1699
|
+
const spanIds = spans.map((span) => span.spanId);
|
|
1700
|
+
for (const spanId of spanIds) {
|
|
1701
|
+
queuedSpanIds.delete(spanId);
|
|
1702
|
+
exportedSpanIds.add(spanId);
|
|
1703
|
+
}
|
|
1704
|
+
tracer.pruneFinished(spanIds);
|
|
1705
|
+
for (const spanId of spanIds) exportedSpanIds.delete(spanId);
|
|
1706
|
+
}
|
|
1707
|
+
}) : void 0;
|
|
1708
|
+
const otlpTraces = _optionalChain([options, 'access', _201 => _201.otlp, 'optionalAccess', _202 => _202.tracesUrl]) ? {
|
|
1709
|
+
pipeline: tracePipeline,
|
|
1710
|
+
export: async () => {
|
|
1711
|
+
const spans = pendingSpans();
|
|
1712
|
+
for (const span of spans) queuedSpanIds.add(span.spanId);
|
|
1713
|
+
tracePipeline.enqueue(spans);
|
|
1714
|
+
if (tracePipeline.stats().queueSize === 0) {
|
|
1715
|
+
return { status: void 0, body: "", spanCount: 0 };
|
|
1716
|
+
}
|
|
1717
|
+
const result = await tracePipeline.flush();
|
|
1718
|
+
if (result.errors.length > 0) throw result.errors[0];
|
|
1719
|
+
return {
|
|
1720
|
+
status: result.status,
|
|
1721
|
+
body: _nullishCoalesce(result.body, () => ( "")),
|
|
1722
|
+
spanCount: result.exported
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
} : void 0;
|
|
1726
|
+
const otlpLogs = logPipeline ? {
|
|
1727
|
+
pipeline: logPipeline,
|
|
1728
|
+
export: async () => {
|
|
1729
|
+
if (logPipeline.stats().queueSize === 0) {
|
|
1730
|
+
return { status: void 0, body: "", logCount: 0 };
|
|
1731
|
+
}
|
|
1732
|
+
const result = await logPipeline.flush();
|
|
1733
|
+
if (result.errors.length > 0) throw result.errors[0];
|
|
1734
|
+
return {
|
|
1735
|
+
status: result.status,
|
|
1736
|
+
body: _nullishCoalesce(result.body, () => ( "")),
|
|
1737
|
+
logCount: result.exported
|
|
1738
|
+
};
|
|
1739
|
+
}
|
|
1740
|
+
} : void 0;
|
|
1741
|
+
let interval;
|
|
1742
|
+
const flushNow = async () => {
|
|
1743
|
+
eventBus.flush();
|
|
1744
|
+
const errors = [];
|
|
1745
|
+
let metricsResult;
|
|
1746
|
+
let tracesResult;
|
|
1747
|
+
let logsResult;
|
|
1748
|
+
if (otlpMetricsExporter) {
|
|
1749
|
+
try {
|
|
1750
|
+
metricsResult = await otlpMetricsExporter.export();
|
|
1751
|
+
} catch (error) {
|
|
1752
|
+
errors.push({ signal: "metrics", error });
|
|
1753
|
+
_optionalChain([options, 'access', _203 => _203.onFlushError, 'optionalCall', _204 => _204(error, "metrics")]);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
if (otlpTraces) {
|
|
1757
|
+
try {
|
|
1758
|
+
tracesResult = await otlpTraces.export();
|
|
1759
|
+
} catch (error) {
|
|
1760
|
+
errors.push({ signal: "traces", error });
|
|
1761
|
+
_optionalChain([options, 'access', _205 => _205.onFlushError, 'optionalCall', _206 => _206(error, "traces")]);
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
if (otlpLogs) {
|
|
1765
|
+
try {
|
|
1766
|
+
logsResult = await otlpLogs.export();
|
|
1767
|
+
} catch (error) {
|
|
1768
|
+
errors.push({ signal: "logs", error });
|
|
1769
|
+
_optionalChain([options, 'access', _207 => _207.onFlushError, 'optionalCall', _208 => _208(error, "logs")]);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
return { metrics: metricsResult, traces: tracesResult, logs: logsResult, errors };
|
|
1773
|
+
};
|
|
1774
|
+
let inFlightFlush;
|
|
1775
|
+
const flush = () => {
|
|
1776
|
+
if (inFlightFlush) {
|
|
1777
|
+
metrics.counter("brass_export_flush_singleflight_total").increment();
|
|
1778
|
+
return inFlightFlush;
|
|
1779
|
+
}
|
|
1780
|
+
inFlightFlush = flushNow().finally(() => {
|
|
1781
|
+
inFlightFlush = void 0;
|
|
1782
|
+
});
|
|
1783
|
+
return inFlightFlush;
|
|
1784
|
+
};
|
|
1785
|
+
const start = () => {
|
|
1786
|
+
if (interval || !options.flushIntervalMs || options.flushIntervalMs <= 0) return;
|
|
1787
|
+
interval = setInterval(() => {
|
|
1788
|
+
void flush();
|
|
1789
|
+
}, options.flushIntervalMs);
|
|
1790
|
+
_optionalChain([interval, 'access', _209 => _209.unref, 'optionalCall', _210 => _210()]);
|
|
1791
|
+
};
|
|
1792
|
+
const stop = () => {
|
|
1793
|
+
if (!interval) return;
|
|
1794
|
+
clearInterval(interval);
|
|
1795
|
+
interval = void 0;
|
|
1796
|
+
};
|
|
1797
|
+
const shutdown = async () => {
|
|
1798
|
+
stop();
|
|
1799
|
+
const result = await flush();
|
|
1800
|
+
const drainErrors = [];
|
|
1801
|
+
let traces = result.traces;
|
|
1802
|
+
let logs = result.logs;
|
|
1803
|
+
if (tracePipeline && tracePipeline.stats().queueSize > 0) {
|
|
1804
|
+
const drain = await tracePipeline.shutdown(_optionalChain([options, 'access', _211 => _211.otlp, 'optionalAccess', _212 => _212.pipeline, 'optionalAccess', _213 => _213.shutdownTimeoutMs]));
|
|
1805
|
+
for (const error of drain.errors) {
|
|
1806
|
+
drainErrors.push({ signal: "traces", error });
|
|
1807
|
+
_optionalChain([options, 'access', _214 => _214.onFlushError, 'optionalCall', _215 => _215(error, "traces")]);
|
|
1808
|
+
}
|
|
1809
|
+
traces = _nullishCoalesce(traces, () => ( {
|
|
1810
|
+
status: drain.status,
|
|
1811
|
+
body: _nullishCoalesce(drain.body, () => ( "")),
|
|
1812
|
+
spanCount: drain.exported
|
|
1813
|
+
}));
|
|
1814
|
+
}
|
|
1815
|
+
if (logPipeline && logPipeline.stats().queueSize > 0) {
|
|
1816
|
+
const drain = await logPipeline.shutdown(_optionalChain([options, 'access', _216 => _216.otlp, 'optionalAccess', _217 => _217.pipeline, 'optionalAccess', _218 => _218.shutdownTimeoutMs]));
|
|
1817
|
+
for (const error of drain.errors) {
|
|
1818
|
+
drainErrors.push({ signal: "logs", error });
|
|
1819
|
+
_optionalChain([options, 'access', _219 => _219.onFlushError, 'optionalCall', _220 => _220(error, "logs")]);
|
|
1820
|
+
}
|
|
1821
|
+
logs = _nullishCoalesce(logs, () => ( {
|
|
1822
|
+
status: drain.status,
|
|
1823
|
+
body: _nullishCoalesce(drain.body, () => ( "")),
|
|
1824
|
+
logCount: drain.exported
|
|
1825
|
+
}));
|
|
1826
|
+
}
|
|
1827
|
+
if (drainErrors.length === 0 && traces === result.traces && logs === result.logs) return result;
|
|
1828
|
+
return {
|
|
1829
|
+
...result,
|
|
1830
|
+
traces,
|
|
1831
|
+
logs,
|
|
1832
|
+
errors: [...result.errors, ...drainErrors]
|
|
1833
|
+
};
|
|
1834
|
+
};
|
|
1835
|
+
if (_nullishCoalesce(options.autoStart, () => ( Boolean(options.flushIntervalMs)))) {
|
|
1836
|
+
start();
|
|
1837
|
+
}
|
|
1838
|
+
return {
|
|
1839
|
+
hooks: eventBus,
|
|
1840
|
+
eventBus,
|
|
1841
|
+
env,
|
|
1842
|
+
envForRequest,
|
|
1843
|
+
metrics,
|
|
1844
|
+
tracer,
|
|
1845
|
+
traceIdGenerator,
|
|
1846
|
+
exporters: {
|
|
1847
|
+
prometheus,
|
|
1848
|
+
otlpMetrics: otlpMetricsExporter,
|
|
1849
|
+
otlpTraces,
|
|
1850
|
+
otlpLogs
|
|
1851
|
+
},
|
|
1852
|
+
prometheus,
|
|
1853
|
+
health: (healthOptions = {}) => snapshotRuntimeHealth({ ...healthOptions, metrics }),
|
|
1854
|
+
readiness: (healthOptions = {}) => snapshotRuntimeHealth({ ...healthOptions, metrics }).ready,
|
|
1855
|
+
flush,
|
|
1856
|
+
start,
|
|
1857
|
+
stop,
|
|
1858
|
+
shutdown
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
function resolveRequestTraceSeed(input) {
|
|
1862
|
+
if (!input) return void 0;
|
|
1863
|
+
if (isTraceContext(input)) return input;
|
|
1864
|
+
const maybeRequest = input;
|
|
1865
|
+
const explicitBaggage = typeof maybeRequest.baggage === "string" ? parseBaggage(maybeRequest.baggage) : maybeRequest.baggage;
|
|
1866
|
+
if (maybeRequest.trace) {
|
|
1867
|
+
return {
|
|
1868
|
+
...maybeRequest.trace,
|
|
1869
|
+
...explicitBaggage ? { baggage: { ..._nullishCoalesce(maybeRequest.trace.baggage, () => ( {})), ...explicitBaggage } } : {}
|
|
1870
|
+
};
|
|
1871
|
+
}
|
|
1872
|
+
if (maybeRequest.traceparent) {
|
|
1873
|
+
const trace = parseTraceparent(maybeRequest.traceparent);
|
|
1874
|
+
return trace ? {
|
|
1875
|
+
...trace,
|
|
1876
|
+
...maybeRequest.tracestate ? { traceState: maybeRequest.tracestate } : {},
|
|
1877
|
+
...explicitBaggage ? { baggage: explicitBaggage } : {}
|
|
1878
|
+
} : void 0;
|
|
1879
|
+
}
|
|
1880
|
+
if (maybeRequest.headers) {
|
|
1881
|
+
const trace = extractTraceContext(maybeRequest.headers);
|
|
1882
|
+
return trace && explicitBaggage ? { ...trace, baggage: { ..._nullishCoalesce(trace.baggage, () => ( {})), ...explicitBaggage } } : trace;
|
|
1883
|
+
}
|
|
1884
|
+
return extractTraceContext(input);
|
|
1885
|
+
}
|
|
1886
|
+
function resolveRequestBaggage(input) {
|
|
1887
|
+
if (!input) return void 0;
|
|
1888
|
+
if (isTraceContext(input)) return input.baggage;
|
|
1889
|
+
const maybeRequest = input;
|
|
1890
|
+
if (typeof maybeRequest.baggage === "string") return parseBaggage(maybeRequest.baggage);
|
|
1891
|
+
if (maybeRequest.baggage) return maybeRequest.baggage;
|
|
1892
|
+
if (_optionalChain([maybeRequest, 'access', _221 => _221.trace, 'optionalAccess', _222 => _222.baggage])) return maybeRequest.trace.baggage;
|
|
1893
|
+
if (maybeRequest.headers) return extractBaggage(maybeRequest.headers);
|
|
1894
|
+
return extractBaggage(input);
|
|
1895
|
+
}
|
|
1896
|
+
function isTraceContext(value) {
|
|
1897
|
+
return typeof value === "object" && value !== null && typeof value.traceId === "string" && typeof value.spanId === "string";
|
|
1898
|
+
}
|
|
1899
|
+
function makeRuntimeTraceIdGenerator() {
|
|
1900
|
+
return {
|
|
1901
|
+
newTraceId: () => randomHexId(32),
|
|
1902
|
+
newSpanId: () => randomHexId(16)
|
|
1903
|
+
};
|
|
1904
|
+
}
|
|
1905
|
+
var HEX_BYTE = Array.from({ length: 256 }, (_, byte) => byte.toString(16).padStart(2, "0"));
|
|
1906
|
+
function randomHexId(length) {
|
|
1907
|
+
const cryptoLike = globalThis.crypto;
|
|
1908
|
+
if (typeof _optionalChain([cryptoLike, 'optionalAccess', _223 => _223.getRandomValues]) === "function") {
|
|
1909
|
+
const bytes = new Uint8Array(Math.ceil(length / 2));
|
|
1910
|
+
cryptoLike.getRandomValues(bytes);
|
|
1911
|
+
return bytesToHex(bytes, length);
|
|
1912
|
+
}
|
|
1913
|
+
if (typeof _optionalChain([cryptoLike, 'optionalAccess', _224 => _224.randomUUID]) === "function") {
|
|
1914
|
+
const hex = cryptoLike.randomUUID().replace(/-/g, "");
|
|
1915
|
+
if (hex.length >= length) return hex.slice(0, length);
|
|
1916
|
+
}
|
|
1917
|
+
let out = "";
|
|
1918
|
+
while (out.length < length) {
|
|
1919
|
+
out += Math.floor(Math.random() * 4294967295).toString(16).padStart(8, "0");
|
|
1920
|
+
}
|
|
1921
|
+
return out.slice(0, length);
|
|
1922
|
+
}
|
|
1923
|
+
function bytesToHex(bytes, length) {
|
|
1924
|
+
let out = "";
|
|
1925
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
1926
|
+
out += HEX_BYTE[bytes[i]];
|
|
1927
|
+
}
|
|
1928
|
+
return out.length === length ? out : out.slice(0, length);
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
// src/observability/request.ts
|
|
1932
|
+
function makeRequestObservabilityContext(observability, input = {}) {
|
|
1933
|
+
const trace = resolveRequestTraceSeed(input);
|
|
1934
|
+
const env = observability.envForRequest(input);
|
|
1935
|
+
const attributes = requestSpanAttributes(input);
|
|
1936
|
+
const defaultSpanName = _nullishCoalesce(input.spanName, () => ( (input.method && input.route ? `${input.method} ${input.route}` : _nullishCoalesce(input.route, () => ( "request")))));
|
|
1937
|
+
const makeRuntime = (extraEnv, options = {}) => new (0, _chunkGLE2WY7Zcjs.Runtime)({
|
|
1938
|
+
...options,
|
|
1939
|
+
env: mergeEnv(extraEnv, env),
|
|
1940
|
+
hooks: observability.hooks
|
|
1941
|
+
});
|
|
1942
|
+
return {
|
|
1943
|
+
env,
|
|
1944
|
+
trace,
|
|
1945
|
+
route: input.route,
|
|
1946
|
+
attributes,
|
|
1947
|
+
makeRuntime,
|
|
1948
|
+
run: (effect, extraEnv, options) => makeRuntime(extraEnv, options).toPromise(effect),
|
|
1949
|
+
span: (name, effect, spanAttributes) => withSpan(name, effect, spanAttributes),
|
|
1950
|
+
withRequestSpan: (effect, spanAttributes) => withSpan(defaultSpanName, effect, { ...attributes, ..._nullishCoalesce(spanAttributes, () => ( {})) }),
|
|
1951
|
+
flush: observability.flush,
|
|
1952
|
+
shutdown: observability.shutdown
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1955
|
+
function mergeEnv(extraEnv, observabilityEnv) {
|
|
1956
|
+
return Object.assign({}, _nullishCoalesce(extraEnv, () => ( {})), observabilityEnv);
|
|
1957
|
+
}
|
|
1958
|
+
function requestSpanAttributes(input) {
|
|
1959
|
+
return {
|
|
1960
|
+
"span.kind": "server",
|
|
1961
|
+
...input.method ? { "http.method": input.method } : {},
|
|
1962
|
+
...input.method ? { "http.request.method": input.method } : {},
|
|
1963
|
+
...input.target ? { "http.target": input.target } : {},
|
|
1964
|
+
...input.target ? { "url.path": targetPath(input.target) } : {},
|
|
1965
|
+
...input.route ? { "http.route": input.route } : {},
|
|
1966
|
+
..._nullishCoalesce(input.attributes, () => ( {}))
|
|
1967
|
+
};
|
|
1968
|
+
}
|
|
1969
|
+
function targetPath(target) {
|
|
1970
|
+
try {
|
|
1971
|
+
return new URL(target, "http://local").pathname;
|
|
1972
|
+
} catch (e7) {
|
|
1973
|
+
return _nullishCoalesce(target.split("?", 1)[0], () => ( target));
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
// src/observability/server.ts
|
|
1978
|
+
var DEFAULT_DURATION_BUCKETS2 = [1, 5, 10, 25, 50, 100, 250, 500, 1e3, 2500, 5e3, 1e4];
|
|
1979
|
+
async function runObservedHttpServerEffect(observability, input, effect, options = {}, env, runtimeOptions) {
|
|
1980
|
+
return observeHttpServerRequest(
|
|
1981
|
+
observability,
|
|
1982
|
+
input,
|
|
1983
|
+
async (ctx) => {
|
|
1984
|
+
const effectWithServerEvents = withServerSpanEvents(effect, options);
|
|
1985
|
+
const wrapped = options.spans === false ? effectWithServerEvents : ctx.span(serverSpanName(input, options), effectWithServerEvents, {
|
|
1986
|
+
...ctx.attributes,
|
|
1987
|
+
...serverSpanAttributes(input, options)
|
|
1988
|
+
});
|
|
1989
|
+
return ctx.run(wrapped, env, runtimeOptions);
|
|
1990
|
+
},
|
|
1991
|
+
options
|
|
1992
|
+
);
|
|
1993
|
+
}
|
|
1994
|
+
function withServerSpanEvents(effect, options) {
|
|
1995
|
+
return _chunkMVGUEJ5Zcjs.asyncFold.call(void 0,
|
|
1996
|
+
effect,
|
|
1997
|
+
(error) => _chunkMVGUEJ5Zcjs.asyncFlatMap.call(void 0,
|
|
1998
|
+
spanEvent("http.server.error", {
|
|
1999
|
+
"http.response.status_code": 500,
|
|
2000
|
+
"error.type": error instanceof Error ? error.name : typeof error
|
|
2001
|
+
}),
|
|
2002
|
+
() => _chunkMVGUEJ5Zcjs.asyncFail.call(void 0, error)
|
|
2003
|
+
),
|
|
2004
|
+
(value) => {
|
|
2005
|
+
const statusCode = _nullishCoalesce(_optionalChain([options, 'access', _225 => _225.statusCode, 'optionalCall', _226 => _226(value)]), () => ( 200));
|
|
2006
|
+
return _chunkMVGUEJ5Zcjs.asyncFlatMap.call(void 0,
|
|
2007
|
+
spanEvent("http.server.response", {
|
|
2008
|
+
"http.response.status_code": statusCode,
|
|
2009
|
+
"http.outcome": statusCode >= 500 ? "error" : "success"
|
|
2010
|
+
}),
|
|
2011
|
+
() => _chunkMVGUEJ5Zcjs.asyncSucceed.call(void 0, value)
|
|
2012
|
+
);
|
|
2013
|
+
}
|
|
2014
|
+
);
|
|
2015
|
+
}
|
|
2016
|
+
async function observeHttpServerRequest(observability, input, handler, options = {}) {
|
|
2017
|
+
const resolved = resolveServerOptions(observability, options);
|
|
2018
|
+
const ctx = makeRequestObservabilityContext(observability, input);
|
|
2019
|
+
const startedAt = resolved.clock();
|
|
2020
|
+
const baseLabels = requestBaseLabels(input, resolved);
|
|
2021
|
+
const inFlight = _optionalChain([resolved, 'access', _227 => _227.metrics, 'optionalAccess', _228 => _228.gauge, 'call', _229 => _229("brass_http_server_in_flight", baseLabels)]);
|
|
2022
|
+
_optionalChain([inFlight, 'optionalAccess', _230 => _230.increment, 'call', _231 => _231()]);
|
|
2023
|
+
try {
|
|
2024
|
+
await ctx.run(logHttpServerRequest(input, resolved));
|
|
2025
|
+
const value = await handler(ctx);
|
|
2026
|
+
const statusCode = _nullishCoalesce(resolved.statusCode(value), () => ( 200));
|
|
2027
|
+
const durationMs = finishServerObservation(startedAt, input, ctx.trace, statusCode, "success", resolved);
|
|
2028
|
+
await ctx.run(logHttpServerResponse(input, statusCode, durationMs, "success", resolved));
|
|
2029
|
+
return { value, ctx, statusCode, durationMs };
|
|
2030
|
+
} catch (error) {
|
|
2031
|
+
const durationMs = finishServerObservation(startedAt, input, ctx.trace, 500, "exception", resolved);
|
|
2032
|
+
await ctx.run(logHttpServerError(input, error, durationMs, resolved));
|
|
2033
|
+
throw error;
|
|
2034
|
+
} finally {
|
|
2035
|
+
if (inFlight && inFlight.value() > 0) inFlight.decrement();
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
function resolveServerOptions(observability, options) {
|
|
2039
|
+
return {
|
|
2040
|
+
metrics: options.metrics === false ? void 0 : _nullishCoalesce(options.metrics, () => ( observability.metrics)),
|
|
2041
|
+
logs: _nullishCoalesce(options.logs, () => ( {})),
|
|
2042
|
+
spans: _nullishCoalesce(options.spans, () => ( {})),
|
|
2043
|
+
includeRouteLabel: _nullishCoalesce(options.includeRouteLabel, () => ( true)),
|
|
2044
|
+
clock: _nullishCoalesce(options.clock, () => ( Date.now)),
|
|
2045
|
+
durationBuckets: options.durationBuckets,
|
|
2046
|
+
statusCode: _nullishCoalesce(options.statusCode, () => ( (() => void 0)))
|
|
2047
|
+
};
|
|
2048
|
+
}
|
|
2049
|
+
function finishServerObservation(startedAt, input, trace, statusCode, outcome, options) {
|
|
2050
|
+
const durationMs = Math.max(0, options.clock() - startedAt);
|
|
2051
|
+
const labels = {
|
|
2052
|
+
...requestBaseLabels(input, options),
|
|
2053
|
+
outcome: statusCode >= 500 ? "error" : outcome,
|
|
2054
|
+
status: String(statusCode)
|
|
2055
|
+
};
|
|
2056
|
+
_optionalChain([options, 'access', _232 => _232.metrics, 'optionalAccess', _233 => _233.counter, 'call', _234 => _234("brass_http_server_requests_total", labels), 'access', _235 => _235.increment, 'call', _236 => _236()]);
|
|
2057
|
+
_optionalChain([options, 'access', _237 => _237.metrics, 'optionalAccess', _238 => _238.histogram, 'call', _239 => _239("brass_http_server_duration_ms", [..._nullishCoalesce(options.durationBuckets, () => ( DEFAULT_DURATION_BUCKETS2))], labels), 'access', _240 => _240.observe, 'call', _241 => _241(
|
|
2058
|
+
durationMs,
|
|
2059
|
+
exemplarFromTraceContext(trace, durationMs, startedAt + durationMs)
|
|
2060
|
+
)]);
|
|
2061
|
+
return durationMs;
|
|
2062
|
+
}
|
|
2063
|
+
function logHttpServerRequest(input, options) {
|
|
2064
|
+
const level = options.logs === false ? false : _nullishCoalesce(options.logs.requestLevel, () => ( false));
|
|
2065
|
+
if (!level) return logNoop();
|
|
2066
|
+
return logEffect(level, "http.server.request", requestLogFields(input));
|
|
2067
|
+
}
|
|
2068
|
+
function logHttpServerResponse(input, statusCode, durationMs, outcome, options) {
|
|
2069
|
+
const configured = options.logs === false ? false : _nullishCoalesce(options.logs.responseLevel, () => ( false));
|
|
2070
|
+
const level = configured || (statusCode >= 500 ? options.logs !== false ? _nullishCoalesce(options.logs.errorLevel, () => ( "warn")) : false : false);
|
|
2071
|
+
if (!level) return logNoop();
|
|
2072
|
+
return logEffect(level, "http.server.response", {
|
|
2073
|
+
...requestLogFields(input),
|
|
2074
|
+
status: statusCode,
|
|
2075
|
+
outcome,
|
|
2076
|
+
durationMs
|
|
2077
|
+
});
|
|
2078
|
+
}
|
|
2079
|
+
function logHttpServerError(input, error, durationMs, options) {
|
|
2080
|
+
const level = options.logs === false ? false : _nullishCoalesce(options.logs.errorLevel, () => ( "error"));
|
|
2081
|
+
if (!level) return logNoop();
|
|
2082
|
+
return logEffect(level, "http.server.error", {
|
|
2083
|
+
...requestLogFields(input),
|
|
2084
|
+
status: 500,
|
|
2085
|
+
outcome: "exception",
|
|
2086
|
+
durationMs,
|
|
2087
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2088
|
+
});
|
|
2089
|
+
}
|
|
2090
|
+
function logNoop() {
|
|
2091
|
+
return _chunkMVGUEJ5Zcjs.asyncSucceed.call(void 0, void 0);
|
|
2092
|
+
}
|
|
2093
|
+
function serverSpanName(input, options) {
|
|
2094
|
+
if (options.spans && options.spans.name) {
|
|
2095
|
+
return typeof options.spans.name === "function" ? options.spans.name(input) : options.spans.name;
|
|
2096
|
+
}
|
|
2097
|
+
return input.method && input.route ? `${input.method} ${input.route}` : _nullishCoalesce(input.route, () => ( "request"));
|
|
2098
|
+
}
|
|
2099
|
+
function serverSpanAttributes(input, options) {
|
|
2100
|
+
const spanOptions = options.spans === false ? {} : _optionalChain([options, 'access', _242 => _242.spans, 'optionalAccess', _243 => _243.attributes]);
|
|
2101
|
+
const custom = typeof spanOptions === "function" ? spanOptions(input) : _nullishCoalesce(spanOptions, () => ( {}));
|
|
2102
|
+
return {
|
|
2103
|
+
"span.kind": "server",
|
|
2104
|
+
...input.method ? { "http.request.method": input.method } : {},
|
|
2105
|
+
...input.route ? { "http.route": input.route } : {},
|
|
2106
|
+
...custom
|
|
2107
|
+
};
|
|
2108
|
+
}
|
|
2109
|
+
function requestBaseLabels(input, options) {
|
|
2110
|
+
return compactLabels({
|
|
2111
|
+
method: input.method,
|
|
2112
|
+
...options.includeRouteLabel ? { route: input.route } : {}
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
2115
|
+
function requestLogFields(input) {
|
|
2116
|
+
return {
|
|
2117
|
+
...input.method ? { method: input.method } : {},
|
|
2118
|
+
...input.route ? { route: input.route } : {},
|
|
2119
|
+
...input.target ? { target: input.target } : {}
|
|
2120
|
+
};
|
|
2121
|
+
}
|
|
2122
|
+
function compactLabels(labels) {
|
|
2123
|
+
const out = {};
|
|
2124
|
+
for (const [key, value] of Object.entries(labels)) {
|
|
2125
|
+
if (value !== void 0 && value !== "") out[key] = value;
|
|
2126
|
+
}
|
|
2127
|
+
return out;
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
|
|
2131
|
+
|
|
2132
|
+
|
|
2133
|
+
|
|
2134
|
+
|
|
2135
|
+
|
|
2136
|
+
|
|
2137
|
+
|
|
2138
|
+
|
|
2139
|
+
|
|
2140
|
+
|
|
2141
|
+
|
|
2142
|
+
|
|
2143
|
+
|
|
2144
|
+
|
|
2145
|
+
|
|
2146
|
+
|
|
2147
|
+
|
|
2148
|
+
|
|
2149
|
+
|
|
2150
|
+
|
|
2151
|
+
|
|
2152
|
+
|
|
2153
|
+
|
|
2154
|
+
|
|
2155
|
+
|
|
2156
|
+
|
|
2157
|
+
|
|
2158
|
+
|
|
2159
|
+
|
|
2160
|
+
|
|
2161
|
+
|
|
2162
|
+
|
|
2163
|
+
|
|
2164
|
+
|
|
2165
|
+
|
|
2166
|
+
|
|
2167
|
+
|
|
2168
|
+
|
|
2169
|
+
|
|
2170
|
+
|
|
2171
|
+
|
|
2172
|
+
|
|
2173
|
+
|
|
2174
|
+
|
|
2175
|
+
|
|
2176
|
+
|
|
2177
|
+
|
|
2178
|
+
|
|
2179
|
+
|
|
2180
|
+
|
|
2181
|
+
|
|
2182
|
+
|
|
2183
|
+
|
|
2184
|
+
|
|
2185
|
+
|
|
2186
|
+
|
|
2187
|
+
|
|
2188
|
+
|
|
2189
|
+
|
|
2190
|
+
|
|
2191
|
+
|
|
2192
|
+
|
|
2193
|
+
exports.snapshotRuntimeHealth = snapshotRuntimeHealth; exports.makeRuntimeHealth = makeRuntimeHealth; exports.runtimeHealth = runtimeHealth; exports.readiness = readiness; exports.healthToHttpResponse = healthToHttpResponse; exports.makeObservabilityRedactor = makeObservabilityRedactor; exports.parseTraceparent = parseTraceparent; exports.formatTraceparent = formatTraceparent; exports.extractTraceContext = extractTraceContext; exports.injectTraceContext = injectTraceContext; exports.parseBaggage = parseBaggage; exports.formatBaggage = formatBaggage; exports.extractBaggage = extractBaggage; exports.injectBaggage = injectBaggage; exports.normalizeTraceId = normalizeTraceId; exports.normalizeSpanId = normalizeSpanId; exports.PROMETHEUS_CONTENT_TYPE = PROMETHEUS_CONTENT_TYPE; exports.OTLP_JSON_CONTENT_TYPE = OTLP_JSON_CONTENT_TYPE; exports.makePrometheusMetricsExporter = makePrometheusMetricsExporter; exports.formatPrometheusMetrics = formatPrometheusMetrics; exports.metricsSnapshotToOtlp = metricsSnapshotToOtlp; exports.makeOtlpHttpMetricsExporter = makeOtlpHttpMetricsExporter; exports.makeRuntimeMetricsSink = makeRuntimeMetricsSink; exports.exemplarFromTraceContext = exemplarFromTraceContext; exports.toOtlpAttributes = toOtlpAttributes; exports.otlpAnyValue = otlpAnyValue; exports.postOtlpJson = postOtlpJson; exports.unixNanoFromMs = unixNanoFromMs; exports.makeStructuredLogSink = makeStructuredLogSink; exports.formatStructuredLog = formatStructuredLog; exports.structuredLogsToOtlp = structuredLogsToOtlp; exports.makeOtlpHttpLogExporter = makeOtlpHttpLogExporter; exports.logEffect = logEffect; exports.withLogContext = withLogContext; exports.defaultStructuredLogWriter = defaultStructuredLogWriter; exports.alwaysOnSampler = alwaysOnSampler; exports.alwaysOffSampler = alwaysOffSampler; exports.ratioSampler = ratioSampler; exports.makeTraceSampler = makeTraceSampler; exports.resolveTraceSampling = resolveTraceSampling; exports.shouldSampleWith = shouldSampleWith; exports.withSpan = withSpan; exports.spanLink = spanLink; exports.currentSpanLink = currentSpanLink; exports.withBaggage = withBaggage; exports.currentBaggage = currentBaggage; exports.spanEvent = spanEvent; exports.spansToOtlp = spansToOtlp; exports.makeOtlpHttpSpanExporter = makeOtlpHttpSpanExporter; exports.makeExportPipeline = makeExportPipeline; exports.exportWithRetry = exportWithRetry; exports.withTimeout = withTimeout; exports.makeCardinalityLimitedMetrics = makeCardinalityLimitedMetrics; exports.normalizeHttpRoute = normalizeHttpRoute; exports.sanitizeHttpTarget = sanitizeHttpTarget; exports.validateHttpObservabilityOptions = validateHttpObservabilityOptions; exports.makeObservability = makeObservability; exports.resolveRequestTraceSeed = resolveRequestTraceSeed; exports.resolveRequestBaggage = resolveRequestBaggage; exports.makeRequestObservabilityContext = makeRequestObservabilityContext; exports.runObservedHttpServerEffect = runObservedHttpServerEffect; exports.observeHttpServerRequest = observeHttpServerRequest;
|