@fougere/observability 0.9.2-alpha.0 → 0.10.0-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/OtlpExporter.d.ts +12 -0
- package/dist/OtlpExporter.d.ts.map +1 -0
- package/dist/{otlp.js → OtlpExporter.js} +9 -4
- package/dist/OtlpExporter.js.map +1 -0
- package/dist/{otlp.d.ts → OtlpOptions.d.ts} +2 -12
- package/dist/OtlpOptions.d.ts.map +1 -0
- package/dist/OtlpOptions.js +2 -0
- package/dist/OtlpOptions.js.map +1 -0
- package/dist/extension.d.ts +19 -0
- package/dist/extension.d.ts.map +1 -1
- package/dist/extension.js +32 -9
- package/dist/extension.js.map +1 -1
- package/dist/index.d.ts +90 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +106 -13
- package/dist/index.js.map +1 -1
- package/dist/logs/CapturedLog.d.ts +7 -0
- package/dist/logs/CapturedLog.d.ts.map +1 -0
- package/dist/logs/CapturedLog.js +2 -0
- package/dist/logs/CapturedLog.js.map +1 -0
- package/dist/logs/LogExporter.d.ts +12 -0
- package/dist/logs/LogExporter.d.ts.map +1 -0
- package/dist/{logs.js → logs/LogExporter.js} +4 -5
- package/dist/logs/LogExporter.js.map +1 -0
- package/dist/logs/LogsOptions.d.ts +13 -0
- package/dist/logs/LogsOptions.d.ts.map +1 -0
- package/dist/logs/LogsOptions.js +2 -0
- package/dist/logs/LogsOptions.js.map +1 -0
- package/dist/metrics/Bucketed.d.ts +16 -0
- package/dist/metrics/Bucketed.d.ts.map +1 -0
- package/dist/metrics/Bucketed.js +2 -0
- package/dist/metrics/Bucketed.js.map +1 -0
- package/dist/metrics/Metrics.d.ts +42 -0
- package/dist/metrics/Metrics.d.ts.map +1 -0
- package/dist/{metrics.js → metrics/Metrics.js} +97 -28
- package/dist/metrics/Metrics.js.map +1 -0
- package/dist/metrics/MetricsSnapshot.d.ts +14 -0
- package/dist/metrics/MetricsSnapshot.d.ts.map +1 -0
- package/dist/metrics/MetricsSnapshot.js +2 -0
- package/dist/metrics/MetricsSnapshot.js.map +1 -0
- package/dist/otlp/OtlpExporter.d.ts +12 -0
- package/dist/otlp/OtlpExporter.d.ts.map +1 -0
- package/dist/otlp/OtlpExporter.js +64 -0
- package/dist/otlp/OtlpExporter.js.map +1 -0
- package/dist/otlp/OtlpOptions.d.ts +16 -0
- package/dist/otlp/OtlpOptions.d.ts.map +1 -0
- package/dist/otlp/OtlpOptions.js +2 -0
- package/dist/otlp/OtlpOptions.js.map +1 -0
- package/package.json +14 -7
- package/src/{otlp.ts → OtlpExporter.ts} +12 -18
- package/src/OtlpOptions.ts +16 -0
- package/src/extension.ts +53 -9
- package/src/index.ts +189 -14
- package/src/logs/CapturedLog.ts +7 -0
- package/src/{logs.ts → logs/LogExporter.ts} +5 -23
- package/src/logs/LogsOptions.ts +12 -0
- package/src/metrics/Bucketed.ts +15 -0
- package/src/{metrics.ts → metrics/Metrics.ts} +100 -53
- package/src/metrics/MetricsSnapshot.ts +14 -0
- package/src/otlp/OtlpExporter.ts +87 -0
- package/src/otlp/OtlpOptions.ts +16 -0
- package/dist/logs.d.ts +0 -28
- package/dist/logs.d.ts.map +0 -1
- package/dist/logs.js.map +0 -1
- package/dist/metrics.d.ts +0 -59
- package/dist/metrics.d.ts.map +0 -1
- package/dist/metrics.js.map +0 -1
- package/dist/otlp.d.ts.map +0 -1
- package/dist/otlp.js.map +0 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface LogsOptions {
|
|
2
|
+
/** Which service these lines belong to. */
|
|
3
|
+
service: string;
|
|
4
|
+
/** Collector endpoint. Default: the OTLP/HTTP convention on localhost. */
|
|
5
|
+
url?: string;
|
|
6
|
+
/** How often a batch leaves. Default: every second. */
|
|
7
|
+
flushMs?: number;
|
|
8
|
+
/** Told when a batch could not be sent. Default: silence. */
|
|
9
|
+
onError?: (err: unknown) => void;
|
|
10
|
+
/** Drop anything below this level before it leaves the process. */
|
|
11
|
+
minimum?: 'debug' | 'info' | 'warn' | 'error';
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=LogsOptions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LogsOptions.d.ts","sourceRoot":"","sources":["../../src/logs/LogsOptions.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IACjC,mEAAmE;IACnE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;CAC/C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LogsOptions.js","sourceRoot":"","sources":["../../src/logs/LogsOptions.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface Bucketed {
|
|
2
|
+
frond: string | undefined;
|
|
3
|
+
entity: string;
|
|
4
|
+
operation: string;
|
|
5
|
+
error: string | undefined;
|
|
6
|
+
count: number;
|
|
7
|
+
sum: number;
|
|
8
|
+
/** One more than the bounds: the last holds everything above the highest bound. */
|
|
9
|
+
buckets: number[];
|
|
10
|
+
/** The same measurement over `selfMs` — what the op did rather than what it waited for. */
|
|
11
|
+
selfSum: number;
|
|
12
|
+
selfBuckets: number[];
|
|
13
|
+
/** Every statement these calls ran. Against `count`, it is statements per call. */
|
|
14
|
+
statements: number;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=Bucketed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Bucketed.d.ts","sourceRoot":"","sources":["../../src/metrics/Bucketed.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,mFAAmF;IACnF,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,2FAA2F;IAC3F,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,mFAAmF;IACnF,UAAU,EAAE,MAAM,CAAC;CACpB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Bucketed.js","sourceRoot":"","sources":["../../src/metrics/Bucketed.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type App } from '@fougere/core';
|
|
2
|
+
import { type SpanSink } from '../index.js';
|
|
3
|
+
import type { MetricsSnapshot } from './MetricsSnapshot.js';
|
|
4
|
+
export interface Metrics {
|
|
5
|
+
/** Hand to `onSpan` — it reads the same span the tracer reads. */
|
|
6
|
+
sink: SpanSink;
|
|
7
|
+
/** What to publish now. */
|
|
8
|
+
snapshot(): MetricsSnapshot;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* `app` is optional and only feeds the topology: what fronds this process found, and which of them
|
|
12
|
+
* run elsewhere.
|
|
13
|
+
*/
|
|
14
|
+
export declare function metrics(app?: App, bounds?: readonly number[]): Metrics;
|
|
15
|
+
/**
|
|
16
|
+
* Serve the topology on `rpc.topology` — read from inside the process it describes.
|
|
17
|
+
*
|
|
18
|
+
* The declared half is read at every call rather than once here: `reloadFougere()` builds the app
|
|
19
|
+
* again, and a config read at boot would keep answering for the app that was released.
|
|
20
|
+
*/
|
|
21
|
+
export declare function serveTopology(app: App, measured: Metrics): void;
|
|
22
|
+
/** One snapshot, as OTLP/JSON spells metrics. Cumulative, which is what Prometheus reads. */
|
|
23
|
+
export declare function metricsPayload(service: string, snapshot: MetricsSnapshot): {
|
|
24
|
+
resourceMetrics: {
|
|
25
|
+
resource: {
|
|
26
|
+
attributes: {
|
|
27
|
+
key: string;
|
|
28
|
+
value: {
|
|
29
|
+
stringValue: string;
|
|
30
|
+
};
|
|
31
|
+
}[];
|
|
32
|
+
};
|
|
33
|
+
scopeMetrics: {
|
|
34
|
+
scope: {
|
|
35
|
+
name: string;
|
|
36
|
+
};
|
|
37
|
+
/** Only what has points. */
|
|
38
|
+
metrics: Record<string, any>[];
|
|
39
|
+
}[];
|
|
40
|
+
}[];
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=Metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Metrics.d.ts","sourceRoot":"","sources":["../../src/metrics/Metrics.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,KAAK,GAAG,EAAuD,MAAM,eAAe,CAAC;AAClH,OAAO,EAAkC,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAkB5D,MAAM,WAAW,OAAO;IACtB,kEAAkE;IAClE,IAAI,EAAE,QAAQ,CAAC;IACf,2BAA2B;IAC3B,QAAQ,IAAI,eAAe,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,GAAE,SAAS,MAAM,EAAW,GAAG,OAAO,CA2D9E;AA0CD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI,CAM/D;AAED,6FAA6F;AAC7F,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe;;;YAgBrD,UAAU;;;oBAbkC,WAAW;;;;;;gBAgBtD,IAAI;;YACb,4BAA4B;;;;EA8HvC"}
|
|
@@ -1,14 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { declaredTopologyOf } from '@fougere/core';
|
|
2
|
+
import { activeCalls } from '../index.js';
|
|
2
3
|
/**
|
|
3
|
-
* Bucket bounds in SECONDS, the
|
|
4
|
-
*
|
|
4
|
+
* Bucket bounds in SECONDS, and the operator's to choose — this is the default, not the rule.
|
|
5
|
+
*
|
|
6
|
+
* The OpenTelemetry recommendation for request durations: dense under 100 ms because that is
|
|
7
|
+
* where a healthy op lives, and open above 10 s. A system whose ops all cross two processes has
|
|
8
|
+
* its floor elsewhere and wastes half these buckets.
|
|
9
|
+
*
|
|
10
|
+
* Deliberately NOT derived per operation, though `reach` could: an explicit-bucket histogram only
|
|
11
|
+
* aggregates across series that share its bounds, so a panel asking for the p95 of the whole
|
|
12
|
+
* service — which sums over every op — would stop meaning anything, silently. One list per
|
|
13
|
+
* process is what keeps that reading honest. The number that IS per-op is the load threshold,
|
|
14
|
+
* where nothing aggregates.
|
|
5
15
|
*/
|
|
6
16
|
const BOUNDS = [0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10];
|
|
7
17
|
/**
|
|
8
18
|
* `app` is optional and only feeds the topology: what fronds this process found, and which of them
|
|
9
19
|
* run elsewhere.
|
|
10
20
|
*/
|
|
11
|
-
export function metrics(app) {
|
|
21
|
+
export function metrics(app, bounds = BOUNDS) {
|
|
12
22
|
const since = Date.now();
|
|
13
23
|
const series = new Map();
|
|
14
24
|
/** Every frond this process has actually CALLED — the other half of the topology. */
|
|
@@ -17,6 +27,11 @@ export function metrics(app) {
|
|
|
17
27
|
const edges = new Map();
|
|
18
28
|
return {
|
|
19
29
|
sink: (span) => {
|
|
30
|
+
// The one reader that has to choose: a statement is a step, not a call this process
|
|
31
|
+
// answered. Counted here it would publish `select.post` as an operation nothing
|
|
32
|
+
// serves, and every topology and saturation figure would count it too.
|
|
33
|
+
if (span.kind !== 'operation')
|
|
34
|
+
return;
|
|
20
35
|
const key = `${span.entity}\0${span.operation}\0${span.error ?? ''}`;
|
|
21
36
|
let row = series.get(key);
|
|
22
37
|
if (!row) {
|
|
@@ -27,7 +42,10 @@ export function metrics(app) {
|
|
|
27
42
|
error: span.error,
|
|
28
43
|
count: 0,
|
|
29
44
|
sum: 0,
|
|
30
|
-
buckets: new Array(
|
|
45
|
+
buckets: new Array(bounds.length + 1).fill(0),
|
|
46
|
+
selfSum: 0,
|
|
47
|
+
selfBuckets: new Array(bounds.length + 1).fill(0),
|
|
48
|
+
statements: 0,
|
|
31
49
|
};
|
|
32
50
|
series.set(key, row);
|
|
33
51
|
}
|
|
@@ -44,13 +62,17 @@ export function metrics(app) {
|
|
|
44
62
|
const seconds = span.ms / 1000;
|
|
45
63
|
row.count += 1;
|
|
46
64
|
row.sum += seconds;
|
|
47
|
-
row.buckets[bucketOf(seconds)] += 1;
|
|
65
|
+
row.buckets[bucketOf(seconds, bounds)] += 1;
|
|
66
|
+
const self = span.selfMs / 1000;
|
|
67
|
+
row.selfSum += self;
|
|
68
|
+
row.selfBuckets[bucketOf(self, bounds)] += 1;
|
|
69
|
+
row.statements += span.statements;
|
|
48
70
|
},
|
|
49
71
|
snapshot: () => ({
|
|
50
72
|
since,
|
|
51
73
|
series: [...series.values()],
|
|
52
74
|
active: activeCalls(),
|
|
53
|
-
bounds:
|
|
75
|
+
bounds: [...bounds],
|
|
54
76
|
topology: topologyOf(app, seen),
|
|
55
77
|
edges: [...edges.values()],
|
|
56
78
|
}),
|
|
@@ -63,34 +85,45 @@ export function metrics(app) {
|
|
|
63
85
|
function topologyOf(app, seen) {
|
|
64
86
|
// What the app SERVES: a frond an extension brought is instrumentation, and reporting it
|
|
65
87
|
// would describe this package to itself.
|
|
88
|
+
//
|
|
89
|
+
// A frond named in `remotes:` is scanned when its code sits in the same project, and stays
|
|
90
|
+
// in `app.fronds` — the boot says `declared remote — not hosted locally` and keeps it. This
|
|
91
|
+
// half reports what RUNS here, so it is not one of them: it used to answer `local` for a
|
|
92
|
+
// frond every call reached over HTTP. Not a config-derived node either — it is named below
|
|
93
|
+
// only once it has answered, like any other remote.
|
|
66
94
|
const local = new Map((app?.fronds ?? [])
|
|
67
|
-
.filter((frond) => !frond.brought)
|
|
95
|
+
.filter((frond) => !frond.brought && !app?.remotes[frond.name])
|
|
68
96
|
.map((frond) => [frond.name, frond]));
|
|
69
97
|
const here = [...local.values()].map((frond) => ({
|
|
70
98
|
frond: frond.name,
|
|
71
99
|
placement: 'local',
|
|
72
100
|
entities: frond.entities.length,
|
|
73
|
-
|
|
101
|
+
facades: frond.handlers.length,
|
|
74
102
|
}));
|
|
75
103
|
// A frond we called but never scanned is hosted elsewhere. Its shape is not ours to
|
|
76
104
|
// report — it is published by the process that owns it, under its own service name.
|
|
77
105
|
const elsewhere = [...seen]
|
|
78
106
|
.filter((name) => !local.has(name))
|
|
79
|
-
.map((name) => ({ frond: name, placement: 'remote', entities: 0,
|
|
107
|
+
.map((name) => ({ frond: name, placement: 'remote', entities: 0, facades: 0 }));
|
|
80
108
|
return [...here, ...elsewhere];
|
|
81
109
|
}
|
|
82
110
|
/** The first bound this duration does not exceed, or the overflow bucket. */
|
|
83
|
-
function bucketOf(seconds) {
|
|
84
|
-
for (let i = 0; i <
|
|
85
|
-
if (seconds <=
|
|
111
|
+
function bucketOf(seconds, bounds) {
|
|
112
|
+
for (let i = 0; i < bounds.length; i++)
|
|
113
|
+
if (seconds <= bounds[i])
|
|
86
114
|
return i;
|
|
87
|
-
return
|
|
115
|
+
return bounds.length;
|
|
88
116
|
}
|
|
89
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* Serve the topology on `rpc.topology` — read from inside the process it describes.
|
|
119
|
+
*
|
|
120
|
+
* The declared half is read at every call rather than once here: `reloadFougere()` builds the app
|
|
121
|
+
* again, and a config read at boot would keep answering for the app that was released.
|
|
122
|
+
*/
|
|
90
123
|
export function serveTopology(app, measured) {
|
|
91
124
|
app.serveRpc('topology', () => {
|
|
92
125
|
const { since, active, topology, edges } = measured.snapshot();
|
|
93
|
-
return { since, active, fronds: topology, edges };
|
|
126
|
+
return { since, active, fronds: topology, edges, declared: declaredTopologyOf(app) };
|
|
94
127
|
});
|
|
95
128
|
}
|
|
96
129
|
/** One snapshot, as OTLP/JSON spells metrics. Cumulative, which is what Prometheus reads. */
|
|
@@ -98,6 +131,14 @@ export function metricsPayload(service, snapshot) {
|
|
|
98
131
|
const since = `${snapshot.since * 1e6}`;
|
|
99
132
|
const now = `${Date.now() * 1e6}`;
|
|
100
133
|
const attr = (key, value) => ({ key, value: { stringValue: value } });
|
|
134
|
+
/** What an operation's three series are all sliced by — said once, so they stay comparable. */
|
|
135
|
+
const dimensions = (row) => [
|
|
136
|
+
...(row.frond ? [attr('fougere.frond', row.frond)] : []),
|
|
137
|
+
attr('fougere.entity', row.entity),
|
|
138
|
+
attr('fougere.operation', row.operation),
|
|
139
|
+
attr('fougere.outcome', row.error ? 'error' : 'ok'),
|
|
140
|
+
...(row.error ? [attr('fougere.error.code', row.error)] : []),
|
|
141
|
+
];
|
|
101
142
|
return {
|
|
102
143
|
resourceMetrics: [
|
|
103
144
|
{
|
|
@@ -116,13 +157,7 @@ export function metricsPayload(service, snapshot) {
|
|
|
116
157
|
// only temporality Prometheus reads without a collector in between.
|
|
117
158
|
aggregationTemporality: 2,
|
|
118
159
|
dataPoints: snapshot.series.map((row) => ({
|
|
119
|
-
attributes:
|
|
120
|
-
...(row.frond ? [attr('fougere.frond', row.frond)] : []),
|
|
121
|
-
attr('fougere.entity', row.entity),
|
|
122
|
-
attr('fougere.operation', row.operation),
|
|
123
|
-
attr('fougere.outcome', row.error ? 'error' : 'ok'),
|
|
124
|
-
...(row.error ? [attr('fougere.error.code', row.error)] : []),
|
|
125
|
-
],
|
|
160
|
+
attributes: dimensions(row),
|
|
126
161
|
startTimeUnixNano: since,
|
|
127
162
|
timeUnixNano: now,
|
|
128
163
|
count: `${row.count}`,
|
|
@@ -132,6 +167,40 @@ export function metricsPayload(service, snapshot) {
|
|
|
132
167
|
})),
|
|
133
168
|
},
|
|
134
169
|
},
|
|
170
|
+
{
|
|
171
|
+
name: 'fougere.operation.self',
|
|
172
|
+
description: 'How long an operation took on its own, with what it waited for taken out.',
|
|
173
|
+
unit: 's',
|
|
174
|
+
histogram: {
|
|
175
|
+
aggregationTemporality: 2,
|
|
176
|
+
dataPoints: snapshot.series.map((row) => ({
|
|
177
|
+
attributes: dimensions(row),
|
|
178
|
+
startTimeUnixNano: since,
|
|
179
|
+
timeUnixNano: now,
|
|
180
|
+
count: `${row.count}`,
|
|
181
|
+
sum: row.selfSum,
|
|
182
|
+
bucketCounts: row.selfBuckets.map((n) => `${n}`),
|
|
183
|
+
explicitBounds: snapshot.bounds,
|
|
184
|
+
})),
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: 'fougere.operation.statements',
|
|
189
|
+
description: 'Statements run under an operation. Against its count, statements per call.',
|
|
190
|
+
unit: '{statement}',
|
|
191
|
+
sum: {
|
|
192
|
+
aggregationTemporality: 2,
|
|
193
|
+
isMonotonic: true,
|
|
194
|
+
dataPoints: snapshot.series
|
|
195
|
+
.filter((row) => row.statements > 0)
|
|
196
|
+
.map((row) => ({
|
|
197
|
+
attributes: dimensions(row),
|
|
198
|
+
startTimeUnixNano: since,
|
|
199
|
+
timeUnixNano: now,
|
|
200
|
+
asInt: `${row.statements}`,
|
|
201
|
+
})),
|
|
202
|
+
},
|
|
203
|
+
},
|
|
135
204
|
{
|
|
136
205
|
name: 'fougere.operations.active',
|
|
137
206
|
description: 'Operations running right now — the saturation signal.',
|
|
@@ -184,13 +253,13 @@ export function metricsPayload(service, snapshot) {
|
|
|
184
253
|
},
|
|
185
254
|
},
|
|
186
255
|
{
|
|
187
|
-
name: 'fougere.frond.
|
|
188
|
-
description: 'How many
|
|
189
|
-
unit: '{
|
|
256
|
+
name: 'fougere.frond.facades',
|
|
257
|
+
description: 'How many facades a frond serves — its surface, as scanned.',
|
|
258
|
+
unit: '{facade}',
|
|
190
259
|
gauge: {
|
|
191
260
|
dataPoints: snapshot.topology.map((f) => ({
|
|
192
261
|
attributes: [attr('fougere.frond', f.frond)],
|
|
193
|
-
asInt: `${f.
|
|
262
|
+
asInt: `${f.facades}`,
|
|
194
263
|
timeUnixNano: now,
|
|
195
264
|
})),
|
|
196
265
|
},
|
|
@@ -202,4 +271,4 @@ export function metricsPayload(service, snapshot) {
|
|
|
202
271
|
],
|
|
203
272
|
};
|
|
204
273
|
}
|
|
205
|
-
//# sourceMappingURL=
|
|
274
|
+
//# sourceMappingURL=Metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Metrics.js","sourceRoot":"","sources":["../../src/metrics/Metrics.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAiE,MAAM,eAAe,CAAC;AAClH,OAAO,EAAE,WAAW,EAAoC,MAAM,aAAa,CAAC;AAG5E;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;AAU3F;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,GAAS,EAAE,MAAM,GAAsB,MAAM;IACnE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC3C,qFAAqF;IACrF,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,oDAAoD;IACpD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAgB,CAAC;IAEtC,OAAO;QACL,IAAI,EAAE,CAAC,IAAkB,EAAE,EAAE;YAC3B,oFAAoF;YACpF,gFAAgF;YAChF,uEAAuE;YACvE,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW;gBAAE,OAAO;YAEtC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;YACrE,IAAI,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC1B,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,GAAG,GAAG;oBACJ,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,SAAS,EAAE,IAAI,CAAC,SAAS;oBACzB,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,KAAK,EAAE,CAAC;oBACR,GAAG,EAAE,CAAC;oBACN,OAAO,EAAE,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;oBAC7C,OAAO,EAAE,CAAC;oBACV,WAAW,EAAE,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;oBACjD,UAAU,EAAE,CAAC;iBACd,CAAC;gBACF,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YACvB,CAAC;YACD,IAAI,IAAI,CAAC,KAAK;gBAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrC,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACnC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,WAAW,SAAS,IAAI,CAAC,KAAK,EAAE,CAAC;gBACrD,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;gBAC/F,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;gBAChB,IAAI,IAAI,CAAC,KAAK;oBAAE,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;gBACjC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACvB,CAAC;YACD,MAAM,OAAO,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;YAC/B,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC;YACf,GAAG,CAAC,GAAG,IAAI,OAAO,CAAC;YACnB,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;YAE5C,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YAChC,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC;YACpB,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;YAC7C,GAAG,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,CAAC;QACpC,CAAC;QACD,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;YACf,KAAK;YACL,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;YAC5B,MAAM,EAAE,WAAW,EAAE;YACrB,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC;YACnB,QAAQ,EAAE,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC;YAC/B,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;SAC3B,CAAC;KACH,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,UAAU,CAAC,GAAoB,EAAE,IAAiB;IACzD,yFAAyF;IACzF,yCAAyC;IACzC,EAAE;IACF,2FAA2F;IAC3F,4FAA4F;IAC5F,yFAAyF;IACzF,2FAA2F;IAC3F,oDAAoD;IACpD,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,IAAI,EAAE,CAAC;SACtC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;SAC9D,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAU,CAAC,CAAC,CAAC;IAEjD,MAAM,IAAI,GAAqB,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACjE,KAAK,EAAE,KAAK,CAAC,IAAI;QACjB,SAAS,EAAE,OAAgB;QAC3B,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM;QAC/B,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM;KAC/B,CAAC,CAAC,CAAC;IAEJ,oFAAoF;IACpF,oFAAoF;IACpF,MAAM,SAAS,GAAqB,CAAC,GAAG,IAAI,CAAC;SAC1C,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;SAClC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,QAAiB,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAE3F,OAAO,CAAC,GAAG,IAAI,EAAE,GAAG,SAAS,CAAC,CAAC;AACjC,CAAC;AAED,6EAA6E;AAC7E,SAAS,QAAQ,CAAC,OAAe,EAAE,MAAyB;IAC1D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,IAAI,OAAO,IAAI,MAAM,CAAC,CAAC,CAAE;YAAE,OAAO,CAAC,CAAC;IAE5E,OAAO,MAAM,CAAC,MAAM,CAAC;AACvB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,GAAQ,EAAE,QAAiB;IACvD,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,GAAmB,EAAE;QAC5C,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC,QAAQ,EAAE,CAAC;QAE/D,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;IACvF,CAAC,CAAC,CAAC;AACL,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,QAAyB;IACvE,MAAM,KAAK,GAAG,GAAG,QAAQ,CAAC,KAAK,GAAG,GAAG,EAAE,CAAC;IACxC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,CAAC,GAAW,EAAE,KAAa,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;IACtF,+FAA+F;IAC/F,MAAM,UAAU,GAAG,CAAC,GAAa,EAAE,EAAE,CAAC;QACpC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACxD,IAAI,CAAC,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC;QAClC,IAAI,CAAC,mBAAmB,EAAE,GAAG,CAAC,SAAS,CAAC;QACxC,IAAI,CAAC,iBAAiB,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QACnD,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,oBAAoB,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9D,CAAC;IAEF,OAAO;QACL,eAAe,EAAE;YACf;gBACE,QAAQ,EAAE,EAAE,UAAU,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,EAAE;gBACzD,YAAY,EAAE;oBACZ;wBACE,KAAK,EAAE,EAAE,IAAI,EAAE,wBAAwB,EAAE;wBACzC,4BAA4B;wBAC5B,OAAO,EAAG;4BACR;gCACE,IAAI,EAAE,4BAA4B;gCAClC,WAAW,EAAE,mDAAmD;gCAChE,IAAI,EAAE,GAAG;gCACT,SAAS,EAAE;oCACT,qEAAqE;oCACrE,oEAAoE;oCACpE,sBAAsB,EAAE,CAAC;oCACzB,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;wCACxC,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC;wCAC3B,iBAAiB,EAAE,KAAK;wCACxB,YAAY,EAAE,GAAG;wCACjB,KAAK,EAAE,GAAG,GAAG,CAAC,KAAK,EAAE;wCACrB,GAAG,EAAE,GAAG,CAAC,GAAG;wCACZ,YAAY,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;wCAC5C,cAAc,EAAE,QAAQ,CAAC,MAAM;qCAChC,CAAC,CAAC;iCACJ;6BACF;4BACD;gCACE,IAAI,EAAE,wBAAwB;gCAC9B,WAAW,EAAE,2EAA2E;gCACxF,IAAI,EAAE,GAAG;gCACT,SAAS,EAAE;oCACT,sBAAsB,EAAE,CAAC;oCACzB,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;wCACxC,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC;wCAC3B,iBAAiB,EAAE,KAAK;wCACxB,YAAY,EAAE,GAAG;wCACjB,KAAK,EAAE,GAAG,GAAG,CAAC,KAAK,EAAE;wCACrB,GAAG,EAAE,GAAG,CAAC,OAAO;wCAChB,YAAY,EAAE,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;wCAChD,cAAc,EAAE,QAAQ,CAAC,MAAM;qCAChC,CAAC,CAAC;iCACJ;6BACF;4BACD;gCACE,IAAI,EAAE,8BAA8B;gCACpC,WAAW,EAAE,4EAA4E;gCACzF,IAAI,EAAE,aAAa;gCACnB,GAAG,EAAE;oCACH,sBAAsB,EAAE,CAAC;oCACzB,WAAW,EAAE,IAAI;oCACjB,UAAU,EAAE,QAAQ,CAAC,MAAM;yCACxB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,GAAG,CAAC,CAAC;yCACnC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;wCACb,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC;wCAC3B,iBAAiB,EAAE,KAAK;wCACxB,YAAY,EAAE,GAAG;wCACjB,KAAK,EAAE,GAAG,GAAG,CAAC,UAAU,EAAE;qCAC3B,CAAC,CAAC;iCACN;6BACF;4BACD;gCACE,IAAI,EAAE,2BAA2B;gCACjC,WAAW,EAAE,uDAAuD;gCACpE,IAAI,EAAE,QAAQ;gCACd,KAAK,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC,MAAM,EAAE,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,EAAE;6BAC5E;4BACD;gCACE,IAAI,EAAE,gBAAgB;gCACtB,WAAW,EAAE,4DAA4D;gCACzE,IAAI,EAAE,SAAS;gCACf,KAAK,EAAE;oCACL,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;wCACxC,UAAU,EAAE;4CACV,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC;4CAC9B,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC,SAAS,CAAC;yCACvC;wCACD,KAAK,EAAE,GAAG;wCACV,YAAY,EAAE,GAAG;qCAClB,CAAC,CAAC;iCACJ;6BACF;4BACD;gCACE,IAAI,EAAE,eAAe;gCACrB,WAAW,EAAE,6DAA6D;gCAC1E,IAAI,EAAE,QAAQ;gCACd,GAAG,EAAE;oCACH,sBAAsB,EAAE,CAAC;oCACzB,WAAW,EAAE,IAAI;oCACjB,UAAU,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;wCACrC,UAAU,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;wCACpE,iBAAiB,EAAE,KAAK;wCACxB,YAAY,EAAE,GAAG;wCACjB,KAAK,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE;qCACpB,CAAC,CAAC;iCACJ;6BACF;4BACD;gCACE,IAAI,EAAE,sBAAsB;gCAC5B,WAAW,EAAE,+BAA+B;gCAC5C,IAAI,EAAE,QAAQ;gCACd,GAAG,EAAE;oCACH,sBAAsB,EAAE,CAAC;oCACzB,WAAW,EAAE,IAAI;oCACjB,UAAU,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;wCACrC,UAAU,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;wCACpE,iBAAiB,EAAE,KAAK;wCACxB,YAAY,EAAE,GAAG;wCACjB,KAAK,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE;qCACrB,CAAC,CAAC;iCACJ;6BACF;4BACD;gCACE,IAAI,EAAE,uBAAuB;gCAC7B,WAAW,EAAE,4DAA4D;gCACzE,IAAI,EAAE,UAAU;gCAChB,KAAK,EAAE;oCACL,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;wCACxC,UAAU,EAAE,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;wCAC5C,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE;wCACrB,YAAY,EAAE,GAAG;qCAClB,CAAC,CAAC;iCACJ;6BACF;yBACwB,CAAC,MAAM,CAChC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAC/D;qBACF;iBACF;aACF;SACF;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Bucketed } from './Bucketed.js';
|
|
2
|
+
import { type Edge, type FrondPlacement } from '@fougere/core';
|
|
3
|
+
export interface MetricsSnapshot {
|
|
4
|
+
/** When this process started counting — cumulative metrics are read against it. */
|
|
5
|
+
since: number;
|
|
6
|
+
series: Bucketed[];
|
|
7
|
+
active: number;
|
|
8
|
+
bounds: number[];
|
|
9
|
+
/** The shape of the system as this process discovered it — declared nowhere. */
|
|
10
|
+
topology: FrondPlacement[];
|
|
11
|
+
/** Who calls whom, as observed here. Bounded by fronds², so it is a safe dimension. */
|
|
12
|
+
edges: Edge[];
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=MetricsSnapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MetricsSnapshot.d.ts","sourceRoot":"","sources":["../../src/metrics/MetricsSnapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/D,MAAM,WAAW,eAAe;IAC9B,mFAAmF;IACnF,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,gFAAgF;IAChF,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,uFAAuF;IACvF,KAAK,EAAE,IAAI,EAAE,CAAC;CACf"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MetricsSnapshot.js","sourceRoot":"","sources":["../../src/metrics/MetricsSnapshot.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SpanSink } from '../index.js';
|
|
2
|
+
import type { OtlpOptions } from './OtlpOptions.js';
|
|
3
|
+
export interface OtlpExporter {
|
|
4
|
+
/** Hand to `onSpan`. */
|
|
5
|
+
sink: SpanSink;
|
|
6
|
+
/** Send what is buffered now — a process about to exit has to call this. */
|
|
7
|
+
flush(): Promise<void>;
|
|
8
|
+
/** Stop the timer and send what is left. */
|
|
9
|
+
stop(): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export declare function otlp(options: OtlpOptions): OtlpExporter;
|
|
12
|
+
//# sourceMappingURL=OtlpExporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OtlpExporter.d.ts","sourceRoot":"","sources":["../../src/otlp/OtlpExporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAgB,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,YAAY;IAC3B,wBAAwB;IACxB,IAAI,EAAE,QAAQ,CAAC;IACf,4EAA4E;IAC5E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,4CAA4C;IAC5C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAYD,wBAAgB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,YAAY,CA2BvD"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Beat } from '../Beat.js';
|
|
2
|
+
import { Endpoint } from '../Endpoint.js';
|
|
3
|
+
import { metricsPayload } from '../metrics/Metrics.js';
|
|
4
|
+
/** OTLP status codes: 0 unset, 1 ok, 2 error. */
|
|
5
|
+
const OK = 1;
|
|
6
|
+
const ERROR = 2;
|
|
7
|
+
/** OTLP span kinds, of the six only these two are ours. */
|
|
8
|
+
const INTERNAL = 1;
|
|
9
|
+
const CLIENT = 3;
|
|
10
|
+
export function otlp(options) {
|
|
11
|
+
const url = options.url ?? 'http://localhost:4318/v1/traces';
|
|
12
|
+
const traces = Endpoint.at(url, options.onError);
|
|
13
|
+
const metrics = Endpoint.at(options.metricsUrl ?? url.replace(/\/v1\/traces$/, '/v1/metrics'), options.onError);
|
|
14
|
+
let buffer = [];
|
|
15
|
+
async function flush() {
|
|
16
|
+
const batch = buffer;
|
|
17
|
+
buffer = [];
|
|
18
|
+
// Metrics go on every beat even when no span finished: a gauge that stops being
|
|
19
|
+
// published reads as "gone", not as "idle".
|
|
20
|
+
await Promise.all([
|
|
21
|
+
batch.length > 0 ? traces.post(payload(options.service, batch)) : Promise.resolve(),
|
|
22
|
+
options.metrics ? metrics.post(metricsPayload(options.service, options.metrics.snapshot())) : Promise.resolve(),
|
|
23
|
+
]);
|
|
24
|
+
}
|
|
25
|
+
const beat = Beat.every(options.flushMs, flush);
|
|
26
|
+
return {
|
|
27
|
+
sink: (span) => { buffer.push(span); },
|
|
28
|
+
flush,
|
|
29
|
+
stop: () => beat.stop(),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** One batch, as OTLP/JSON spells it — trace and span ids stay hex, times are nanos. */
|
|
33
|
+
function payload(service, spans) {
|
|
34
|
+
return {
|
|
35
|
+
resourceSpans: [
|
|
36
|
+
{
|
|
37
|
+
resource: { attributes: [{ key: 'service.name', value: { stringValue: service } }] },
|
|
38
|
+
scopeSpans: [
|
|
39
|
+
{
|
|
40
|
+
scope: { name: '@fougere/observability' },
|
|
41
|
+
spans: spans.map((span) => ({
|
|
42
|
+
traceId: span.traceId,
|
|
43
|
+
spanId: span.spanId,
|
|
44
|
+
...(span.parentId ? { parentSpanId: span.parentId } : {}),
|
|
45
|
+
name: `${span.entity}.${span.operation}`,
|
|
46
|
+
// 1 INTERNAL, 3 CLIENT: a statement left this process for an engine, and a
|
|
47
|
+
// viewer draws the two differently. `selfMs` is NOT sent — a collector
|
|
48
|
+
// derives it from the tree it already holds.
|
|
49
|
+
kind: span.kind === 'statement' ? CLIENT : INTERNAL,
|
|
50
|
+
startTimeUnixNano: nanos(span.startedAt),
|
|
51
|
+
endTimeUnixNano: nanos(span.startedAt + span.ms),
|
|
52
|
+
status: span.error ? { code: ERROR, message: span.error } : { code: OK },
|
|
53
|
+
})),
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** Epoch milliseconds → the int64 nanoseconds OTLP wants, as a string. */
|
|
61
|
+
function nanos(ms) {
|
|
62
|
+
return `${Math.round(ms * 1e6)}`;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=OtlpExporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OtlpExporter.js","sourceRoot":"","sources":["../../src/otlp/OtlpExporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Metrics } from '../metrics/Metrics.js';
|
|
2
|
+
export interface OtlpOptions {
|
|
3
|
+
/** Which service these spans belong to — what a dashboard groups by. */
|
|
4
|
+
service: string;
|
|
5
|
+
/** Collector endpoint. Default: the OTLP/HTTP convention on localhost. */
|
|
6
|
+
url?: string;
|
|
7
|
+
/** How often a full batch leaves. Default: every second. */
|
|
8
|
+
flushMs?: number;
|
|
9
|
+
/** Told when a batch could not be sent. Default: silence — a trace must never break a call. */
|
|
10
|
+
onError?: (err: unknown) => void;
|
|
11
|
+
/** Publish these metrics on the same beat. */
|
|
12
|
+
metrics?: Metrics;
|
|
13
|
+
/** Where metrics go when it is not the same collector as traces. */
|
|
14
|
+
metricsUrl?: string;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=OtlpOptions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OtlpOptions.d.ts","sourceRoot":"","sources":["../../src/otlp/OtlpOptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+FAA+F;IAC/F,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IACjC,8CAA8C;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OtlpOptions.js","sourceRoot":"","sources":["../../src/otlp/OtlpOptions.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fougere/observability",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0-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,17 @@
|
|
|
31
31
|
"src"
|
|
32
32
|
],
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@fougere/
|
|
34
|
+
"@fougere/adapter-sql": "^0.10.0-alpha.0",
|
|
35
|
+
"@fougere/core": "^0.10.0-alpha.0"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
38
|
"vitest": "^4.1.0",
|
|
38
|
-
"@fougere/
|
|
39
|
-
"@fougere/
|
|
40
|
-
"@fougere/
|
|
41
|
-
"@fougere/
|
|
42
|
-
"@fougere/
|
|
39
|
+
"@fougere/adapter-sql": "0.10.0-alpha.0",
|
|
40
|
+
"@fougere/schema": "0.10.0-alpha.0",
|
|
41
|
+
"@fougere/compiler": "0.10.0-alpha.0",
|
|
42
|
+
"@fougere/core": "0.10.0-alpha.0",
|
|
43
|
+
"@fougere/container": "0.10.0-alpha.0",
|
|
44
|
+
"@fougere/transport-http": "0.10.0-alpha.0"
|
|
43
45
|
},
|
|
44
46
|
"publishConfig": {
|
|
45
47
|
"access": "public"
|
|
@@ -51,6 +53,11 @@
|
|
|
51
53
|
"default": "./dist/context/als.js"
|
|
52
54
|
}
|
|
53
55
|
},
|
|
56
|
+
"peerDependenciesMeta": {
|
|
57
|
+
"@fougere/adapter-sql": {
|
|
58
|
+
"optional": true
|
|
59
|
+
}
|
|
60
|
+
},
|
|
54
61
|
"scripts": {
|
|
55
62
|
"build": "rm -rf dist && tsc",
|
|
56
63
|
"test": "vitest run",
|
|
@@ -1,23 +1,8 @@
|
|
|
1
|
-
/** OTLP export — the spans this process finished, in the one shape every collector reads. */
|
|
2
1
|
import { Beat } from './Beat.js';
|
|
3
2
|
import { Endpoint } from './Endpoint.js';
|
|
4
3
|
import type { FinishedSpan, SpanSink } from './index.js';
|
|
5
|
-
import { metricsPayload
|
|
6
|
-
|
|
7
|
-
export interface OtlpOptions {
|
|
8
|
-
/** Which service these spans belong to — what a dashboard groups by. */
|
|
9
|
-
service: string;
|
|
10
|
-
/** Collector endpoint. Default: the OTLP/HTTP convention on localhost. */
|
|
11
|
-
url?: string;
|
|
12
|
-
/** How often a full batch leaves. Default: every second. */
|
|
13
|
-
flushMs?: number;
|
|
14
|
-
/** Told when a batch could not be sent. Default: silence — a trace must never break a call. */
|
|
15
|
-
onError?: (err: unknown) => void;
|
|
16
|
-
/** Publish these metrics on the same beat. */
|
|
17
|
-
metrics?: Metrics;
|
|
18
|
-
/** Where metrics go when it is not the same collector as traces. */
|
|
19
|
-
metricsUrl?: string;
|
|
20
|
-
}
|
|
4
|
+
import { metricsPayload } from './metrics/Metrics.js';
|
|
5
|
+
import type { OtlpOptions } from './OtlpOptions.js';
|
|
21
6
|
|
|
22
7
|
export interface OtlpExporter {
|
|
23
8
|
/** Hand to `onSpan`. */
|
|
@@ -30,8 +15,14 @@ export interface OtlpExporter {
|
|
|
30
15
|
|
|
31
16
|
/** OTLP status codes: 0 unset, 1 ok, 2 error. */
|
|
32
17
|
const OK = 1;
|
|
18
|
+
|
|
33
19
|
const ERROR = 2;
|
|
34
20
|
|
|
21
|
+
/** OTLP span kinds, of the six only these two are ours. */
|
|
22
|
+
const INTERNAL = 1;
|
|
23
|
+
|
|
24
|
+
const CLIENT = 3;
|
|
25
|
+
|
|
35
26
|
export function otlp(options: OtlpOptions): OtlpExporter {
|
|
36
27
|
const url = options.url ?? 'http://localhost:4318/v1/traces';
|
|
37
28
|
const traces = Endpoint.at(url, options.onError);
|
|
@@ -75,7 +66,10 @@ function payload(service: string, spans: FinishedSpan[]) {
|
|
|
75
66
|
spanId: span.spanId,
|
|
76
67
|
...(span.parentId ? { parentSpanId: span.parentId } : {}),
|
|
77
68
|
name: `${span.entity}.${span.operation}`,
|
|
78
|
-
|
|
69
|
+
// 1 INTERNAL, 3 CLIENT: a statement left this process for an engine, and a
|
|
70
|
+
// viewer draws the two differently. `selfMs` is NOT sent — a collector
|
|
71
|
+
// derives it from the tree it already holds.
|
|
72
|
+
kind: span.kind === 'statement' ? CLIENT : INTERNAL,
|
|
79
73
|
startTimeUnixNano: nanos(span.startedAt),
|
|
80
74
|
endTimeUnixNano: nanos(span.startedAt + span.ms),
|
|
81
75
|
status: span.error ? { code: ERROR, message: span.error } : { code: OK },
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Metrics } from './metrics/Metrics.js';
|
|
2
|
+
|
|
3
|
+
export interface OtlpOptions {
|
|
4
|
+
/** Which service these spans belong to — what a dashboard groups by. */
|
|
5
|
+
service: string;
|
|
6
|
+
/** Collector endpoint. Default: the OTLP/HTTP convention on localhost. */
|
|
7
|
+
url?: string;
|
|
8
|
+
/** How often a full batch leaves. Default: every second. */
|
|
9
|
+
flushMs?: number;
|
|
10
|
+
/** Told when a batch could not be sent. Default: silence — a trace must never break a call. */
|
|
11
|
+
onError?: (err: unknown) => void;
|
|
12
|
+
/** Publish these metrics on the same beat. */
|
|
13
|
+
metrics?: Metrics;
|
|
14
|
+
/** Where metrics go when it is not the same collector as traces. */
|
|
15
|
+
metricsUrl?: string;
|
|
16
|
+
}
|