@nexusts/metrics 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -0
- package/dist/index.js +946 -0
- package/dist/index.js.map +19 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# @nexusts/metrics
|
|
2
|
+
|
|
3
|
+
> **NexusTS** — Bun-native fullstack framework
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
Prometheus / OpenMetrics counters and gauges.
|
|
8
|
+
|
|
9
|
+
Counter / Gauge / Histogram / Summary primitives. @Counted / @Timed method decorators. /metrics endpoint with content negotiation. Default Node.js process metrics.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
This module is part of the NexusTS monorepo. Each module is published as its own npm package under the `@nexusts/` scope.
|
|
14
|
+
|
|
15
|
+
Most apps start with just the core:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bun add @nexusts/core reflect-metadata zod hono
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then add this module only if you need it:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
bun add @nexusts/metrics
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Peer dependencies
|
|
28
|
+
|
|
29
|
+
None. This module is fully self-contained.
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { /* public API */ } from "@nexusts/metrics";
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
See the [user guide](../../docs/user-guide/metrics.md) and the [example app](../../examples/) for a working demo.
|
|
38
|
+
|
|
39
|
+
## License
|
|
40
|
+
|
|
41
|
+
MIT — see the root [LICENSE](../../LICENSE).
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,946 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __legacyDecorateClassTS = function(decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
5
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
6
|
+
else
|
|
7
|
+
for (var i = decorators.length - 1;i >= 0; i--)
|
|
8
|
+
if (d = decorators[i])
|
|
9
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
10
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
11
|
+
};
|
|
12
|
+
var __legacyMetadataTS = (k, v) => {
|
|
13
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
|
|
14
|
+
return Reflect.metadata(k, v);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// packages/metrics/src/counter.ts
|
|
18
|
+
function renderLabels(declared, values) {
|
|
19
|
+
if (declared.length === 0)
|
|
20
|
+
return "";
|
|
21
|
+
return declared.map((n) => `${n}="${escapeLabelValue(values[n] ?? "")}"`).join(",");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class CounterImpl {
|
|
25
|
+
name;
|
|
26
|
+
help;
|
|
27
|
+
labelNames;
|
|
28
|
+
values = new Map;
|
|
29
|
+
constructor(opts) {
|
|
30
|
+
this.name = opts.name;
|
|
31
|
+
this.help = opts.help;
|
|
32
|
+
this.labelNames = opts.labelNames ?? [];
|
|
33
|
+
}
|
|
34
|
+
inc(labels) {
|
|
35
|
+
this.incBy(1, labels);
|
|
36
|
+
}
|
|
37
|
+
incBy(n, labels) {
|
|
38
|
+
if (n < 0) {
|
|
39
|
+
throw new Error(`Counter ${this.name} can only increase (got ${n})`);
|
|
40
|
+
}
|
|
41
|
+
this.assertLabels(labels);
|
|
42
|
+
const key = this.key(labels);
|
|
43
|
+
this.values.set(key, (this.values.get(key) ?? 0) + n);
|
|
44
|
+
}
|
|
45
|
+
reset() {
|
|
46
|
+
this.values.clear();
|
|
47
|
+
}
|
|
48
|
+
getSamples() {
|
|
49
|
+
const out = [];
|
|
50
|
+
for (const [k, v] of this.values) {
|
|
51
|
+
out.push({ labels: this.parseKey(k), value: v });
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
renderPrometheus() {
|
|
56
|
+
const lines = [];
|
|
57
|
+
if (this.help)
|
|
58
|
+
lines.push(`# HELP ${this.name} ${this.help}`);
|
|
59
|
+
lines.push(`# TYPE ${this.name} counter`);
|
|
60
|
+
for (const [k, v] of this.values) {
|
|
61
|
+
const labels = this.parseKey(k);
|
|
62
|
+
const lbl = renderLabels(this.labelNames, labels);
|
|
63
|
+
lines.push(lbl ? `${this.name}{${lbl}} ${v}` : `${this.name} ${v}`);
|
|
64
|
+
}
|
|
65
|
+
return lines.join(`
|
|
66
|
+
`);
|
|
67
|
+
}
|
|
68
|
+
assertLabels(labels) {
|
|
69
|
+
if (!labels && this.labelNames.length === 0)
|
|
70
|
+
return;
|
|
71
|
+
if (this.labelNames.length === 0) {
|
|
72
|
+
throw new Error(`Counter ${this.name} has no labels declared`);
|
|
73
|
+
}
|
|
74
|
+
if (!labels) {
|
|
75
|
+
throw new Error(`Counter ${this.name} requires labels: ${this.labelNames.join(", ")}`);
|
|
76
|
+
}
|
|
77
|
+
for (const required of this.labelNames) {
|
|
78
|
+
if (!(required in labels)) {
|
|
79
|
+
throw new Error(`Counter ${this.name} missing label "${required}"`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
key(labels) {
|
|
84
|
+
if (!labels)
|
|
85
|
+
return "";
|
|
86
|
+
return this.labelNames.map((n) => `${n}=${escapeLabelValue(labels[n] ?? "")}`).join(",");
|
|
87
|
+
}
|
|
88
|
+
parseKey(key) {
|
|
89
|
+
if (!key)
|
|
90
|
+
return {};
|
|
91
|
+
const out = {};
|
|
92
|
+
for (const part of key.split(",")) {
|
|
93
|
+
const [k, ...rest] = part.split("=");
|
|
94
|
+
out[k] = rest.join("=");
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function escapeLabelValue(v) {
|
|
100
|
+
return v.replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/"/g, "\\\"");
|
|
101
|
+
}
|
|
102
|
+
// packages/metrics/src/gauge.ts
|
|
103
|
+
class GaugeImpl {
|
|
104
|
+
name;
|
|
105
|
+
help;
|
|
106
|
+
labelNames;
|
|
107
|
+
values = new Map;
|
|
108
|
+
constructor(opts) {
|
|
109
|
+
this.name = opts.name;
|
|
110
|
+
this.help = opts.help;
|
|
111
|
+
this.labelNames = opts.labelNames ?? [];
|
|
112
|
+
}
|
|
113
|
+
set(value, labels) {
|
|
114
|
+
this.assertLabels(labels);
|
|
115
|
+
this.values.set(this.key(labels), value);
|
|
116
|
+
}
|
|
117
|
+
inc(n = 1, labels) {
|
|
118
|
+
this.assertLabels(labels);
|
|
119
|
+
const k = this.key(labels);
|
|
120
|
+
this.values.set(k, (this.values.get(k) ?? 0) + n);
|
|
121
|
+
}
|
|
122
|
+
dec(n = 1, labels) {
|
|
123
|
+
this.assertLabels(labels);
|
|
124
|
+
const k = this.key(labels);
|
|
125
|
+
this.values.set(k, (this.values.get(k) ?? 0) - n);
|
|
126
|
+
}
|
|
127
|
+
setToCurrentTime(labels) {
|
|
128
|
+
this.set(Date.now() / 1000, labels);
|
|
129
|
+
}
|
|
130
|
+
reset() {
|
|
131
|
+
this.values.clear();
|
|
132
|
+
}
|
|
133
|
+
getSamples() {
|
|
134
|
+
const out = [];
|
|
135
|
+
for (const [k, v] of this.values) {
|
|
136
|
+
out.push({ labels: this.parseKey(k), value: v });
|
|
137
|
+
}
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
renderPrometheus() {
|
|
141
|
+
const lines = [];
|
|
142
|
+
if (this.help)
|
|
143
|
+
lines.push(`# HELP ${this.name} ${this.help}`);
|
|
144
|
+
lines.push(`# TYPE ${this.name} gauge`);
|
|
145
|
+
for (const [k, v] of this.values) {
|
|
146
|
+
const labels = this.parseKey(k);
|
|
147
|
+
const lbl = renderLabels(this.labelNames, labels);
|
|
148
|
+
lines.push(lbl ? `${this.name}{${lbl}} ${v}` : `${this.name} ${v}`);
|
|
149
|
+
}
|
|
150
|
+
return lines.join(`
|
|
151
|
+
`);
|
|
152
|
+
}
|
|
153
|
+
assertLabels(labels) {
|
|
154
|
+
if (!labels && this.labelNames.length === 0)
|
|
155
|
+
return;
|
|
156
|
+
if (this.labelNames.length === 0) {
|
|
157
|
+
throw new Error(`Gauge ${this.name} has no labels declared`);
|
|
158
|
+
}
|
|
159
|
+
if (!labels) {
|
|
160
|
+
throw new Error(`Gauge ${this.name} requires labels: ${this.labelNames.join(", ")}`);
|
|
161
|
+
}
|
|
162
|
+
for (const required of this.labelNames) {
|
|
163
|
+
if (!(required in labels)) {
|
|
164
|
+
throw new Error(`Gauge ${this.name} missing label "${required}"`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
key(labels) {
|
|
169
|
+
if (!labels)
|
|
170
|
+
return "";
|
|
171
|
+
return this.labelNames.map((n) => `${n}=${escapeLabelValue(labels[n] ?? "")}`).join(",");
|
|
172
|
+
}
|
|
173
|
+
parseKey(key) {
|
|
174
|
+
if (!key)
|
|
175
|
+
return {};
|
|
176
|
+
const out = {};
|
|
177
|
+
for (const part of key.split(",")) {
|
|
178
|
+
const [k, ...rest] = part.split("=");
|
|
179
|
+
out[k] = rest.join("=");
|
|
180
|
+
}
|
|
181
|
+
return out;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// packages/metrics/src/histogram.ts
|
|
185
|
+
var DEFAULT_BUCKETS = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];
|
|
186
|
+
|
|
187
|
+
class HistogramImpl {
|
|
188
|
+
name;
|
|
189
|
+
help;
|
|
190
|
+
labelNames;
|
|
191
|
+
upperBounds;
|
|
192
|
+
states = new Map;
|
|
193
|
+
constructor(opts) {
|
|
194
|
+
this.name = opts.name;
|
|
195
|
+
this.help = opts.help;
|
|
196
|
+
this.labelNames = opts.labelNames ?? [];
|
|
197
|
+
const buckets = opts.buckets ?? DEFAULT_BUCKETS;
|
|
198
|
+
this.upperBounds = [...buckets].sort((a, b) => a - b);
|
|
199
|
+
if (this.upperBounds.length === 0) {
|
|
200
|
+
throw new Error(`Histogram ${this.name} requires at least one bucket`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
observe(value, labels) {
|
|
204
|
+
this.assertLabels(labels);
|
|
205
|
+
const k = this.key(labels);
|
|
206
|
+
let s = this.states.get(k);
|
|
207
|
+
if (!s) {
|
|
208
|
+
s = { buckets: new Array(this.upperBounds.length).fill(0), sum: 0, count: 0 };
|
|
209
|
+
this.states.set(k, s);
|
|
210
|
+
}
|
|
211
|
+
s.sum += value;
|
|
212
|
+
s.count++;
|
|
213
|
+
for (let i = 0;i < this.upperBounds.length; i++) {
|
|
214
|
+
if (value <= this.upperBounds[i])
|
|
215
|
+
s.buckets[i]++;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async time(fn, labels) {
|
|
219
|
+
const start = performance.now();
|
|
220
|
+
try {
|
|
221
|
+
return await fn(start);
|
|
222
|
+
} finally {
|
|
223
|
+
const elapsedMs = performance.now() - start;
|
|
224
|
+
this.observe(elapsedMs / 1000, labels);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
reset() {
|
|
228
|
+
this.states.clear();
|
|
229
|
+
}
|
|
230
|
+
getSamples() {
|
|
231
|
+
const out = [];
|
|
232
|
+
for (const [k, s] of this.states) {
|
|
233
|
+
const labels = this.parseKey(k);
|
|
234
|
+
for (let i = 0;i < this.upperBounds.length; i++) {
|
|
235
|
+
out.push({
|
|
236
|
+
labels: { ...labels, le: String(this.upperBounds[i]) },
|
|
237
|
+
value: s.buckets[i]
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
out.push({ labels: { ...labels, le: "+Inf" }, value: s.count });
|
|
241
|
+
out.push({ labels: { ...labels }, value: s.sum });
|
|
242
|
+
}
|
|
243
|
+
return out;
|
|
244
|
+
}
|
|
245
|
+
renderPrometheus() {
|
|
246
|
+
const lines = [];
|
|
247
|
+
if (this.help)
|
|
248
|
+
lines.push(`# HELP ${this.name} ${this.help}`);
|
|
249
|
+
lines.push(`# TYPE ${this.name} histogram`);
|
|
250
|
+
for (const { labels, state } of this.enumerateStates()) {
|
|
251
|
+
lines.push(this.renderState(labels, state));
|
|
252
|
+
}
|
|
253
|
+
return lines.join(`
|
|
254
|
+
`);
|
|
255
|
+
}
|
|
256
|
+
renderState(labels, state) {
|
|
257
|
+
const baseLabels = renderLabels(this.labelNames, labels);
|
|
258
|
+
const lines = [];
|
|
259
|
+
let cumulative = 0;
|
|
260
|
+
for (let i = 0;i < this.upperBounds.length; i++) {
|
|
261
|
+
cumulative = state.buckets[i];
|
|
262
|
+
const le = this.upperBounds[i];
|
|
263
|
+
const leStr = String(le);
|
|
264
|
+
const lbl = baseLabels ? `${baseLabels},le="${leStr}"` : `le="${leStr}"`;
|
|
265
|
+
lines.push(`${this.name}_bucket{${lbl}} ${cumulative}`);
|
|
266
|
+
}
|
|
267
|
+
{
|
|
268
|
+
const lbl = baseLabels ? `${baseLabels},le="+Inf"` : `le="+Inf"`;
|
|
269
|
+
lines.push(`${this.name}_bucket{${lbl}} ${state.count}`);
|
|
270
|
+
}
|
|
271
|
+
{
|
|
272
|
+
const lbl = baseLabels ? `{${baseLabels}}` : "";
|
|
273
|
+
lines.push(`${this.name}_sum${lbl} ${state.sum}`);
|
|
274
|
+
}
|
|
275
|
+
{
|
|
276
|
+
const lbl = baseLabels ? `{${baseLabels}}` : "";
|
|
277
|
+
lines.push(`${this.name}_count${lbl} ${state.count}`);
|
|
278
|
+
}
|
|
279
|
+
return lines.join(`
|
|
280
|
+
`);
|
|
281
|
+
}
|
|
282
|
+
enumerateStates() {
|
|
283
|
+
const out = [];
|
|
284
|
+
for (const [k, s] of this.states) {
|
|
285
|
+
out.push({ labels: this.parseKey(k), state: s });
|
|
286
|
+
}
|
|
287
|
+
return out;
|
|
288
|
+
}
|
|
289
|
+
assertLabels(labels) {
|
|
290
|
+
if (!labels && this.labelNames.length === 0)
|
|
291
|
+
return;
|
|
292
|
+
if (this.labelNames.length === 0) {
|
|
293
|
+
throw new Error(`Histogram ${this.name} has no labels declared`);
|
|
294
|
+
}
|
|
295
|
+
if (!labels) {
|
|
296
|
+
throw new Error(`Histogram ${this.name} requires labels: ${this.labelNames.join(", ")}`);
|
|
297
|
+
}
|
|
298
|
+
for (const required of this.labelNames) {
|
|
299
|
+
if (!(required in labels)) {
|
|
300
|
+
throw new Error(`Histogram ${this.name} missing label "${required}"`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
key(labels) {
|
|
305
|
+
if (!labels)
|
|
306
|
+
return "";
|
|
307
|
+
return this.labelNames.map((n) => `${n}=${escapeLabelValue(labels[n] ?? "")}`).join(",");
|
|
308
|
+
}
|
|
309
|
+
parseKey(key) {
|
|
310
|
+
if (!key)
|
|
311
|
+
return {};
|
|
312
|
+
const out = {};
|
|
313
|
+
for (const part of key.split(",")) {
|
|
314
|
+
const [k, ...rest] = part.split("=");
|
|
315
|
+
out[k] = rest.join("=");
|
|
316
|
+
}
|
|
317
|
+
return out;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// packages/metrics/src/summary.ts
|
|
321
|
+
var DEFAULT_PERCENTILES = [0.5, 0.9, 0.99];
|
|
322
|
+
var DEFAULT_MAX_AGE_SECONDS = 600;
|
|
323
|
+
var DEFAULT_AGE_BUCKETS = 5;
|
|
324
|
+
|
|
325
|
+
class SummaryImpl {
|
|
326
|
+
name;
|
|
327
|
+
help;
|
|
328
|
+
labelNames;
|
|
329
|
+
percentiles;
|
|
330
|
+
maxAgeSeconds;
|
|
331
|
+
ageBuckets;
|
|
332
|
+
bucketSize;
|
|
333
|
+
states = new Map;
|
|
334
|
+
constructor(opts) {
|
|
335
|
+
this.name = opts.name;
|
|
336
|
+
this.help = opts.help;
|
|
337
|
+
this.labelNames = opts.labelNames ?? [];
|
|
338
|
+
this.percentiles = opts.percentiles ?? DEFAULT_PERCENTILES;
|
|
339
|
+
this.maxAgeSeconds = opts.maxAgeSeconds ?? DEFAULT_MAX_AGE_SECONDS;
|
|
340
|
+
this.ageBuckets = opts.ageBuckets ?? DEFAULT_AGE_BUCKETS;
|
|
341
|
+
this.bucketSize = Math.ceil(this.maxAgeSeconds / this.ageBuckets);
|
|
342
|
+
}
|
|
343
|
+
observe(value, labels) {
|
|
344
|
+
this.assertLabels(labels);
|
|
345
|
+
const k = this.key(labels);
|
|
346
|
+
let s = this.states.get(k);
|
|
347
|
+
if (!s) {
|
|
348
|
+
s = this.newState();
|
|
349
|
+
this.states.set(k, s);
|
|
350
|
+
}
|
|
351
|
+
this.maybeRotate(s);
|
|
352
|
+
const b = s.buckets[s.currentBucket];
|
|
353
|
+
b.values.push(value);
|
|
354
|
+
b.sum += value;
|
|
355
|
+
b.count++;
|
|
356
|
+
}
|
|
357
|
+
async time(fn, labels) {
|
|
358
|
+
const start = performance.now();
|
|
359
|
+
try {
|
|
360
|
+
return await fn();
|
|
361
|
+
} finally {
|
|
362
|
+
const elapsedMs = performance.now() - start;
|
|
363
|
+
this.observe(elapsedMs / 1000, labels);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
reset() {
|
|
367
|
+
this.states.clear();
|
|
368
|
+
}
|
|
369
|
+
getSamples() {
|
|
370
|
+
const out = [];
|
|
371
|
+
for (const [k] of this.states) {
|
|
372
|
+
out.push({ labels: this.parseKey(k), value: 0 });
|
|
373
|
+
}
|
|
374
|
+
return out;
|
|
375
|
+
}
|
|
376
|
+
renderPrometheus() {
|
|
377
|
+
const lines = [];
|
|
378
|
+
if (this.help)
|
|
379
|
+
lines.push(`# HELP ${this.name} ${this.help}`);
|
|
380
|
+
lines.push(`# TYPE ${this.name} summary`);
|
|
381
|
+
for (const [k, s] of this.states) {
|
|
382
|
+
const labels = this.parseKey(k);
|
|
383
|
+
this.maybeRotate(s);
|
|
384
|
+
const totals = this.totals(s);
|
|
385
|
+
for (const q of this.percentiles) {
|
|
386
|
+
const qval = this.quantile(s, q);
|
|
387
|
+
const qstr = clampQuantileLabel(q);
|
|
388
|
+
const fullLabels = { ...labels, quantile: qstr };
|
|
389
|
+
const baseStr = renderLabels(this.labelNames, fullLabels);
|
|
390
|
+
const lbl = baseStr ? `${baseStr},quantile="${qstr}"` : `quantile="${qstr}"`;
|
|
391
|
+
lines.push(`${this.name}{${lbl}} ${qval}`);
|
|
392
|
+
}
|
|
393
|
+
{
|
|
394
|
+
const lbl = renderLabels(this.labelNames, labels);
|
|
395
|
+
lines.push(`${this.name}_sum${lbl ? `{${lbl}}` : ""} ${totals.sum}`);
|
|
396
|
+
lines.push(`${this.name}_count${lbl ? `{${lbl}}` : ""} ${totals.count}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return lines.join(`
|
|
400
|
+
`);
|
|
401
|
+
}
|
|
402
|
+
newState() {
|
|
403
|
+
const buckets = [];
|
|
404
|
+
for (let i = 0;i < this.ageBuckets; i++) {
|
|
405
|
+
buckets.push({ values: [], sum: 0, count: 0 });
|
|
406
|
+
}
|
|
407
|
+
return { buckets, currentBucket: 0, lastRotation: Date.now() };
|
|
408
|
+
}
|
|
409
|
+
maybeRotate(s) {
|
|
410
|
+
const now = Date.now();
|
|
411
|
+
const elapsed = (now - s.lastRotation) / 1000;
|
|
412
|
+
if (elapsed >= this.bucketSize) {
|
|
413
|
+
const rotations = Math.floor(elapsed / this.bucketSize);
|
|
414
|
+
for (let i = 0;i < rotations; i++) {
|
|
415
|
+
s.currentBucket = (s.currentBucket + 1) % this.ageBuckets;
|
|
416
|
+
s.buckets[s.currentBucket] = { values: [], sum: 0, count: 0 };
|
|
417
|
+
}
|
|
418
|
+
s.lastRotation = now;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
totals(s) {
|
|
422
|
+
let sum = 0;
|
|
423
|
+
let count = 0;
|
|
424
|
+
for (const b of s.buckets) {
|
|
425
|
+
sum += b.sum;
|
|
426
|
+
count += b.count;
|
|
427
|
+
}
|
|
428
|
+
return { sum, count };
|
|
429
|
+
}
|
|
430
|
+
quantile(s, q) {
|
|
431
|
+
const all = [];
|
|
432
|
+
for (const b of s.buckets) {
|
|
433
|
+
for (const v of b.values)
|
|
434
|
+
all.push(v);
|
|
435
|
+
}
|
|
436
|
+
if (all.length === 0)
|
|
437
|
+
return 0;
|
|
438
|
+
all.sort((a, b) => a - b);
|
|
439
|
+
const idx = Math.min(all.length - 1, Math.max(0, Math.floor(q * all.length)));
|
|
440
|
+
return all[idx];
|
|
441
|
+
}
|
|
442
|
+
assertLabels(labels) {
|
|
443
|
+
if (!labels && this.labelNames.length === 0)
|
|
444
|
+
return;
|
|
445
|
+
if (this.labelNames.length === 0) {
|
|
446
|
+
throw new Error(`Summary ${this.name} has no labels declared`);
|
|
447
|
+
}
|
|
448
|
+
if (!labels) {
|
|
449
|
+
throw new Error(`Summary ${this.name} requires labels: ${this.labelNames.join(", ")}`);
|
|
450
|
+
}
|
|
451
|
+
for (const required of this.labelNames) {
|
|
452
|
+
if (!(required in labels)) {
|
|
453
|
+
throw new Error(`Summary ${this.name} missing label "${required}"`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
key(labels) {
|
|
458
|
+
if (!labels)
|
|
459
|
+
return "";
|
|
460
|
+
return this.labelNames.map((n) => `${n}=${escapeLabelValue(labels[n] ?? "")}`).join(",");
|
|
461
|
+
}
|
|
462
|
+
parseKey(key) {
|
|
463
|
+
if (!key)
|
|
464
|
+
return {};
|
|
465
|
+
const out = {};
|
|
466
|
+
for (const part of key.split(",")) {
|
|
467
|
+
const [k, ...rest] = part.split("=");
|
|
468
|
+
out[k] = rest.join("=");
|
|
469
|
+
}
|
|
470
|
+
return out;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
function clampQuantileLabel(q) {
|
|
474
|
+
const s = String(q);
|
|
475
|
+
if (s.includes(".") && s.length > 8) {
|
|
476
|
+
return q.toFixed(6).replace(/0+$/, "").replace(/\.$/, "");
|
|
477
|
+
}
|
|
478
|
+
return s;
|
|
479
|
+
}
|
|
480
|
+
// packages/metrics/src/registry.ts
|
|
481
|
+
class MetricsRegistry {
|
|
482
|
+
metrics = new Map;
|
|
483
|
+
globalLabels = {};
|
|
484
|
+
setGlobalLabels(labels) {
|
|
485
|
+
this.globalLabels = { ...labels };
|
|
486
|
+
}
|
|
487
|
+
getGlobalLabels() {
|
|
488
|
+
return { ...this.globalLabels };
|
|
489
|
+
}
|
|
490
|
+
registerCounter(opts) {
|
|
491
|
+
if (this.metrics.has(opts.name)) {
|
|
492
|
+
throw new Error(`Metric ${opts.name} is already registered`);
|
|
493
|
+
}
|
|
494
|
+
const c = new CounterImpl(opts);
|
|
495
|
+
this.metrics.set(opts.name, { name: opts.name, type: "counter", impl: c });
|
|
496
|
+
return c;
|
|
497
|
+
}
|
|
498
|
+
registerGauge(opts) {
|
|
499
|
+
if (this.metrics.has(opts.name)) {
|
|
500
|
+
throw new Error(`Metric ${opts.name} is already registered`);
|
|
501
|
+
}
|
|
502
|
+
const g = new GaugeImpl(opts);
|
|
503
|
+
this.metrics.set(opts.name, { name: opts.name, type: "gauge", impl: g });
|
|
504
|
+
return g;
|
|
505
|
+
}
|
|
506
|
+
registerHistogram(opts) {
|
|
507
|
+
if (this.metrics.has(opts.name)) {
|
|
508
|
+
throw new Error(`Metric ${opts.name} is already registered`);
|
|
509
|
+
}
|
|
510
|
+
const h = new HistogramImpl(opts);
|
|
511
|
+
this.metrics.set(opts.name, { name: opts.name, type: "histogram", impl: h });
|
|
512
|
+
return h;
|
|
513
|
+
}
|
|
514
|
+
registerSummary(opts) {
|
|
515
|
+
if (this.metrics.has(opts.name)) {
|
|
516
|
+
throw new Error(`Metric ${opts.name} is already registered`);
|
|
517
|
+
}
|
|
518
|
+
const s = new SummaryImpl(opts);
|
|
519
|
+
this.metrics.set(opts.name, { name: opts.name, type: "summary", impl: s });
|
|
520
|
+
return s;
|
|
521
|
+
}
|
|
522
|
+
get(name) {
|
|
523
|
+
return this.metrics.get(name);
|
|
524
|
+
}
|
|
525
|
+
getCounter(name) {
|
|
526
|
+
const m = this.metrics.get(name);
|
|
527
|
+
if (!m)
|
|
528
|
+
throw new Error(`Counter ${name} is not registered`);
|
|
529
|
+
if (m.type !== "counter")
|
|
530
|
+
throw new Error(`Metric ${name} is a ${m.type}, not a counter`);
|
|
531
|
+
return m.impl;
|
|
532
|
+
}
|
|
533
|
+
getGauge(name) {
|
|
534
|
+
const m = this.metrics.get(name);
|
|
535
|
+
if (!m)
|
|
536
|
+
throw new Error(`Gauge ${name} is not registered`);
|
|
537
|
+
if (m.type !== "gauge")
|
|
538
|
+
throw new Error(`Metric ${name} is a ${m.type}, not a gauge`);
|
|
539
|
+
return m.impl;
|
|
540
|
+
}
|
|
541
|
+
getHistogram(name) {
|
|
542
|
+
const m = this.metrics.get(name);
|
|
543
|
+
if (!m)
|
|
544
|
+
throw new Error(`Histogram ${name} is not registered`);
|
|
545
|
+
if (m.type !== "histogram")
|
|
546
|
+
throw new Error(`Metric ${name} is a ${m.type}, not a histogram`);
|
|
547
|
+
return m.impl;
|
|
548
|
+
}
|
|
549
|
+
getSummary(name) {
|
|
550
|
+
const m = this.metrics.get(name);
|
|
551
|
+
if (!m)
|
|
552
|
+
throw new Error(`Summary ${name} is not registered`);
|
|
553
|
+
if (m.type !== "summary")
|
|
554
|
+
throw new Error(`Metric ${name} is a ${m.type}, not a summary`);
|
|
555
|
+
return m.impl;
|
|
556
|
+
}
|
|
557
|
+
get size() {
|
|
558
|
+
return this.metrics.size;
|
|
559
|
+
}
|
|
560
|
+
names() {
|
|
561
|
+
return [...this.metrics.keys()].sort();
|
|
562
|
+
}
|
|
563
|
+
resetAll() {
|
|
564
|
+
for (const m of this.metrics.values()) {
|
|
565
|
+
m.impl.reset();
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
expose(format = "prometheus") {
|
|
569
|
+
const sections = [];
|
|
570
|
+
const globalLabelsStr = renderGlobalLabels(this.globalLabels);
|
|
571
|
+
for (const m of this.metrics.values()) {
|
|
572
|
+
const out = m.impl.renderPrometheus();
|
|
573
|
+
if (globalLabelsStr) {
|
|
574
|
+
sections.push(applyGlobalLabels(out, this.globalLabels));
|
|
575
|
+
} else {
|
|
576
|
+
sections.push(out);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
const body = sections.filter((s) => s.length > 0).join(`
|
|
580
|
+
|
|
581
|
+
`);
|
|
582
|
+
const contentType = format === "openmetrics" ? "application/openmetrics-text; version=1.0.0; charset=utf-8" : "text/plain; version=0.0.4; charset=utf-8";
|
|
583
|
+
return { body: body + (body.endsWith(`
|
|
584
|
+
`) ? "" : `
|
|
585
|
+
`), contentType };
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
function renderGlobalLabels(labels) {
|
|
589
|
+
const keys = Object.keys(labels);
|
|
590
|
+
if (keys.length === 0)
|
|
591
|
+
return "";
|
|
592
|
+
return keys.map((k) => `${k}="${labels[k]}"`).join(",");
|
|
593
|
+
}
|
|
594
|
+
function applyGlobalLabels(block, labels) {
|
|
595
|
+
const keys = Object.keys(labels);
|
|
596
|
+
if (keys.length === 0)
|
|
597
|
+
return block;
|
|
598
|
+
const prefix = keys.map((k) => `${k}="${labels[k]}"`).join(",");
|
|
599
|
+
return block.split(`
|
|
600
|
+
`).map((line) => {
|
|
601
|
+
if (line.startsWith("#"))
|
|
602
|
+
return line;
|
|
603
|
+
const openBrace = line.indexOf("{");
|
|
604
|
+
if (openBrace === -1) {
|
|
605
|
+
const space = line.indexOf(" ");
|
|
606
|
+
if (space === -1)
|
|
607
|
+
return line;
|
|
608
|
+
const metric = line.slice(0, space);
|
|
609
|
+
const rest = line.slice(space);
|
|
610
|
+
return `${metric}{${prefix}}${rest}`;
|
|
611
|
+
}
|
|
612
|
+
const closeBrace = line.indexOf("}", openBrace);
|
|
613
|
+
if (closeBrace === -1)
|
|
614
|
+
return line;
|
|
615
|
+
const existing = line.slice(openBrace + 1, closeBrace);
|
|
616
|
+
return `${line.slice(0, openBrace + 1)}${prefix},${existing}${line.slice(closeBrace)}`;
|
|
617
|
+
}).join(`
|
|
618
|
+
`);
|
|
619
|
+
}
|
|
620
|
+
// packages/metrics/src/service.ts
|
|
621
|
+
var METRICS_SERVICE_TOKEN = Symbol.for("nexus:MetricsService");
|
|
622
|
+
var _current;
|
|
623
|
+
function setMetricsService(service) {
|
|
624
|
+
_current = service;
|
|
625
|
+
}
|
|
626
|
+
function getMetricsService() {
|
|
627
|
+
return _current;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
class MetricsService {
|
|
631
|
+
registry = new MetricsRegistry;
|
|
632
|
+
counter(opts) {
|
|
633
|
+
return this.registry.registerCounter(opts);
|
|
634
|
+
}
|
|
635
|
+
gauge(opts) {
|
|
636
|
+
return this.registry.registerGauge(opts);
|
|
637
|
+
}
|
|
638
|
+
histogram(opts) {
|
|
639
|
+
return this.registry.registerHistogram(opts);
|
|
640
|
+
}
|
|
641
|
+
summary(opts) {
|
|
642
|
+
return this.registry.registerSummary(opts);
|
|
643
|
+
}
|
|
644
|
+
getCounter(name) {
|
|
645
|
+
return this.registry.getCounter(name);
|
|
646
|
+
}
|
|
647
|
+
getOrCreateCounter(name, help, labelNames) {
|
|
648
|
+
try {
|
|
649
|
+
return this.registry.getCounter(name);
|
|
650
|
+
} catch {
|
|
651
|
+
return this.registry.registerCounter({ name, help, labelNames });
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
getOrCreateHistogram(name, help, labelNames, buckets) {
|
|
655
|
+
try {
|
|
656
|
+
return this.registry.getHistogram(name);
|
|
657
|
+
} catch {
|
|
658
|
+
return this.registry.registerHistogram({ name, help, labelNames, buckets });
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
getGauge(name) {
|
|
662
|
+
return this.registry.getGauge(name);
|
|
663
|
+
}
|
|
664
|
+
getHistogram(name) {
|
|
665
|
+
return this.registry.getHistogram(name);
|
|
666
|
+
}
|
|
667
|
+
getSummary(name) {
|
|
668
|
+
return this.registry.getSummary(name);
|
|
669
|
+
}
|
|
670
|
+
expose(format = "prometheus") {
|
|
671
|
+
return this.registry.expose(format);
|
|
672
|
+
}
|
|
673
|
+
get size() {
|
|
674
|
+
return this.registry.size;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
// packages/metrics/src/module.ts
|
|
678
|
+
import { Module } from "@nexusts/core/decorators/module.js";
|
|
679
|
+
|
|
680
|
+
// packages/metrics/src/controller.ts
|
|
681
|
+
class MetricsController {
|
|
682
|
+
static PATH = "/metrics";
|
|
683
|
+
static handler(service) {
|
|
684
|
+
return (c) => {
|
|
685
|
+
const accept = c.req.header("accept") ?? "";
|
|
686
|
+
const format = accept.includes("application/openmetrics-text") ? "openmetrics" : "prometheus";
|
|
687
|
+
const { contentType, body } = service.expose(format);
|
|
688
|
+
return c.body(body, 200, {
|
|
689
|
+
"content-type": contentType,
|
|
690
|
+
"cache-control": "no-store"
|
|
691
|
+
});
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
static mount(app, service, path = MetricsController.PATH) {
|
|
695
|
+
app.get(path, MetricsController.handler(service));
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// packages/metrics/src/module.ts
|
|
700
|
+
class MetricsModule {
|
|
701
|
+
static forRoot(config = {}) {
|
|
702
|
+
const fullConfig = {
|
|
703
|
+
defaultBuckets: config.defaultBuckets ?? [],
|
|
704
|
+
defaultPercentiles: config.defaultPercentiles ?? [],
|
|
705
|
+
path: config.path ?? "/metrics",
|
|
706
|
+
enableDefaultMetrics: config.enableDefaultMetrics ?? true,
|
|
707
|
+
mountController: config.mountController ?? true,
|
|
708
|
+
globalLabels: config.globalLabels ?? {}
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
class ConfiguredMetricsModule {
|
|
712
|
+
svc;
|
|
713
|
+
constructor(svc = new MetricsService) {
|
|
714
|
+
this.svc = svc;
|
|
715
|
+
if (Object.keys(fullConfig.globalLabels).length > 0) {
|
|
716
|
+
svc.registry.setGlobalLabels(fullConfig.globalLabels);
|
|
717
|
+
}
|
|
718
|
+
if (fullConfig.enableDefaultMetrics) {
|
|
719
|
+
registerDefaultMetrics(svc);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
static get path() {
|
|
723
|
+
return fullConfig.path;
|
|
724
|
+
}
|
|
725
|
+
static get service() {
|
|
726
|
+
return new MetricsService;
|
|
727
|
+
}
|
|
728
|
+
static get controllerPath() {
|
|
729
|
+
return fullConfig.path;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
ConfiguredMetricsModule = __legacyDecorateClassTS([
|
|
733
|
+
Module({
|
|
734
|
+
providers: [
|
|
735
|
+
MetricsService,
|
|
736
|
+
{ provide: METRICS_SERVICE_TOKEN, useExisting: MetricsService },
|
|
737
|
+
{ provide: "METRICS_CONFIG", useValue: fullConfig }
|
|
738
|
+
],
|
|
739
|
+
exports: [MetricsService, METRICS_SERVICE_TOKEN, "METRICS_CONFIG"]
|
|
740
|
+
}),
|
|
741
|
+
__legacyMetadataTS("design:paramtypes", [
|
|
742
|
+
typeof MetricsService === "undefined" ? Object : MetricsService
|
|
743
|
+
])
|
|
744
|
+
], ConfiguredMetricsModule);
|
|
745
|
+
Object.defineProperty(ConfiguredMetricsModule, "name", {
|
|
746
|
+
value: "ConfiguredMetricsModule"
|
|
747
|
+
});
|
|
748
|
+
ConfiguredMetricsModule.mount = (app, service) => {
|
|
749
|
+
MetricsController.mount(app, service, fullConfig.path);
|
|
750
|
+
};
|
|
751
|
+
return ConfiguredMetricsModule;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
MetricsModule = __legacyDecorateClassTS([
|
|
755
|
+
Module({
|
|
756
|
+
providers: [
|
|
757
|
+
MetricsService,
|
|
758
|
+
{ provide: METRICS_SERVICE_TOKEN, useExisting: MetricsService }
|
|
759
|
+
],
|
|
760
|
+
exports: [MetricsService, METRICS_SERVICE_TOKEN]
|
|
761
|
+
})
|
|
762
|
+
], MetricsModule);
|
|
763
|
+
function registerDefaultMetrics(service) {
|
|
764
|
+
const processStartTime = service.gauge({
|
|
765
|
+
name: "process_start_time_seconds",
|
|
766
|
+
help: "Start time of the process since unix epoch in seconds.",
|
|
767
|
+
labelNames: [],
|
|
768
|
+
collect: () => {
|
|
769
|
+
processStartTime.set(Math.floor(Date.now() / 1000));
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
const processResidentMemory = service.gauge({
|
|
773
|
+
name: "process_resident_memory_bytes",
|
|
774
|
+
help: "Resident memory size in bytes.",
|
|
775
|
+
labelNames: [],
|
|
776
|
+
collect: () => {
|
|
777
|
+
const mem = process.memoryUsage();
|
|
778
|
+
processResidentMemory.set(mem.rss);
|
|
779
|
+
}
|
|
780
|
+
});
|
|
781
|
+
const processHeapUsed = service.gauge({
|
|
782
|
+
name: "nodejs_heap_size_used_bytes",
|
|
783
|
+
help: "Node.js heap size used in bytes.",
|
|
784
|
+
labelNames: [],
|
|
785
|
+
collect: () => {
|
|
786
|
+
const mem = process.memoryUsage();
|
|
787
|
+
processHeapUsed.set(mem.heapUsed);
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
const processHeapTotal = service.gauge({
|
|
791
|
+
name: "nodejs_heap_size_total_bytes",
|
|
792
|
+
help: "Node.js heap size total in bytes.",
|
|
793
|
+
labelNames: [],
|
|
794
|
+
collect: () => {
|
|
795
|
+
const mem = process.memoryUsage();
|
|
796
|
+
processHeapTotal.set(mem.heapTotal);
|
|
797
|
+
}
|
|
798
|
+
});
|
|
799
|
+
const processExternalMemory = service.gauge({
|
|
800
|
+
name: "nodejs_external_memory_bytes",
|
|
801
|
+
help: "Node.js external memory size in bytes.",
|
|
802
|
+
labelNames: [],
|
|
803
|
+
collect: () => {
|
|
804
|
+
const mem = process.memoryUsage();
|
|
805
|
+
processExternalMemory.set(mem.external);
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
const processCpuUser = service.gauge({
|
|
809
|
+
name: "process_cpu_user_seconds_total",
|
|
810
|
+
help: "Total user CPU time spent in seconds.",
|
|
811
|
+
labelNames: [],
|
|
812
|
+
collect: () => {
|
|
813
|
+
const cpu = process.cpuUsage();
|
|
814
|
+
processCpuUser.set(cpu.user / 1e6);
|
|
815
|
+
}
|
|
816
|
+
});
|
|
817
|
+
const processCpuSystem = service.gauge({
|
|
818
|
+
name: "process_cpu_system_seconds_total",
|
|
819
|
+
help: "Total system CPU time spent in seconds.",
|
|
820
|
+
labelNames: [],
|
|
821
|
+
collect: () => {
|
|
822
|
+
const cpu = process.cpuUsage();
|
|
823
|
+
processCpuSystem.set(cpu.system / 1e6);
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
const eventLoopLag = service.gauge({
|
|
827
|
+
name: "nodejs_eventloop_lag_seconds",
|
|
828
|
+
help: "Lag of the event loop in seconds.",
|
|
829
|
+
labelNames: [],
|
|
830
|
+
collect: () => {
|
|
831
|
+
const start = process.hrtime.bigint();
|
|
832
|
+
setImmediate(() => {
|
|
833
|
+
const lagNs = Number(process.hrtime.bigint() - start);
|
|
834
|
+
eventLoopLag.set(lagNs / 1e9);
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
});
|
|
838
|
+
const processActiveHandles = service.gauge({
|
|
839
|
+
name: "nodejs_active_handles_total",
|
|
840
|
+
help: "Number of active handles.",
|
|
841
|
+
labelNames: [],
|
|
842
|
+
collect: () => {
|
|
843
|
+
const handles = process._getActiveHandles?.() ?? [];
|
|
844
|
+
processActiveHandles.set(handles.length);
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
const processActiveRequests = service.gauge({
|
|
848
|
+
name: "nodejs_active_requests_total",
|
|
849
|
+
help: "Number of active requests.",
|
|
850
|
+
labelNames: [],
|
|
851
|
+
collect: () => {
|
|
852
|
+
const reqs = process._getActiveRequests?.() ?? [];
|
|
853
|
+
processActiveRequests.set(reqs.length);
|
|
854
|
+
}
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
// packages/metrics/src/decorators/counted.ts
|
|
858
|
+
function Counted(metricName, options = {}) {
|
|
859
|
+
return function(_target, _propertyKey, descriptor) {
|
|
860
|
+
const original = descriptor.value;
|
|
861
|
+
if (typeof original !== "function") {
|
|
862
|
+
throw new Error(`@Counted() can only be applied to methods, got ${typeof original}`);
|
|
863
|
+
}
|
|
864
|
+
const isAsync = original.constructor?.name === "AsyncFunction";
|
|
865
|
+
if (isAsync) {
|
|
866
|
+
descriptor.value = async function wrapped(...args) {
|
|
867
|
+
const svc = getMetricsService();
|
|
868
|
+
if (svc) {
|
|
869
|
+
const labels = options.labels?.();
|
|
870
|
+
svc.getOrCreateCounter(metricName, undefined, labels ? Object.keys(labels) : undefined).inc(labels);
|
|
871
|
+
}
|
|
872
|
+
return original.apply(this, args);
|
|
873
|
+
};
|
|
874
|
+
} else {
|
|
875
|
+
descriptor.value = function wrapped(...args) {
|
|
876
|
+
const svc = getMetricsService();
|
|
877
|
+
if (svc) {
|
|
878
|
+
const labels = options.labels?.();
|
|
879
|
+
svc.getOrCreateCounter(metricName, undefined, labels ? Object.keys(labels) : undefined).inc(labels);
|
|
880
|
+
}
|
|
881
|
+
return original.apply(this, args);
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
return descriptor;
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
// packages/metrics/src/decorators/timed.ts
|
|
888
|
+
function Timed(metricName, options = {}) {
|
|
889
|
+
return function(_target, _propertyKey, descriptor) {
|
|
890
|
+
const original = descriptor.value;
|
|
891
|
+
if (typeof original !== "function") {
|
|
892
|
+
throw new Error(`@Timed() can only be applied to methods, got ${typeof original}`);
|
|
893
|
+
}
|
|
894
|
+
const isAsync = original.constructor?.name === "AsyncFunction";
|
|
895
|
+
if (isAsync) {
|
|
896
|
+
descriptor.value = async function wrapped(...args) {
|
|
897
|
+
const svc = getMetricsService();
|
|
898
|
+
const start = performance.now();
|
|
899
|
+
try {
|
|
900
|
+
return await original.apply(this, args);
|
|
901
|
+
} finally {
|
|
902
|
+
if (svc) {
|
|
903
|
+
const elapsedMs = performance.now() - start;
|
|
904
|
+
const labels = options.labels?.();
|
|
905
|
+
svc.getOrCreateHistogram(metricName, undefined, labels ? Object.keys(labels) : undefined, options.buckets).observe(elapsedMs / 1000, labels);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
};
|
|
909
|
+
} else {
|
|
910
|
+
descriptor.value = function wrapped(...args) {
|
|
911
|
+
const svc = getMetricsService();
|
|
912
|
+
const start = performance.now();
|
|
913
|
+
try {
|
|
914
|
+
return original.apply(this, args);
|
|
915
|
+
} finally {
|
|
916
|
+
if (svc) {
|
|
917
|
+
const elapsedMs = performance.now() - start;
|
|
918
|
+
const labels = options.labels?.();
|
|
919
|
+
svc.getOrCreateHistogram(metricName, undefined, labels ? Object.keys(labels) : undefined, options.buckets).observe(elapsedMs / 1000, labels);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
return descriptor;
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
export {
|
|
928
|
+
setMetricsService,
|
|
929
|
+
getMetricsService,
|
|
930
|
+
Timed,
|
|
931
|
+
SummaryImpl,
|
|
932
|
+
MetricsService,
|
|
933
|
+
MetricsRegistry,
|
|
934
|
+
MetricsModule,
|
|
935
|
+
MetricsController,
|
|
936
|
+
METRICS_SERVICE_TOKEN,
|
|
937
|
+
HistogramImpl,
|
|
938
|
+
GaugeImpl,
|
|
939
|
+
DEFAULT_PERCENTILES,
|
|
940
|
+
DEFAULT_BUCKETS,
|
|
941
|
+
CounterImpl,
|
|
942
|
+
Counted
|
|
943
|
+
};
|
|
944
|
+
|
|
945
|
+
//# debugId=FBFB8DC36D7055E964756E2164756E21
|
|
946
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/counter.ts", "../src/gauge.ts", "../src/histogram.ts", "../src/summary.ts", "../src/registry.ts", "../src/service.ts", "../src/module.ts", "../src/controller.ts", "../src/decorators/counted.ts", "../src/decorators/timed.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * `Counter` — monotonically increasing value.\n *\n * Counters can only go up (use `Gauge` for values that can decrease).\n * They're ideal for: request counts, error counts, bytes sent, etc.\n *\n * The standard Prometheus naming convention is to suffix counters\n * with `_total`.\n *\n * Example:\n * const c = new Counter({ name: \"http_requests_total\" });\n * c.inc();\n * c.incBy(5, { method: \"GET\" });\n */\n\nimport type { CounterOptions, Counter as ICounter, MetricSample } from \"./types.js\";\n\n/** Internal helper used by all metric types. */\nexport function renderLabels(\n\tdeclared: string[],\n\tvalues: Record<string, string>,\n): string {\n\tif (declared.length === 0) return \"\";\n\treturn declared\n\t\t.map((n) => `${n}=\"${escapeLabelValue(values[n] ?? \"\")}\"`)\n\t\t.join(\",\");\n}\n\nexport class CounterImpl implements ICounter {\n\treadonly name: string;\n\treadonly help?: string;\n\treadonly labelNames: string[];\n\t/** Map from \"label1=value1,label2=value2\" -> value. */\n\tprivate values = new Map<string, number>();\n\n\tconstructor(opts: CounterOptions) {\n\t\tthis.name = opts.name;\n\t\tthis.help = opts.help;\n\t\tthis.labelNames = opts.labelNames ?? [];\n\t}\n\n\tinc(labels?: Record<string, string>): void {\n\t\tthis.incBy(1, labels);\n\t}\n\n\tincBy(n: number, labels?: Record<string, string>): void {\n\t\tif (n < 0) {\n\t\t\tthrow new Error(`Counter ${this.name} can only increase (got ${n})`);\n\t\t}\n\t\tthis.assertLabels(labels);\n\t\tconst key = this.key(labels);\n\t\tthis.values.set(key, (this.values.get(key) ?? 0) + n);\n\t}\n\n\treset(): void {\n\t\tthis.values.clear();\n\t}\n\n\tgetSamples(): MetricSample[] {\n\t\tconst out: MetricSample[] = [];\n\t\tfor (const [k, v] of this.values) {\n\t\t\tout.push({ labels: this.parseKey(k), value: v });\n\t\t}\n\t\treturn out;\n\t}\n\n\t/** Render this counter in Prometheus exposition format. */\n\trenderPrometheus(): string {\n\t\tconst lines: string[] = [];\n\t\tif (this.help) lines.push(`# HELP ${this.name} ${this.help}`);\n\t\tlines.push(`# TYPE ${this.name} counter`);\n\t\tfor (const [k, v] of this.values) {\n\t\t\tconst labels = this.parseKey(k);\n\t\t\tconst lbl = renderLabels(this.labelNames, labels);\n\t\t\tlines.push(lbl ? `${this.name}{${lbl}} ${v}` : `${this.name} ${v}`);\n\t\t}\n\t\treturn lines.join(\"\\n\");\n\t}\n\n\tprivate assertLabels(labels?: Record<string, string>): void {\n\t\tif (!labels && this.labelNames.length === 0) return;\n\t\tif (this.labelNames.length === 0) {\n\t\t\tthrow new Error(`Counter ${this.name} has no labels declared`);\n\t\t}\n\t\tif (!labels) {\n\t\t\tthrow new Error(`Counter ${this.name} requires labels: ${this.labelNames.join(\", \")}`);\n\t\t}\n\t\tfor (const required of this.labelNames) {\n\t\t\tif (!(required in labels)) {\n\t\t\t\tthrow new Error(`Counter ${this.name} missing label \"${required}\"`);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate key(labels?: Record<string, string>): string {\n\t\tif (!labels) return \"\";\n\t\treturn this.labelNames.map((n) => `${n}=${escapeLabelValue(labels[n] ?? \"\")}`).join(\",\");\n\t}\n\n\tprivate parseKey(key: string): Record<string, string> {\n\t\tif (!key) return {};\n\t\tconst out: Record<string, string> = {};\n\t\tfor (const part of key.split(\",\")) {\n\t\t\tconst [k, ...rest] = part.split(\"=\");\n\t\t\tout[k] = rest.join(\"=\");\n\t\t}\n\t\treturn out;\n\t}\n}\n\nexport function escapeLabelValue(v: string): string {\n\treturn v.replace(/\\\\/g, \"\\\\\\\\\").replace(/\\n/g, \"\\\\n\").replace(/\"/g, \"\\\\\\\"\");\n}\n",
|
|
6
|
+
"/**\n * `Gauge` — point-in-time value that can go up or down.\n *\n * Ideal for: queue size, memory usage, active connections, in-flight\n * requests, etc.\n *\n * The value is stored per label combination. `inc()` and `dec()` are\n * convenience helpers; `set()` replaces the value outright.\n *\n * Example:\n * const g = new Gauge({\n * name: \"active_connections\",\n * collect: () => { return; },\n * });\n * g.inc();\n * g.dec(2);\n * g.set(42);\n */\n\nimport type { GaugeOptions, Gauge as IGauge, MetricSample } from \"./types.js\";\nimport { escapeLabelValue, renderLabels } from \"./counter.js\";\n\nexport class GaugeImpl implements IGauge {\n\treadonly name: string;\n\treadonly help?: string;\n\treadonly labelNames: string[];\n\tprivate values = new Map<string, number>();\n\n\tconstructor(opts: GaugeOptions) {\n\t\tthis.name = opts.name;\n\t\tthis.help = opts.help;\n\t\tthis.labelNames = opts.labelNames ?? [];\n\t}\n\n\tset(value: number, labels?: Record<string, string>): void {\n\t\tthis.assertLabels(labels);\n\t\tthis.values.set(this.key(labels), value);\n\t}\n\n\tinc(n: number = 1, labels?: Record<string, string>): void {\n\t\tthis.assertLabels(labels);\n\t\tconst k = this.key(labels);\n\t\tthis.values.set(k, (this.values.get(k) ?? 0) + n);\n\t}\n\n\tdec(n: number = 1, labels?: Record<string, string>): void {\n\t\tthis.assertLabels(labels);\n\t\tconst k = this.key(labels);\n\t\tthis.values.set(k, (this.values.get(k) ?? 0) - n);\n\t}\n\n\tsetToCurrentTime(labels?: Record<string, string>): void {\n\t\tthis.set(Date.now() / 1000, labels);\n\t}\n\n\treset(): void {\n\t\tthis.values.clear();\n\t}\n\n\tgetSamples(): MetricSample[] {\n\t\tconst out: MetricSample[] = [];\n\t\tfor (const [k, v] of this.values) {\n\t\t\tout.push({ labels: this.parseKey(k), value: v });\n\t\t}\n\t\treturn out;\n\t}\n\n\t/** Render this gauge in Prometheus exposition format. */\n\trenderPrometheus(): string {\n\t\tconst lines: string[] = [];\n\t\tif (this.help) lines.push(`# HELP ${this.name} ${this.help}`);\n\t\tlines.push(`# TYPE ${this.name} gauge`);\n\t\tfor (const [k, v] of this.values) {\n\t\t\tconst labels = this.parseKey(k);\n\t\t\tconst lbl = renderLabels(this.labelNames, labels);\n\t\t\tlines.push(lbl ? `${this.name}{${lbl}} ${v}` : `${this.name} ${v}`);\n\t\t}\n\t\treturn lines.join(\"\\n\");\n\t}\n\n\tprivate assertLabels(labels?: Record<string, string>): void {\n\t\tif (!labels && this.labelNames.length === 0) return;\n\t\tif (this.labelNames.length === 0) {\n\t\t\tthrow new Error(`Gauge ${this.name} has no labels declared`);\n\t\t}\n\t\tif (!labels) {\n\t\t\tthrow new Error(`Gauge ${this.name} requires labels: ${this.labelNames.join(\", \")}`);\n\t\t}\n\t\tfor (const required of this.labelNames) {\n\t\t\tif (!(required in labels)) {\n\t\t\t\tthrow new Error(`Gauge ${this.name} missing label \"${required}\"`);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate key(labels?: Record<string, string>): string {\n\t\tif (!labels) return \"\";\n\t\treturn this.labelNames.map((n) => `${n}=${escapeLabelValue(labels[n] ?? \"\")}`).join(\",\");\n\t}\n\n\tprivate parseKey(key: string): Record<string, string> {\n\t\tif (!key) return {};\n\t\tconst out: Record<string, string> = {};\n\t\tfor (const part of key.split(\",\")) {\n\t\t\tconst [k, ...rest] = part.split(\"=\");\n\t\t\tout[k] = rest.join(\"=\");\n\t\t}\n\t\treturn out;\n\t}\n}\n",
|
|
7
|
+
"/**\n * `Histogram` — distribution of observed values into buckets.\n *\n * Each `observe()` adds the value to every bucket whose upper bound\n * is >= value. The metric exposes:\n * - `<name>_bucket{le=\"...\"}` — cumulative count per bucket\n * - `<name>_sum` — total sum of all observed values\n * - `<name>_count` — total count\n *\n * Default buckets (Prometheus convention): [0.005, 0.01, 0.025,\n * 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10] seconds.\n *\n * Example:\n * const h = new Histogram({ name: \"http_duration_seconds\" });\n * h.observe(0.123);\n * await h.time(async () => fetch(\"/slow\"));\n */\n\nimport type { HistogramOptions, Histogram as IHistogram, MetricSample } from \"./types.js\";\nimport { escapeLabelValue, renderLabels } from \"./counter.js\";\n\n/** Prometheus default buckets (seconds). */\nexport const DEFAULT_BUCKETS = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];\n\ninterface HistogramState {\n\tbuckets: number[]; // count per upper bound\n\tsum: number;\n\tcount: number;\n}\n\nexport class HistogramImpl implements IHistogram {\n\treadonly name: string;\n\treadonly help?: string;\n\treadonly labelNames: string[];\n\tprivate readonly upperBounds: number[];\n\t/** Map from \"label1=v1,label2=v2\" -> state. */\n\tprivate states = new Map<string, HistogramState>();\n\n\tconstructor(opts: HistogramOptions) {\n\t\tthis.name = opts.name;\n\t\tthis.help = opts.help;\n\t\tthis.labelNames = opts.labelNames ?? [];\n\t\tconst buckets = opts.buckets ?? DEFAULT_BUCKETS;\n\t\t// Copy and sort, ensure monotonic increasing\n\t\tthis.upperBounds = [...buckets].sort((a, b) => a - b);\n\t\tif (this.upperBounds.length === 0) {\n\t\t\tthrow new Error(`Histogram ${this.name} requires at least one bucket`);\n\t\t}\n\t}\n\n\tobserve(value: number, labels?: Record<string, string>): void {\n\t\tthis.assertLabels(labels);\n\t\tconst k = this.key(labels);\n\t\tlet s = this.states.get(k);\n\t\tif (!s) {\n\t\t\ts = { buckets: new Array(this.upperBounds.length).fill(0), sum: 0, count: 0 };\n\t\t\tthis.states.set(k, s);\n\t\t}\n\t\ts.sum += value;\n\t\ts.count++;\n\t\tfor (let i = 0; i < this.upperBounds.length; i++) {\n\t\t\tif (value <= this.upperBounds[i]) s.buckets[i]++;\n\t\t}\n\t}\n\n\tasync time<T>(\n\t\tfn: (start: number) => Promise<T> | T,\n\t\tlabels?: Record<string, string>,\n\t): Promise<T> {\n\t\tconst start = performance.now();\n\t\ttry {\n\t\t\treturn await fn(start);\n\t\t} finally {\n\t\t\tconst elapsedMs = performance.now() - start;\n\t\t\tthis.observe(elapsedMs / 1000, labels);\n\t\t}\n\t}\n\n\treset(): void {\n\t\tthis.states.clear();\n\t}\n\n\tgetSamples(): MetricSample[] {\n\t\tconst out: MetricSample[] = [];\n\t\tfor (const [k, s] of this.states) {\n\t\t\tconst labels = this.parseKey(k);\n\t\t\tfor (let i = 0; i < this.upperBounds.length; i++) {\n\t\t\t\tout.push({\n\t\t\t\t\tlabels: { ...labels, le: String(this.upperBounds[i]) },\n\t\t\t\t\tvalue: s.buckets[i],\n\t\t\t\t});\n\t\t\t}\n\t\t\tout.push({ labels: { ...labels, le: \"+Inf\" }, value: s.count });\n\t\t\tout.push({ labels: { ...labels }, value: s.sum });\n\t\t}\n\t\treturn out;\n\t}\n\n\t/** Render this histogram in Prometheus exposition format. */\n\trenderPrometheus(): string {\n\t\tconst lines: string[] = [];\n\t\tif (this.help) lines.push(`# HELP ${this.name} ${this.help}`);\n\t\tlines.push(`# TYPE ${this.name} histogram`);\n\t\tfor (const { labels, state } of this.enumerateStates()) {\n\t\t\tlines.push(this.renderState(labels, state));\n\t\t}\n\t\treturn lines.join(\"\\n\");\n\t}\n\n\t/** Internal: produce Prometheus-formatted lines for a single state. */\n\trenderState(labels: Record<string, string>, state: HistogramState): string {\n\t\tconst baseLabels = renderLabels(this.labelNames, labels);\n\t\tconst lines: string[] = [];\n\n\t\tlet cumulative = 0;\n\t\tfor (let i = 0; i < this.upperBounds.length; i++) {\n\t\t\tcumulative = state.buckets[i];\n\t\t\tconst le = this.upperBounds[i];\n\t\t\tconst leStr = String(le);\n\t\t\tconst lbl = baseLabels ? `${baseLabels},le=\"${leStr}\"` : `le=\"${leStr}\"`;\n\t\t\tlines.push(`${this.name}_bucket{${lbl}} ${cumulative}`);\n\t\t}\n\t\t{\n\t\t\tconst lbl = baseLabels ? `${baseLabels},le=\"+Inf\"` : `le=\"+Inf\"`;\n\t\t\tlines.push(`${this.name}_bucket{${lbl}} ${state.count}`);\n\t\t}\n\t\t{\n\t\t\tconst lbl = baseLabels ? `{${baseLabels}}` : \"\";\n\t\t\tlines.push(`${this.name}_sum${lbl} ${state.sum}`);\n\t\t}\n\t\t{\n\t\t\tconst lbl = baseLabels ? `{${baseLabels}}` : \"\";\n\t\t\tlines.push(`${this.name}_count${lbl} ${state.count}`);\n\t\t}\n\t\treturn lines.join(\"\\n\");\n\t}\n\n\t/** Internal: enumerate all states for the registry. */\n\tenumerateStates(): Array<{ labels: Record<string, string>; state: HistogramState }> {\n\t\tconst out: Array<{ labels: Record<string, string>; state: HistogramState }> = [];\n\t\tfor (const [k, s] of this.states) {\n\t\t\tout.push({ labels: this.parseKey(k), state: s });\n\t\t}\n\t\treturn out;\n\t}\n\n\tprivate assertLabels(labels?: Record<string, string>): void {\n\t\tif (!labels && this.labelNames.length === 0) return;\n\t\tif (this.labelNames.length === 0) {\n\t\t\tthrow new Error(`Histogram ${this.name} has no labels declared`);\n\t\t}\n\t\tif (!labels) {\n\t\t\tthrow new Error(`Histogram ${this.name} requires labels: ${this.labelNames.join(\", \")}`);\n\t\t}\n\t\tfor (const required of this.labelNames) {\n\t\t\tif (!(required in labels)) {\n\t\t\t\tthrow new Error(`Histogram ${this.name} missing label \"${required}\"`);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate key(labels?: Record<string, string>): string {\n\t\tif (!labels) return \"\";\n\t\treturn this.labelNames.map((n) => `${n}=${escapeLabelValue(labels[n] ?? \"\")}`).join(\",\");\n\t}\n\n\tprivate parseKey(key: string): Record<string, string> {\n\t\tif (!key) return {};\n\t\tconst out: Record<string, string> = {};\n\t\tfor (const part of key.split(\",\")) {\n\t\t\tconst [k, ...rest] = part.split(\"=\");\n\t\t\tout[k] = rest.join(\"=\");\n\t\t}\n\t\treturn out;\n\t}\n}\n",
|
|
8
|
+
"/**\n * `Summary` — quantile estimation over a sliding window of values.\n *\n * Summary is similar to Histogram but computes client-side quantiles\n * (via t-digest-style sorting). It's useful when you need a small\n * number of percentiles (50/90/99) without the storage cost of\n * histograms.\n *\n * Quantile computation uses a simple sorted-window approach: each\n * label combination keeps a circular buffer of the last N values.\n *\n * Example:\n * const s = new Summary({\n * name: \"http_request_size_bytes\",\n * percentiles: [0.5, 0.9, 0.99],\n * });\n * s.observe(1024);\n */\n\nimport type { SummaryOptions, Summary as ISummary, MetricSample } from \"./types.js\";\nimport { escapeLabelValue, renderLabels } from \"./counter.js\";\n\n/** Default percentiles. */\nexport const DEFAULT_PERCENTILES = [0.5, 0.9, 0.99];\n/** Default sliding window size. */\nexport const DEFAULT_MAX_AGE_SECONDS = 600;\nexport const DEFAULT_AGE_BUCKETS = 5;\n\ninterface SummaryState {\n\t/** For each age bucket, an array of (sum, count) pairs. */\n\tbuckets: Array<{ values: number[]; sum: number; count: number }>;\n\t/** Index of the next bucket to rotate. */\n\tcurrentBucket: number;\n\t/** Last rotation time. */\n\tlastRotation: number;\n}\n\nexport class SummaryImpl implements ISummary {\n\treadonly name: string;\n\treadonly help?: string;\n\treadonly labelNames: string[];\n\tprivate readonly percentiles: number[];\n\tprivate readonly maxAgeSeconds: number;\n\tprivate readonly ageBuckets: number;\n\tprivate readonly bucketSize: number;\n\t/** Map from \"label1=v1,label2=v2\" -> state. */\n\tprivate states = new Map<string, SummaryState>();\n\n\tconstructor(opts: SummaryOptions) {\n\t\tthis.name = opts.name;\n\t\tthis.help = opts.help;\n\t\tthis.labelNames = opts.labelNames ?? [];\n\t\tthis.percentiles = opts.percentiles ?? DEFAULT_PERCENTILES;\n\t\tthis.maxAgeSeconds = opts.maxAgeSeconds ?? DEFAULT_MAX_AGE_SECONDS;\n\t\tthis.ageBuckets = opts.ageBuckets ?? DEFAULT_AGE_BUCKETS;\n\t\tthis.bucketSize = Math.ceil(this.maxAgeSeconds / this.ageBuckets);\n\t}\n\n\tobserve(value: number, labels?: Record<string, string>): void {\n\t\tthis.assertLabels(labels);\n\t\tconst k = this.key(labels);\n\t\tlet s = this.states.get(k);\n\t\tif (!s) {\n\t\t\ts = this.newState();\n\t\t\tthis.states.set(k, s);\n\t\t}\n\t\tthis.maybeRotate(s);\n\t\tconst b = s.buckets[s.currentBucket];\n\t\tb.values.push(value);\n\t\tb.sum += value;\n\t\tb.count++;\n\t}\n\n\tasync time<T>(\n\t\tfn: () => Promise<T> | T,\n\t\tlabels?: Record<string, string>,\n\t): Promise<T> {\n\t\tconst start = performance.now();\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tconst elapsedMs = performance.now() - start;\n\t\t\tthis.observe(elapsedMs / 1000, labels);\n\t\t}\n\t}\n\n\treset(): void {\n\t\tthis.states.clear();\n\t}\n\n\tgetSamples(): MetricSample[] {\n\t\tconst out: MetricSample[] = [];\n\t\tfor (const [k] of this.states) {\n\t\t\tout.push({ labels: this.parseKey(k), value: 0 });\n\t\t}\n\t\treturn out;\n\t}\n\n\t/** Render this summary in Prometheus exposition format. */\n\trenderPrometheus(): string {\n\t\tconst lines: string[] = [];\n\t\tif (this.help) lines.push(`# HELP ${this.name} ${this.help}`);\n\t\tlines.push(`# TYPE ${this.name} summary`);\n\n\t\tfor (const [k, s] of this.states) {\n\t\t\tconst labels = this.parseKey(k);\n\t\t\tthis.maybeRotate(s);\n\t\t\tconst totals = this.totals(s);\n\n\t\t\tfor (const q of this.percentiles) {\n\t\t\t\tconst qval = this.quantile(s, q);\n\t\t\t\tconst qstr = clampQuantileLabel(q);\n\t\t\t\t// The \"quantile\" label is always present, even if no\n\t\t\t\t// labelNames are declared. It's a special summary label.\n\t\t\t\tconst fullLabels: Record<string, string> = { ...labels, quantile: qstr };\n\t\t\t\tconst baseStr = renderLabels(this.labelNames, fullLabels);\n\t\t\t\tconst lbl = baseStr ? `${baseStr},quantile=\"${qstr}\"` : `quantile=\"${qstr}\"`;\n\t\t\t\tlines.push(`${this.name}{${lbl}} ${qval}`);\n\t\t\t}\n\t\t\t{\n\t\t\t\tconst lbl = renderLabels(this.labelNames, labels);\n\t\t\t\tlines.push(`${this.name}_sum${lbl ? `{${lbl}}` : \"\"} ${totals.sum}`);\n\t\t\t\tlines.push(`${this.name}_count${lbl ? `{${lbl}}` : \"\"} ${totals.count}`);\n\t\t\t}\n\t\t}\n\t\treturn lines.join(\"\\n\");\n\t}\n\n\t/* ----------------- internals ----------------- */\n\n\tprivate newState(): SummaryState {\n\t\tconst buckets: SummaryState[\"buckets\"] = [];\n\t\tfor (let i = 0; i < this.ageBuckets; i++) {\n\t\t\tbuckets.push({ values: [], sum: 0, count: 0 });\n\t\t}\n\t\treturn { buckets, currentBucket: 0, lastRotation: Date.now() };\n\t}\n\n\tprivate maybeRotate(s: SummaryState): void {\n\t\tconst now = Date.now();\n\t\tconst elapsed = (now - s.lastRotation) / 1000;\n\t\tif (elapsed >= this.bucketSize) {\n\t\t\tconst rotations = Math.floor(elapsed / this.bucketSize);\n\t\t\tfor (let i = 0; i < rotations; i++) {\n\t\t\t\ts.currentBucket = (s.currentBucket + 1) % this.ageBuckets;\n\t\t\t\ts.buckets[s.currentBucket] = { values: [], sum: 0, count: 0 };\n\t\t\t}\n\t\t\ts.lastRotation = now;\n\t\t}\n\t}\n\n\tprivate totals(s: SummaryState): { sum: number; count: number } {\n\t\tlet sum = 0;\n\t\tlet count = 0;\n\t\tfor (const b of s.buckets) {\n\t\t\tsum += b.sum;\n\t\t\tcount += b.count;\n\t\t}\n\t\treturn { sum, count };\n\t}\n\n\tprivate quantile(s: SummaryState, q: number): number {\n\t\t// Collect all values across buckets\n\t\tconst all: number[] = [];\n\t\tfor (const b of s.buckets) {\n\t\t\tfor (const v of b.values) all.push(v);\n\t\t}\n\t\tif (all.length === 0) return 0;\n\t\tall.sort((a, b) => a - b);\n\t\tconst idx = Math.min(all.length - 1, Math.max(0, Math.floor(q * all.length)));\n\t\treturn all[idx];\n\t}\n\n\tprivate assertLabels(labels?: Record<string, string>): void {\n\t\tif (!labels && this.labelNames.length === 0) return;\n\t\tif (this.labelNames.length === 0) {\n\t\t\tthrow new Error(`Summary ${this.name} has no labels declared`);\n\t\t}\n\t\tif (!labels) {\n\t\t\tthrow new Error(`Summary ${this.name} requires labels: ${this.labelNames.join(\", \")}`);\n\t\t}\n\t\tfor (const required of this.labelNames) {\n\t\t\tif (!(required in labels)) {\n\t\t\t\tthrow new Error(`Summary ${this.name} missing label \"${required}\"`);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate key(labels?: Record<string, string>): string {\n\t\tif (!labels) return \"\";\n\t\treturn this.labelNames.map((n) => `${n}=${escapeLabelValue(labels[n] ?? \"\")}`).join(\",\");\n\t}\n\n\tprivate parseKey(key: string): Record<string, string> {\n\t\tif (!key) return {};\n\t\tconst out: Record<string, string> = {};\n\t\tfor (const part of key.split(\",\")) {\n\t\t\tconst [k, ...rest] = part.split(\"=\");\n\t\t\tout[k] = rest.join(\"=\");\n\t\t}\n\t\treturn out;\n\t}\n}\n\nfunction clampQuantileLabel(q: number): string {\n\t// Use Number's default string representation, which handles JS\n\t// float precision better than toFixed(17). E.g. 0.9 -> \"0.9\",\n\t// 0.99 -> \"0.99\", 0.5 -> \"0.5\".\n\tconst s = String(q);\n\t// If JS gives us a long decimal like \"0.90000000000000002\",\n\t// round it to a reasonable precision.\n\tif (s.includes(\".\") && s.length > 8) {\n\t\treturn q.toFixed(6).replace(/0+$/, \"\").replace(/\\.$/, \"\");\n\t}\n\treturn s;\n}\n",
|
|
9
|
+
"/**\n * `MetricsRegistry` — collection of all metrics registered in the app.\n *\n * The registry is the source of truth for `MetricsService`. It:\n * - Holds every metric (counter / gauge / histogram / summary) by name.\n * - Serializes them to the Prometheus text exposition format.\n * - Supports content negotiation (OpenMetrics vs. Prometheus).\n *\n * Each metric is stored as a unified `RegisteredMetric` so the\n * registry can iterate and call `renderPrometheus()` on each.\n */\n\nimport { CounterImpl } from \"./counter.js\";\nimport { GaugeImpl } from \"./gauge.js\";\nimport { HistogramImpl } from \"./histogram.js\";\nimport { SummaryImpl } from \"./summary.js\";\nimport type {\n\tCounter,\n\tCounterOptions,\n\tExpositionFormat,\n\tExpositionResult,\n\tGauge,\n\tGaugeOptions,\n\tHistogram,\n\tHistogramOptions,\n\tSummary,\n\tSummaryOptions,\n} from \"./types.js\";\n\ninterface RegisteredMetric {\n\tname: string;\n\ttype: \"counter\" | \"gauge\" | \"histogram\" | \"summary\";\n\timpl: { renderPrometheus(): string; reset(): void };\n}\n\nexport class MetricsRegistry {\n\tprivate metrics = new Map<string, RegisteredMetric>();\n\tprivate globalLabels: Record<string, string> = {};\n\n\t/** Add a global label that's prepended to every metric line. */\n\tsetGlobalLabels(labels: Record<string, string>): void {\n\t\tthis.globalLabels = { ...labels };\n\t}\n\n\tgetGlobalLabels(): Record<string, string> {\n\t\treturn { ...this.globalLabels };\n\t}\n\n\t/** Register a counter; returns the same instance for chaining. */\n\tregisterCounter(opts: CounterOptions): Counter {\n\t\tif (this.metrics.has(opts.name)) {\n\t\t\tthrow new Error(`Metric ${opts.name} is already registered`);\n\t\t}\n\t\tconst c = new CounterImpl(opts);\n\t\tthis.metrics.set(opts.name, { name: opts.name, type: \"counter\", impl: c });\n\t\treturn c;\n\t}\n\n\tregisterGauge(opts: GaugeOptions): Gauge {\n\t\tif (this.metrics.has(opts.name)) {\n\t\t\tthrow new Error(`Metric ${opts.name} is already registered`);\n\t\t}\n\t\tconst g = new GaugeImpl(opts);\n\t\tthis.metrics.set(opts.name, { name: opts.name, type: \"gauge\", impl: g });\n\t\treturn g;\n\t}\n\n\tregisterHistogram(opts: HistogramOptions): Histogram {\n\t\tif (this.metrics.has(opts.name)) {\n\t\t\tthrow new Error(`Metric ${opts.name} is already registered`);\n\t\t}\n\t\tconst h = new HistogramImpl(opts);\n\t\tthis.metrics.set(opts.name, { name: opts.name, type: \"histogram\", impl: h });\n\t\treturn h;\n\t}\n\n\tregisterSummary(opts: SummaryOptions): Summary {\n\t\tif (this.metrics.has(opts.name)) {\n\t\t\tthrow new Error(`Metric ${opts.name} is already registered`);\n\t\t}\n\t\tconst s = new SummaryImpl(opts);\n\t\tthis.metrics.set(opts.name, { name: opts.name, type: \"summary\", impl: s });\n\t\treturn s;\n\t}\n\n\t/** Return a metric by name, regardless of type. */\n\tget(name: string): RegisteredMetric | undefined {\n\t\treturn this.metrics.get(name);\n\t}\n\n\t/** Return a counter by name. Throws if it isn't a counter. */\n\tgetCounter(name: string): Counter {\n\t\tconst m = this.metrics.get(name);\n\t\tif (!m) throw new Error(`Counter ${name} is not registered`);\n\t\tif (m.type !== \"counter\") throw new Error(`Metric ${name} is a ${m.type}, not a counter`);\n\t\treturn m.impl as unknown as Counter;\n\t}\n\n\t/** Return a gauge by name. */\n\tgetGauge(name: string): Gauge {\n\t\tconst m = this.metrics.get(name);\n\t\tif (!m) throw new Error(`Gauge ${name} is not registered`);\n\t\tif (m.type !== \"gauge\") throw new Error(`Metric ${name} is a ${m.type}, not a gauge`);\n\t\treturn m.impl as unknown as Gauge;\n\t}\n\n\t/** Return a histogram by name. */\n\tgetHistogram(name: string): Histogram {\n\t\tconst m = this.metrics.get(name);\n\t\tif (!m) throw new Error(`Histogram ${name} is not registered`);\n\t\tif (m.type !== \"histogram\") throw new Error(`Metric ${name} is a ${m.type}, not a histogram`);\n\t\treturn m.impl as unknown as Histogram;\n\t}\n\n\t/** Return a summary by name. */\n\tgetSummary(name: string): Summary {\n\t\tconst m = this.metrics.get(name);\n\t\tif (!m) throw new Error(`Summary ${name} is not registered`);\n\t\tif (m.type !== \"summary\") throw new Error(`Metric ${name} is a ${m.type}, not a summary`);\n\t\treturn m.impl as unknown as Summary;\n\t}\n\n\t/** Number of registered metrics. */\n\tget size(): number {\n\t\treturn this.metrics.size;\n\t}\n\n\t/** Names of all registered metrics, sorted. */\n\tnames(): string[] {\n\t\treturn [...this.metrics.keys()].sort();\n\t}\n\n\t/** Reset all metrics (clear values). */\n\tresetAll(): void {\n\t\tfor (const m of this.metrics.values()) {\n\t\t\tm.impl.reset();\n\t\t}\n\t}\n\n\t/** Serialize all metrics to the requested exposition format. */\n\texpose(format: ExpositionFormat = \"prometheus\"): ExpositionResult {\n\t\tconst sections: string[] = [];\n\t\tconst globalLabelsStr = renderGlobalLabels(this.globalLabels);\n\n\t\tfor (const m of this.metrics.values()) {\n\t\t\tconst out = m.impl.renderPrometheus();\n\t\t\tif (globalLabelsStr) {\n\t\t\t\tsections.push(applyGlobalLabels(out, this.globalLabels));\n\t\t\t} else {\n\t\t\t\tsections.push(out);\n\t\t\t}\n\t\t}\n\n\t\tconst body = sections.filter((s) => s.length > 0).join(\"\\n\\n\");\n\t\tconst contentType =\n\t\t\tformat === \"openmetrics\"\n\t\t\t\t? \"application/openmetrics-text; version=1.0.0; charset=utf-8\"\n\t\t\t\t: \"text/plain; version=0.0.4; charset=utf-8\";\n\t\treturn { body: body + (body.endsWith(\"\\n\") ? \"\" : \"\\n\"), contentType };\n\t}\n}\n\nfunction renderGlobalLabels(labels: Record<string, string>): string {\n\tconst keys = Object.keys(labels);\n\tif (keys.length === 0) return \"\";\n\treturn keys.map((k) => `${k}=\"${labels[k]}\"`).join(\",\");\n}\n\nfunction applyGlobalLabels(block: string, labels: Record<string, string>): string {\n\tconst keys = Object.keys(labels);\n\tif (keys.length === 0) return block;\n\tconst prefix = keys.map((k) => `${k}=\"${labels[k]}\"`).join(\",\");\n\treturn block\n\t\t.split(\"\\n\")\n\t\t.map((line) => {\n\t\t\tif (line.startsWith(\"#\")) return line;\n\t\t\tconst openBrace = line.indexOf(\"{\");\n\t\t\tif (openBrace === -1) {\n\t\t\t\tconst space = line.indexOf(\" \");\n\t\t\t\tif (space === -1) return line;\n\t\t\t\tconst metric = line.slice(0, space);\n\t\t\t\tconst rest = line.slice(space);\n\t\t\t\treturn `${metric}{${prefix}}${rest}`;\n\t\t\t}\n\t\t\tconst closeBrace = line.indexOf(\"}\", openBrace);\n\t\t\tif (closeBrace === -1) return line;\n\t\t\tconst existing = line.slice(openBrace + 1, closeBrace);\n\t\t\treturn `${line.slice(0, openBrace + 1)}${prefix},${existing}${line.slice(closeBrace)}`;\n\t\t})\n\t\t.join(\"\\n\");\n}\n",
|
|
10
|
+
"/**\n * `MetricsService` — DI-friendly facade over `MetricsRegistry`.\n *\n * The service is always available; you can call `counter()`,\n * `gauge()`, `histogram()`, `summary()` at any time. Registered\n * metrics live for the lifetime of the application.\n *\n * Default Node.js process metrics are registered when\n * `MetricsModule.forRoot({ enableDefaultMetrics: true })` is\n * called. Without `forRoot()`, the service is fully functional\n * but no default metrics are registered.\n */\n\nimport { MetricsRegistry } from \"./registry.js\";\nimport type {\n\tCounter,\n\tCounterOptions,\n\tExpositionFormat,\n\tExpositionResult,\n\tGauge,\n\tGaugeOptions,\n\tHistogram,\n\tHistogramOptions,\n\tSummary,\n\tSummaryOptions,\n} from \"./types.js\";\n\nexport const METRICS_SERVICE_TOKEN = Symbol.for(\"nexus:MetricsService\");\n\n/**\n * Global registry of the active `MetricsService` instance.\n * Set by `MetricsModule.forRoot()`. Read by `@Counted()` and\n * `@Timed()` decorators.\n */\nlet _current: MetricsService | undefined;\n\nexport function setMetricsService(service: MetricsService): void {\n\t_current = service;\n}\n\nexport function getMetricsService(): MetricsService | undefined {\n\treturn _current;\n}\n\nexport class MetricsService {\n\treadonly registry: MetricsRegistry = new MetricsRegistry();\n\n\t/* ---------------- factory methods ---------------- */\n\n\tcounter(opts: CounterOptions): Counter {\n\t\treturn this.registry.registerCounter(opts);\n\t}\n\n\tgauge(opts: GaugeOptions): Gauge {\n\t\treturn this.registry.registerGauge(opts);\n\t}\n\n\thistogram(opts: HistogramOptions): Histogram {\n\t\treturn this.registry.registerHistogram(opts);\n\t}\n\n\tsummary(opts: SummaryOptions): Summary {\n\t\treturn this.registry.registerSummary(opts);\n\t}\n\n\t/* ---------------- lookup ---------------- */\n\n\tgetCounter(name: string): Counter {\n\t\treturn this.registry.getCounter(name);\n\t}\n\n\t/**\n\t * Get an existing counter or create a new one with the given\n\t * label names. Used by the `@Counted()` decorator.\n\t */\n\tgetOrCreateCounter(name: string, help: string | undefined, labelNames: string[] | undefined): Counter {\n\t\ttry {\n\t\t\treturn this.registry.getCounter(name);\n\t\t} catch {\n\t\t\treturn this.registry.registerCounter({ name, help, labelNames });\n\t\t}\n\t}\n\n\t/**\n\t * Get an existing histogram or create a new one. Used by the\n\t * `@Timed()` decorator.\n\t */\n\tgetOrCreateHistogram(name: string, help: string | undefined, labelNames: string[] | undefined, buckets: number[] | undefined): Histogram {\n\t\ttry {\n\t\t\treturn this.registry.getHistogram(name);\n\t\t} catch {\n\t\t\treturn this.registry.registerHistogram({ name, help, labelNames, buckets });\n\t\t}\n\t}\n\n\tgetGauge(name: string): Gauge {\n\t\treturn this.registry.getGauge(name);\n\t}\n\n\tgetHistogram(name: string): Histogram {\n\t\treturn this.registry.getHistogram(name);\n\t}\n\n\tgetSummary(name: string): Summary {\n\t\treturn this.registry.getSummary(name);\n\t}\n\n\t/* ---------------- exposition ---------------- */\n\n\texpose(format: ExpositionFormat = \"prometheus\"): ExpositionResult {\n\t\treturn this.registry.expose(format);\n\t}\n\n\t/** Number of registered metrics. */\n\tget size(): number {\n\t\treturn this.registry.size;\n\t}\n}\n",
|
|
11
|
+
"/**\n * `MetricsModule` — wires up the metrics service into the DI\n * container, registers default Node.js process metrics, and mounts\n * the `GET /metrics` controller.\n *\n * Usage:\n * @Module({\n * imports: [\n * MetricsModule.forRoot({\n * enableDefaultMetrics: true,\n * path: \"/metrics\",\n * globalLabels: { service: \"my-app\" },\n * }),\n * ],\n * })\n * class AppModule {}\n *\n * Without `forRoot()`, `MetricsService` is still available (it's\n * a no-op-friendly singleton), but no metrics are pre-registered\n * and the controller is not mounted. Apps can register their own\n * metrics and mount the controller manually.\n */\n\nimport { Module } from \"@nexusts/core/decorators/module.js\";\nimport { MetricsService, METRICS_SERVICE_TOKEN } from \"./service.js\";\nimport { MetricsController } from \"./controller.js\";\nimport type { MetricsConfig } from \"./types.js\";\n\n@Module({\n\tproviders: [\n\t\tMetricsService,\n\t\t{ provide: METRICS_SERVICE_TOKEN, useExisting: MetricsService },\n\t],\n\texports: [MetricsService, METRICS_SERVICE_TOKEN],\n})\nexport class MetricsModule {\n\tstatic forRoot(config: MetricsConfig = {}) {\n\t\tconst fullConfig: Required<MetricsConfig> = {\n\t\t\tdefaultBuckets: config.defaultBuckets ?? [],\n\t\t\tdefaultPercentiles: config.defaultPercentiles ?? [],\n\t\t\tpath: config.path ?? \"/metrics\",\n\t\t\tenableDefaultMetrics: config.enableDefaultMetrics ?? true,\n\t\t\tmountController: config.mountController ?? true,\n\t\t\tglobalLabels: config.globalLabels ?? {},\n\t\t};\n\n\t\t@Module({\n\t\t\tproviders: [\n\t\t\t\tMetricsService,\n\t\t\t\t{ provide: METRICS_SERVICE_TOKEN, useExisting: MetricsService },\n\t\t\t\t{ provide: \"METRICS_CONFIG\", useValue: fullConfig },\n\t\t\t],\n\t\t\texports: [MetricsService, METRICS_SERVICE_TOKEN, \"METRICS_CONFIG\"],\n\t\t})\n\t\tclass ConfiguredMetricsModule {\n\t\t\tconstructor(private svc: MetricsService = new MetricsService()) {\n\t\t\t\tif (Object.keys(fullConfig.globalLabels).length > 0) {\n\t\t\t\t\tsvc.registry.setGlobalLabels(fullConfig.globalLabels);\n\t\t\t\t}\n\t\t\t\tif (fullConfig.enableDefaultMetrics) {\n\t\t\t\t\tregisterDefaultMetrics(svc);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tstatic get path(): string {\n\t\t\t\treturn fullConfig.path;\n\t\t\t}\n\n\t\t\tstatic get service(): MetricsService {\n\t\t\t\treturn new MetricsService();\n\t\t\t}\n\n\t\t\tstatic get controllerPath(): string {\n\t\t\t\treturn fullConfig.path;\n\t\t\t}\n\t\t}\n\t\tObject.defineProperty(ConfiguredMetricsModule, \"name\", {\n\t\t\tvalue: \"ConfiguredMetricsModule\",\n\t\t});\n\n\t\t// Attach helpers as static methods on the module class.\n\t\t(ConfiguredMetricsModule as unknown as { mount: (app: unknown, service: MetricsService) => void }).mount = (\n\t\t\tapp: unknown,\n\t\t\tservice: MetricsService,\n\t\t) => {\n\t\t\tMetricsController.mount(\n\t\t\t\tapp as { get: (path: string, ...handlers: unknown[]) => unknown },\n\t\t\t\tservice,\n\t\t\t\tfullConfig.path,\n\t\t\t);\n\t\t};\n\n\t\treturn ConfiguredMetricsModule as unknown as typeof MetricsModule & {\n\t\t\tmount: (app: unknown, service: MetricsService) => void;\n\t\t\tpath: string;\n\t\t\tcontrollerPath: string;\n\t\t\tservice: MetricsService;\n\t\t};\n\t}\n}\n\n/* ------------------------------------------------------------------ *\n * Default Node.js process metrics\n * ------------------------------------------------------------------ */\n\nfunction registerDefaultMetrics(service: MetricsService): void {\n\t// Process metrics\n\tconst processStartTime = service.gauge({\n\t\tname: \"process_start_time_seconds\",\n\t\thelp: \"Start time of the process since unix epoch in seconds.\",\n\t\tlabelNames: [],\n\t\tcollect: () => {\n\t\t\tprocessStartTime.set(Math.floor(Date.now() / 1000));\n\t\t},\n\t});\n\n\tconst processResidentMemory = service.gauge({\n\t\tname: \"process_resident_memory_bytes\",\n\t\thelp: \"Resident memory size in bytes.\",\n\t\tlabelNames: [],\n\t\tcollect: () => {\n\t\t\tconst mem = process.memoryUsage();\n\t\t\tprocessResidentMemory.set(mem.rss);\n\t\t},\n\t});\n\n\tconst processHeapUsed = service.gauge({\n\t\tname: \"nodejs_heap_size_used_bytes\",\n\t\thelp: \"Node.js heap size used in bytes.\",\n\t\tlabelNames: [],\n\t\tcollect: () => {\n\t\t\tconst mem = process.memoryUsage();\n\t\t\tprocessHeapUsed.set(mem.heapUsed);\n\t\t},\n\t});\n\n\tconst processHeapTotal = service.gauge({\n\t\tname: \"nodejs_heap_size_total_bytes\",\n\t\thelp: \"Node.js heap size total in bytes.\",\n\t\tlabelNames: [],\n\t\tcollect: () => {\n\t\t\tconst mem = process.memoryUsage();\n\t\t\tprocessHeapTotal.set(mem.heapTotal);\n\t\t},\n\t});\n\n\tconst processExternalMemory = service.gauge({\n\t\tname: \"nodejs_external_memory_bytes\",\n\t\thelp: \"Node.js external memory size in bytes.\",\n\t\tlabelNames: [],\n\t\tcollect: () => {\n\t\t\tconst mem = process.memoryUsage();\n\t\t\tprocessExternalMemory.set(mem.external);\n\t\t},\n\t});\n\n\tconst processCpuUser = service.gauge({\n\t\tname: \"process_cpu_user_seconds_total\",\n\t\thelp: \"Total user CPU time spent in seconds.\",\n\t\tlabelNames: [],\n\t\tcollect: () => {\n\t\t\tconst cpu = process.cpuUsage();\n\t\t\tprocessCpuUser.set(cpu.user / 1_000_000);\n\t\t},\n\t});\n\n\tconst processCpuSystem = service.gauge({\n\t\tname: \"process_cpu_system_seconds_total\",\n\t\thelp: \"Total system CPU time spent in seconds.\",\n\t\tlabelNames: [],\n\t\tcollect: () => {\n\t\t\tconst cpu = process.cpuUsage();\n\t\t\tprocessCpuSystem.set(cpu.system / 1_000_000);\n\t\t},\n\t});\n\n\tconst eventLoopLag = service.gauge({\n\t\tname: \"nodejs_eventloop_lag_seconds\",\n\t\thelp: \"Lag of the event loop in seconds.\",\n\t\tlabelNames: [],\n\t\tcollect: () => {\n\t\t\t// Sample event loop lag using a setImmediate.\n\t\t\tconst start = process.hrtime.bigint();\n\t\t\tsetImmediate(() => {\n\t\t\t\tconst lagNs = Number(process.hrtime.bigint() - start);\n\t\t\t\teventLoopLag.set(lagNs / 1e9);\n\t\t\t});\n\t\t},\n\t});\n\n\tconst processActiveHandles = service.gauge({\n\t\tname: \"nodejs_active_handles_total\",\n\t\thelp: \"Number of active handles.\",\n\t\tlabelNames: [],\n\t\tcollect: () => {\n\t\t\t// @ts-ignore - Bun may or may not expose this\n\t\t\tconst handles = (process as any)._getActiveHandles?.() ?? [];\n\t\t\tprocessActiveHandles.set(handles.length);\n\t\t},\n\t});\n\n\tconst processActiveRequests = service.gauge({\n\t\tname: \"nodejs_active_requests_total\",\n\t\thelp: \"Number of active requests.\",\n\t\tlabelNames: [],\n\t\tcollect: () => {\n\t\t\t// @ts-ignore - Bun may or may not expose this\n\t\t\tconst reqs = (process as any)._getActiveRequests?.() ?? [];\n\t\t\tprocessActiveRequests.set(reqs.length);\n\t\t},\n\t});\n\n\t// Touch all gauges to make sure they're eagerly evaluated once\n\tvoid processStartTime;\n\tvoid processResidentMemory;\n\tvoid processHeapUsed;\n\tvoid processHeapTotal;\n\tvoid processExternalMemory;\n\tvoid processCpuUser;\n\tvoid processCpuSystem;\n\tvoid eventLoopLag;\n\tvoid processActiveHandles;\n\tvoid processActiveRequests;\n}\n",
|
|
12
|
+
"/**\n * `MetricsController` — exposes `GET /metrics` for Prometheus\n * scrapers. Supports content negotiation: the response format is\n * `text/plain` (Prometheus 0.0.4) by default, or\n * `application/openmetrics-text` if the request asks for it.\n *\n * The controller is auto-mounted by `MetricsModule.forRoot()`. If\n * you want to mount it manually (e.g. on a different path), call\n * `MetricsController.handler(service)` and wire it up yourself.\n */\n\nimport type { Context } from \"hono\";\nimport type { MetricsService } from \"./service.js\";\n\nexport class MetricsController {\n\tstatic readonly PATH = \"/metrics\";\n\n\t/**\n\t * Returns a Hono handler that serves the /metrics endpoint.\n\t *\n\t * @example\n\t * app.get(\"/metrics\", MetricsController.handler(svc));\n\t */\n\tstatic handler(service: MetricsService) {\n\t\treturn (c: Context) => {\n\t\t\tconst accept = c.req.header(\"accept\") ?? \"\";\n\t\t\tconst format = accept.includes(\"application/openmetrics-text\") ? \"openmetrics\" : \"prometheus\";\n\t\t\tconst { contentType, body } = service.expose(format);\n\t\t\treturn c.body(body, 200, {\n\t\t\t\t\"content-type\": contentType,\n\t\t\t\t\"cache-control\": \"no-store\",\n\t\t\t});\n\t\t};\n\t}\n\n\t/** Mount the controller on the given Hono app at `path`. */\n\tstatic mount(app: { get: (path: string, ...handlers: unknown[]) => unknown }, service: MetricsService, path: string = MetricsController.PATH): void {\n\t\tapp.get(path, MetricsController.handler(service));\n\t}\n}",
|
|
13
|
+
"/**\n * `@Counted()` — class-method decorator that increments a counter\n * on each call.\n *\n * Usage:\n * class UserController {\n * @Counted('http_requests_total', { labels: () => ({ method: 'GET' }) })\n * list() { ... }\n * }\n *\n * The decorator reads the `MetricsService` from the global registry\n * (set by `MetricsModule.forRoot()`). When no service is registered\n * the decorator is a pass-through.\n */\n\nimport { getMetricsService } from \"../service.js\";\n\nexport interface CountedOptions {\n\t/** Optional label values, computed at call time. */\n\tlabels?: () => Record<string, string>;\n}\n\n/**\n * Method decorator: increment a counter on each invocation.\n *\n * The counter is registered lazily the first time the method is\n * called, using the service from the global registry.\n */\nexport function Counted(metricName: string, options: CountedOptions = {}) {\n\treturn function (_target: object, _propertyKey: string | symbol, descriptor: PropertyDescriptor) {\n\t\tconst original = descriptor.value as (...args: unknown[]) => unknown;\n\t\tif (typeof original !== \"function\") {\n\t\t\tthrow new Error(`@Counted() can only be applied to methods, got ${typeof original}`);\n\t\t}\n\n\t\tconst isAsync = (original as any).constructor?.name === \"AsyncFunction\";\n\n\t\tif (isAsync) {\n\t\t\tdescriptor.value = async function wrapped(this: unknown, ...args: unknown[]) {\n\t\t\t\tconst svc = getMetricsService();\n\t\t\t\tif (svc) {\n\t\t\t\t\tconst labels = options.labels?.();\n\t\t\t\t\tsvc.getOrCreateCounter(\n\t\t\t\t\t\tmetricName,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\tlabels ? Object.keys(labels) : undefined,\n\t\t\t\t\t).inc(labels);\n\t\t\t\t}\n\t\t\t\treturn original.apply(this, args);\n\t\t\t};\n\t\t} else {\n\t\t\tdescriptor.value = function wrapped(this: unknown, ...args: unknown[]) {\n\t\t\t\tconst svc = getMetricsService();\n\t\t\t\tif (svc) {\n\t\t\t\t\tconst labels = options.labels?.();\n\t\t\t\t\tsvc.getOrCreateCounter(\n\t\t\t\t\t\tmetricName,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\tlabels ? Object.keys(labels) : undefined,\n\t\t\t\t\t).inc(labels);\n\t\t\t\t}\n\t\t\t\treturn original.apply(this, args);\n\t\t\t};\n\t\t}\n\t\treturn descriptor;\n\t};\n}",
|
|
14
|
+
"/**\n * `@Timed()` — class-method decorator that records a method's\n * duration in a histogram.\n *\n * Usage:\n * class UserController {\n * @Timed('http_request_duration_seconds', { labels: () => ({ method: 'GET' }) })\n * list() { ... }\n * }\n *\n * The decorator reads the `MetricsService` from the global registry.\n * When no service is registered the decorator is a pass-through.\n */\n\nimport { getMetricsService } from \"../service.js\";\n\nexport interface TimedOptions {\n\t/** Optional buckets override. */\n\tbuckets?: number[];\n\t/** Optional label values, computed at call time. */\n\tlabels?: () => Record<string, string>;\n}\n\n/**\n * Method decorator: observe a method's duration (in seconds) in a\n * histogram. Async methods are timed across their full await.\n */\nexport function Timed(metricName: string, options: TimedOptions = {}) {\n\treturn function (_target: object, _propertyKey: string | symbol, descriptor: PropertyDescriptor) {\n\t\tconst original = descriptor.value as (...args: unknown[]) => unknown;\n\t\tif (typeof original !== \"function\") {\n\t\t\tthrow new Error(`@Timed() can only be applied to methods, got ${typeof original}`);\n\t\t}\n\n\t\tconst isAsync = (original as any).constructor?.name === \"AsyncFunction\";\n\n\t\tif (isAsync) {\n\t\t\tdescriptor.value = async function wrapped(this: unknown, ...args: unknown[]) {\n\t\t\t\tconst svc = getMetricsService();\n\t\t\t\tconst start = performance.now();\n\t\t\t\ttry {\n\t\t\t\t\treturn await original.apply(this, args);\n\t\t\t\t} finally {\n\t\t\t\t\tif (svc) {\n\t\t\t\t\t\tconst elapsedMs = performance.now() - start;\n\t\t\t\t\t\tconst labels = options.labels?.();\n\t\t\t\t\t\tsvc.getOrCreateHistogram(\n\t\t\t\t\t\t\tmetricName,\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\tlabels ? Object.keys(labels) : undefined,\n\t\t\t\t\t\t\toptions.buckets,\n\t\t\t\t\t\t).observe(elapsedMs / 1000, labels);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t} else {\n\t\t\tdescriptor.value = function wrapped(this: unknown, ...args: unknown[]) {\n\t\t\t\tconst svc = getMetricsService();\n\t\t\t\tconst start = performance.now();\n\t\t\t\ttry {\n\t\t\t\t\treturn original.apply(this, args);\n\t\t\t\t} finally {\n\t\t\t\t\tif (svc) {\n\t\t\t\t\t\tconst elapsedMs = performance.now() - start;\n\t\t\t\t\t\tconst labels = options.labels?.();\n\t\t\t\t\t\tsvc.getOrCreateHistogram(\n\t\t\t\t\t\t\tmetricName,\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\tlabels ? Object.keys(labels) : undefined,\n\t\t\t\t\t\t\toptions.buckets,\n\t\t\t\t\t\t).observe(elapsedMs / 1000, labels);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\treturn descriptor;\n\t};\n}\n"
|
|
15
|
+
],
|
|
16
|
+
"mappings": "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",
|
|
17
|
+
"debugId": "FBFB8DC36D7055E964756E2164756E21",
|
|
18
|
+
"names": []
|
|
19
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nexusts/metrics",
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "Prometheus / OpenMetrics counters and gauges",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "bun run ../../build.ts"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"nexusts",
|
|
24
|
+
"framework",
|
|
25
|
+
"bun"
|
|
26
|
+
],
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@nexusts/core": "^0.7.0"
|
|
30
|
+
}
|
|
31
|
+
}
|