@maroonedsoftware/telemetry 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/AGENTS.md +179 -0
  2. package/LICENSE +21 -0
  3. package/README.md +124 -2
  4. package/dist/chunk-7QVYU63E.js +7 -0
  5. package/dist/chunk-7QVYU63E.js.map +1 -0
  6. package/dist/chunk-E7OXUL3D.js +25 -0
  7. package/dist/chunk-E7OXUL3D.js.map +1 -0
  8. package/dist/finished.span.d.ts +44 -0
  9. package/dist/finished.span.d.ts.map +1 -0
  10. package/dist/index.d.ts +8 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +426 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/kysely.d.ts +2 -0
  15. package/dist/kysely.d.ts.map +1 -0
  16. package/dist/kysely.js +237 -0
  17. package/dist/kysely.js.map +1 -0
  18. package/dist/metric.aggregate.d.ts +41 -0
  19. package/dist/metric.aggregate.d.ts.map +1 -0
  20. package/dist/metric.bucket.d.ts +25 -0
  21. package/dist/metric.bucket.d.ts.map +1 -0
  22. package/dist/metric.labels.d.ts +18 -0
  23. package/dist/metric.labels.d.ts.map +1 -0
  24. package/dist/metric.resolution.d.ts +19 -0
  25. package/dist/metric.resolution.d.ts.map +1 -0
  26. package/dist/metrics.recorder.d.ts +96 -0
  27. package/dist/metrics.recorder.d.ts.map +1 -0
  28. package/dist/metrics.store.d.ts +143 -0
  29. package/dist/metrics.store.d.ts.map +1 -0
  30. package/dist/otlp.d.ts +4 -0
  31. package/dist/otlp.d.ts.map +1 -0
  32. package/dist/otlp.export.d.ts +8 -0
  33. package/dist/otlp.export.d.ts.map +1 -0
  34. package/dist/otlp.ids.d.ts +16 -0
  35. package/dist/otlp.ids.d.ts.map +1 -0
  36. package/dist/otlp.js +237 -0
  37. package/dist/otlp.js.map +1 -0
  38. package/dist/otlp.metrics.bridge.d.ts +64 -0
  39. package/dist/otlp.metrics.bridge.d.ts.map +1 -0
  40. package/dist/otlp.metrics.receiver.d.ts +79 -0
  41. package/dist/otlp.metrics.receiver.d.ts.map +1 -0
  42. package/dist/otlp.span.bridge.d.ts +44 -0
  43. package/dist/otlp.span.bridge.d.ts.map +1 -0
  44. package/package.json +103 -3
package/dist/index.js ADDED
@@ -0,0 +1,426 @@
1
+ import {
2
+ aggregateOf,
3
+ mergeAggregates
4
+ } from "./chunk-E7OXUL3D.js";
5
+ import {
6
+ __name
7
+ } from "./chunk-7QVYU63E.js";
8
+
9
+ // src/metric.bucket.ts
10
+ var minuteOf = /* @__PURE__ */ __name((at) => at.toUTC().startOf("minute"), "minuteOf");
11
+
12
+ // src/metric.labels.ts
13
+ var labelsKey = /* @__PURE__ */ __name((labels) => {
14
+ const sorted = {};
15
+ for (const key of Object.keys(labels).sort()) {
16
+ const value = labels[key];
17
+ if (value !== void 0) {
18
+ sorted[key] = value;
19
+ }
20
+ }
21
+ return JSON.stringify(sorted);
22
+ }, "labelsKey");
23
+
24
+ // src/metric.resolution.ts
25
+ import { Duration } from "luxon";
26
+ var MINUTE_RESOLUTION_LIMIT = Duration.fromObject({
27
+ hours: 48
28
+ });
29
+ var resolutionFor = /* @__PURE__ */ __name((from, to, limit = MINUTE_RESOLUTION_LIMIT) => to.diff(from) <= limit ? "minute" : "hour", "resolutionFor");
30
+
31
+ // src/metrics.recorder.ts
32
+ import { Injectable } from "injectkit";
33
+ import { DateTime, Duration as Duration2 } from "luxon";
34
+ function _ts_decorate(decorators, target, key, desc) {
35
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
36
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
37
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
38
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
39
+ }
40
+ __name(_ts_decorate, "_ts_decorate");
41
+ var MetricsRecorder = class {
42
+ static {
43
+ __name(this, "MetricsRecorder");
44
+ }
45
+ open = /* @__PURE__ */ new Map();
46
+ gauges = /* @__PURE__ */ new Map();
47
+ /**
48
+ * Told about a gauge whose reader threw. Unset, such failures are silent: a gauge that cannot be
49
+ * read simply leaves a gap, which is what a chart should show.
50
+ */
51
+ onGaugeError;
52
+ /**
53
+ * Records one observation into the minute `at` falls in.
54
+ *
55
+ * Non-finite values (`NaN`, `Infinity`) are dropped: one of them would poison every aggregate
56
+ * of the bucket it lands in.
57
+ *
58
+ * @param name - The metric's name.
59
+ * @param value - The observed value.
60
+ * @param labels - The series' labels.
61
+ * @param at - When it was observed. Defaults to now.
62
+ */
63
+ observe(name, value, labels = {}, at = DateTime.now()) {
64
+ if (!Number.isFinite(value)) {
65
+ return;
66
+ }
67
+ const start = minuteOf(at);
68
+ const key = labelsKey(labels);
69
+ const id = `${name}\0${key}\0${start.toMillis()}`;
70
+ const existing = this.open.get(id);
71
+ if (existing === void 0) {
72
+ this.open.set(id, {
73
+ name,
74
+ labels,
75
+ labelsKey: key,
76
+ start,
77
+ aggregate: aggregateOf(value)
78
+ });
79
+ } else {
80
+ existing.aggregate = mergeAggregates(existing.aggregate, aggregateOf(value));
81
+ }
82
+ }
83
+ /**
84
+ * Registers a gauge to be sampled by {@link sampleGauges}.
85
+ *
86
+ * Registering the same name and labels again replaces the earlier reader.
87
+ *
88
+ * @param name - The metric's name.
89
+ * @param read - Returns the current value.
90
+ * @param options - Labels and sampling cadence.
91
+ * @returns A function that unregisters this gauge.
92
+ */
93
+ gauge(name, read, options = {}) {
94
+ const labels = options.labels ?? {};
95
+ const id = `${name}\0${labelsKey(labels)}`;
96
+ const registered = {
97
+ name,
98
+ labels,
99
+ read,
100
+ every: options.every ?? Duration2.fromMillis(0)
101
+ };
102
+ this.gauges.set(id, registered);
103
+ return () => {
104
+ if (this.gauges.get(id) === registered) {
105
+ this.gauges.delete(id);
106
+ }
107
+ };
108
+ }
109
+ /**
110
+ * Reads every gauge that is due and observes its value at `now`.
111
+ *
112
+ * Readers run concurrently, so one slow gauge does not delay the others. A reader that throws
113
+ * or answers `undefined` records nothing; it is still marked sampled, so a failing reader is
114
+ * retried at its own cadence rather than on every call.
115
+ *
116
+ * @param now - The sampling instant. Defaults to now.
117
+ */
118
+ async sampleGauges(now = DateTime.now()) {
119
+ const due = [
120
+ ...this.gauges.values()
121
+ ].filter((gauge) => gauge.sampledAt === void 0 || gauge.sampledAt.plus(gauge.every) <= now);
122
+ await Promise.all(due.map(async (gauge) => {
123
+ gauge.sampledAt = now;
124
+ try {
125
+ const value = await gauge.read();
126
+ if (value !== void 0) {
127
+ this.observe(gauge.name, value, gauge.labels, now);
128
+ }
129
+ } catch (error) {
130
+ this.onGaugeError?.(gauge.name, error);
131
+ }
132
+ }));
133
+ }
134
+ /**
135
+ * Hands over every bucket recorded since the last drain and empties the buffer.
136
+ *
137
+ * @returns The buckets, oldest minute first.
138
+ */
139
+ drain() {
140
+ const buckets = [
141
+ ...this.open.values()
142
+ ];
143
+ this.open = /* @__PURE__ */ new Map();
144
+ return buckets.sort((a, b) => a.start.toMillis() - b.start.toMillis());
145
+ }
146
+ /** How many buckets are waiting to be drained. */
147
+ get pending() {
148
+ return this.open.size;
149
+ }
150
+ };
151
+ MetricsRecorder = _ts_decorate([
152
+ Injectable()
153
+ ], MetricsRecorder);
154
+
155
+ // src/otlp.metrics.receiver.ts
156
+ import { DateTime as DateTime2 } from "luxon";
157
+ var OtlpPayloadError = class extends Error {
158
+ static {
159
+ __name(this, "OtlpPayloadError");
160
+ }
161
+ constructor(message) {
162
+ super(message);
163
+ this.name = "OtlpPayloadError";
164
+ }
165
+ };
166
+ var DELTA = 1;
167
+ var CUMULATIVE = 2;
168
+ var isObject = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isObject");
169
+ var arrayOf = /* @__PURE__ */ __name((value) => Array.isArray(value) ? value : [], "arrayOf");
170
+ var numberOf = /* @__PURE__ */ __name((value) => {
171
+ if (typeof value === "number") {
172
+ return value;
173
+ }
174
+ if (typeof value === "string" && value.trim() !== "") {
175
+ const parsed = Number(value);
176
+ return Number.isFinite(parsed) ? parsed : void 0;
177
+ }
178
+ return void 0;
179
+ }, "numberOf");
180
+ var temporalityOf = /* @__PURE__ */ __name((value) => {
181
+ if (value === DELTA || value === "AGGREGATION_TEMPORALITY_DELTA") {
182
+ return DELTA;
183
+ }
184
+ if (value === CUMULATIVE || value === "AGGREGATION_TEMPORALITY_CUMULATIVE") {
185
+ return CUMULATIVE;
186
+ }
187
+ return void 0;
188
+ }, "temporalityOf");
189
+ var attributeValueOf = /* @__PURE__ */ __name((value) => {
190
+ if (!isObject(value)) {
191
+ return void 0;
192
+ }
193
+ if (typeof value.stringValue === "string") {
194
+ return value.stringValue;
195
+ }
196
+ if (typeof value.boolValue === "boolean") {
197
+ return String(value.boolValue);
198
+ }
199
+ const numeric = numberOf(value.intValue) ?? numberOf(value.doubleValue);
200
+ if (numeric !== void 0) {
201
+ return String(numeric);
202
+ }
203
+ if (value.arrayValue !== void 0 || value.kvlistValue !== void 0) {
204
+ return JSON.stringify(value.arrayValue ?? value.kvlistValue);
205
+ }
206
+ return void 0;
207
+ }, "attributeValueOf");
208
+ var labelsOf = /* @__PURE__ */ __name((attributes) => {
209
+ const labels = {};
210
+ for (const attribute of arrayOf(attributes)) {
211
+ if (isObject(attribute) && typeof attribute.key === "string") {
212
+ const value = attributeValueOf(attribute.value);
213
+ if (value !== void 0) {
214
+ labels[attribute.key] = value;
215
+ }
216
+ }
217
+ }
218
+ return labels;
219
+ }, "labelsOf");
220
+ var instantOf = /* @__PURE__ */ __name((nanos, fallback) => {
221
+ if (typeof nanos !== "string" && typeof nanos !== "number") {
222
+ return fallback;
223
+ }
224
+ try {
225
+ const millis = Number(BigInt(nanos) / 1000000n);
226
+ return millis > 0 ? DateTime2.fromMillis(millis, {
227
+ zone: "utc"
228
+ }) : fallback;
229
+ } catch {
230
+ return fallback;
231
+ }
232
+ }, "instantOf");
233
+ var OtlpMetricsReceiver = class {
234
+ static {
235
+ __name(this, "OtlpMetricsReceiver");
236
+ }
237
+ maxSeriesPerName;
238
+ maxNames;
239
+ series = /* @__PURE__ */ new Map();
240
+ cumulative = /* @__PURE__ */ new Map();
241
+ /**
242
+ * @param options - The series and name caps.
243
+ */
244
+ constructor(options = {}) {
245
+ this.maxSeriesPerName = options.maxSeriesPerName ?? 50;
246
+ this.maxNames = options.maxNames ?? 500;
247
+ }
248
+ /**
249
+ * Reads one export request.
250
+ *
251
+ * @param body - The parsed JSON body.
252
+ * @param now - Used for points with no timestamp. Defaults to now.
253
+ * @throws {@link OtlpPayloadError} when the body has no `resourceMetrics` array.
254
+ */
255
+ receive(body, now = DateTime2.now()) {
256
+ if (!isObject(body) || !Array.isArray(body.resourceMetrics)) {
257
+ throw new OtlpPayloadError("Expected an OTLP metrics export with a resourceMetrics array");
258
+ }
259
+ const buckets = /* @__PURE__ */ new Map();
260
+ const result = {
261
+ buckets: [],
262
+ dropped: 0,
263
+ unsupported: 0
264
+ };
265
+ const add = /* @__PURE__ */ __name((name, labels, at, aggregate) => {
266
+ const key = labelsKey(labels);
267
+ const start = minuteOf(at);
268
+ const id = `${name}\0${key}\0${start.toMillis()}`;
269
+ const existing = buckets.get(id);
270
+ buckets.set(id, existing === void 0 ? {
271
+ name,
272
+ labels,
273
+ labelsKey: key,
274
+ start,
275
+ aggregate
276
+ } : {
277
+ ...existing,
278
+ aggregate: mergeAggregates(existing.aggregate, aggregate)
279
+ });
280
+ }, "add");
281
+ for (const resourceMetrics of body.resourceMetrics) {
282
+ if (!isObject(resourceMetrics)) {
283
+ continue;
284
+ }
285
+ const resource = labelsOf(isObject(resourceMetrics.resource) ? resourceMetrics.resource.attributes : void 0);
286
+ const base = resource["service.name"] === void 0 ? {} : {
287
+ "service.name": resource["service.name"]
288
+ };
289
+ for (const scopeMetrics of arrayOf(resourceMetrics.scopeMetrics)) {
290
+ for (const metric of isObject(scopeMetrics) ? arrayOf(scopeMetrics.metrics) : []) {
291
+ if (!isObject(metric) || typeof metric.name !== "string" || metric.name === "") {
292
+ result.unsupported += 1;
293
+ continue;
294
+ }
295
+ const name = metric.name;
296
+ const shape = isObject(metric.gauge) ? metric.gauge : isObject(metric.sum) ? metric.sum : isObject(metric.histogram) ? metric.histogram : void 0;
297
+ if (shape === void 0) {
298
+ const other = isObject(metric.exponentialHistogram) ? metric.exponentialHistogram : isObject(metric.summary) ? metric.summary : void 0;
299
+ result.unsupported += Math.max(1, arrayOf(other?.dataPoints).length);
300
+ continue;
301
+ }
302
+ const temporality = temporalityOf(shape.aggregationTemporality);
303
+ const monotonic = shape === metric.sum && shape.isMonotonic === true;
304
+ for (const point of arrayOf(shape.dataPoints)) {
305
+ if (!isObject(point)) {
306
+ result.unsupported += 1;
307
+ continue;
308
+ }
309
+ const labels = {
310
+ ...base,
311
+ ...labelsOf(point.attributes)
312
+ };
313
+ if (!this.admit(name, labelsKey(labels))) {
314
+ result.dropped += 1;
315
+ continue;
316
+ }
317
+ const at = instantOf(point.timeUnixNano, now);
318
+ const seriesId = `${name}\0${labelsKey(labels)}`;
319
+ const start = String(point.startTimeUnixNano ?? "");
320
+ if (shape === metric.histogram) {
321
+ const count = numberOf(point.count);
322
+ const sum = numberOf(point.sum);
323
+ if (count === void 0 || sum === void 0) {
324
+ result.unsupported += 1;
325
+ continue;
326
+ }
327
+ if (temporality === CUMULATIVE) {
328
+ const increase = this.increase(seriesId, start, sum, count);
329
+ if (increase !== void 0 && increase.count > 0) {
330
+ const mean = increase.sum / increase.count;
331
+ add(name, labels, at, {
332
+ count: increase.count,
333
+ sum: increase.sum,
334
+ min: mean,
335
+ max: mean,
336
+ last: mean
337
+ });
338
+ }
339
+ } else if (count > 0) {
340
+ const mean = sum / count;
341
+ add(name, labels, at, {
342
+ count,
343
+ sum,
344
+ min: numberOf(point.min) ?? mean,
345
+ max: numberOf(point.max) ?? mean,
346
+ last: mean
347
+ });
348
+ }
349
+ continue;
350
+ }
351
+ const value = numberOf(point.asDouble) ?? numberOf(point.asInt);
352
+ if (value === void 0) {
353
+ result.unsupported += 1;
354
+ continue;
355
+ }
356
+ if (monotonic && temporality === CUMULATIVE) {
357
+ const increase = this.increase(seriesId, start, value);
358
+ if (increase !== void 0) {
359
+ add(name, labels, at, aggregateOf(increase.sum));
360
+ }
361
+ } else {
362
+ add(name, labels, at, aggregateOf(value));
363
+ }
364
+ }
365
+ }
366
+ }
367
+ }
368
+ result.buckets = [
369
+ ...buckets.values()
370
+ ].sort((a, b) => a.start.toMillis() - b.start.toMillis());
371
+ return result;
372
+ }
373
+ /** Whether a series may be kept, registering it if it is new and there is room. */
374
+ admit(name, key) {
375
+ let seen = this.series.get(name);
376
+ if (seen === void 0) {
377
+ if (this.series.size >= this.maxNames) {
378
+ return false;
379
+ }
380
+ seen = /* @__PURE__ */ new Set();
381
+ this.series.set(name, seen);
382
+ }
383
+ if (seen.has(key)) {
384
+ return true;
385
+ }
386
+ if (seen.size >= this.maxSeriesPerName) {
387
+ return false;
388
+ }
389
+ seen.add(key);
390
+ return true;
391
+ }
392
+ /**
393
+ * The increase of a cumulative series since its previous point, or `undefined` when this point only
394
+ * sets the baseline (first sight, a new start time, or a total that went backwards).
395
+ *
396
+ * @param sum - The point's running total.
397
+ * @param count - The point's running observation count, for a histogram; zero for a sum.
398
+ */
399
+ increase(seriesId, start, sum, count = 0) {
400
+ const previous = this.cumulative.get(seriesId);
401
+ this.cumulative.set(seriesId, {
402
+ start,
403
+ count,
404
+ sum
405
+ });
406
+ if (previous === void 0 || previous.start !== start || sum < previous.sum || count < previous.count) {
407
+ return void 0;
408
+ }
409
+ return {
410
+ count: count - previous.count,
411
+ sum: sum - previous.sum
412
+ };
413
+ }
414
+ };
415
+ export {
416
+ MINUTE_RESOLUTION_LIMIT,
417
+ MetricsRecorder,
418
+ OtlpMetricsReceiver,
419
+ OtlpPayloadError,
420
+ aggregateOf,
421
+ labelsKey,
422
+ mergeAggregates,
423
+ minuteOf,
424
+ resolutionFor
425
+ };
426
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/metric.bucket.ts","../src/metric.labels.ts","../src/metric.resolution.ts","../src/metrics.recorder.ts","../src/otlp.metrics.receiver.ts"],"sourcesContent":["import { DateTime } from 'luxon';\nimport { MetricAggregate } from './metric.aggregate.js';\nimport { MetricLabels } from './metric.labels.js';\n\n/**\n * One series' aggregate over one minute: what a recorder drains and what a store writes.\n */\nexport interface MetricBucket {\n /** The metric's name, in dot notation: `work.job.ms`. */\n name: string;\n /** The series' labels. */\n labels: MetricLabels;\n /** {@link labelsKey} of `labels`, computed once at observation time. */\n labelsKey: string;\n /** The start of the bucket's minute, in UTC. */\n start: DateTime;\n /** Everything observed for this series in this minute. */\n aggregate: MetricAggregate;\n}\n\n/**\n * The start of the minute `at` falls in, in UTC.\n *\n * @param at - Any instant.\n */\nexport const minuteOf = (at: DateTime): DateTime => at.toUTC().startOf('minute');\n","/**\n * The labels that tell one series of a metric from another, e.g. `{ queue: 'catalog.sync' }`.\n *\n * Values are strings only. A number used as a label is almost always a cardinality mistake waiting\n * to happen, and making the caller write `String(x)` is a moment to notice it.\n */\nexport type MetricLabels = Readonly<Record<string, string>>;\n\n/**\n * A canonical string for a label set: JSON with keys sorted.\n *\n * Two label sets with the same pairs in a different order produce the same key, so the key can sit\n * in a primary key where the `jsonb` labels themselves cannot be compared cheaply.\n *\n * @param labels - The label set.\n * @returns `'{}'` for no labels, otherwise sorted JSON.\n */\nexport const labelsKey = (labels: MetricLabels): string => {\n const sorted: Record<string, string> = {};\n for (const key of Object.keys(labels).sort()) {\n const value = labels[key];\n if (value !== undefined) {\n sorted[key] = value;\n }\n }\n return JSON.stringify(sorted);\n};\n","import { DateTime, Duration } from 'luxon';\n\n/**\n * The two widths a stored metric row can have: raw minutes, and the hours they are rolled up into.\n */\nexport type MetricResolution = 'minute' | 'hour';\n\n/**\n * The widest range still read at minute resolution by {@link resolutionFor}: two days is 2,880\n * points per series, about as many as a chart can draw usefully.\n */\nexport const MINUTE_RESOLUTION_LIMIT = Duration.fromObject({ hours: 48 });\n\n/**\n * The resolution to read a range at: minutes for a range up to `limit`, hours beyond it.\n *\n * @param from - The start of the range.\n * @param to - The end of the range.\n * @param limit - The widest range read in minutes. Defaults to {@link MINUTE_RESOLUTION_LIMIT}.\n */\nexport const resolutionFor = (from: DateTime, to: DateTime, limit: Duration = MINUTE_RESOLUTION_LIMIT): MetricResolution =>\n to.diff(from) <= limit ? 'minute' : 'hour';\n","import { Injectable } from 'injectkit';\nimport { DateTime, Duration } from 'luxon';\nimport { aggregateOf, mergeAggregates } from './metric.aggregate.js';\nimport { MetricBucket, minuteOf } from './metric.bucket.js';\nimport { labelsKey, MetricLabels } from './metric.labels.js';\n\n/**\n * Reads a gauge's current value. `undefined` means \"no reading this time\" and records nothing.\n */\nexport type GaugeReader = () => number | undefined | Promise<number | undefined>;\n\n/**\n * Called when a gauge's reader throws. The sample is skipped either way.\n */\nexport type GaugeErrorHandler = (name: string, error: unknown) => void;\n\n/**\n * How a gauge is registered.\n */\nexport interface GaugeOptions {\n /** Labels for the gauge's series. Defaults to none. */\n labels?: MetricLabels;\n /**\n * The least time between two samples. Defaults to zero: sampled on every\n * {@link MetricsRecorder.sampleGauges} call. Use it for a reading that is expensive (a disk scan)\n * while the sampler runs every minute for the cheap ones.\n */\n every?: Duration;\n}\n\ninterface RegisteredGauge {\n name: string;\n labels: MetricLabels;\n read: GaugeReader;\n every: Duration;\n sampledAt?: DateTime;\n}\n\n/**\n * Accumulates observations into one-minute buckets in memory, and samples registered gauges.\n *\n * Nothing here does I/O. The owner runs a loop that calls {@link sampleGauges} and then\n * {@link drain}, and writes what it drained wherever it keeps metrics (the `/kysely` store, an OTLP\n * bridge, both). Draining hands over the current, still-open minute too: a store merges a later\n * flush of the same minute into the row already written, so nothing waits for a minute to close.\n *\n * Register it as a singleton. One instance per process is the point: every producer observes into\n * the same buffer.\n *\n * @example\n * ```typescript\n * const recorder = container.get(MetricsRecorder);\n * recorder.observe('work.job.ms', 412, { queue: 'catalog.sync' });\n * recorder.gauge('station.listeners', () => audience.count());\n *\n * await recorder.sampleGauges();\n * await store.upsertMinutes(recorder.drain());\n * ```\n */\n@Injectable()\nexport class MetricsRecorder {\n private open = new Map<string, MetricBucket>();\n private readonly gauges = new Map<string, RegisteredGauge>();\n\n /**\n * Told about a gauge whose reader threw. Unset, such failures are silent: a gauge that cannot be\n * read simply leaves a gap, which is what a chart should show.\n */\n onGaugeError?: GaugeErrorHandler;\n\n /**\n * Records one observation into the minute `at` falls in.\n *\n * Non-finite values (`NaN`, `Infinity`) are dropped: one of them would poison every aggregate\n * of the bucket it lands in.\n *\n * @param name - The metric's name.\n * @param value - The observed value.\n * @param labels - The series' labels.\n * @param at - When it was observed. Defaults to now.\n */\n observe(name: string, value: number, labels: MetricLabels = {}, at: DateTime = DateTime.now()): void {\n if (!Number.isFinite(value)) {\n return;\n }\n const start = minuteOf(at);\n const key = labelsKey(labels);\n const id = `${name}\\u0000${key}\\u0000${start.toMillis()}`;\n const existing = this.open.get(id);\n if (existing === undefined) {\n this.open.set(id, { name, labels, labelsKey: key, start, aggregate: aggregateOf(value) });\n } else {\n existing.aggregate = mergeAggregates(existing.aggregate, aggregateOf(value));\n }\n }\n\n /**\n * Registers a gauge to be sampled by {@link sampleGauges}.\n *\n * Registering the same name and labels again replaces the earlier reader.\n *\n * @param name - The metric's name.\n * @param read - Returns the current value.\n * @param options - Labels and sampling cadence.\n * @returns A function that unregisters this gauge.\n */\n gauge(name: string, read: GaugeReader, options: GaugeOptions = {}): () => void {\n const labels = options.labels ?? {};\n const id = `${name}\\u0000${labelsKey(labels)}`;\n const registered: RegisteredGauge = { name, labels, read, every: options.every ?? Duration.fromMillis(0) };\n this.gauges.set(id, registered);\n return () => {\n if (this.gauges.get(id) === registered) {\n this.gauges.delete(id);\n }\n };\n }\n\n /**\n * Reads every gauge that is due and observes its value at `now`.\n *\n * Readers run concurrently, so one slow gauge does not delay the others. A reader that throws\n * or answers `undefined` records nothing; it is still marked sampled, so a failing reader is\n * retried at its own cadence rather than on every call.\n *\n * @param now - The sampling instant. Defaults to now.\n */\n async sampleGauges(now: DateTime = DateTime.now()): Promise<void> {\n const due = [...this.gauges.values()].filter(gauge => gauge.sampledAt === undefined || gauge.sampledAt.plus(gauge.every) <= now);\n await Promise.all(\n due.map(async gauge => {\n gauge.sampledAt = now;\n try {\n const value = await gauge.read();\n if (value !== undefined) {\n this.observe(gauge.name, value, gauge.labels, now);\n }\n } catch (error) {\n this.onGaugeError?.(gauge.name, error);\n }\n }),\n );\n }\n\n /**\n * Hands over every bucket recorded since the last drain and empties the buffer.\n *\n * @returns The buckets, oldest minute first.\n */\n drain(): MetricBucket[] {\n const buckets = [...this.open.values()];\n this.open = new Map();\n return buckets.sort((a, b) => a.start.toMillis() - b.start.toMillis());\n }\n\n /** How many buckets are waiting to be drained. */\n get pending(): number {\n return this.open.size;\n }\n}\n","import { DateTime } from 'luxon';\nimport { aggregateOf, MetricAggregate, mergeAggregates } from './metric.aggregate.js';\nimport { MetricBucket, minuteOf } from './metric.bucket.js';\nimport { labelsKey, MetricLabels } from './metric.labels.js';\n\n/**\n * Thrown when a body is not an OTLP metrics export at all. A body that is one but carries parts this\n * receiver cannot use is not an error; those parts are counted in {@link OtlpReceiveResult}.\n */\nexport class OtlpPayloadError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'OtlpPayloadError';\n }\n}\n\n/**\n * How an {@link OtlpMetricsReceiver} bounds what it keeps.\n */\nexport interface OtlpMetricsReceiverOptions {\n /** Distinct label sets kept per metric name. Further series are dropped. Defaults to 50. */\n maxSeriesPerName?: number;\n /** Distinct metric names kept. Further names are dropped. Defaults to 500. */\n maxNames?: number;\n}\n\n/**\n * What one {@link OtlpMetricsReceiver.receive} call produced.\n */\nexport interface OtlpReceiveResult {\n /** Minute buckets, ready for a store. */\n buckets: MetricBucket[];\n /** Data points dropped by the series or name cap. */\n dropped: number;\n /** Data points of kinds this receiver does not read (exponential histograms, summaries), or malformed. */\n unsupported: number;\n}\n\ntype Json = Record<string, unknown>;\n\nconst DELTA = 1;\nconst CUMULATIVE = 2;\n\ninterface CumulativeState {\n start: string;\n count: number;\n sum: number;\n}\n\nconst isObject = (value: unknown): value is Json => typeof value === 'object' && value !== null && !Array.isArray(value);\nconst arrayOf = (value: unknown): unknown[] => (Array.isArray(value) ? value : []);\n\n/** OTLP/JSON writes 64-bit integers as strings and doubles as numbers; either may carry a value. */\nconst numberOf = (value: unknown): number | undefined => {\n if (typeof value === 'number') {\n return value;\n }\n if (typeof value === 'string' && value.trim() !== '') {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n }\n return undefined;\n};\n\n/** The enum is an integer in OTLP/JSON, but some encoders write its name. */\nconst temporalityOf = (value: unknown): number | undefined => {\n if (value === DELTA || value === 'AGGREGATION_TEMPORALITY_DELTA') {\n return DELTA;\n }\n if (value === CUMULATIVE || value === 'AGGREGATION_TEMPORALITY_CUMULATIVE') {\n return CUMULATIVE;\n }\n return undefined;\n};\n\nconst attributeValueOf = (value: unknown): string | undefined => {\n if (!isObject(value)) {\n return undefined;\n }\n if (typeof value.stringValue === 'string') {\n return value.stringValue;\n }\n if (typeof value.boolValue === 'boolean') {\n return String(value.boolValue);\n }\n const numeric = numberOf(value.intValue) ?? numberOf(value.doubleValue);\n if (numeric !== undefined) {\n return String(numeric);\n }\n if (value.arrayValue !== undefined || value.kvlistValue !== undefined) {\n return JSON.stringify(value.arrayValue ?? value.kvlistValue);\n }\n return undefined;\n};\n\nconst labelsOf = (attributes: unknown): Record<string, string> => {\n const labels: Record<string, string> = {};\n for (const attribute of arrayOf(attributes)) {\n if (isObject(attribute) && typeof attribute.key === 'string') {\n const value = attributeValueOf(attribute.value);\n if (value !== undefined) {\n labels[attribute.key] = value;\n }\n }\n }\n return labels;\n};\n\nconst instantOf = (nanos: unknown, fallback: DateTime): DateTime => {\n if (typeof nanos !== 'string' && typeof nanos !== 'number') {\n return fallback;\n }\n try {\n const millis = Number(BigInt(nanos) / 1_000_000n);\n return millis > 0 ? DateTime.fromMillis(millis, { zone: 'utc' }) : fallback;\n } catch {\n return fallback;\n }\n};\n\n/**\n * Turns OTLP/JSON metric exports (the body of `POST /v1/metrics` with `Content-Type: application/json`)\n * into minute buckets.\n *\n * - A **gauge** point, or a **non-monotonic sum** (an up-down counter), is one observation of its value.\n * - A **monotonic sum** contributes its increase. A delta point is the increase; a cumulative point is\n * the difference from the previous point of the same series, so the first point of a series (and the\n * first after the sender restarts, which shows as a new start time or a smaller total) only sets the\n * baseline.\n * - A **histogram** contributes its count and sum the same way, with `min` and `max` when a delta point\n * carries them. A cumulative histogram's extremes cover the sender's whole life, so they are replaced\n * by the interval's mean.\n * - Exponential histograms and summaries are counted as unsupported.\n *\n * Labels are the point's attributes over the resource's `service.name`. Each name keeps at most\n * `maxSeriesPerName` label sets and the receiver at most `maxNames` names, for its whole life: a\n * sender that puts a request id in an attribute cannot grow the table without bound.\n *\n * Keep one instance per process. It holds the cumulative baselines, so a fresh instance costs every\n * cumulative series one point.\n */\nexport class OtlpMetricsReceiver {\n private readonly maxSeriesPerName: number;\n private readonly maxNames: number;\n private readonly series = new Map<string, Set<string>>();\n private readonly cumulative = new Map<string, CumulativeState>();\n\n /**\n * @param options - The series and name caps.\n */\n constructor(options: OtlpMetricsReceiverOptions = {}) {\n this.maxSeriesPerName = options.maxSeriesPerName ?? 50;\n this.maxNames = options.maxNames ?? 500;\n }\n\n /**\n * Reads one export request.\n *\n * @param body - The parsed JSON body.\n * @param now - Used for points with no timestamp. Defaults to now.\n * @throws {@link OtlpPayloadError} when the body has no `resourceMetrics` array.\n */\n receive(body: unknown, now: DateTime = DateTime.now()): OtlpReceiveResult {\n if (!isObject(body) || !Array.isArray(body.resourceMetrics)) {\n throw new OtlpPayloadError('Expected an OTLP metrics export with a resourceMetrics array');\n }\n const buckets = new Map<string, MetricBucket>();\n const result: OtlpReceiveResult = { buckets: [], dropped: 0, unsupported: 0 };\n\n const add = (name: string, labels: MetricLabels, at: DateTime, aggregate: MetricAggregate) => {\n const key = labelsKey(labels);\n const start = minuteOf(at);\n const id = `${name}\\u0000${key}\\u0000${start.toMillis()}`;\n const existing = buckets.get(id);\n buckets.set(\n id,\n existing === undefined\n ? { name, labels, labelsKey: key, start, aggregate }\n : { ...existing, aggregate: mergeAggregates(existing.aggregate, aggregate) },\n );\n };\n\n for (const resourceMetrics of body.resourceMetrics) {\n if (!isObject(resourceMetrics)) {\n continue;\n }\n const resource = labelsOf(isObject(resourceMetrics.resource) ? resourceMetrics.resource.attributes : undefined);\n const base: Record<string, string> = resource['service.name'] === undefined ? {} : { 'service.name': resource['service.name'] };\n\n for (const scopeMetrics of arrayOf(resourceMetrics.scopeMetrics)) {\n for (const metric of isObject(scopeMetrics) ? arrayOf(scopeMetrics.metrics) : []) {\n if (!isObject(metric) || typeof metric.name !== 'string' || metric.name === '') {\n result.unsupported += 1;\n continue;\n }\n const name = metric.name;\n const shape = isObject(metric.gauge)\n ? metric.gauge\n : isObject(metric.sum)\n ? metric.sum\n : isObject(metric.histogram)\n ? metric.histogram\n : undefined;\n if (shape === undefined) {\n const other = isObject(metric.exponentialHistogram) ? metric.exponentialHistogram : isObject(metric.summary) ? metric.summary : undefined;\n result.unsupported += Math.max(1, arrayOf(other?.dataPoints).length);\n continue;\n }\n const temporality = temporalityOf(shape.aggregationTemporality);\n const monotonic = shape === metric.sum && shape.isMonotonic === true;\n\n for (const point of arrayOf(shape.dataPoints)) {\n if (!isObject(point)) {\n result.unsupported += 1;\n continue;\n }\n const labels: MetricLabels = { ...base, ...labelsOf(point.attributes) };\n if (!this.admit(name, labelsKey(labels))) {\n result.dropped += 1;\n continue;\n }\n const at = instantOf(point.timeUnixNano, now);\n const seriesId = `${name}\\u0000${labelsKey(labels)}`;\n const start = String(point.startTimeUnixNano ?? '');\n\n if (shape === metric.histogram) {\n const count = numberOf(point.count);\n const sum = numberOf(point.sum);\n if (count === undefined || sum === undefined) {\n result.unsupported += 1;\n continue;\n }\n if (temporality === CUMULATIVE) {\n const increase = this.increase(seriesId, start, sum, count);\n if (increase !== undefined && increase.count > 0) {\n const mean = increase.sum / increase.count;\n add(name, labels, at, { count: increase.count, sum: increase.sum, min: mean, max: mean, last: mean });\n }\n } else if (count > 0) {\n const mean = sum / count;\n add(name, labels, at, { count, sum, min: numberOf(point.min) ?? mean, max: numberOf(point.max) ?? mean, last: mean });\n }\n continue;\n }\n\n const value = numberOf(point.asDouble) ?? numberOf(point.asInt);\n if (value === undefined) {\n result.unsupported += 1;\n continue;\n }\n if (monotonic && temporality === CUMULATIVE) {\n const increase = this.increase(seriesId, start, value);\n if (increase !== undefined) {\n add(name, labels, at, aggregateOf(increase.sum));\n }\n } else {\n add(name, labels, at, aggregateOf(value));\n }\n }\n }\n }\n }\n result.buckets = [...buckets.values()].sort((a, b) => a.start.toMillis() - b.start.toMillis());\n return result;\n }\n\n /** Whether a series may be kept, registering it if it is new and there is room. */\n private admit(name: string, key: string): boolean {\n let seen = this.series.get(name);\n if (seen === undefined) {\n if (this.series.size >= this.maxNames) {\n return false;\n }\n seen = new Set();\n this.series.set(name, seen);\n }\n if (seen.has(key)) {\n return true;\n }\n if (seen.size >= this.maxSeriesPerName) {\n return false;\n }\n seen.add(key);\n return true;\n }\n\n /**\n * The increase of a cumulative series since its previous point, or `undefined` when this point only\n * sets the baseline (first sight, a new start time, or a total that went backwards).\n *\n * @param sum - The point's running total.\n * @param count - The point's running observation count, for a histogram; zero for a sum.\n */\n private increase(seriesId: string, start: string, sum: number, count = 0): { count: number; sum: number } | undefined {\n const previous = this.cumulative.get(seriesId);\n this.cumulative.set(seriesId, { start, count, sum });\n if (previous === undefined || previous.start !== start || sum < previous.sum || count < previous.count) {\n return undefined;\n }\n return { count: count - previous.count, sum: sum - previous.sum };\n }\n}\n"],"mappings":"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","names":["minuteOf","at","toUTC","startOf","labelsKey","labels","sorted","key","Object","keys","sort","value","undefined","JSON","stringify","Duration","MINUTE_RESOLUTION_LIMIT","Duration","fromObject","hours","resolutionFor","from","to","limit","diff","Injectable","DateTime","Duration","MetricsRecorder","open","Map","gauges","onGaugeError","observe","name","value","labels","at","DateTime","now","Number","isFinite","start","minuteOf","key","labelsKey","id","toMillis","existing","get","undefined","set","aggregate","aggregateOf","mergeAggregates","gauge","read","options","registered","every","Duration","fromMillis","delete","sampleGauges","due","values","filter","sampledAt","plus","Promise","all","map","error","drain","buckets","sort","a","b","pending","size","DateTime","OtlpPayloadError","Error","message","name","DELTA","CUMULATIVE","isObject","value","Array","isArray","arrayOf","numberOf","trim","parsed","Number","isFinite","undefined","temporalityOf","attributeValueOf","stringValue","boolValue","String","numeric","intValue","doubleValue","arrayValue","kvlistValue","JSON","stringify","labelsOf","attributes","labels","attribute","key","instantOf","nanos","fallback","millis","BigInt","DateTime","fromMillis","zone","OtlpMetricsReceiver","maxSeriesPerName","maxNames","series","Map","cumulative","options","receive","body","now","resourceMetrics","buckets","result","dropped","unsupported","add","at","aggregate","labelsKey","start","minuteOf","id","toMillis","existing","get","set","mergeAggregates","resource","base","scopeMetrics","metric","metrics","shape","gauge","sum","histogram","other","exponentialHistogram","summary","Math","max","dataPoints","length","temporality","aggregationTemporality","monotonic","isMonotonic","point","admit","timeUnixNano","seriesId","startTimeUnixNano","count","increase","mean","min","last","asDouble","asInt","aggregateOf","values","sort","a","b","seen","size","Set","has","previous"]}
@@ -0,0 +1,2 @@
1
+ export * from './metrics.store.js';
2
+ //# sourceMappingURL=kysely.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kysely.d.ts","sourceRoot":"","sources":["../src/kysely.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC"}