@grest-ts/metrics 0.0.5

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 (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +40 -0
  3. package/dist/src/GGMetric.d.ts +60 -0
  4. package/dist/src/GGMetric.d.ts.map +1 -0
  5. package/dist/src/GGMetric.js +64 -0
  6. package/dist/src/GGMetric.js.map +1 -0
  7. package/dist/src/GGMetricKey.d.ts +13 -0
  8. package/dist/src/GGMetricKey.d.ts.map +1 -0
  9. package/dist/src/GGMetricKey.js +29 -0
  10. package/dist/src/GGMetricKey.js.map +1 -0
  11. package/dist/src/GGMetrics.d.ts +10 -0
  12. package/dist/src/GGMetrics.d.ts.map +1 -0
  13. package/dist/src/GGMetrics.js +29 -0
  14. package/dist/src/GGMetrics.js.map +1 -0
  15. package/dist/src/GGMetricsDefineStorage.d.ts +4 -0
  16. package/dist/src/GGMetricsDefineStorage.d.ts.map +1 -0
  17. package/dist/src/GGMetricsDefineStorage.js +7 -0
  18. package/dist/src/GGMetricsDefineStorage.js.map +1 -0
  19. package/dist/src/GGMetricsLoader.d.ts +8 -0
  20. package/dist/src/GGMetricsLoader.d.ts.map +1 -0
  21. package/dist/src/GGMetricsLoader.js +17 -0
  22. package/dist/src/GGMetricsLoader.js.map +1 -0
  23. package/dist/src/GGMetricsStore.d.ts +9 -0
  24. package/dist/src/GGMetricsStore.d.ts.map +1 -0
  25. package/dist/src/GGMetricsStore.js +20 -0
  26. package/dist/src/GGMetricsStore.js.map +1 -0
  27. package/dist/src/exporters/GGJsonMetricsExporter.d.ts +41 -0
  28. package/dist/src/exporters/GGJsonMetricsExporter.d.ts.map +1 -0
  29. package/dist/src/exporters/GGJsonMetricsExporter.js +129 -0
  30. package/dist/src/exporters/GGJsonMetricsExporter.js.map +1 -0
  31. package/dist/src/exporters/GGMetricsExporter.d.ts +41 -0
  32. package/dist/src/exporters/GGMetricsExporter.d.ts.map +1 -0
  33. package/dist/src/exporters/GGMetricsExporter.js +70 -0
  34. package/dist/src/exporters/GGMetricsExporter.js.map +1 -0
  35. package/dist/src/exporters/GGNestedMetricsExporter.d.ts +33 -0
  36. package/dist/src/exporters/GGNestedMetricsExporter.d.ts.map +1 -0
  37. package/dist/src/exporters/GGNestedMetricsExporter.js +275 -0
  38. package/dist/src/exporters/GGNestedMetricsExporter.js.map +1 -0
  39. package/dist/src/index-browser.d.ts +17 -0
  40. package/dist/src/index-browser.d.ts.map +1 -0
  41. package/dist/src/index-browser.js +17 -0
  42. package/dist/src/index-browser.js.map +1 -0
  43. package/dist/src/index-node.d.ts +17 -0
  44. package/dist/src/index-node.d.ts.map +1 -0
  45. package/dist/src/index-node.js +20 -0
  46. package/dist/src/index-node.js.map +1 -0
  47. package/dist/src/keys/GGCounterKey.d.ts +11 -0
  48. package/dist/src/keys/GGCounterKey.d.ts.map +1 -0
  49. package/dist/src/keys/GGCounterKey.js +21 -0
  50. package/dist/src/keys/GGCounterKey.js.map +1 -0
  51. package/dist/src/keys/GGGaugeKey.d.ts +13 -0
  52. package/dist/src/keys/GGGaugeKey.d.ts.map +1 -0
  53. package/dist/src/keys/GGGaugeKey.js +27 -0
  54. package/dist/src/keys/GGGaugeKey.js.map +1 -0
  55. package/dist/src/keys/GGHistogramKey.d.ts +13 -0
  56. package/dist/src/keys/GGHistogramKey.d.ts.map +1 -0
  57. package/dist/src/keys/GGHistogramKey.js +27 -0
  58. package/dist/src/keys/GGHistogramKey.js.map +1 -0
  59. package/dist/src/keys/GGLazyGaugeKey.d.ts +21 -0
  60. package/dist/src/keys/GGLazyGaugeKey.d.ts.map +1 -0
  61. package/dist/src/keys/GGLazyGaugeKey.js +25 -0
  62. package/dist/src/keys/GGLazyGaugeKey.js.map +1 -0
  63. package/dist/src/metric/GGCounter.d.ts +7 -0
  64. package/dist/src/metric/GGCounter.d.ts.map +1 -0
  65. package/dist/src/metric/GGCounter.js +14 -0
  66. package/dist/src/metric/GGCounter.js.map +1 -0
  67. package/dist/src/metric/GGGauge.d.ts +9 -0
  68. package/dist/src/metric/GGGauge.d.ts.map +1 -0
  69. package/dist/src/metric/GGGauge.js +31 -0
  70. package/dist/src/metric/GGGauge.js.map +1 -0
  71. package/dist/src/metric/GGHistogram.d.ts +27 -0
  72. package/dist/src/metric/GGHistogram.d.ts.map +1 -0
  73. package/dist/src/metric/GGHistogram.js +41 -0
  74. package/dist/src/metric/GGHistogram.js.map +1 -0
  75. package/dist/src/metric/GGLazyGauge.d.ts +20 -0
  76. package/dist/src/metric/GGLazyGauge.d.ts.map +1 -0
  77. package/dist/src/metric/GGLazyGauge.js +27 -0
  78. package/dist/src/metric/GGLazyGauge.js.map +1 -0
  79. package/dist/src/tsconfig.json +16 -0
  80. package/dist/testkit/GGMetricsCommands.d.ts +18 -0
  81. package/dist/testkit/GGMetricsCommands.d.ts.map +1 -0
  82. package/dist/testkit/GGMetricsCommands.js +77 -0
  83. package/dist/testkit/GGMetricsCommands.js.map +1 -0
  84. package/dist/testkit/GGMetricsInterceptor.d.ts +34 -0
  85. package/dist/testkit/GGMetricsInterceptor.d.ts.map +1 -0
  86. package/dist/testkit/GGMetricsInterceptor.js +202 -0
  87. package/dist/testkit/GGMetricsInterceptor.js.map +1 -0
  88. package/dist/testkit/GGMetricsWith.d.ts +22 -0
  89. package/dist/testkit/GGMetricsWith.d.ts.map +1 -0
  90. package/dist/testkit/GGMetricsWith.js +61 -0
  91. package/dist/testkit/GGMetricsWith.js.map +1 -0
  92. package/dist/testkit/GGTestMetricsExporter.d.ts +40 -0
  93. package/dist/testkit/GGTestMetricsExporter.d.ts.map +1 -0
  94. package/dist/testkit/GGTestMetricsExporter.js +119 -0
  95. package/dist/testkit/GGTestMetricsExporter.js.map +1 -0
  96. package/dist/testkit/GGTestSelectorMetrics.d.ts +15 -0
  97. package/dist/testkit/GGTestSelectorMetrics.d.ts.map +1 -0
  98. package/dist/testkit/GGTestSelectorMetrics.js +16 -0
  99. package/dist/testkit/GGTestSelectorMetrics.js.map +1 -0
  100. package/dist/testkit/index-testkit.d.ts +6 -0
  101. package/dist/testkit/index-testkit.d.ts.map +1 -0
  102. package/dist/testkit/index-testkit.js +6 -0
  103. package/dist/testkit/index-testkit.js.map +1 -0
  104. package/dist/tsconfig.publish.tsbuildinfo +1 -0
  105. package/package.json +58 -0
  106. package/src/GGMetric.ts +124 -0
  107. package/src/GGMetricKey.ts +38 -0
  108. package/src/GGMetrics.ts +34 -0
  109. package/src/GGMetricsDefineStorage.ts +8 -0
  110. package/src/GGMetricsLoader.ts +21 -0
  111. package/src/GGMetricsStore.ts +26 -0
  112. package/src/exporters/GGJsonMetricsExporter.ts +176 -0
  113. package/src/exporters/GGMetricsExporter.ts +88 -0
  114. package/src/exporters/GGNestedMetricsExporter.ts +335 -0
  115. package/src/index-browser.ts +16 -0
  116. package/src/index-node.ts +21 -0
  117. package/src/keys/GGCounterKey.ts +29 -0
  118. package/src/keys/GGGaugeKey.ts +37 -0
  119. package/src/keys/GGHistogramKey.ts +37 -0
  120. package/src/keys/GGLazyGaugeKey.ts +36 -0
  121. package/src/metric/GGCounter.ts +19 -0
  122. package/src/metric/GGGauge.ts +38 -0
  123. package/src/metric/GGHistogram.ts +68 -0
  124. package/src/metric/GGLazyGauge.ts +31 -0
  125. package/src/tsconfig.json +16 -0
@@ -0,0 +1,335 @@
1
+ import {GGMetric} from "../GGMetric.js";
2
+ import {GGCounter} from "../metric/GGCounter.js";
3
+ import {GGGauge} from "../metric/GGGauge.js";
4
+ import {GGLazyGauge} from "../metric/GGLazyGauge.js";
5
+ import {GGHistogram, HistogramData} from "../metric/GGHistogram.js";
6
+ import {GGMetricKey} from "../GGMetricKey.js";
7
+ import {GGMetricsExporter, ExporterConfig} from "./GGMetricsExporter.js";
8
+
9
+ export type NestedValueConverter = (metric: GGMetric<any>, value: any, exporter: GGNestedMetricsExporter) => any;
10
+
11
+ /**
12
+ * Exports metrics in a nested, human-readable format.
13
+ * Groups metrics by their groupBy configuration and nests remaining labels.
14
+ */
15
+ export class GGNestedMetricsExporter extends GGMetricsExporter<NestedMetricsOutput> {
16
+
17
+ // Static map for extensibility - register value converters for new metric types
18
+ private static converters = new Map<Function, NestedValueConverter>();
19
+
20
+ static {
21
+ GGNestedMetricsExporter.registerConverter(GGCounter, convertCounterValue);
22
+ GGNestedMetricsExporter.registerConverter(GGGauge, convertGaugeValue);
23
+ GGNestedMetricsExporter.registerConverter(GGLazyGauge, convertLazyGaugeValue);
24
+ GGNestedMetricsExporter.registerConverter(GGHistogram, convertHistogramValue);
25
+ }
26
+
27
+ /**
28
+ * Register a value converter for a custom metric type.
29
+ */
30
+ static registerConverter(metricClass: Function, converter: NestedValueConverter): void {
31
+ GGNestedMetricsExporter.converters.set(metricClass, converter);
32
+ }
33
+
34
+ constructor(config: ExporterConfig = {}) {
35
+ super(config);
36
+ }
37
+
38
+ getMetrics(): NestedMetricsOutput {
39
+ const output: NestedMetricsOutput = {
40
+ timestamp: Date.now(),
41
+ groups: {}
42
+ };
43
+
44
+ // Collect all metric values with their parsed data
45
+ const allValues: MetricValueEntry[] = [];
46
+
47
+ for (const metric of this.getFilteredMetrics()) {
48
+ const entries = this.collectMetricValues(metric);
49
+ allValues.push(...entries);
50
+ }
51
+
52
+ // Group by the groupBy key
53
+ const groupedByKey = new Map<string, MetricValueEntry[]>();
54
+ for (const entry of allValues) {
55
+ const existing = groupedByKey.get(entry.groupKey);
56
+ if (existing) {
57
+ existing.push(entry);
58
+ } else {
59
+ groupedByKey.set(entry.groupKey, [entry]);
60
+ }
61
+ }
62
+
63
+ // Build the nested structure for each group
64
+ for (const [groupKey, entries] of groupedByKey) {
65
+ output.groups[groupKey] = this.buildGroupEntries(entries);
66
+ }
67
+
68
+ return output;
69
+ }
70
+
71
+ /**
72
+ * Parse a label key string into a labels object.
73
+ * Exposed for use by converters.
74
+ */
75
+ parseLabels(labelKey: string): Record<string, string> {
76
+ if (!labelKey) return {};
77
+ const labels: Record<string, string> = {};
78
+ const parts = labelKey.split(',');
79
+ for (const part of parts) {
80
+ const [key, val] = part.split('=');
81
+ if (key && val !== undefined) {
82
+ labels[key] = val;
83
+ }
84
+ }
85
+ return labels;
86
+ }
87
+
88
+ private collectMetricValues(metric: GGMetric<any>): MetricValueEntry[] {
89
+ const entries: MetricValueEntry[] = [];
90
+ const key = metric.key as GGMetricKey<any>;
91
+ const groupByLabels = key.groupBy?.labels ?? [];
92
+ const metricName = this.getShortName(key);
93
+
94
+ for (const [labelKey, value] of metric.getValues()) {
95
+ const labels = this.parseLabels(labelKey);
96
+
97
+ // Compute groupBy key
98
+ const groupKey = this.computeGroupKey(key, labels);
99
+
100
+ // Compute remaining labels (not in groupBy)
101
+ const remainingLabels: Record<string, string> = {};
102
+ for (const [k, v] of Object.entries(labels)) {
103
+ if (!groupByLabels.includes(k)) {
104
+ remainingLabels[k] = v;
105
+ }
106
+ }
107
+
108
+ entries.push({
109
+ groupKey,
110
+ metricName,
111
+ metricType: this.getMetricType(metric),
112
+ remainingLabels,
113
+ value: this.formatValue(metric, value)
114
+ });
115
+ }
116
+
117
+ return entries;
118
+ }
119
+
120
+ private computeGroupKey(key: GGMetricKey<any>, labels: Record<string, string>): string {
121
+ const groupBy = key.groupBy;
122
+ if (!groupBy || groupBy.labels.length === 0) {
123
+ return key.root; // Use metric root as default group
124
+ }
125
+
126
+ if (groupBy.template) {
127
+ // Template string - replace {labelName} with values, missing values become empty string
128
+ return groupBy.template.replace(/\{(\w+)\}/g, (_, labelName) => {
129
+ return String(labels[labelName] ?? '');
130
+ });
131
+ } else {
132
+ // Default: join values with comma
133
+ return groupBy.labels.map(l => String(labels[l] ?? '')).join(',');
134
+ }
135
+ }
136
+
137
+ private getShortName(key: GGMetricKey<any>): string {
138
+ // Remove the root prefix to get just the metric name
139
+ return key.name.replace(key.root, '');
140
+ }
141
+
142
+ private getMetricType(metric: GGMetric<any>): string {
143
+ return metric.constructor.name;
144
+ }
145
+
146
+ private formatValue(metric: GGMetric<any>, value: any): any {
147
+ const converter = GGNestedMetricsExporter.converters.get(metric.constructor);
148
+ if (!converter) {
149
+ // Fallback: return raw value
150
+ return value;
151
+ }
152
+ return converter(metric, value, this);
153
+ }
154
+
155
+ private buildGroupEntries(entries: MetricValueEntry[]): any[] {
156
+ // Find all unique "shared" label combinations
157
+ // Shared labels are those that exist in multiple metrics (like 'path')
158
+ // Non-shared labels are metric-specific (like 'result' only on counter)
159
+
160
+ // First, find which labels are common across all metrics in this group
161
+ const labelSets = new Map<string, Set<string>>();
162
+ for (const entry of entries) {
163
+ const labelNames = Object.keys(entry.remainingLabels);
164
+ const existing = labelSets.get(entry.metricName);
165
+ if (!existing) {
166
+ labelSets.set(entry.metricName, new Set(labelNames));
167
+ }
168
+ }
169
+
170
+ // Find common labels (present in all metrics)
171
+ const allMetrics = [...labelSets.keys()];
172
+ let commonLabels: Set<string> = new Set();
173
+ if (allMetrics.length > 0) {
174
+ commonLabels = new Set(labelSets.get(allMetrics[0])!);
175
+ for (let i = 1; i < allMetrics.length; i++) {
176
+ const metricLabels = labelSets.get(allMetrics[i])!;
177
+ for (const label of commonLabels) {
178
+ if (!metricLabels.has(label)) {
179
+ commonLabels.delete(label);
180
+ }
181
+ }
182
+ }
183
+ }
184
+
185
+ // Group entries by their common label values
186
+ const byCommonLabels = new Map<string, MetricValueEntry[]>();
187
+ for (const entry of entries) {
188
+ const commonKey = [...commonLabels]
189
+ .sort()
190
+ .map(l => `${l}=${entry.remainingLabels[l] ?? ''}`)
191
+ .join(',');
192
+
193
+ const existing = byCommonLabels.get(commonKey);
194
+ if (existing) {
195
+ existing.push(entry);
196
+ } else {
197
+ byCommonLabels.set(commonKey, [entry]);
198
+ }
199
+ }
200
+
201
+ // Build output entries
202
+ const result: any[] = [];
203
+ for (const [_, groupEntries] of byCommonLabels) {
204
+ const outputEntry: Record<string, any> = {};
205
+
206
+ // Add common labels as direct properties
207
+ if (groupEntries.length > 0) {
208
+ for (const label of commonLabels) {
209
+ outputEntry[label] = groupEntries[0].remainingLabels[label];
210
+ }
211
+ }
212
+
213
+ // Group entries by metric name
214
+ const byMetric = new Map<string, MetricValueEntry[]>();
215
+ for (const entry of groupEntries) {
216
+ const existing = byMetric.get(entry.metricName);
217
+ if (existing) {
218
+ existing.push(entry);
219
+ } else {
220
+ byMetric.set(entry.metricName, [entry]);
221
+ }
222
+ }
223
+
224
+ // Add each metric's values
225
+ for (const [metricName, metricEntries] of byMetric) {
226
+ // Get extra labels (not in commonLabels)
227
+ const extraLabels = Object.keys(metricEntries[0].remainingLabels)
228
+ .filter(l => !commonLabels.has(l));
229
+
230
+ if (extraLabels.length === 0) {
231
+ // No extra labels - value goes directly under metric name
232
+ outputEntry[metricName] = metricEntries[0].value;
233
+ } else {
234
+ // Has extra labels - nest by those label names
235
+ this.nestByLabels(outputEntry, metricName, metricEntries, extraLabels, 0);
236
+ }
237
+ }
238
+
239
+ result.push(outputEntry);
240
+ }
241
+
242
+ return result;
243
+ }
244
+
245
+ private nestByLabels(
246
+ obj: Record<string, any>,
247
+ metricName: string,
248
+ entries: MetricValueEntry[],
249
+ extraLabels: string[],
250
+ labelIndex: number
251
+ ): void {
252
+ const currentLabel = extraLabels[labelIndex];
253
+ const isLastLabel = labelIndex === extraLabels.length - 1;
254
+
255
+ // Group entries by current label value
256
+ const byLabelValue = new Map<string, MetricValueEntry[]>();
257
+ for (const entry of entries) {
258
+ const labelValue = entry.remainingLabels[currentLabel] ?? '';
259
+ const existing = byLabelValue.get(labelValue);
260
+ if (existing) {
261
+ existing.push(entry);
262
+ } else {
263
+ byLabelValue.set(labelValue, [entry]);
264
+ }
265
+ }
266
+
267
+ // Create or get the label container
268
+ if (!obj[currentLabel]) {
269
+ obj[currentLabel] = {};
270
+ }
271
+ const labelContainer = obj[currentLabel];
272
+
273
+ // Add each label value
274
+ for (const [labelValue, valueEntries] of byLabelValue) {
275
+ if (isLastLabel) {
276
+ // Last label - add metric name and value
277
+ if (!labelContainer[labelValue]) {
278
+ labelContainer[labelValue] = {};
279
+ }
280
+ labelContainer[labelValue][metricName] = valueEntries[0].value;
281
+ } else {
282
+ // More labels to go - recurse
283
+ if (!labelContainer[labelValue]) {
284
+ labelContainer[labelValue] = {};
285
+ }
286
+ this.nestByLabels(labelContainer[labelValue], metricName, valueEntries, extraLabels, labelIndex + 1);
287
+ }
288
+ }
289
+ }
290
+ }
291
+
292
+ // Built-in value converters
293
+
294
+ function convertCounterValue(_metric: GGCounter<any>, value: number, _exporter: GGNestedMetricsExporter): number {
295
+ return value;
296
+ }
297
+
298
+ function convertGaugeValue(_metric: GGGauge<any>, value: number, _exporter: GGNestedMetricsExporter): number {
299
+ return value;
300
+ }
301
+
302
+ function convertLazyGaugeValue(_metric: GGLazyGauge, value: number, _exporter: GGNestedMetricsExporter): number {
303
+ return value;
304
+ }
305
+
306
+ function convertHistogramValue(metric: GGHistogram<any>, value: HistogramData, _exporter: GGNestedMetricsExporter): any {
307
+ const buckets = metric.getBuckets();
308
+ const bucketObj: Record<string, number> = {};
309
+ for (let i = 0; i < buckets.length; i++) {
310
+ bucketObj[String(buckets[i])] = value.values[i] ?? 0;
311
+ }
312
+ return {
313
+ count: value.count,
314
+ sum: value.sum,
315
+ avg: value.count > 0 ? value.sum / value.count : 0,
316
+ min: value.min === Infinity ? 0 : value.min,
317
+ max: value.max === -Infinity ? 0 : value.max,
318
+ buckets: bucketObj
319
+ };
320
+ }
321
+
322
+ // Types
323
+
324
+ interface MetricValueEntry {
325
+ groupKey: string;
326
+ metricName: string;
327
+ metricType: string;
328
+ remainingLabels: Record<string, string>;
329
+ value: any;
330
+ }
331
+
332
+ export interface NestedMetricsOutput {
333
+ timestamp: number;
334
+ groups: Record<string, any[]>;
335
+ }
@@ -0,0 +1,16 @@
1
+ export * from './GGMetric';
2
+ export * from './metric/GGCounter';
3
+ export * from './metric/GGGauge';
4
+ export * from './metric/GGLazyGauge';
5
+ export * from './metric/GGHistogram';
6
+ export * from './GGMetrics';
7
+ export * from './GGMetricsLoader';
8
+ export * from "./GGMetricKey";
9
+ export * from "./keys/GGCounterKey";
10
+ export * from "./keys/GGGaugeKey";
11
+ export * from "./keys/GGLazyGaugeKey";
12
+ export * from "./keys/GGHistogramKey";
13
+ export * from './exporters/GGMetricsExporter';
14
+ export * from './exporters/GGJsonMetricsExporter';
15
+ export * from './exporters/GGNestedMetricsExporter';
16
+ export * from "./GGMetricsStore";
@@ -0,0 +1,21 @@
1
+ import {AsyncLocalStorage} from "node:async_hooks";
2
+ import {_initMetricsStorage} from "./GGMetricsDefineStorage";
3
+ _initMetricsStorage(new AsyncLocalStorage());
4
+
5
+ export * from './GGMetric';
6
+ export * from './metric/GGCounter';
7
+ export * from './metric/GGGauge';
8
+ export * from './metric/GGLazyGauge';
9
+ export * from './metric/GGHistogram';
10
+ export * from './GGMetrics';
11
+ export * from './GGMetricsLoader';
12
+ export * from "./GGMetricKey";
13
+ export * from "./keys/GGCounterKey";
14
+ export * from "./keys/GGGaugeKey";
15
+ export * from "./keys/GGLazyGaugeKey";
16
+ export * from "./keys/GGHistogramKey";
17
+ export * from './exporters/GGMetricsExporter';
18
+ export * from './exporters/GGJsonMetricsExporter';
19
+ export * from './exporters/GGNestedMetricsExporter';
20
+ export * from "./GGMetricsStore";
21
+
@@ -0,0 +1,29 @@
1
+ import {GGMetricKey} from "../GGMetricKey.js";
2
+ import {GGCounter} from "../metric/GGCounter.js";
3
+ import {GGMetricLabels, GGMetricOptions, LabelsArgs} from "../GGMetric.js";
4
+
5
+ export class GGCounterKey<
6
+ TLabels extends GGMetricLabels = {}
7
+ > extends GGMetricKey<TLabels, GGCounter<TLabels>> {
8
+
9
+ constructor(name: string, options: GGMetricOptions<TLabels>) {
10
+ super(name, options);
11
+ Object.freeze(this);
12
+ }
13
+
14
+ public create(): GGCounter<TLabels> {
15
+ return new GGCounter<TLabels>(this);
16
+ }
17
+
18
+ public inc(value: number = 1, ...args: LabelsArgs<TLabels>): void {
19
+ this.getMetric().inc(value, ...args);
20
+ }
21
+
22
+ public getValue(...args: LabelsArgs<TLabels>): number {
23
+ return this.getMetric().getValue(...args);
24
+ }
25
+
26
+ public reset(): void {
27
+ this.getMetric().reset();
28
+ }
29
+ }
@@ -0,0 +1,37 @@
1
+ import {GGMetricKey} from "../GGMetricKey.js";
2
+ import {GGGauge} from "../metric/GGGauge.js";
3
+ import {GGMetricLabels, GGMetricOptions, LabelsArgs} from "../GGMetric.js";
4
+
5
+ export class GGGaugeKey<
6
+ TLabels extends GGMetricLabels = {}
7
+ > extends GGMetricKey<TLabels, GGGauge<TLabels>> {
8
+
9
+ constructor(name: string, options: GGMetricOptions<TLabels>) {
10
+ super(name, options);
11
+ Object.freeze(this);
12
+ }
13
+
14
+ public create(): GGGauge<TLabels> {
15
+ return new GGGauge<TLabels>(this);
16
+ }
17
+
18
+ public set(value: number, ...args: LabelsArgs<TLabels>): void {
19
+ this.getMetric().set(value, ...args);
20
+ }
21
+
22
+ public inc(value: number = 1, ...args: LabelsArgs<TLabels>): void {
23
+ this.getMetric().inc(value, ...args);
24
+ }
25
+
26
+ public dec(value: number = 1, ...args: LabelsArgs<TLabels>): void {
27
+ this.getMetric().dec(value, ...args);
28
+ }
29
+
30
+ public getValue(...args: LabelsArgs<TLabels>): number {
31
+ return this.getMetric().getValue(...args);
32
+ }
33
+
34
+ public reset(): void {
35
+ this.getMetric().reset();
36
+ }
37
+ }
@@ -0,0 +1,37 @@
1
+ import {GGMetricKey} from "../GGMetricKey.js";
2
+ import {GGHistogram, HistogramData, HistogramOptions} from "../metric/GGHistogram.js";
3
+ import {GGMetricLabels, LabelsArgs} from "../GGMetric.js";
4
+
5
+ export class GGHistogramKey<
6
+ TLabels extends GGMetricLabels = {}
7
+ > extends GGMetricKey<TLabels, GGHistogram<TLabels>> {
8
+
9
+ public readonly buckets: number[];
10
+
11
+ constructor(name: string, options: HistogramOptions<TLabels>) {
12
+ super(name, options);
13
+ this.buckets = options.buckets.sort((a, b) => a - b)
14
+ Object.freeze(this.buckets)
15
+ Object.freeze(this);
16
+ }
17
+
18
+ public create(): GGHistogram<TLabels> {
19
+ return new GGHistogram<TLabels>(this);
20
+ }
21
+
22
+ public observe(value: number, ...args: LabelsArgs<TLabels>): void {
23
+ this.getMetric().observe(value, ...args);
24
+ }
25
+
26
+ public startTimer(...args: LabelsArgs<TLabels>): () => void {
27
+ return this.getMetric().startTimer(...args);
28
+ }
29
+
30
+ public getValue(...args: LabelsArgs<TLabels>): HistogramData | undefined {
31
+ return this.getMetric().getValue(...args);
32
+ }
33
+
34
+ public reset(): void {
35
+ this.getMetric().reset();
36
+ }
37
+ }
@@ -0,0 +1,36 @@
1
+ import {GGMetricKey} from "../GGMetricKey.js";
2
+ import {GGLazyGauge} from "../metric/GGLazyGauge.js";
3
+ import {GGMetricOptionsBase} from "../GGMetric.js";
4
+
5
+ export interface LazyGaugeOptions extends GGMetricOptionsBase {
6
+ getValue: () => number;
7
+ }
8
+
9
+ /**
10
+ * Key for a lazy gauge metric.
11
+ * The getValue function is called whenever the metric value is read.
12
+ */
13
+ export class GGLazyGaugeKey extends GGMetricKey<{}, GGLazyGauge> {
14
+
15
+ public declare readonly options: LazyGaugeOptions;
16
+
17
+ constructor(name: string, options: LazyGaugeOptions) {
18
+ super(name, options);
19
+ Object.freeze(this);
20
+ }
21
+
22
+ public create(): GGLazyGauge {
23
+ return new GGLazyGauge(this);
24
+ }
25
+
26
+ /**
27
+ * Get the current value by calling the getValue function.
28
+ */
29
+ public getValue(): number {
30
+ return this.getMetric().getValue();
31
+ }
32
+
33
+ public reset(): void {
34
+ // No-op for lazy gauge - nothing to reset
35
+ }
36
+ }
@@ -0,0 +1,19 @@
1
+ import {GGMetric, GGMetricLabels, LabelsArgs} from "../GGMetric.js";
2
+ import type {GGCounterKey} from "../keys/GGCounterKey";
3
+
4
+ export class GGCounter<
5
+ TLabels extends GGMetricLabels = {}
6
+ > extends GGMetric<TLabels, number, GGCounterKey<TLabels>> {
7
+
8
+ protected getDefaultValue(): number {
9
+ return 0;
10
+ }
11
+
12
+ public inc(value: number = 1, ...args: LabelsArgs<TLabels>): void {
13
+ const key = this.getKey(args[0] as TLabels);
14
+ const current = this.getByKey(key);
15
+ if (current !== undefined) {
16
+ this.setByKey(key, current + value);
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,38 @@
1
+ import {GGMetric, GGMetricLabels, LabelsArgs} from "../GGMetric.js";
2
+ import type {GGGaugeKey} from "../keys/GGGaugeKey";
3
+
4
+ export class GGGauge<
5
+ TLabels extends GGMetricLabels = {}
6
+ > extends GGMetric<TLabels, number, GGGaugeKey<TLabels>> {
7
+
8
+ protected getDefaultValue(): number {
9
+ return 0;
10
+ }
11
+
12
+ public set(value: number, ...args: LabelsArgs<TLabels>): void {
13
+ const key = this.getKey(args[0] as TLabels);
14
+ const current = this.getByKey(key);
15
+ if (current === undefined) {
16
+ return;
17
+ }
18
+ this.setByKey(key, value);
19
+ }
20
+
21
+ public inc(value: number = 1, ...args: LabelsArgs<TLabels>): void {
22
+ const key = this.getKey(args[0] as TLabels);
23
+ const current = this.getByKey(key);
24
+ if (current === undefined) {
25
+ return;
26
+ }
27
+ this.setByKey(key, current + value);
28
+ }
29
+
30
+ public dec(value: number = 1, ...args: LabelsArgs<TLabels>): void {
31
+ const key = this.getKey(args[0] as TLabels);
32
+ const current = this.getByKey(key);
33
+ if (current === undefined) {
34
+ return;
35
+ }
36
+ this.setByKey(key, current - value);
37
+ }
38
+ }
@@ -0,0 +1,68 @@
1
+ import {GGMetric, GGMetricLabels, GGMetricOptions, LabelsArgs} from "../GGMetric.js";
2
+ import type {GGHistogramKey} from "../keys/GGHistogramKey";
3
+
4
+ export type HistogramOptions<TLabels extends GGMetricLabels = {}> =
5
+ GGMetricOptions<TLabels> & { buckets: number[] };
6
+
7
+ export class GGHistogram<
8
+ TLabels extends GGMetricLabels = {}
9
+ > extends GGMetric<TLabels, HistogramData, GGHistogramKey<TLabels>> {
10
+
11
+ public getBuckets(): number[] {
12
+ return this.key.buckets;
13
+ }
14
+
15
+ protected getDefaultValue(): HistogramData {
16
+ return {
17
+ count: 0,
18
+ sum: 0,
19
+ min: Infinity,
20
+ max: -Infinity,
21
+ values: this.key.buckets.map(() => 0)
22
+ };
23
+ }
24
+
25
+ public observe(value: number, ...args: LabelsArgs<TLabels>): void {
26
+ const key = this.getKey(args[0] as TLabels);
27
+ const data = this.getByKey(key);
28
+ if (data === undefined) {
29
+ return;
30
+ }
31
+ data.count++;
32
+ data.sum += value;
33
+ if (value < data.min) {
34
+ data.min = value;
35
+ }
36
+ if (value > data.max) {
37
+ data.max = value;
38
+ }
39
+ for (let i = 0; i < this.key.buckets.length; i++) {
40
+ if (value <= this.key.buckets[i]) {
41
+ data.values[i]++;
42
+ }
43
+ }
44
+ }
45
+
46
+ public startTimer(...args: LabelsArgs<TLabels>): () => void {
47
+ const start = Date.now();
48
+ const labels = args[0] as TLabels;
49
+ return () => this.observe(Date.now() - start, ...([labels] as LabelsArgs<TLabels>));
50
+ }
51
+ }
52
+
53
+ export interface HistogramData {
54
+ count: number;
55
+ sum: number;
56
+ min: number;
57
+ max: number;
58
+ values: number[];
59
+ }
60
+
61
+ export interface SerializedHistogramData {
62
+ count: number;
63
+ sum: number;
64
+ min: number;
65
+ max: number;
66
+ buckets: number[];
67
+ values: number[];
68
+ }
@@ -0,0 +1,31 @@
1
+ import {GGMetric} from "../GGMetric.js";
2
+ import type {GGLazyGaugeKey} from "../keys/GGLazyGaugeKey";
3
+
4
+ /**
5
+ * A gauge that computes its value lazily by calling a function.
6
+ * Perfect for "current state" metrics like memory usage, active handles, etc.
7
+ * Does not support labels - use regular GGGauge if you need labels.
8
+ */
9
+ export class GGLazyGauge extends GGMetric<{}, number, GGLazyGaugeKey> {
10
+
11
+ protected getDefaultValue(): number {
12
+ return 0;
13
+ }
14
+
15
+ /**
16
+ * Get the current value by calling the getValue function.
17
+ */
18
+ public getValue(): number {
19
+ return this.key.options.getValue();
20
+ }
21
+
22
+ /**
23
+ * Returns a map with single entry (empty key -> current value).
24
+ * Compatible with exporter iteration pattern.
25
+ */
26
+ public getValues(): Map<string, number> {
27
+ const map = new Map<string, number>();
28
+ map.set('', this.getValue());
29
+ return map;
30
+ }
31
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "//": "THIS FILE IS GENERATED - DO NOT EDIT",
3
+ "extends": "../../../tsconfig.base.json",
4
+ "compilerOptions": {
5
+ "rootDir": ".",
6
+ "lib": [
7
+ "ES2022"
8
+ ],
9
+ "types": [
10
+ "node"
11
+ ]
12
+ },
13
+ "include": [
14
+ "**/*"
15
+ ]
16
+ }