@grest-ts/metrics 0.0.6 → 0.0.7

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.
@@ -1,176 +1,176 @@
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 {GGMetricsExporter, ExporterConfig} from "./GGMetricsExporter.js";
7
-
8
- export type JsonMetricConverter = (metric: GGMetric<any>, exporter: GGJsonMetricsExporter) => JsonMetric;
9
-
10
- export class GGJsonMetricsExporter extends GGMetricsExporter<JsonMetricsOutput> {
11
-
12
- // Static map for extensibility - register converters for new metric types
13
- private static converters = new Map<Function, JsonMetricConverter>();
14
-
15
- static {
16
- GGJsonMetricsExporter.registerConverter(GGCounter, convertCounter);
17
- GGJsonMetricsExporter.registerConverter(GGGauge, convertGauge);
18
- GGJsonMetricsExporter.registerConverter(GGLazyGauge, convertLazyGauge);
19
- GGJsonMetricsExporter.registerConverter(GGHistogram, convertHistogram);
20
- }
21
-
22
- /**
23
- * Register a converter for a custom metric type.
24
- */
25
- static registerConverter(metricClass: Function, converter: JsonMetricConverter): void {
26
- GGJsonMetricsExporter.converters.set(metricClass, converter);
27
- }
28
-
29
- constructor(config: ExporterConfig = {}) {
30
- super(config);
31
- }
32
-
33
- getMetrics(): JsonMetricsOutput {
34
- const output: JsonMetricsOutput = {
35
- timestamp: Date.now(),
36
- metrics: {}
37
- };
38
-
39
- for (const metric of this.getFilteredMetrics()) {
40
- output.metrics[metric.name] = this.convertMetric(metric);
41
- }
42
-
43
- return output;
44
- }
45
-
46
- private convertMetric(metric: GGMetric<any>): JsonMetric {
47
- const converter = GGJsonMetricsExporter.converters.get(metric.constructor);
48
- if (!converter) {
49
- throw new Error(`No converter registered for metric type: ${metric.constructor.name}`);
50
- }
51
- return converter(metric, this);
52
- }
53
-
54
- /**
55
- * Parse a label key string into a labels object.
56
- * Exposed for use by converters.
57
- */
58
- parseLabels(labelKey: string): Record<string, string> {
59
- if (!labelKey) return {};
60
- const labels: Record<string, string> = {};
61
- const parts = labelKey.split(',');
62
- for (const part of parts) {
63
- const [key, val] = part.split('=');
64
- if (key && val !== undefined) {
65
- labels[key] = val;
66
- }
67
- }
68
- return labels;
69
- }
70
- }
71
-
72
- // Built-in converters
73
-
74
- function convertCounter(metric: GGCounter<any>, exporter: GGJsonMetricsExporter): JsonMetric {
75
- const values: JsonMetricValue[] = [];
76
- for (const [labelKey, value] of metric.getValues()) {
77
- values.push({
78
- labels: exporter.parseLabels(labelKey),
79
- value
80
- });
81
- }
82
- return {
83
- name: metric.name,
84
- type: 'counter',
85
- help: metric.key.help,
86
- values
87
- };
88
- }
89
-
90
- function convertGauge(metric: GGGauge<any>, exporter: GGJsonMetricsExporter): JsonMetric {
91
- const values: JsonMetricValue[] = [];
92
- for (const [labelKey, value] of metric.getValues()) {
93
- values.push({
94
- labels: exporter.parseLabels(labelKey),
95
- value
96
- });
97
- }
98
- return {
99
- name: metric.name,
100
- type: 'gauge',
101
- help: metric.key.help,
102
- values
103
- };
104
- }
105
-
106
- function convertLazyGauge(metric: GGLazyGauge, exporter: GGJsonMetricsExporter): JsonMetric {
107
- // Lazy gauge computes value on read - no labels
108
- return {
109
- name: metric.name,
110
- type: 'gauge',
111
- help: metric.key.help,
112
- values: [{
113
- labels: {},
114
- value: metric.getValue()
115
- }]
116
- };
117
- }
118
-
119
- function convertHistogram(metric: GGHistogram<any>, exporter: GGJsonMetricsExporter): JsonMetric {
120
- const values: JsonMetricValue[] = [];
121
- const buckets = metric.getBuckets();
122
-
123
- for (const [labelKey, data] of metric.getValues() as Map<string, HistogramData>) {
124
- const bucketObj: Record<string, number> = {};
125
- for (let i = 0; i < buckets.length; i++) {
126
- bucketObj[String(buckets[i])] = data.values[i] ?? 0;
127
- }
128
-
129
- values.push({
130
- labels: exporter.parseLabels(labelKey),
131
- value: {
132
- count: data.count,
133
- sum: data.sum,
134
- avg: data.count > 0 ? data.sum / data.count : 0,
135
- min: data.min === Infinity ? 0 : data.min,
136
- max: data.max === -Infinity ? 0 : data.max,
137
- buckets: bucketObj
138
- }
139
- });
140
- }
141
-
142
- return {
143
- name: metric.name,
144
- type: 'histogram',
145
- help: metric.key.help,
146
- values
147
- };
148
- }
149
-
150
- // Types
151
-
152
- export interface JsonMetricValue {
153
- labels: Record<string, string>;
154
- value: number | JsonHistogramValue;
155
- }
156
-
157
- export interface JsonHistogramValue {
158
- count: number;
159
- sum: number;
160
- avg: number;
161
- min: number;
162
- max: number;
163
- buckets: Record<string, number>;
164
- }
165
-
166
- export interface JsonMetric {
167
- name: string;
168
- type: string;
169
- help: string;
170
- values: JsonMetricValue[];
171
- }
172
-
173
- export interface JsonMetricsOutput {
174
- timestamp: number;
175
- metrics: Record<string, JsonMetric>;
176
- }
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 {GGMetricsExporter, ExporterConfig} from "./GGMetricsExporter.js";
7
+
8
+ export type JsonMetricConverter = (metric: GGMetric<any>, exporter: GGJsonMetricsExporter) => JsonMetric;
9
+
10
+ export class GGJsonMetricsExporter extends GGMetricsExporter<JsonMetricsOutput> {
11
+
12
+ // Static map for extensibility - register converters for new metric types
13
+ private static converters = new Map<Function, JsonMetricConverter>();
14
+
15
+ static {
16
+ GGJsonMetricsExporter.registerConverter(GGCounter, convertCounter);
17
+ GGJsonMetricsExporter.registerConverter(GGGauge, convertGauge);
18
+ GGJsonMetricsExporter.registerConverter(GGLazyGauge, convertLazyGauge);
19
+ GGJsonMetricsExporter.registerConverter(GGHistogram, convertHistogram);
20
+ }
21
+
22
+ /**
23
+ * Register a converter for a custom metric type.
24
+ */
25
+ static registerConverter(metricClass: Function, converter: JsonMetricConverter): void {
26
+ GGJsonMetricsExporter.converters.set(metricClass, converter);
27
+ }
28
+
29
+ constructor(config: ExporterConfig = {}) {
30
+ super(config);
31
+ }
32
+
33
+ getMetrics(): JsonMetricsOutput {
34
+ const output: JsonMetricsOutput = {
35
+ timestamp: Date.now(),
36
+ metrics: {}
37
+ };
38
+
39
+ for (const metric of this.getFilteredMetrics()) {
40
+ output.metrics[metric.name] = this.convertMetric(metric);
41
+ }
42
+
43
+ return output;
44
+ }
45
+
46
+ private convertMetric(metric: GGMetric<any>): JsonMetric {
47
+ const converter = GGJsonMetricsExporter.converters.get(metric.constructor);
48
+ if (!converter) {
49
+ throw new Error(`No converter registered for metric type: ${metric.constructor.name}`);
50
+ }
51
+ return converter(metric, this);
52
+ }
53
+
54
+ /**
55
+ * Parse a label key string into a labels object.
56
+ * Exposed for use by converters.
57
+ */
58
+ parseLabels(labelKey: string): Record<string, string> {
59
+ if (!labelKey) return {};
60
+ const labels: Record<string, string> = {};
61
+ const parts = labelKey.split(',');
62
+ for (const part of parts) {
63
+ const [key, val] = part.split('=');
64
+ if (key && val !== undefined) {
65
+ labels[key] = val;
66
+ }
67
+ }
68
+ return labels;
69
+ }
70
+ }
71
+
72
+ // Built-in converters
73
+
74
+ function convertCounter(metric: GGCounter<any>, exporter: GGJsonMetricsExporter): JsonMetric {
75
+ const values: JsonMetricValue[] = [];
76
+ for (const [labelKey, value] of metric.getValues()) {
77
+ values.push({
78
+ labels: exporter.parseLabels(labelKey),
79
+ value
80
+ });
81
+ }
82
+ return {
83
+ name: metric.name,
84
+ type: 'counter',
85
+ help: metric.key.help,
86
+ values
87
+ };
88
+ }
89
+
90
+ function convertGauge(metric: GGGauge<any>, exporter: GGJsonMetricsExporter): JsonMetric {
91
+ const values: JsonMetricValue[] = [];
92
+ for (const [labelKey, value] of metric.getValues()) {
93
+ values.push({
94
+ labels: exporter.parseLabels(labelKey),
95
+ value
96
+ });
97
+ }
98
+ return {
99
+ name: metric.name,
100
+ type: 'gauge',
101
+ help: metric.key.help,
102
+ values
103
+ };
104
+ }
105
+
106
+ function convertLazyGauge(metric: GGLazyGauge, exporter: GGJsonMetricsExporter): JsonMetric {
107
+ // Lazy gauge computes value on read - no labels
108
+ return {
109
+ name: metric.name,
110
+ type: 'gauge',
111
+ help: metric.key.help,
112
+ values: [{
113
+ labels: {},
114
+ value: metric.getValue()
115
+ }]
116
+ };
117
+ }
118
+
119
+ function convertHistogram(metric: GGHistogram<any>, exporter: GGJsonMetricsExporter): JsonMetric {
120
+ const values: JsonMetricValue[] = [];
121
+ const buckets = metric.getBuckets();
122
+
123
+ for (const [labelKey, data] of metric.getValues() as Map<string, HistogramData>) {
124
+ const bucketObj: Record<string, number> = {};
125
+ for (let i = 0; i < buckets.length; i++) {
126
+ bucketObj[String(buckets[i])] = data.values[i] ?? 0;
127
+ }
128
+
129
+ values.push({
130
+ labels: exporter.parseLabels(labelKey),
131
+ value: {
132
+ count: data.count,
133
+ sum: data.sum,
134
+ avg: data.count > 0 ? data.sum / data.count : 0,
135
+ min: data.min === Infinity ? 0 : data.min,
136
+ max: data.max === -Infinity ? 0 : data.max,
137
+ buckets: bucketObj
138
+ }
139
+ });
140
+ }
141
+
142
+ return {
143
+ name: metric.name,
144
+ type: 'histogram',
145
+ help: metric.key.help,
146
+ values
147
+ };
148
+ }
149
+
150
+ // Types
151
+
152
+ export interface JsonMetricValue {
153
+ labels: Record<string, string>;
154
+ value: number | JsonHistogramValue;
155
+ }
156
+
157
+ export interface JsonHistogramValue {
158
+ count: number;
159
+ sum: number;
160
+ avg: number;
161
+ min: number;
162
+ max: number;
163
+ buckets: Record<string, number>;
164
+ }
165
+
166
+ export interface JsonMetric {
167
+ name: string;
168
+ type: string;
169
+ help: string;
170
+ values: JsonMetricValue[];
171
+ }
172
+
173
+ export interface JsonMetricsOutput {
174
+ timestamp: number;
175
+ metrics: Record<string, JsonMetric>;
176
+ }
@@ -1,88 +1,88 @@
1
- import {GGMetricsStore} from "../GGMetricsStore.js";
2
- import {GG_METRICS} from "../GGMetricsLoader.js";
3
- import {GGMetric} from "../GGMetric.js";
4
- import {GGMetricKey} from "../GGMetricKey.js";
5
-
6
- export interface ExporterConfig {
7
- store?: GGMetricsStore;
8
- include?: unknown[];
9
- exclude?: unknown[];
10
- }
11
-
12
- /**
13
- * Abstract base class for metrics exporters.
14
- * Handles config parsing, metric filtering, and key discovery.
15
- */
16
- export abstract class GGMetricsExporter<TOutput> {
17
- protected readonly store: GGMetricsStore;
18
- private readonly includeKeys?: Set<GGMetricKey<any>>;
19
- private readonly excludeKeys?: Set<GGMetricKey<any>>;
20
-
21
- constructor(config: ExporterConfig = {}) {
22
- this.store = config.store ?? GG_METRICS.get();
23
- this.includeKeys = config.include ? this.discoverKeys(config.include) : undefined;
24
- this.excludeKeys = config.exclude ? this.discoverKeys(config.exclude) : undefined;
25
- }
26
-
27
- /**
28
- * Discover all GGMetricKey instances from an array of objects.
29
- * Objects can be individual keys, or nested structures containing keys.
30
- */
31
- private discoverKeys(objects: unknown[]): Set<GGMetricKey<any>> {
32
- const keys = new Set<GGMetricKey<any>>();
33
- for (const obj of objects) {
34
- this.discoverKeysRecursive(obj, keys);
35
- }
36
- return keys;
37
- }
38
-
39
- private discoverKeysRecursive(obj: unknown, keys: Set<GGMetricKey<any>>): void {
40
- if (obj instanceof GGMetricKey) {
41
- keys.add(obj);
42
- } else if (obj && typeof obj === 'object') {
43
- for (const value of Object.values(obj)) {
44
- this.discoverKeysRecursive(value, keys);
45
- }
46
- }
47
- }
48
-
49
- /**
50
- * Check if a metric should be included in the export.
51
- * Exclude takes precedence over include.
52
- */
53
- protected shouldIncludeMetric(metric: GGMetric<any>): boolean {
54
- // Exclude takes precedence
55
- if (this.excludeKeys?.has(metric.key)) {
56
- return false;
57
- }
58
- // If include is set, only include those
59
- if (this.includeKeys) {
60
- return this.includeKeys.has(metric.key);
61
- }
62
- // Default: include all
63
- return true;
64
- }
65
-
66
- /**
67
- * Get all metrics that pass the include/exclude filters.
68
- */
69
- protected* getFilteredMetrics(): Iterable<GGMetric<any>> {
70
- for (const metric of this.store.getAllMetrics()) {
71
- if (this.shouldIncludeMetric(metric)) {
72
- yield metric;
73
- }
74
- }
75
- }
76
-
77
- /**
78
- * Export metrics to the output format.
79
- */
80
- abstract getMetrics(): TOutput;
81
-
82
- /**
83
- * Export metrics as a JSON string.
84
- */
85
- getMetricsString(): string {
86
- return JSON.stringify(this.getMetrics(), null, 2);
87
- }
88
- }
1
+ import {GGMetricsStore} from "../GGMetricsStore.js";
2
+ import {GG_METRICS} from "../GGMetricsLoader.js";
3
+ import {GGMetric} from "../GGMetric.js";
4
+ import {GGMetricKey} from "../GGMetricKey.js";
5
+
6
+ export interface ExporterConfig {
7
+ store?: GGMetricsStore;
8
+ include?: unknown[];
9
+ exclude?: unknown[];
10
+ }
11
+
12
+ /**
13
+ * Abstract base class for metrics exporters.
14
+ * Handles config parsing, metric filtering, and key discovery.
15
+ */
16
+ export abstract class GGMetricsExporter<TOutput> {
17
+ protected readonly store: GGMetricsStore;
18
+ private readonly includeKeys?: Set<GGMetricKey<any>>;
19
+ private readonly excludeKeys?: Set<GGMetricKey<any>>;
20
+
21
+ constructor(config: ExporterConfig = {}) {
22
+ this.store = config.store ?? GG_METRICS.get();
23
+ this.includeKeys = config.include ? this.discoverKeys(config.include) : undefined;
24
+ this.excludeKeys = config.exclude ? this.discoverKeys(config.exclude) : undefined;
25
+ }
26
+
27
+ /**
28
+ * Discover all GGMetricKey instances from an array of objects.
29
+ * Objects can be individual keys, or nested structures containing keys.
30
+ */
31
+ private discoverKeys(objects: unknown[]): Set<GGMetricKey<any>> {
32
+ const keys = new Set<GGMetricKey<any>>();
33
+ for (const obj of objects) {
34
+ this.discoverKeysRecursive(obj, keys);
35
+ }
36
+ return keys;
37
+ }
38
+
39
+ private discoverKeysRecursive(obj: unknown, keys: Set<GGMetricKey<any>>): void {
40
+ if (obj instanceof GGMetricKey) {
41
+ keys.add(obj);
42
+ } else if (obj && typeof obj === 'object') {
43
+ for (const value of Object.values(obj)) {
44
+ this.discoverKeysRecursive(value, keys);
45
+ }
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Check if a metric should be included in the export.
51
+ * Exclude takes precedence over include.
52
+ */
53
+ protected shouldIncludeMetric(metric: GGMetric<any>): boolean {
54
+ // Exclude takes precedence
55
+ if (this.excludeKeys?.has(metric.key)) {
56
+ return false;
57
+ }
58
+ // If include is set, only include those
59
+ if (this.includeKeys) {
60
+ return this.includeKeys.has(metric.key);
61
+ }
62
+ // Default: include all
63
+ return true;
64
+ }
65
+
66
+ /**
67
+ * Get all metrics that pass the include/exclude filters.
68
+ */
69
+ protected* getFilteredMetrics(): Iterable<GGMetric<any>> {
70
+ for (const metric of this.store.getAllMetrics()) {
71
+ if (this.shouldIncludeMetric(metric)) {
72
+ yield metric;
73
+ }
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Export metrics to the output format.
79
+ */
80
+ abstract getMetrics(): TOutput;
81
+
82
+ /**
83
+ * Export metrics as a JSON string.
84
+ */
85
+ getMetricsString(): string {
86
+ return JSON.stringify(this.getMetrics(), null, 2);
87
+ }
88
+ }