@fougere/observability 0.9.0-alpha.1 → 0.9.2-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extension.d.ts +8 -0
- package/dist/extension.d.ts.map +1 -1
- package/dist/extension.js +11 -4
- package/dist/extension.js.map +1 -1
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -15
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/extension.ts +18 -4
- package/src/index.ts +12 -16
package/dist/extension.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** This package's own ascent and descent, in one value. */
|
|
2
2
|
import { type Extension } from '@fougere/core';
|
|
3
|
+
import { type SpanSink } from './index.js';
|
|
3
4
|
export interface ObservabilityOptions {
|
|
4
5
|
/** What a dashboard groups this process by, and the name a log line carries. */
|
|
5
6
|
service?: string;
|
|
@@ -8,6 +9,13 @@ export interface ObservabilityOptions {
|
|
|
8
9
|
flushMs?: number;
|
|
9
10
|
/** A collector that cannot be reached is the app's business, not its failure. */
|
|
10
11
|
onError?: (error: unknown) => void;
|
|
12
|
+
/**
|
|
13
|
+
* One more taker for this app's spans, beside the metrics and the exporter — a gauge, a
|
|
14
|
+
* console line, anything reading what an operation cost. It is declared here rather than
|
|
15
|
+
* registered afterwards so it is released with the app, like everything else the
|
|
16
|
+
* extension took.
|
|
17
|
+
*/
|
|
18
|
+
onSpan?: SpanSink;
|
|
11
19
|
}
|
|
12
20
|
/** Observe this process — one member of the ascent. */
|
|
13
21
|
export declare function observability(options?: ObservabilityOptions): Extension;
|
package/dist/extension.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,OAAO,EAAsD,KAAK,SAAS,EAAgB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,OAAO,EAAsD,KAAK,SAAS,EAAgB,MAAM,eAAe,CAAC;AAIjH,OAAO,EAAS,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAKlD,MAAM,WAAW,oBAAoB;IACnC,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED,uDAAuD;AACvD,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,SAAS,CAqG3E"}
|
package/dist/extension.js
CHANGED
|
@@ -3,7 +3,7 @@ import { frond, loggerMiddleware, LogLine } from '@fougere/core';
|
|
|
3
3
|
import ExportHandler from './ExportHandler.js';
|
|
4
4
|
import { traceContext } from '#trace-context';
|
|
5
5
|
import { registerFlush } from './index.js';
|
|
6
|
-
import { trace
|
|
6
|
+
import { trace } from './index.js';
|
|
7
7
|
import { metrics, serveTopology } from './metrics.js';
|
|
8
8
|
import { otlp } from './otlp.js';
|
|
9
9
|
import { logs } from './logs.js';
|
|
@@ -14,6 +14,8 @@ export function observability(options = {}) {
|
|
|
14
14
|
const undoing = new WeakMap();
|
|
15
15
|
/** One box the handler resolves, whose content the ascent decides. */
|
|
16
16
|
const exporting = { take: () => { } };
|
|
17
|
+
/** Who takes this app's spans. Per APP for the reason `undoing` is. */
|
|
18
|
+
const takers = new WeakMap();
|
|
17
19
|
return {
|
|
18
20
|
name: 'observability',
|
|
19
21
|
// What it needs a SIGNATURE for: every line this process logs. Stated rather than
|
|
@@ -38,7 +40,9 @@ export function observability(options = {}) {
|
|
|
38
40
|
undoing.set(app, undo);
|
|
39
41
|
// Order matters: `trace()` opens the span that every log line written inside the
|
|
40
42
|
// call will carry. Installed the other way round, the lines leave uncorrelated.
|
|
41
|
-
|
|
43
|
+
const spans = options.onSpan ? [options.onSpan] : [];
|
|
44
|
+
takers.set(app, spans);
|
|
45
|
+
app.use(trace(spans));
|
|
42
46
|
// The app's own logger, named — NOT `new Logger(service)`: a logger built here has
|
|
43
47
|
// no `Carry`, so its lines printed and announced nothing. 20 on the console, 0 in
|
|
44
48
|
// the ring, measured on `demos/observability`.
|
|
@@ -54,7 +58,7 @@ export function observability(options = {}) {
|
|
|
54
58
|
+ 'traceparent on the invocation. Add "nodejs_als" to compatibility_flags to restore the rest.');
|
|
55
59
|
}
|
|
56
60
|
const measured = metrics(app);
|
|
57
|
-
|
|
61
|
+
spans.push(measured.sink);
|
|
58
62
|
serveTopology(app, measured);
|
|
59
63
|
if (!options.otlp)
|
|
60
64
|
return;
|
|
@@ -78,7 +82,8 @@ export function observability(options = {}) {
|
|
|
78
82
|
// What `ExportHandler` asks for — replaced rather than added to, so a reload does not
|
|
79
83
|
// hand the old exporter a new app's lines.
|
|
80
84
|
exporting.take = written.sink;
|
|
81
|
-
|
|
85
|
+
spans.push(telemetry.sink);
|
|
86
|
+
undo.push(registerFlush(() => telemetry.flush()), registerFlush(() => written.flush()), () => telemetry.stop(), () => written.stop(), () => { exporting.take = () => { }; });
|
|
82
87
|
},
|
|
83
88
|
async down(app) {
|
|
84
89
|
// Reverse, and the exporters' `stop()` is in here: it flushes what is buffered, so a
|
|
@@ -87,6 +92,8 @@ export function observability(options = {}) {
|
|
|
87
92
|
if (!undo)
|
|
88
93
|
return;
|
|
89
94
|
undoing.delete(app);
|
|
95
|
+
// Nothing to unsubscribe from: the list was this app's, and it goes with it.
|
|
96
|
+
takers.delete(app);
|
|
90
97
|
for (const step of undo.reverse())
|
|
91
98
|
await step();
|
|
92
99
|
},
|
package/dist/extension.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAU,OAAO,EAA0C,MAAM,eAAe,CAAC;AACjH,OAAO,aAAa,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -27,9 +27,15 @@ export declare function activeCalls(): number;
|
|
|
27
27
|
export declare function flushTelemetry(): Promise<void>;
|
|
28
28
|
/** Declare an exporter's flush, and answer the way to withdraw it — like `onSpan`. */
|
|
29
29
|
export declare function registerFlush(send: () => Promise<void>): () => void;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
/**
|
|
31
|
+
* The middleware, and it is HANDED who takes the spans it finishes.
|
|
32
|
+
*
|
|
33
|
+
* The list belongs to the app the middleware was installed on, never to the process: a
|
|
34
|
+
* discarded app went on feeding the takers of the app that replaced it, so every metric
|
|
35
|
+
* counted twice. A module-level array made that a leak you had to remember to undo; a list
|
|
36
|
+
* held beside the app cannot outlive it. Read at every end, so a taker added later counts.
|
|
37
|
+
*/
|
|
38
|
+
export declare function trace(takers: readonly SpanSink[]): AppMiddleware;
|
|
33
39
|
export { otlp } from './otlp.js';
|
|
34
40
|
export { metrics } from './metrics.js';
|
|
35
41
|
export type { Metrics, TopologyReport, FrondPlacement, Edge } from './metrics.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAA8C,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAOhG,iDAAiD;AACjD,MAAM,WAAW,YAAa,SAAQ,WAAW;IAC/C,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B;;;OAGG;IACH,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,6FAA6F;IAC7F,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,sEAAsE;IACtE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;AAEpD,qCAAqC;AAErC,0CAA0C;AAC1C,wBAAgB,WAAW,IAAI,WAAW,GAAG,SAAS,CAErD;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAA8C,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAOhG,iDAAiD;AACjD,MAAM,WAAW,YAAa,SAAQ,WAAW;IAC/C,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B;;;OAGG;IACH,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,6FAA6F;IAC7F,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,sEAAsE;IACtE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;AAEpD,qCAAqC;AAErC,0CAA0C;AAC1C,wBAAgB,WAAW,IAAI,WAAW,GAAG,SAAS,CAErD;AAQD,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAKD,kCAAkC;AAClC,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAMpD;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAMnE;AAED;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,GAAG,aAAa,CAgEhE;AAOD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,8 +7,6 @@ import { parseTraceparent, traceparentOf, randomHex } from './traceparent.js';
|
|
|
7
7
|
export function currentSpan() {
|
|
8
8
|
return traceContext.current();
|
|
9
9
|
}
|
|
10
|
-
/** Who takes the spans this process finishes, consulted at every end like the log level. */
|
|
11
|
-
const sinks = [];
|
|
12
10
|
/**
|
|
13
11
|
* Calls running right now — the saturation signal, and the only one a FINISHED span
|
|
14
12
|
* cannot carry. Counted at the same two moments the span is opened and closed.
|
|
@@ -37,18 +35,17 @@ export function registerFlush(send) {
|
|
|
37
35
|
flushes.splice(at, 1);
|
|
38
36
|
};
|
|
39
37
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
export function trace() {
|
|
38
|
+
/**
|
|
39
|
+
* The middleware, and it is HANDED who takes the spans it finishes.
|
|
40
|
+
*
|
|
41
|
+
* The list belongs to the app the middleware was installed on, never to the process: a
|
|
42
|
+
* discarded app went on feeding the takers of the app that replaced it, so every metric
|
|
43
|
+
* counted twice. A module-level array made that a leak you had to remember to undo; a list
|
|
44
|
+
* held beside the app cannot outlive it. Read at every end, so a taker added later counts.
|
|
45
|
+
*/
|
|
46
|
+
export function trace(takers) {
|
|
50
47
|
return (ctx, next) => {
|
|
51
|
-
if (
|
|
48
|
+
if (takers.length === 0)
|
|
52
49
|
return next();
|
|
53
50
|
// An op that CARRIES a line is not a call this process made: counting it puts the
|
|
54
51
|
// delivery of a log line in the saturation figure, and spanning it puts a line about
|
|
@@ -94,8 +91,8 @@ export function trace() {
|
|
|
94
91
|
ms: performance.now() - start,
|
|
95
92
|
error,
|
|
96
93
|
};
|
|
97
|
-
// A
|
|
98
|
-
for (const take of
|
|
94
|
+
// A taker that throws is a broken exporter, never a broken call.
|
|
95
|
+
for (const take of takers) {
|
|
99
96
|
try {
|
|
100
97
|
take(done);
|
|
101
98
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAsB,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,SAAS,EAAoB,MAAM,kBAAkB,CAAC;AA4BhG,qCAAqC;AAErC,0CAA0C;AAC1C,MAAM,UAAU,WAAW;IACzB,OAAO,YAAY,CAAC,OAAO,EAAW,CAAC;AACzC,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fougere/observability",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2-alpha.0",
|
|
4
4
|
"description": "Optional observability for Fougere: one span per operation, W3C Trace Context across any transport.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fougere",
|
|
@@ -31,15 +31,15 @@
|
|
|
31
31
|
"src"
|
|
32
32
|
],
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@fougere/core": "^0.9.
|
|
34
|
+
"@fougere/core": "^0.9.2-alpha.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"vitest": "^4.1.0",
|
|
38
|
-
"@fougere/container": "0.9.
|
|
39
|
-
"@fougere/
|
|
40
|
-
"@fougere/
|
|
41
|
-
"@fougere/
|
|
42
|
-
"@fougere/
|
|
38
|
+
"@fougere/container": "0.9.2-alpha.0",
|
|
39
|
+
"@fougere/core": "0.9.2-alpha.0",
|
|
40
|
+
"@fougere/schema": "0.9.2-alpha.0",
|
|
41
|
+
"@fougere/transport-http": "0.9.2-alpha.0",
|
|
42
|
+
"@fougere/compiler": "0.9.2-alpha.0"
|
|
43
43
|
},
|
|
44
44
|
"publishConfig": {
|
|
45
45
|
"access": "public"
|
package/src/extension.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { frond, loggerMiddleware, Logger, LogLine, type App, type Extension, typ
|
|
|
3
3
|
import ExportHandler from './ExportHandler.js';
|
|
4
4
|
import { traceContext } from '#trace-context';
|
|
5
5
|
import { registerFlush } from './index.js';
|
|
6
|
-
import { trace,
|
|
6
|
+
import { trace, type SpanSink } from './index.js';
|
|
7
7
|
import { metrics, serveTopology } from './metrics.js';
|
|
8
8
|
import { otlp } from './otlp.js';
|
|
9
9
|
import { logs } from './logs.js';
|
|
@@ -16,6 +16,13 @@ export interface ObservabilityOptions {
|
|
|
16
16
|
flushMs?: number;
|
|
17
17
|
/** A collector that cannot be reached is the app's business, not its failure. */
|
|
18
18
|
onError?: (error: unknown) => void;
|
|
19
|
+
/**
|
|
20
|
+
* One more taker for this app's spans, beside the metrics and the exporter — a gauge, a
|
|
21
|
+
* console line, anything reading what an operation cost. It is declared here rather than
|
|
22
|
+
* registered afterwards so it is released with the app, like everything else the
|
|
23
|
+
* extension took.
|
|
24
|
+
*/
|
|
25
|
+
onSpan?: SpanSink;
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
/** Observe this process — one member of the ascent. */
|
|
@@ -27,6 +34,9 @@ export function observability(options: ObservabilityOptions = {}): Extension {
|
|
|
27
34
|
/** One box the handler resolves, whose content the ascent decides. */
|
|
28
35
|
const exporting: { take: LogSink } = { take: () => {} };
|
|
29
36
|
|
|
37
|
+
/** Who takes this app's spans. Per APP for the reason `undoing` is. */
|
|
38
|
+
const takers = new WeakMap<App, SpanSink[]>();
|
|
39
|
+
|
|
30
40
|
return {
|
|
31
41
|
name: 'observability',
|
|
32
42
|
|
|
@@ -53,7 +63,9 @@ export function observability(options: ObservabilityOptions = {}): Extension {
|
|
|
53
63
|
undoing.set(app, undo);
|
|
54
64
|
// Order matters: `trace()` opens the span that every log line written inside the
|
|
55
65
|
// call will carry. Installed the other way round, the lines leave uncorrelated.
|
|
56
|
-
|
|
66
|
+
const spans: SpanSink[] = options.onSpan ? [options.onSpan] : [];
|
|
67
|
+
takers.set(app, spans);
|
|
68
|
+
app.use(trace(spans));
|
|
57
69
|
// The app's own logger, named — NOT `new Logger(service)`: a logger built here has
|
|
58
70
|
// no `Carry`, so its lines printed and announced nothing. 20 on the console, 0 in
|
|
59
71
|
// the ring, measured on `demos/observability`.
|
|
@@ -73,7 +85,7 @@ export function observability(options: ObservabilityOptions = {}): Extension {
|
|
|
73
85
|
}
|
|
74
86
|
|
|
75
87
|
const measured = metrics(app);
|
|
76
|
-
|
|
88
|
+
spans.push(measured.sink);
|
|
77
89
|
serveTopology(app, measured);
|
|
78
90
|
|
|
79
91
|
if (!options.otlp) return;
|
|
@@ -97,8 +109,8 @@ export function observability(options: ObservabilityOptions = {}): Extension {
|
|
|
97
109
|
// What `ExportHandler` asks for — replaced rather than added to, so a reload does not
|
|
98
110
|
// hand the old exporter a new app's lines.
|
|
99
111
|
exporting.take = written.sink;
|
|
112
|
+
spans.push(telemetry.sink);
|
|
100
113
|
undo.push(
|
|
101
|
-
onSpan(telemetry.sink),
|
|
102
114
|
registerFlush(() => telemetry.flush()), registerFlush(() => written.flush()),
|
|
103
115
|
() => telemetry.stop(), () => written.stop(),
|
|
104
116
|
() => { exporting.take = () => {}; },
|
|
@@ -110,6 +122,8 @@ export function observability(options: ObservabilityOptions = {}): Extension {
|
|
|
110
122
|
const undo = undoing.get(app);
|
|
111
123
|
if (!undo) return;
|
|
112
124
|
undoing.delete(app);
|
|
125
|
+
// Nothing to unsubscribe from: the list was this app's, and it goes with it.
|
|
126
|
+
takers.delete(app);
|
|
113
127
|
for (const step of undo.reverse()) await step();
|
|
114
128
|
},
|
|
115
129
|
};
|
package/src/index.ts
CHANGED
|
@@ -36,9 +36,6 @@ export function currentSpan(): SpanContext | undefined {
|
|
|
36
36
|
return traceContext.current<Running>();
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
/** Who takes the spans this process finishes, consulted at every end like the log level. */
|
|
40
|
-
const sinks: SpanSink[] = [];
|
|
41
|
-
|
|
42
39
|
/**
|
|
43
40
|
* Calls running right now — the saturation signal, and the only one a FINISHED span
|
|
44
41
|
* cannot carry. Counted at the same two moments the span is opened and closed.
|
|
@@ -70,18 +67,17 @@ export function registerFlush(send: () => Promise<void>): () => void {
|
|
|
70
67
|
};
|
|
71
68
|
}
|
|
72
69
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
export function trace(): AppMiddleware {
|
|
70
|
+
/**
|
|
71
|
+
* The middleware, and it is HANDED who takes the spans it finishes.
|
|
72
|
+
*
|
|
73
|
+
* The list belongs to the app the middleware was installed on, never to the process: a
|
|
74
|
+
* discarded app went on feeding the takers of the app that replaced it, so every metric
|
|
75
|
+
* counted twice. A module-level array made that a leak you had to remember to undo; a list
|
|
76
|
+
* held beside the app cannot outlive it. Read at every end, so a taker added later counts.
|
|
77
|
+
*/
|
|
78
|
+
export function trace(takers: readonly SpanSink[]): AppMiddleware {
|
|
83
79
|
return (ctx, next) => {
|
|
84
|
-
if (
|
|
80
|
+
if (takers.length === 0) return next();
|
|
85
81
|
// An op that CARRIES a line is not a call this process made: counting it puts the
|
|
86
82
|
// delivery of a log line in the saturation figure, and spanning it puts a line about
|
|
87
83
|
// the span back on the wire. Same rule as `loggerMiddleware`, one declaration.
|
|
@@ -128,8 +124,8 @@ export function trace(): AppMiddleware {
|
|
|
128
124
|
ms: performance.now() - start,
|
|
129
125
|
error,
|
|
130
126
|
};
|
|
131
|
-
// A
|
|
132
|
-
for (const take of
|
|
127
|
+
// A taker that throws is a broken exporter, never a broken call.
|
|
128
|
+
for (const take of takers) { try { take(done); } catch { /* observing never refuses */ } }
|
|
133
129
|
};
|
|
134
130
|
|
|
135
131
|
return traceContext.within(span, async () => {
|