@grest-ts/metrics 0.0.5 → 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.
- package/LICENSE +21 -21
- package/README.md +45 -40
- package/dist/tsconfig.publish.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/src/GGMetric.ts +124 -124
- package/src/GGMetricKey.ts +38 -38
- package/src/GGMetrics.ts +34 -34
- package/src/GGMetricsDefineStorage.ts +8 -8
- package/src/GGMetricsLoader.ts +21 -21
- package/src/GGMetricsStore.ts +26 -26
- package/src/exporters/GGJsonMetricsExporter.ts +176 -176
- package/src/exporters/GGMetricsExporter.ts +88 -88
- package/src/exporters/GGNestedMetricsExporter.ts +335 -335
- package/src/index-browser.ts +16 -16
- package/src/index-node.ts +21 -21
- package/src/keys/GGCounterKey.ts +29 -29
- package/src/keys/GGGaugeKey.ts +37 -37
- package/src/keys/GGHistogramKey.ts +37 -37
- package/src/keys/GGLazyGaugeKey.ts +36 -36
- package/src/metric/GGCounter.ts +19 -19
- package/src/metric/GGGauge.ts +38 -38
- package/src/metric/GGHistogram.ts +68 -68
- package/src/metric/GGLazyGauge.ts +31 -31
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grest-ts/metrics",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Metrics library for Grest Framework",
|
|
@@ -48,11 +48,11 @@
|
|
|
48
48
|
"node": ">=22"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@grest-ts/common": "0.0.
|
|
52
|
-
"@grest-ts/locator": "0.0.
|
|
51
|
+
"@grest-ts/common": "0.0.7",
|
|
52
|
+
"@grest-ts/locator": "0.0.7"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@grest-ts/testkit": "0.0.
|
|
56
|
-
"@grest-ts/x-packager": "0.0.
|
|
55
|
+
"@grest-ts/testkit": "0.0.7",
|
|
56
|
+
"@grest-ts/x-packager": "0.0.7"
|
|
57
57
|
}
|
|
58
58
|
}
|
package/src/GGMetric.ts
CHANGED
|
@@ -1,124 +1,124 @@
|
|
|
1
|
-
import {GGMetricKey} from "./GGMetricKey";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Configuration for grouping metrics in nested exporters.
|
|
5
|
-
* - labels: Which labels to use for grouping (must be subset of labelNames)
|
|
6
|
-
* - template: Optional template string for the group key. Use {labelName} for placeholders.
|
|
7
|
-
* Missing values become empty strings. Defaults to values.join(',')
|
|
8
|
-
* Example: "{api}.{method}" produces "MyApi.myMethod"
|
|
9
|
-
*/
|
|
10
|
-
export interface GroupByConfig<TLabels extends GGMetricLabels = {}> {
|
|
11
|
-
labels: readonly (keyof TLabels & string)[];
|
|
12
|
-
template?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface GGMetricOptionsBase {
|
|
16
|
-
help: string;
|
|
17
|
-
maxLabelCombinations?: number;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface GGMetricOptionsWithLabels<TLabels extends GGMetricLabels> {
|
|
21
|
-
help: string;
|
|
22
|
-
maxLabelCombinations?: number;
|
|
23
|
-
labelNames: readonly (keyof TLabels & string)[];
|
|
24
|
-
groupBy?: GroupByConfig<TLabels>;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Options for metrics. When TLabels has keys, labelNames is required and must match those keys.
|
|
29
|
-
* Uses {} as "no labels" because keyof {} = never, while keyof Record<string, never> = string.
|
|
30
|
-
*/
|
|
31
|
-
export type GGMetricOptions<TLabels extends GGMetricLabels = {}> =
|
|
32
|
-
keyof TLabels extends never
|
|
33
|
-
? GGMetricOptionsBase
|
|
34
|
-
: GGMetricOptionsWithLabels<TLabels>;
|
|
35
|
-
|
|
36
|
-
export abstract class GGMetric<
|
|
37
|
-
TLabels extends GGMetricLabels = {},
|
|
38
|
-
TValue = unknown,
|
|
39
|
-
TKey extends GGMetricKey<TLabels> = GGMetricKey<TLabels>
|
|
40
|
-
> {
|
|
41
|
-
|
|
42
|
-
public readonly key: TKey;
|
|
43
|
-
private readonly values = new Map<string, TValue>();
|
|
44
|
-
private readonly compiledGetKey: (labels: TLabels) => tMetricKey;
|
|
45
|
-
|
|
46
|
-
public constructor(key: TKey) {
|
|
47
|
-
this.key = key;
|
|
48
|
-
this.compiledGetKey = this.compileGetKey();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
private compileGetKey(): (labels: TLabels) => tMetricKey {
|
|
52
|
-
const labelNames = this.key.labelNames;
|
|
53
|
-
if (labelNames.length === 0) {
|
|
54
|
-
return () => '' as tMetricKey;
|
|
55
|
-
}
|
|
56
|
-
// Build the function body: "name1=" + (l?.name1 ?? "") + ",name2=" + (l?.name2 ?? "")
|
|
57
|
-
let body = 'return ';
|
|
58
|
-
for (let i = 0; i < labelNames.length; i++) {
|
|
59
|
-
const name = labelNames[i];
|
|
60
|
-
if (i > 0) body += '+","+';
|
|
61
|
-
body += `"${name}="+(l?.${name}??"")`;
|
|
62
|
-
}
|
|
63
|
-
return new Function('l', body) as (labels: TLabels) => tMetricKey;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
public get name(): string {
|
|
67
|
-
return this.key.name;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
public reset(): void {
|
|
71
|
-
this.values.clear();
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
protected abstract getDefaultValue(): TValue;
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Can't throw.
|
|
78
|
-
* Uses AOT-compiled function for fast key generation.
|
|
79
|
-
*/
|
|
80
|
-
protected getKey(labels: TLabels): tMetricKey {
|
|
81
|
-
return this.compiledGetKey(labels);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Can't throw.
|
|
86
|
-
*/
|
|
87
|
-
protected getByKey(key: tMetricKey): TValue | undefined {
|
|
88
|
-
const value = this.values.get(key);
|
|
89
|
-
if (value === undefined) {
|
|
90
|
-
if (this.values.size >= this.key.maxLabelCombinations) {
|
|
91
|
-
return undefined;
|
|
92
|
-
}
|
|
93
|
-
const defaultValue = this.getDefaultValue();
|
|
94
|
-
this.setByKey(key, defaultValue);
|
|
95
|
-
return defaultValue;
|
|
96
|
-
} else {
|
|
97
|
-
return value;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
protected setByKey(key: tMetricKey, value: TValue): void {
|
|
102
|
-
this.values.set(key, value);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
public getValue(...args: LabelsArgs<TLabels>): TValue {
|
|
106
|
-
return this.getByKey(this.getKey(args[0] as TLabels));
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
public getValues(): Map<string, TValue> {
|
|
110
|
-
return this.values;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export type tMetricKey = string & { tMetricKey: never };
|
|
115
|
-
|
|
116
|
-
export type GGMetricLabels = Record<string, string | number | boolean>;
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Helper type for labels parameter. Makes labels required when TLabels has keys.
|
|
120
|
-
* - When TLabels = {}, no argument needed
|
|
121
|
-
* - When TLabels has keys, labels argument is required
|
|
122
|
-
*/
|
|
123
|
-
export type LabelsArgs<TLabels extends GGMetricLabels> =
|
|
124
|
-
keyof TLabels extends never ? [] : [labels: TLabels];
|
|
1
|
+
import {GGMetricKey} from "./GGMetricKey";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for grouping metrics in nested exporters.
|
|
5
|
+
* - labels: Which labels to use for grouping (must be subset of labelNames)
|
|
6
|
+
* - template: Optional template string for the group key. Use {labelName} for placeholders.
|
|
7
|
+
* Missing values become empty strings. Defaults to values.join(',')
|
|
8
|
+
* Example: "{api}.{method}" produces "MyApi.myMethod"
|
|
9
|
+
*/
|
|
10
|
+
export interface GroupByConfig<TLabels extends GGMetricLabels = {}> {
|
|
11
|
+
labels: readonly (keyof TLabels & string)[];
|
|
12
|
+
template?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface GGMetricOptionsBase {
|
|
16
|
+
help: string;
|
|
17
|
+
maxLabelCombinations?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface GGMetricOptionsWithLabels<TLabels extends GGMetricLabels> {
|
|
21
|
+
help: string;
|
|
22
|
+
maxLabelCombinations?: number;
|
|
23
|
+
labelNames: readonly (keyof TLabels & string)[];
|
|
24
|
+
groupBy?: GroupByConfig<TLabels>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Options for metrics. When TLabels has keys, labelNames is required and must match those keys.
|
|
29
|
+
* Uses {} as "no labels" because keyof {} = never, while keyof Record<string, never> = string.
|
|
30
|
+
*/
|
|
31
|
+
export type GGMetricOptions<TLabels extends GGMetricLabels = {}> =
|
|
32
|
+
keyof TLabels extends never
|
|
33
|
+
? GGMetricOptionsBase
|
|
34
|
+
: GGMetricOptionsWithLabels<TLabels>;
|
|
35
|
+
|
|
36
|
+
export abstract class GGMetric<
|
|
37
|
+
TLabels extends GGMetricLabels = {},
|
|
38
|
+
TValue = unknown,
|
|
39
|
+
TKey extends GGMetricKey<TLabels> = GGMetricKey<TLabels>
|
|
40
|
+
> {
|
|
41
|
+
|
|
42
|
+
public readonly key: TKey;
|
|
43
|
+
private readonly values = new Map<string, TValue>();
|
|
44
|
+
private readonly compiledGetKey: (labels: TLabels) => tMetricKey;
|
|
45
|
+
|
|
46
|
+
public constructor(key: TKey) {
|
|
47
|
+
this.key = key;
|
|
48
|
+
this.compiledGetKey = this.compileGetKey();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private compileGetKey(): (labels: TLabels) => tMetricKey {
|
|
52
|
+
const labelNames = this.key.labelNames;
|
|
53
|
+
if (labelNames.length === 0) {
|
|
54
|
+
return () => '' as tMetricKey;
|
|
55
|
+
}
|
|
56
|
+
// Build the function body: "name1=" + (l?.name1 ?? "") + ",name2=" + (l?.name2 ?? "")
|
|
57
|
+
let body = 'return ';
|
|
58
|
+
for (let i = 0; i < labelNames.length; i++) {
|
|
59
|
+
const name = labelNames[i];
|
|
60
|
+
if (i > 0) body += '+","+';
|
|
61
|
+
body += `"${name}="+(l?.${name}??"")`;
|
|
62
|
+
}
|
|
63
|
+
return new Function('l', body) as (labels: TLabels) => tMetricKey;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public get name(): string {
|
|
67
|
+
return this.key.name;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public reset(): void {
|
|
71
|
+
this.values.clear();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
protected abstract getDefaultValue(): TValue;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Can't throw.
|
|
78
|
+
* Uses AOT-compiled function for fast key generation.
|
|
79
|
+
*/
|
|
80
|
+
protected getKey(labels: TLabels): tMetricKey {
|
|
81
|
+
return this.compiledGetKey(labels);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Can't throw.
|
|
86
|
+
*/
|
|
87
|
+
protected getByKey(key: tMetricKey): TValue | undefined {
|
|
88
|
+
const value = this.values.get(key);
|
|
89
|
+
if (value === undefined) {
|
|
90
|
+
if (this.values.size >= this.key.maxLabelCombinations) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
const defaultValue = this.getDefaultValue();
|
|
94
|
+
this.setByKey(key, defaultValue);
|
|
95
|
+
return defaultValue;
|
|
96
|
+
} else {
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
protected setByKey(key: tMetricKey, value: TValue): void {
|
|
102
|
+
this.values.set(key, value);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public getValue(...args: LabelsArgs<TLabels>): TValue {
|
|
106
|
+
return this.getByKey(this.getKey(args[0] as TLabels));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public getValues(): Map<string, TValue> {
|
|
110
|
+
return this.values;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export type tMetricKey = string & { tMetricKey: never };
|
|
115
|
+
|
|
116
|
+
export type GGMetricLabels = Record<string, string | number | boolean>;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Helper type for labels parameter. Makes labels required when TLabels has keys.
|
|
120
|
+
* - When TLabels = {}, no argument needed
|
|
121
|
+
* - When TLabels has keys, labels argument is required
|
|
122
|
+
*/
|
|
123
|
+
export type LabelsArgs<TLabels extends GGMetricLabels> =
|
|
124
|
+
keyof TLabels extends never ? [] : [labels: TLabels];
|
package/src/GGMetricKey.ts
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
import {GGMetrics} from "./GGMetrics.js";
|
|
2
|
-
import {GG_METRICS} from "./GGMetricsLoader.js";
|
|
3
|
-
import {GGMetric, GGMetricLabels, GGMetricOptions, GroupByConfig} from "./GGMetric.js";
|
|
4
|
-
|
|
5
|
-
export abstract class GGMetricKey<
|
|
6
|
-
TLabels extends GGMetricLabels = {},
|
|
7
|
-
TMetric extends GGMetric<TLabels, any, any> = GGMetric<TLabels, any, any>
|
|
8
|
-
> {
|
|
9
|
-
|
|
10
|
-
public readonly root: string;
|
|
11
|
-
public readonly name: string;
|
|
12
|
-
public readonly help: string;
|
|
13
|
-
public readonly labelNames: readonly string[];
|
|
14
|
-
public readonly maxLabelCombinations?: number;
|
|
15
|
-
public readonly groupBy?: GroupByConfig<TLabels>;
|
|
16
|
-
|
|
17
|
-
protected constructor(name: string, options: GGMetricOptions<TLabels>) {
|
|
18
|
-
this.root = GGMetrics.getDefinitionContext();
|
|
19
|
-
if (!this.root) {
|
|
20
|
-
throw new Error("Metric key must be created inside GGMetrics.define()");
|
|
21
|
-
}
|
|
22
|
-
this.name = this.root + name;
|
|
23
|
-
this.help = options.help;
|
|
24
|
-
this.labelNames = ('labelNames' in options ? options.labelNames : []) as readonly (keyof TLabels & string)[];
|
|
25
|
-
this.maxLabelCombinations = options.maxLabelCombinations;
|
|
26
|
-
this.groupBy = ('groupBy' in options ? options.groupBy : undefined) as GroupByConfig<TLabels> | undefined;
|
|
27
|
-
Object.freeze(this.labelNames);
|
|
28
|
-
if (this.groupBy) {
|
|
29
|
-
Object.freeze(this.groupBy.labels);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
protected getMetric(): TMetric {
|
|
34
|
-
return GG_METRICS.get().get(this) as TMetric;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
public abstract create(): TMetric;
|
|
38
|
-
}
|
|
1
|
+
import {GGMetrics} from "./GGMetrics.js";
|
|
2
|
+
import {GG_METRICS} from "./GGMetricsLoader.js";
|
|
3
|
+
import {GGMetric, GGMetricLabels, GGMetricOptions, GroupByConfig} from "./GGMetric.js";
|
|
4
|
+
|
|
5
|
+
export abstract class GGMetricKey<
|
|
6
|
+
TLabels extends GGMetricLabels = {},
|
|
7
|
+
TMetric extends GGMetric<TLabels, any, any> = GGMetric<TLabels, any, any>
|
|
8
|
+
> {
|
|
9
|
+
|
|
10
|
+
public readonly root: string;
|
|
11
|
+
public readonly name: string;
|
|
12
|
+
public readonly help: string;
|
|
13
|
+
public readonly labelNames: readonly string[];
|
|
14
|
+
public readonly maxLabelCombinations?: number;
|
|
15
|
+
public readonly groupBy?: GroupByConfig<TLabels>;
|
|
16
|
+
|
|
17
|
+
protected constructor(name: string, options: GGMetricOptions<TLabels>) {
|
|
18
|
+
this.root = GGMetrics.getDefinitionContext();
|
|
19
|
+
if (!this.root) {
|
|
20
|
+
throw new Error("Metric key must be created inside GGMetrics.define()");
|
|
21
|
+
}
|
|
22
|
+
this.name = this.root + name;
|
|
23
|
+
this.help = options.help;
|
|
24
|
+
this.labelNames = ('labelNames' in options ? options.labelNames : []) as readonly (keyof TLabels & string)[];
|
|
25
|
+
this.maxLabelCombinations = options.maxLabelCombinations;
|
|
26
|
+
this.groupBy = ('groupBy' in options ? options.groupBy : undefined) as GroupByConfig<TLabels> | undefined;
|
|
27
|
+
Object.freeze(this.labelNames);
|
|
28
|
+
if (this.groupBy) {
|
|
29
|
+
Object.freeze(this.groupBy.labels);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
protected getMetric(): TMetric {
|
|
34
|
+
return GG_METRICS.get().get(this) as TMetric;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public abstract create(): TMetric;
|
|
38
|
+
}
|
package/src/GGMetrics.ts
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
import {GGMetricKey} from "./GGMetricKey";
|
|
2
|
-
import {METRICS_DEFINE_CONTEXT} from "./GGMetricsDefineStorage";
|
|
3
|
-
|
|
4
|
-
export type GGMetricsDefinition<T> = T & { __isGGMetricsDefinition: never };
|
|
5
|
-
|
|
6
|
-
export class GGMetrics {
|
|
7
|
-
|
|
8
|
-
public static define<T>(name: string, define: () => T): GGMetricsDefinition<T> {
|
|
9
|
-
return METRICS_DEFINE_CONTEXT.run(name, define) as GGMetricsDefinition<T>
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
public static getDefinitionContext(): string {
|
|
13
|
-
return METRICS_DEFINE_CONTEXT.getStore()!
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
public static toJSON(metrics: GGMetricsDefinition<any>): Record<string, unknown> {
|
|
17
|
-
const result: Record<string, unknown> = {};
|
|
18
|
-
this.collectKeys(metrics, result);
|
|
19
|
-
return result;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
private static collectKeys(obj: unknown, result: Record<string, unknown>): void {
|
|
23
|
-
if (obj instanceof GGMetricKey) {
|
|
24
|
-
result[obj.name] = {
|
|
25
|
-
type: obj.constructor.name,
|
|
26
|
-
help: obj.help
|
|
27
|
-
};
|
|
28
|
-
} else if (obj && typeof obj === 'object') {
|
|
29
|
-
for (const value of Object.values(obj)) {
|
|
30
|
-
this.collectKeys(value, result);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
1
|
+
import {GGMetricKey} from "./GGMetricKey";
|
|
2
|
+
import {METRICS_DEFINE_CONTEXT} from "./GGMetricsDefineStorage";
|
|
3
|
+
|
|
4
|
+
export type GGMetricsDefinition<T> = T & { __isGGMetricsDefinition: never };
|
|
5
|
+
|
|
6
|
+
export class GGMetrics {
|
|
7
|
+
|
|
8
|
+
public static define<T>(name: string, define: () => T): GGMetricsDefinition<T> {
|
|
9
|
+
return METRICS_DEFINE_CONTEXT.run(name, define) as GGMetricsDefinition<T>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public static getDefinitionContext(): string {
|
|
13
|
+
return METRICS_DEFINE_CONTEXT.getStore()!
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public static toJSON(metrics: GGMetricsDefinition<any>): Record<string, unknown> {
|
|
17
|
+
const result: Record<string, unknown> = {};
|
|
18
|
+
this.collectKeys(metrics, result);
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private static collectKeys(obj: unknown, result: Record<string, unknown>): void {
|
|
23
|
+
if (obj instanceof GGMetricKey) {
|
|
24
|
+
result[obj.name] = {
|
|
25
|
+
type: obj.constructor.name,
|
|
26
|
+
help: obj.help
|
|
27
|
+
};
|
|
28
|
+
} else if (obj && typeof obj === 'object') {
|
|
29
|
+
for (const value of Object.values(obj)) {
|
|
30
|
+
this.collectKeys(value, result);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {BrowserAsyncStorage, type IAsyncStorage} from "@grest-ts/common";
|
|
2
|
-
|
|
3
|
-
// Browser-safe by default. Node.js entry replaces with real AsyncLocalStorage.
|
|
4
|
-
export let METRICS_DEFINE_CONTEXT: IAsyncStorage<string> = new BrowserAsyncStorage();
|
|
5
|
-
|
|
6
|
-
export function _initMetricsStorage(storage: IAsyncStorage<string>): void {
|
|
7
|
-
METRICS_DEFINE_CONTEXT = storage;
|
|
8
|
-
}
|
|
1
|
+
import {BrowserAsyncStorage, type IAsyncStorage} from "@grest-ts/common";
|
|
2
|
+
|
|
3
|
+
// Browser-safe by default. Node.js entry replaces with real AsyncLocalStorage.
|
|
4
|
+
export let METRICS_DEFINE_CONTEXT: IAsyncStorage<string> = new BrowserAsyncStorage();
|
|
5
|
+
|
|
6
|
+
export function _initMetricsStorage(storage: IAsyncStorage<string>): void {
|
|
7
|
+
METRICS_DEFINE_CONTEXT = storage;
|
|
8
|
+
}
|
package/src/GGMetricsLoader.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import {GGMetricsStore} from "./GGMetricsStore";
|
|
2
|
-
import {GGLocator, GGLocatorKey, GGLocatorServiceType} from "@grest-ts/locator";
|
|
3
|
-
|
|
4
|
-
export const GG_METRICS = new GGLocatorKey<GGMetricsStore>("GGMetrics");
|
|
5
|
-
|
|
6
|
-
export class GGMetricsLoader {
|
|
7
|
-
|
|
8
|
-
private readonly store: GGMetricsStore;
|
|
9
|
-
|
|
10
|
-
constructor(store?: GGMetricsStore) {
|
|
11
|
-
this.store = store ?? new GGMetricsStore();
|
|
12
|
-
|
|
13
|
-
GGLocator.getScope().setWithLifecycle(GG_METRICS, this.store, {
|
|
14
|
-
type: GGLocatorServiceType.CONFIG + 1,
|
|
15
|
-
start: async () => {
|
|
16
|
-
},
|
|
17
|
-
teardown: async () => {
|
|
18
|
-
}
|
|
19
|
-
})
|
|
20
|
-
}
|
|
21
|
-
}
|
|
1
|
+
import {GGMetricsStore} from "./GGMetricsStore";
|
|
2
|
+
import {GGLocator, GGLocatorKey, GGLocatorServiceType} from "@grest-ts/locator";
|
|
3
|
+
|
|
4
|
+
export const GG_METRICS = new GGLocatorKey<GGMetricsStore>("GGMetrics");
|
|
5
|
+
|
|
6
|
+
export class GGMetricsLoader {
|
|
7
|
+
|
|
8
|
+
private readonly store: GGMetricsStore;
|
|
9
|
+
|
|
10
|
+
constructor(store?: GGMetricsStore) {
|
|
11
|
+
this.store = store ?? new GGMetricsStore();
|
|
12
|
+
|
|
13
|
+
GGLocator.getScope().setWithLifecycle(GG_METRICS, this.store, {
|
|
14
|
+
type: GGLocatorServiceType.CONFIG + 1,
|
|
15
|
+
start: async () => {
|
|
16
|
+
},
|
|
17
|
+
teardown: async () => {
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
}
|
package/src/GGMetricsStore.ts
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import {GGMetric, GGMetricLabels} from "./GGMetric.js";
|
|
2
|
-
import {GGMetricKey} from "./GGMetricKey.js";
|
|
3
|
-
|
|
4
|
-
export class GGMetricsStore {
|
|
5
|
-
|
|
6
|
-
private readonly metrics = new Map<string, GGMetric<any>>();
|
|
7
|
-
|
|
8
|
-
public get<TLabel extends GGMetricLabels>(key: GGMetricKey<TLabel>): GGMetric<TLabel> {
|
|
9
|
-
let metric = this.metrics.get(key.name);
|
|
10
|
-
if (!metric) {
|
|
11
|
-
metric = key.create();
|
|
12
|
-
this.metrics.set(key.name, metric);
|
|
13
|
-
}
|
|
14
|
-
return metric;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
public getAllMetrics(): IterableIterator<GGMetric<any>> {
|
|
18
|
-
return this.metrics.values();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
public reset(): void {
|
|
22
|
-
for (const metric of this.metrics.values()) {
|
|
23
|
-
metric.reset();
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
1
|
+
import {GGMetric, GGMetricLabels} from "./GGMetric.js";
|
|
2
|
+
import {GGMetricKey} from "./GGMetricKey.js";
|
|
3
|
+
|
|
4
|
+
export class GGMetricsStore {
|
|
5
|
+
|
|
6
|
+
private readonly metrics = new Map<string, GGMetric<any>>();
|
|
7
|
+
|
|
8
|
+
public get<TLabel extends GGMetricLabels>(key: GGMetricKey<TLabel>): GGMetric<TLabel> {
|
|
9
|
+
let metric = this.metrics.get(key.name);
|
|
10
|
+
if (!metric) {
|
|
11
|
+
metric = key.create();
|
|
12
|
+
this.metrics.set(key.name, metric);
|
|
13
|
+
}
|
|
14
|
+
return metric;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public getAllMetrics(): IterableIterator<GGMetric<any>> {
|
|
18
|
+
return this.metrics.values();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public reset(): void {
|
|
22
|
+
for (const metric of this.metrics.values()) {
|
|
23
|
+
metric.reset();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|