@hkube/metrics 1.0.41 → 1.0.43
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/lib/counter.js +10 -0
- package/lib/gauge.js +10 -0
- package/lib/metrics.js +39 -50
- package/lib/router.js +2 -2
- package/lib/schema.js +6 -3
- package/lib/timeMeasure.js +4 -4
- package/lib/tracer.js +3 -17
- package/lib/validator.js +16 -0
- package/package.json +5 -9
- package/test/counterTest.js +4 -4
- package/test/test.js +4 -0
package/lib/counter.js
CHANGED
|
@@ -24,6 +24,16 @@ class CounterMeasure {
|
|
|
24
24
|
remove() {
|
|
25
25
|
client.register.removeSingleMetric(this._name + COUNTER_POSTFIX);
|
|
26
26
|
}
|
|
27
|
+
removeEntriesByLabel(labelName, labelValue) {
|
|
28
|
+
const keysToDelete = [];
|
|
29
|
+
const hashKeys = Object.keys(this._requestCounter.hashMap);
|
|
30
|
+
hashKeys.forEach((key) => {
|
|
31
|
+
if (this._requestCounter.hashMap[key].labels[labelName] === labelValue) {
|
|
32
|
+
keysToDelete.push(key);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
keysToDelete.forEach(key => delete this._requestCounter.hashMap[key]);
|
|
36
|
+
}
|
|
27
37
|
}
|
|
28
38
|
|
|
29
39
|
module.exports = CounterMeasure;
|
package/lib/gauge.js
CHANGED
|
@@ -36,6 +36,16 @@ class GaugeMeasure {
|
|
|
36
36
|
remove() {
|
|
37
37
|
client.register.removeSingleMetric(this._name + GAUGE_POSTFIX);
|
|
38
38
|
}
|
|
39
|
+
removeEntriesByLabel(labelName, labelValue) {
|
|
40
|
+
const keysToDelete = [];
|
|
41
|
+
const hashKeys = Object.keys(this._requestGauge.hashMap);
|
|
42
|
+
hashKeys.forEach((key) => {
|
|
43
|
+
if (this._requestGauge.hashMap[key].labels[labelName] === labelValue) {
|
|
44
|
+
keysToDelete.push(key);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
keysToDelete.forEach(key => delete this._requestGauge.hashMap[key]);
|
|
48
|
+
}
|
|
39
49
|
}
|
|
40
50
|
|
|
41
51
|
module.exports = GaugeMeasure;
|
package/lib/metrics.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
const optional = require('optional');
|
|
2
|
-
const djsv = require('djsv');
|
|
3
1
|
const { initSchema } = require('./schema');
|
|
4
2
|
const client = require('prom-client');
|
|
3
|
+
const validator = require('./validator');
|
|
5
4
|
const TimeMeasure = require('./timeMeasure');
|
|
6
5
|
const Summary = require('./summary');
|
|
7
6
|
const CounterMeasure = require('./counter');
|
|
8
7
|
const GaugeMeasure = require('./gauge');
|
|
9
8
|
const { addTimeMeasureSchema, addCounterMeasureSchema, addTimeMeasureSummarySchema } = require('./schema');
|
|
10
9
|
const router = require('./router');
|
|
11
|
-
const RestServer =
|
|
10
|
+
const RestServer = require('@hkube/rest-server');
|
|
11
|
+
const promUtils = require('prom-client/lib/util.js');
|
|
12
|
+
|
|
12
13
|
const {
|
|
13
14
|
beforeRoutesMiddlewares,
|
|
14
15
|
afterRoutesMiddlewares,
|
|
@@ -16,6 +17,7 @@ const {
|
|
|
16
17
|
resetMeasureForMiddleware
|
|
17
18
|
} = require('./middleware');
|
|
18
19
|
|
|
20
|
+
|
|
19
21
|
class Metrics {
|
|
20
22
|
constructor() {
|
|
21
23
|
this._metrics = new Map();
|
|
@@ -23,19 +25,12 @@ class Metrics {
|
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
async init(options) {
|
|
26
|
-
this._prefix = (options && options.prefix) || ''
|
|
28
|
+
this._prefix = (options && options.prefix) || '';
|
|
27
29
|
client.register.clear();
|
|
28
30
|
this._metrics.clear();
|
|
29
31
|
resetMeasureForMiddleware(this);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const validatadOptions = validator(options);
|
|
33
|
-
if (validatadOptions.valid) {
|
|
34
|
-
this._options = validatadOptions.instance;
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
throw new Error(validatadOptions.error);
|
|
38
|
-
}
|
|
32
|
+
this._options = options || {};
|
|
33
|
+
validator.validate(initSchema, this._options);
|
|
39
34
|
|
|
40
35
|
if (this._options.collectDefault) {
|
|
41
36
|
const { collectDefaultMetrics } = client;
|
|
@@ -49,9 +44,11 @@ class Metrics {
|
|
|
49
44
|
});
|
|
50
45
|
}
|
|
51
46
|
}
|
|
47
|
+
|
|
52
48
|
getRouter() {
|
|
53
49
|
return { route: this._options.server.path, router: router(this) };
|
|
54
50
|
}
|
|
51
|
+
|
|
55
52
|
getMiddleware() {
|
|
56
53
|
createMeasureForMiddleware(this);
|
|
57
54
|
return {
|
|
@@ -59,6 +56,7 @@ class Metrics {
|
|
|
59
56
|
afterRoutesMiddlewares: [afterRoutesMiddlewares([this._options.server.path])]
|
|
60
57
|
};
|
|
61
58
|
}
|
|
59
|
+
|
|
62
60
|
metrics() {
|
|
63
61
|
return client.register.metrics();
|
|
64
62
|
}
|
|
@@ -77,15 +75,8 @@ class Metrics {
|
|
|
77
75
|
*/
|
|
78
76
|
addTimeMeasure(options) {
|
|
79
77
|
options = options || {};
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (validatadOptions.valid) {
|
|
83
|
-
options = validatadOptions.instance;
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
throw new Error(validatadOptions.error);
|
|
87
|
-
}
|
|
88
|
-
options.name = this.prefix + (options.name || '');
|
|
78
|
+
validator.validate(addTimeMeasureSchema, options);
|
|
79
|
+
options.name = this.prefix + options.name;
|
|
89
80
|
if (this._metrics.has(options.name)) {
|
|
90
81
|
throw new Error(`the measure ${options.name} is already registered`);
|
|
91
82
|
}
|
|
@@ -93,6 +84,7 @@ class Metrics {
|
|
|
93
84
|
this._metrics.set(options.name, measure);
|
|
94
85
|
return measure;
|
|
95
86
|
}
|
|
87
|
+
|
|
96
88
|
/**
|
|
97
89
|
*
|
|
98
90
|
* @param {Object} options
|
|
@@ -104,15 +96,8 @@ class Metrics {
|
|
|
104
96
|
*/
|
|
105
97
|
addSummary(options) {
|
|
106
98
|
options = options || {};
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (validatadOptions.valid) {
|
|
110
|
-
options = validatadOptions.instance;
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
throw new Error(validatadOptions.error);
|
|
114
|
-
}
|
|
115
|
-
options.name = this.prefix + (options.name || '');
|
|
99
|
+
validator.validate(addTimeMeasureSummarySchema, options);
|
|
100
|
+
options.name = this.prefix + options.name;
|
|
116
101
|
if (this._metrics.has(options.name)) {
|
|
117
102
|
throw new Error(`the summary ${options.name} is already registered`);
|
|
118
103
|
}
|
|
@@ -130,15 +115,8 @@ class Metrics {
|
|
|
130
115
|
*/
|
|
131
116
|
addCounterMeasure(options) {
|
|
132
117
|
options = options || {};
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (validatadOptions.valid) {
|
|
136
|
-
options = validatadOptions.instance;
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
throw new Error(validatadOptions.error);
|
|
140
|
-
}
|
|
141
|
-
options.name = this.prefix + (options.name || '');
|
|
118
|
+
validator.validate(addCounterMeasureSchema, options);
|
|
119
|
+
options.name = this.prefix + options.name;
|
|
142
120
|
if (this._metrics.has(options.name)) {
|
|
143
121
|
throw new Error(`the measure ${options.name} is already registered`);
|
|
144
122
|
}
|
|
@@ -146,6 +124,7 @@ class Metrics {
|
|
|
146
124
|
this._metrics.set(options.name, measure);
|
|
147
125
|
return measure;
|
|
148
126
|
}
|
|
127
|
+
|
|
149
128
|
/**
|
|
150
129
|
*
|
|
151
130
|
* @param {Object} options
|
|
@@ -156,15 +135,8 @@ class Metrics {
|
|
|
156
135
|
*/
|
|
157
136
|
addGaugeMeasure(options) {
|
|
158
137
|
options = options || {};
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (validatadOptions.valid) {
|
|
162
|
-
options = validatadOptions.instance;
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
throw new Error(validatadOptions.error);
|
|
166
|
-
}
|
|
167
|
-
options.name = this.prefix + (options.name || '');
|
|
138
|
+
validator.validate(addCounterMeasureSchema, options);
|
|
139
|
+
options.name = this.prefix + options.name;
|
|
168
140
|
if (this._metrics.has(options.name)) {
|
|
169
141
|
throw new Error(`the measure ${options.name} is already registered`);
|
|
170
142
|
}
|
|
@@ -172,7 +144,7 @@ class Metrics {
|
|
|
172
144
|
this._metrics.set(options.name, measure);
|
|
173
145
|
return measure;
|
|
174
146
|
}
|
|
175
|
-
|
|
147
|
+
|
|
176
148
|
/**
|
|
177
149
|
* get metric by name
|
|
178
150
|
* @param name metric name, without prefix
|
|
@@ -196,6 +168,23 @@ class Metrics {
|
|
|
196
168
|
this._metrics.delete(this.prefix + name);
|
|
197
169
|
}
|
|
198
170
|
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* remove all entries in the specified metrics, that are assosiated with the specified jobId
|
|
174
|
+
* @param {*} options
|
|
175
|
+
* @param {string} options.labelName
|
|
176
|
+
* @param {string} options.labelValue
|
|
177
|
+
* @param {string[]} options.metricsToRemove array of metric names to remove
|
|
178
|
+
*/
|
|
179
|
+
removeMeasureEntries(options) {
|
|
180
|
+
const { labelName, labelValue, metricsToRemove } = options;
|
|
181
|
+
metricsToRemove.forEach((metricName) => {
|
|
182
|
+
const measureInstance = this.get(metricName);
|
|
183
|
+
if (measureInstance) {
|
|
184
|
+
measureInstance.removeEntriesByLabel(labelName, labelValue);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
199
188
|
}
|
|
200
189
|
|
|
201
190
|
module.exports = new Metrics();
|
package/lib/router.js
CHANGED
package/lib/schema.js
CHANGED
|
@@ -26,7 +26,8 @@ const addCounterMeasureSchema = {
|
|
|
26
26
|
type: 'object',
|
|
27
27
|
properties: {
|
|
28
28
|
name: {
|
|
29
|
-
type: 'string'
|
|
29
|
+
type: 'string',
|
|
30
|
+
minLength: 1
|
|
30
31
|
},
|
|
31
32
|
description: {
|
|
32
33
|
type: 'string'
|
|
@@ -46,7 +47,8 @@ const addTimeMeasureSchema = {
|
|
|
46
47
|
type: 'object',
|
|
47
48
|
properties: {
|
|
48
49
|
name: {
|
|
49
|
-
type: 'string'
|
|
50
|
+
type: 'string',
|
|
51
|
+
minLength: 1
|
|
50
52
|
},
|
|
51
53
|
description: {
|
|
52
54
|
type: 'string'
|
|
@@ -74,7 +76,8 @@ const addTimeMeasureSummarySchema = {
|
|
|
74
76
|
type: 'object',
|
|
75
77
|
properties: {
|
|
76
78
|
name: {
|
|
77
|
-
type: 'string'
|
|
79
|
+
type: 'string',
|
|
80
|
+
minLength: 1
|
|
78
81
|
},
|
|
79
82
|
description: {
|
|
80
83
|
type: 'string'
|
package/lib/timeMeasure.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const client = require('prom-client');
|
|
2
2
|
const uuid = require('uuid/v4');
|
|
3
|
-
const
|
|
3
|
+
const HISTOGRAM_POSTFIX = '_histogram';
|
|
4
4
|
const COUNTER_POSTFIX = '_counter';
|
|
5
5
|
class TimeMeasure {
|
|
6
6
|
/**
|
|
@@ -9,13 +9,13 @@ class TimeMeasure {
|
|
|
9
9
|
* @param {string} options.name The name of the measure. Must be unique
|
|
10
10
|
* @param {string} options.description The description of the measure. If not specified, the name is used
|
|
11
11
|
* @param {string[]} [options.labels] array of label names
|
|
12
|
-
* @param {number[]} [options.buckets] array of
|
|
12
|
+
* @param {number[]} [options.buckets] array of histogram buckets. in not defined, the default is used
|
|
13
13
|
*/
|
|
14
14
|
constructor(options) {
|
|
15
15
|
this._name = options.name;
|
|
16
16
|
this._startedMeasures = new Map();
|
|
17
17
|
this._histogram = new client.Histogram({
|
|
18
|
-
name: options.name +
|
|
18
|
+
name: options.name + HISTOGRAM_POSTFIX,
|
|
19
19
|
help: options.description || options.name,
|
|
20
20
|
labelNames: options.labels,
|
|
21
21
|
buckets: options.buckets
|
|
@@ -72,7 +72,7 @@ class TimeMeasure {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
remove() {
|
|
75
|
-
client.register.removeSingleMetric(this._name +
|
|
75
|
+
client.register.removeSingleMetric(this._name + HISTOGRAM_POSTFIX);
|
|
76
76
|
client.register.removeSingleMetric(this._name + COUNTER_POSTFIX);
|
|
77
77
|
}
|
|
78
78
|
}
|
package/lib/tracer.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { initTracer } = require('jaeger-client');
|
|
2
2
|
const opentracing = require('opentracing');
|
|
3
|
-
const
|
|
3
|
+
const validator = require('./validator');
|
|
4
4
|
const uuid = require('uuid/v4');
|
|
5
5
|
const { tracerSchema, startSpanSchema, parentRelationships } = require('./schema');
|
|
6
6
|
|
|
@@ -12,27 +12,13 @@ class Tracer {
|
|
|
12
12
|
options = options || {};
|
|
13
13
|
const tracerConfig = options.tracerConfig || {};
|
|
14
14
|
const tracerOptions = options.tracerOptions || {};
|
|
15
|
-
|
|
16
|
-
const validatadOptions = validator(tracerConfig);
|
|
17
|
-
if (validatadOptions.valid) {
|
|
18
|
-
this._options = validatadOptions.instance;
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
throw new Error(validatadOptions.error);
|
|
22
|
-
}
|
|
15
|
+
validator.validate(tracerSchema, tracerConfig);
|
|
23
16
|
this._tracer = initTracer(tracerConfig, tracerOptions);
|
|
24
17
|
}
|
|
25
18
|
|
|
26
19
|
startSpan(options) {
|
|
27
20
|
options = options || {};
|
|
28
|
-
|
|
29
|
-
const validatadOptions = validator(options);
|
|
30
|
-
if (validatadOptions.valid) {
|
|
31
|
-
options = validatadOptions.instance;
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
throw new Error(validatadOptions.error);
|
|
35
|
-
}
|
|
21
|
+
validator.validate(startSpanSchema, options);
|
|
36
22
|
const id = options.id || uuid();
|
|
37
23
|
const spanOptions = {};
|
|
38
24
|
let parentContext;
|
package/lib/validator.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const Ajv = require('ajv');
|
|
2
|
+
const validator = new Ajv({ useDefaults: true });
|
|
3
|
+
|
|
4
|
+
class Validator {
|
|
5
|
+
validate(schema, options) {
|
|
6
|
+
const valid = validator.validate(schema, options);
|
|
7
|
+
|
|
8
|
+
if (!valid) {
|
|
9
|
+
const { errors } = validator;
|
|
10
|
+
const error = validator.errorsText(errors);
|
|
11
|
+
throw new Error(error);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = new Validator();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hkube/metrics",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.43",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -9,18 +9,18 @@
|
|
|
9
9
|
"build": "npm publish"
|
|
10
10
|
},
|
|
11
11
|
"keywords": [],
|
|
12
|
-
"author": "",
|
|
12
|
+
"author": "Hkube",
|
|
13
13
|
"license": "ISC",
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"
|
|
15
|
+
"@hkube/rest-server": "^1.0.16",
|
|
16
|
+
"ajv": "^6.12.0",
|
|
17
|
+
"express": "^4.16.0",
|
|
16
18
|
"jaeger-client": "^3.7.0",
|
|
17
19
|
"opentracing": "^0.14.1",
|
|
18
|
-
"optional": "^0.1.4",
|
|
19
20
|
"prom-client": "^10.2.2",
|
|
20
21
|
"uuid": "^3.1.0"
|
|
21
22
|
},
|
|
22
23
|
"devDependencies": {
|
|
23
|
-
"@hkube/rest-server": "^1.0.4",
|
|
24
24
|
"chai": "^4.1.2",
|
|
25
25
|
"chai-as-promised": "^7.1.1",
|
|
26
26
|
"coveralls": "^3.0.0",
|
|
@@ -32,9 +32,5 @@
|
|
|
32
32
|
"istanbul": "^1.1.0-alpha.1",
|
|
33
33
|
"mocha": "^4.0.1",
|
|
34
34
|
"node-mocks-http": "^1.6.6"
|
|
35
|
-
},
|
|
36
|
-
"peerDependencies": {
|
|
37
|
-
"@hkube/rest-server": "^1.0.3",
|
|
38
|
-
"express": "^4.16.0"
|
|
39
35
|
}
|
|
40
36
|
}
|
package/test/counterTest.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const {metrics} = require('../index');
|
|
1
|
+
const { metrics } = require('../index');
|
|
2
2
|
const chai = require('chai');
|
|
3
3
|
const { expect } = chai;
|
|
4
4
|
const chaiAsPromised = require('chai-as-promised');
|
|
@@ -111,10 +111,10 @@ describe('Counter Measure', () => {
|
|
|
111
111
|
name: 'm1',
|
|
112
112
|
labels: ['l1', 'l2']
|
|
113
113
|
});
|
|
114
|
-
measure.inc({labelValues: { l1: 'l1Val' }});
|
|
115
|
-
measure.inc({labelValues: { l1: 'l1Val' }});
|
|
114
|
+
measure.inc({ labelValues: { l1: 'l1Val' } });
|
|
115
|
+
measure.inc({ labelValues: { l1: 'l1Val' } });
|
|
116
116
|
expect(client.register.metrics()).to.include('m1_counter{l1="l1Val"} 2');
|
|
117
|
-
measure.inc({labelValues: { l1: 'l1Val2' }});
|
|
117
|
+
measure.inc({ labelValues: { l1: 'l1Val2' } });
|
|
118
118
|
expect(client.register.metrics()).to.include('m1_counter{l1="l1Val2"} 1');
|
|
119
119
|
});
|
|
120
120
|
});
|
package/test/test.js
CHANGED
|
@@ -31,6 +31,10 @@ describe('Init metrics', () => {
|
|
|
31
31
|
await metrics.init();
|
|
32
32
|
expect(() => metrics.addTimeMeasure({})).to.throw("data should have required property 'name'");
|
|
33
33
|
});
|
|
34
|
+
it('Should throw with empty name', async () => {
|
|
35
|
+
await metrics.init();
|
|
36
|
+
expect(() => metrics.addTimeMeasure({name: ''})).to.throw('data.name should NOT be shorter than 1 characters');
|
|
37
|
+
});
|
|
34
38
|
it('Should throw if no options', async () => {
|
|
35
39
|
await metrics.init();
|
|
36
40
|
expect(() => metrics.addTimeMeasure()).to.throw("data should have required property 'name'");
|