@tramvai/module-metrics 2.70.0 → 2.72.0
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/browser.js +5 -156
- package/lib/constants.es.js +3 -0
- package/lib/constants.js +7 -0
- package/lib/instantMetrics/browser.browser.js +70 -0
- package/lib/instantMetrics/server.es.js +97 -0
- package/lib/instantMetrics/server.js +103 -0
- package/lib/instantMetrics/shared.browser.js +12 -0
- package/lib/instantMetrics/shared.es.js +12 -0
- package/lib/instantMetrics/shared.js +16 -0
- package/lib/instantMetrics/store.browser.js +8 -0
- package/lib/instantMetrics/store.es.js +8 -0
- package/lib/instantMetrics/store.js +13 -0
- package/lib/metrics/commandLine.es.js +22 -0
- package/lib/metrics/commandLine.js +26 -0
- package/lib/metrics/eventLoop.es.js +23 -0
- package/lib/metrics/eventLoop.js +27 -0
- package/lib/performance-devtools/PerfMetrics.browser.js +56 -0
- package/lib/performance-devtools/startMeasure.browser.js +14 -0
- package/lib/performance-devtools/supportsUserTiming.browser.js +7 -0
- package/lib/performance-devtools/uniqueId.browser.js +7 -0
- package/lib/request/MetricsServicesRegistry.es.js +56 -0
- package/lib/request/MetricsServicesRegistry.js +64 -0
- package/lib/request/PrefixTree.es.js +48 -0
- package/lib/request/PrefixTree.js +52 -0
- package/lib/request/createRequestWithMetrics.es.js +123 -0
- package/lib/request/createRequestWithMetrics.js +128 -0
- package/lib/request/index.es.js +57 -0
- package/lib/request/index.js +65 -0
- package/lib/request/initRequestsMetrics.es.js +53 -0
- package/lib/request/initRequestsMetrics.js +61 -0
- package/lib/server.es.js +9 -469
- package/lib/server.js +9 -475
- package/package.json +19 -20
package/lib/server.js
CHANGED
|
@@ -11,487 +11,21 @@ var tokensMetrics = require('@tramvai/tokens-metrics');
|
|
|
11
11
|
var measureFastifyRequests = require('@tinkoff/measure-fastify-requests');
|
|
12
12
|
var promClient = require('prom-client');
|
|
13
13
|
var flatten = require('@tinkoff/utils/array/flatten');
|
|
14
|
-
var
|
|
15
|
-
var
|
|
16
|
-
var
|
|
17
|
-
var
|
|
18
|
-
var
|
|
19
|
-
var monkeypatch = require('@tinkoff/monkeypatch');
|
|
20
|
-
var isString = require('@tinkoff/utils/is/string');
|
|
21
|
-
var papi = require('@tramvai/papi');
|
|
22
|
-
var fromPairs = require('@tinkoff/utils/object/fromPairs');
|
|
23
|
-
var state = require('@tramvai/state');
|
|
14
|
+
var index = require('./request/index.js');
|
|
15
|
+
var server = require('./instantMetrics/server.js');
|
|
16
|
+
var eventLoop = require('./metrics/eventLoop.js');
|
|
17
|
+
var commandLine = require('./metrics/commandLine.js');
|
|
18
|
+
var createRequestWithMetrics = require('./request/createRequestWithMetrics.js');
|
|
24
19
|
|
|
25
20
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
26
21
|
|
|
27
22
|
var flatten__default = /*#__PURE__*/_interopDefaultLegacy(flatten);
|
|
28
|
-
var noop__default = /*#__PURE__*/_interopDefaultLegacy(noop);
|
|
29
|
-
var https__default = /*#__PURE__*/_interopDefaultLegacy(https);
|
|
30
|
-
var http__default = /*#__PURE__*/_interopDefaultLegacy(http);
|
|
31
|
-
var monkeypatch__default = /*#__PURE__*/_interopDefaultLegacy(monkeypatch);
|
|
32
|
-
var isString__default = /*#__PURE__*/_interopDefaultLegacy(isString);
|
|
33
|
-
var fromPairs__default = /*#__PURE__*/_interopDefaultLegacy(fromPairs);
|
|
34
|
-
|
|
35
|
-
// https://nodejs.org/api/errors.html#nodejs-error-codes - Common system errors possible for net/http/dns
|
|
36
|
-
const POSSIBLE_ERRORS = [
|
|
37
|
-
'EADDRINUSE',
|
|
38
|
-
'ECONNREFUSED',
|
|
39
|
-
'ECONNRESET',
|
|
40
|
-
'ENOTFOUND',
|
|
41
|
-
'EPIPE',
|
|
42
|
-
'ETIMEDOUT',
|
|
43
|
-
];
|
|
44
|
-
const getUrlAndOptions = (args) => {
|
|
45
|
-
let url$1;
|
|
46
|
-
let options;
|
|
47
|
-
// У request первый аргумент либо урл либо объект опций, кейс когда первого аргумента нет не валиден
|
|
48
|
-
const isUrlStringFirst = args[0].constructor === String;
|
|
49
|
-
const isUrlObjectFirst = args[0].constructor === URL;
|
|
50
|
-
const isOptionsFirst = !isUrlStringFirst && !isUrlObjectFirst;
|
|
51
|
-
const isOptionsSecond = !isOptionsFirst && !(args[0] instanceof Function);
|
|
52
|
-
if (isUrlStringFirst) {
|
|
53
|
-
[url$1] = args;
|
|
54
|
-
}
|
|
55
|
-
if (isUrlObjectFirst) {
|
|
56
|
-
url$1 = url.format(args[0]);
|
|
57
|
-
}
|
|
58
|
-
if (isOptionsFirst) {
|
|
59
|
-
[options] = args;
|
|
60
|
-
// Тут учитываем случай если передаётся не href в options, а отдельно protocol, host, port, path
|
|
61
|
-
if (options.href) {
|
|
62
|
-
url$1 = options.href;
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
const urlString = url.format({
|
|
66
|
-
protocol: options.protocol,
|
|
67
|
-
host: options.hostname || options.host,
|
|
68
|
-
port: options.port,
|
|
69
|
-
pathname: options.path,
|
|
70
|
-
});
|
|
71
|
-
// format где-то внутри делает encodeURIComponent и из-за этого потом не может обрезать query
|
|
72
|
-
try {
|
|
73
|
-
url$1 = decodeURIComponent(urlString);
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
url$1 = urlString;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
if (isOptionsSecond) {
|
|
81
|
-
[, options] = args;
|
|
82
|
-
}
|
|
83
|
-
const parsedUrl = new URL(url$1);
|
|
84
|
-
const urlWOQuery = parsedUrl.origin + parsedUrl.pathname;
|
|
85
|
-
return [urlWOQuery, options || {}, parsedUrl];
|
|
86
|
-
};
|
|
87
|
-
// in seconds
|
|
88
|
-
const getDuration = (current, prev) =>
|
|
89
|
-
// max to avoid negative values and turn that into zero
|
|
90
|
-
prev === 0 ? 0 : Math.max((current - prev) / 1000, 0);
|
|
91
|
-
const createRequestWithMetrics = ({ metricsInstances: { requestsTotal, requestsErrors, requestsDuration, dnsResolveDuration, tcpConnectDuration, tlsHandshakeDuration, }, getServiceName, config, }) => {
|
|
92
|
-
const socketSet = new WeakSet();
|
|
93
|
-
return function requestWithMetrics(originalRequest, ...args) {
|
|
94
|
-
const [url, options] = getUrlAndOptions(args);
|
|
95
|
-
const serviceName = getServiceName(url);
|
|
96
|
-
const req = originalRequest.apply(this, args);
|
|
97
|
-
const timerDone = requestsDuration.startTimer();
|
|
98
|
-
const labelsValues = {
|
|
99
|
-
method: options.method || 'unknown',
|
|
100
|
-
service: serviceName || new URL(url).origin || 'unknown',
|
|
101
|
-
status: 'unknown',
|
|
102
|
-
};
|
|
103
|
-
req.on('response', (res) => {
|
|
104
|
-
labelsValues.status = res.statusCode.toString();
|
|
105
|
-
if (res.statusCode >= 400) {
|
|
106
|
-
requestsErrors.inc(labelsValues);
|
|
107
|
-
}
|
|
108
|
-
requestsTotal.inc(labelsValues);
|
|
109
|
-
timerDone(labelsValues);
|
|
110
|
-
});
|
|
111
|
-
req.on('error', (e) => {
|
|
112
|
-
if (POSSIBLE_ERRORS.includes(e === null || e === void 0 ? void 0 : e.code)) {
|
|
113
|
-
labelsValues.status = req.aborted ? 'aborted' : e.code;
|
|
114
|
-
}
|
|
115
|
-
requestsTotal.inc(labelsValues);
|
|
116
|
-
requestsErrors.inc(labelsValues);
|
|
117
|
-
timerDone(labelsValues);
|
|
118
|
-
});
|
|
119
|
-
if (config.enableConnectionResolveMetrics) {
|
|
120
|
-
req.on('socket', (socket) => {
|
|
121
|
-
// due to keep-alive tcp option sockets might be reused
|
|
122
|
-
// ignore them because they have already emitted events we are interested in
|
|
123
|
-
if (socketSet.has(socket)) {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
socketSet.add(socket);
|
|
127
|
-
const timings = {
|
|
128
|
-
start: Date.now(),
|
|
129
|
-
lookupEnd: 0,
|
|
130
|
-
connectEnd: 0,
|
|
131
|
-
secureConnectEnd: 0,
|
|
132
|
-
};
|
|
133
|
-
const { service } = labelsValues;
|
|
134
|
-
socket.on('lookup', () => {
|
|
135
|
-
timings.lookupEnd = Date.now();
|
|
136
|
-
dnsResolveDuration.observe({ service }, getDuration(timings.lookupEnd, timings.start));
|
|
137
|
-
});
|
|
138
|
-
socket.on('connect', () => {
|
|
139
|
-
timings.connectEnd = Date.now();
|
|
140
|
-
tcpConnectDuration.observe({ service }, getDuration(timings.connectEnd, timings.lookupEnd));
|
|
141
|
-
});
|
|
142
|
-
socket.on('secureConnect', () => {
|
|
143
|
-
timings.secureConnectEnd = Date.now();
|
|
144
|
-
tlsHandshakeDuration.observe({ service }, getDuration(timings.secureConnectEnd, timings.connectEnd));
|
|
145
|
-
});
|
|
146
|
-
socket.on('close', () => {
|
|
147
|
-
socketSet.delete(socket);
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
return req;
|
|
152
|
-
};
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
const DEFAULT_BUCKETS = [0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 20, 40, 60];
|
|
156
|
-
|
|
157
|
-
const initRequestsMetrics = ({ metrics, getServiceName, http, https, createRequestWithMetrics, config, }) => {
|
|
158
|
-
const metricsInstances = {
|
|
159
|
-
requestsTotal: metrics.counter({
|
|
160
|
-
name: 'http_sent_requests_total',
|
|
161
|
-
help: 'Number of requests sent',
|
|
162
|
-
labelNames: ['status', 'method', 'service'],
|
|
163
|
-
}),
|
|
164
|
-
requestsErrors: metrics.counter({
|
|
165
|
-
name: 'http_sent_requests_errors',
|
|
166
|
-
help: 'Number of requests that failed',
|
|
167
|
-
labelNames: ['status', 'method', 'service'],
|
|
168
|
-
}),
|
|
169
|
-
requestsDuration: metrics.histogram({
|
|
170
|
-
name: 'http_sent_requests_duration',
|
|
171
|
-
help: 'Execution time of the sent requests',
|
|
172
|
-
labelNames: ['status', 'method', 'service'],
|
|
173
|
-
buckets: DEFAULT_BUCKETS,
|
|
174
|
-
}),
|
|
175
|
-
dnsResolveDuration: metrics.histogram({
|
|
176
|
-
name: 'dns_resolve_duration',
|
|
177
|
-
help: 'Time for dns resolve of the outhgoing requests',
|
|
178
|
-
labelNames: ['service'],
|
|
179
|
-
buckets: DEFAULT_BUCKETS,
|
|
180
|
-
}),
|
|
181
|
-
tcpConnectDuration: metrics.histogram({
|
|
182
|
-
name: 'tcp_connect_duration',
|
|
183
|
-
help: 'Duration of tcp connect of the outgoing requests',
|
|
184
|
-
labelNames: ['service'],
|
|
185
|
-
buckets: DEFAULT_BUCKETS,
|
|
186
|
-
}),
|
|
187
|
-
tlsHandshakeDuration: metrics.histogram({
|
|
188
|
-
name: 'tls_handshake_duration',
|
|
189
|
-
help: 'Duration of tls handshake of the outgoing requests',
|
|
190
|
-
labelNames: ['service'],
|
|
191
|
-
buckets: DEFAULT_BUCKETS,
|
|
192
|
-
}),
|
|
193
|
-
};
|
|
194
|
-
monkeypatch__default["default"]({
|
|
195
|
-
obj: https,
|
|
196
|
-
method: 'request',
|
|
197
|
-
handler: createRequestWithMetrics({ metricsInstances, getServiceName, config }),
|
|
198
|
-
});
|
|
199
|
-
monkeypatch__default["default"]({
|
|
200
|
-
obj: http,
|
|
201
|
-
method: 'request',
|
|
202
|
-
handler: createRequestWithMetrics({ metricsInstances, getServiceName, config }),
|
|
203
|
-
});
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
// Дерево использует подход структуры данных trie для прямого доступа к serviceName, но в отличие от
|
|
207
|
-
// trie возвращает не все значения соответствующие префиксу, а значение соответствующее самому
|
|
208
|
-
// длинному ключу в дереве который является подстрокой входной строки
|
|
209
|
-
class PrefixTree {
|
|
210
|
-
constructor(options = {}) {
|
|
211
|
-
this.index = Object.create(null);
|
|
212
|
-
this.delimiter = options.delimiter || '';
|
|
213
|
-
}
|
|
214
|
-
// На случай если value не примитивные значения есть возможность передать функцию и там как угодно
|
|
215
|
-
// модифицировать объект
|
|
216
|
-
set(key, valueOrUpdateFn) {
|
|
217
|
-
let tree = this.index;
|
|
218
|
-
const parts = key.split(this.delimiter);
|
|
219
|
-
do {
|
|
220
|
-
const letter = parts.shift();
|
|
221
|
-
if (letter === '')
|
|
222
|
-
continue;
|
|
223
|
-
if (!tree[letter]) {
|
|
224
|
-
tree[letter] = Object.create(null);
|
|
225
|
-
}
|
|
226
|
-
tree = tree[letter];
|
|
227
|
-
} while (parts.length);
|
|
228
|
-
if (valueOrUpdateFn instanceof Function) {
|
|
229
|
-
tree.value = valueOrUpdateFn(tree.value);
|
|
230
|
-
}
|
|
231
|
-
else {
|
|
232
|
-
tree.value = valueOrUpdateFn;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
get(key) {
|
|
236
|
-
let tree = this.index;
|
|
237
|
-
const parts = key.split(this.delimiter);
|
|
238
|
-
let prevTree;
|
|
239
|
-
do {
|
|
240
|
-
const letter = parts.shift();
|
|
241
|
-
if (letter === '')
|
|
242
|
-
continue;
|
|
243
|
-
prevTree = tree;
|
|
244
|
-
tree = tree[letter];
|
|
245
|
-
} while (parts.length && tree);
|
|
246
|
-
if (tree) {
|
|
247
|
-
return tree.value;
|
|
248
|
-
}
|
|
249
|
-
return prevTree.value;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const NO_PROTOCOL = 'NO PROTOCOL';
|
|
254
|
-
const PROTOCOL_REGEX = /^([a-z0-9.+-]+:)/i;
|
|
255
|
-
const splitProtocolAndUrl = (url) => {
|
|
256
|
-
// Регулярка используется потому что протокол обязателен и класс URL не распарсит такую строку
|
|
257
|
-
const urlMatches = url.match(PROTOCOL_REGEX);
|
|
258
|
-
const protocol = urlMatches && urlMatches[0];
|
|
259
|
-
// Под + 2 тут подразумевается два слеша после протокола например для http: обрежется http://
|
|
260
|
-
const urlWOProtocol = protocol ? url.slice(protocol.length + 2) : url;
|
|
261
|
-
return [protocol, urlWOProtocol];
|
|
262
|
-
};
|
|
263
|
-
class MetricsServicesRegistry {
|
|
264
|
-
constructor() {
|
|
265
|
-
this.registryPrefixTree = new PrefixTree({
|
|
266
|
-
delimiter: '/',
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
registerEnv(env) {
|
|
270
|
-
Object.keys(env).forEach((name) => {
|
|
271
|
-
// В env могут быть undefined, и number, т.к. это явно не урлы, просто пропускаем
|
|
272
|
-
if (isString__default["default"](env[name])) {
|
|
273
|
-
this.register(env[name], name);
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
register(url, serviceName) {
|
|
278
|
-
if (!url) {
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
const [protocol, urlWOProtocol] = splitProtocolAndUrl(url);
|
|
282
|
-
// Некоторые модули не хранят инфу о протоколе, потому что tinkoff-request сам подставляет протокол.
|
|
283
|
-
// В результате при поиске префиксное дерево не отрабатывает.
|
|
284
|
-
// предусматриваем этот случай добавляя инфу о том для какого протокола храним имя сервиса.
|
|
285
|
-
this.registryPrefixTree.set(urlWOProtocol, (value) => ({
|
|
286
|
-
...(value || {}),
|
|
287
|
-
[protocol || NO_PROTOCOL]: serviceName,
|
|
288
|
-
}));
|
|
289
|
-
}
|
|
290
|
-
getServiceName(url) {
|
|
291
|
-
if (!url) {
|
|
292
|
-
return undefined;
|
|
293
|
-
}
|
|
294
|
-
const [protocol, urlWOProtocol] = splitProtocolAndUrl(url);
|
|
295
|
-
const treeValue = this.registryPrefixTree.get(urlWOProtocol);
|
|
296
|
-
if (!treeValue) {
|
|
297
|
-
return undefined;
|
|
298
|
-
}
|
|
299
|
-
// Когда урл запросен из регистри, мы не знаем был ли он добавлен с протоколом, поэтому
|
|
300
|
-
// проверяем оба варианта
|
|
301
|
-
return treeValue[protocol] || treeValue[NO_PROTOCOL];
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
let RequestModule = class RequestModule {
|
|
306
|
-
};
|
|
307
|
-
RequestModule = tslib.__decorate([
|
|
308
|
-
core.Module({
|
|
309
|
-
providers: [
|
|
310
|
-
{
|
|
311
|
-
provide: core.commandLineListTokens.init,
|
|
312
|
-
multi: true,
|
|
313
|
-
useFactory: ({ metrics, envManager, metricsServicesRegistry, metricsModuleConfig, }) => {
|
|
314
|
-
if (!metrics) {
|
|
315
|
-
return noop__default["default"];
|
|
316
|
-
}
|
|
317
|
-
return () => {
|
|
318
|
-
const env = envManager.getAll();
|
|
319
|
-
metricsServicesRegistry.registerEnv(env);
|
|
320
|
-
const getServiceName = metricsServicesRegistry.getServiceName.bind(metricsServicesRegistry);
|
|
321
|
-
initRequestsMetrics({
|
|
322
|
-
metrics,
|
|
323
|
-
getServiceName,
|
|
324
|
-
http: http__default["default"],
|
|
325
|
-
https: https__default["default"],
|
|
326
|
-
createRequestWithMetrics,
|
|
327
|
-
config: metricsModuleConfig,
|
|
328
|
-
});
|
|
329
|
-
};
|
|
330
|
-
},
|
|
331
|
-
deps: {
|
|
332
|
-
metrics: {
|
|
333
|
-
token: tokensMetrics.METRICS_MODULE_TOKEN,
|
|
334
|
-
optional: true,
|
|
335
|
-
},
|
|
336
|
-
metricsServicesRegistry: tokensMetrics.METRICS_SERVICES_REGISTRY_TOKEN,
|
|
337
|
-
envManager: tokensCommon.ENV_MANAGER_TOKEN,
|
|
338
|
-
metricsModuleConfig: tokensMetrics.METRICS_MODULE_CONFIG_TOKEN,
|
|
339
|
-
},
|
|
340
|
-
},
|
|
341
|
-
{
|
|
342
|
-
provide: tokensMetrics.METRICS_SERVICES_REGISTRY_TOKEN,
|
|
343
|
-
useClass: MetricsServicesRegistry,
|
|
344
|
-
scope: core.Scope.SINGLETON,
|
|
345
|
-
},
|
|
346
|
-
],
|
|
347
|
-
})
|
|
348
|
-
], RequestModule);
|
|
349
|
-
|
|
350
|
-
const setInstantMetrics = state.createEvent('set instant metrics map');
|
|
351
|
-
const MetricsStore = state.createReducer('instantMetrics', { instantMetricsMap: {} }).on(setInstantMetrics, (_prevState, nextState) => {
|
|
352
|
-
return nextState;
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
const sharedProviders = [
|
|
356
|
-
{
|
|
357
|
-
provide: tokensCommon.COMBINE_REDUCERS,
|
|
358
|
-
multi: true,
|
|
359
|
-
useValue: [MetricsStore],
|
|
360
|
-
},
|
|
361
|
-
];
|
|
362
|
-
|
|
363
|
-
let InstantMetricsModule = class InstantMetricsModule {
|
|
364
|
-
};
|
|
365
|
-
InstantMetricsModule = tslib.__decorate([
|
|
366
|
-
core.Module({
|
|
367
|
-
providers: [
|
|
368
|
-
...sharedProviders,
|
|
369
|
-
{
|
|
370
|
-
provide: tokensServer.SERVER_MODULE_PAPI_PUBLIC_ROUTE,
|
|
371
|
-
multi: true,
|
|
372
|
-
useFactory({ metrics, logger, instantMetrics, }) {
|
|
373
|
-
const log = logger('instantmetrics:papi');
|
|
374
|
-
const instantMetricsMap = fromPairs__default["default"](instantMetrics || []);
|
|
375
|
-
return papi.createPapiMethod({
|
|
376
|
-
method: 'post',
|
|
377
|
-
path: '/metrics/:metric',
|
|
378
|
-
options: {
|
|
379
|
-
schema: {
|
|
380
|
-
body: {
|
|
381
|
-
type: 'object',
|
|
382
|
-
properties: {
|
|
383
|
-
value: {
|
|
384
|
-
type: 'number',
|
|
385
|
-
minimum: 0,
|
|
386
|
-
},
|
|
387
|
-
},
|
|
388
|
-
additionalProperties: false,
|
|
389
|
-
},
|
|
390
|
-
},
|
|
391
|
-
},
|
|
392
|
-
async handler({ params: { metric }, body, responseManager }) {
|
|
393
|
-
if (!instantMetricsMap[metric]) {
|
|
394
|
-
log.error({
|
|
395
|
-
event: 'client-instant-metric-mismatch',
|
|
396
|
-
metricName: metric,
|
|
397
|
-
error: new Error(`No instant metric instance found with name: ${metric}`),
|
|
398
|
-
});
|
|
399
|
-
responseManager.setStatus(404);
|
|
400
|
-
responseManager.setBody({
|
|
401
|
-
resultCode: 'NOT_FOUND',
|
|
402
|
-
errorMessage: 'metric not found',
|
|
403
|
-
});
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
instantMetricsMap[metric].inc(body === null || body === void 0 ? void 0 : body.value);
|
|
407
|
-
return { status: 'ok' };
|
|
408
|
-
},
|
|
409
|
-
});
|
|
410
|
-
},
|
|
411
|
-
deps: {
|
|
412
|
-
metrics: tokensMetrics.METRICS_MODULE_TOKEN,
|
|
413
|
-
logger: tokensCommon.LOGGER_TOKEN,
|
|
414
|
-
instantMetrics: {
|
|
415
|
-
token: tokensMetrics.REGISTER_INSTANT_METRIC_TOKEN,
|
|
416
|
-
multi: true,
|
|
417
|
-
optional: true,
|
|
418
|
-
},
|
|
419
|
-
},
|
|
420
|
-
},
|
|
421
|
-
{
|
|
422
|
-
provide: core.commandLineListTokens.customerStart,
|
|
423
|
-
multi: true,
|
|
424
|
-
useFactory({ instantMetrics, context, }) {
|
|
425
|
-
return async () => {
|
|
426
|
-
if (!instantMetrics) {
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
const instantMetricsMap = instantMetrics.reduce((acc, [metricName]) => {
|
|
430
|
-
acc[metricName] = true;
|
|
431
|
-
return acc;
|
|
432
|
-
}, {});
|
|
433
|
-
await context.dispatch(setInstantMetrics({ instantMetricsMap }));
|
|
434
|
-
};
|
|
435
|
-
},
|
|
436
|
-
deps: {
|
|
437
|
-
context: tokensCommon.CONTEXT_TOKEN,
|
|
438
|
-
instantMetrics: {
|
|
439
|
-
token: tokensMetrics.REGISTER_INSTANT_METRIC_TOKEN,
|
|
440
|
-
multi: true,
|
|
441
|
-
optional: true,
|
|
442
|
-
},
|
|
443
|
-
},
|
|
444
|
-
},
|
|
445
|
-
],
|
|
446
|
-
})
|
|
447
|
-
], InstantMetricsModule);
|
|
448
|
-
|
|
449
|
-
const NODEJS_EVENTLOOP_LAG = 'nodejs_eventloop_setinterval_lag_seconds';
|
|
450
|
-
function startEventLoopLagMeasure(histogram) {
|
|
451
|
-
let start = process.hrtime();
|
|
452
|
-
const interval = 100;
|
|
453
|
-
setInterval(() => {
|
|
454
|
-
const delta = process.hrtime(start);
|
|
455
|
-
const nanosec = delta[0] * 1e9 + delta[1];
|
|
456
|
-
const ms = nanosec / 1e6;
|
|
457
|
-
const lag = ms - interval;
|
|
458
|
-
histogram.observe(lag / 1e3);
|
|
459
|
-
start = process.hrtime();
|
|
460
|
-
}, interval).unref();
|
|
461
|
-
}
|
|
462
|
-
const eventLoopMetrics = (metrics) => {
|
|
463
|
-
const histogram = metrics.histogram({
|
|
464
|
-
name: NODEJS_EVENTLOOP_LAG,
|
|
465
|
-
help: 'Lag of event loop in seconds (setInterval based).',
|
|
466
|
-
buckets: [0.02, 0.1, 0.2, 0.5, 1, 3, 5, 7.5, 10],
|
|
467
|
-
});
|
|
468
|
-
startEventLoopLagMeasure(histogram);
|
|
469
|
-
};
|
|
470
|
-
|
|
471
|
-
const commandLineMetrics = (metrics) => {
|
|
472
|
-
const metricsInstance = metrics.histogram({
|
|
473
|
-
name: `command_line_runner_execution_time`,
|
|
474
|
-
help: 'Command line processing duration',
|
|
475
|
-
labelNames: ['line'],
|
|
476
|
-
buckets: DEFAULT_BUCKETS,
|
|
477
|
-
});
|
|
478
|
-
return (di, type, status, timingInfo) => {
|
|
479
|
-
for (const line in timingInfo) {
|
|
480
|
-
const info = timingInfo[line];
|
|
481
|
-
if (info.end) {
|
|
482
|
-
const durationInMs = info.end - info.start;
|
|
483
|
-
const durationInSec = durationInMs / 1000;
|
|
484
|
-
metricsInstance.observe({ line }, durationInSec);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
};
|
|
488
|
-
};
|
|
489
23
|
|
|
490
24
|
exports.MetricsModule = class MetricsModule {
|
|
491
25
|
};
|
|
492
26
|
exports.MetricsModule = tslib.__decorate([
|
|
493
27
|
core.Module({
|
|
494
|
-
imports: [RequestModule, InstantMetricsModule],
|
|
28
|
+
imports: [index.RequestModule, server.InstantMetricsModule],
|
|
495
29
|
providers: [
|
|
496
30
|
core.provide({
|
|
497
31
|
provide: tokensMetrics.METRICS_MODULE_CONFIG_TOKEN,
|
|
@@ -582,7 +116,7 @@ exports.MetricsModule = tslib.__decorate([
|
|
|
582
116
|
provide: core.commandLineListTokens.listen,
|
|
583
117
|
useFactory: ({ metrics }) => {
|
|
584
118
|
return () => {
|
|
585
|
-
eventLoopMetrics(metrics);
|
|
119
|
+
eventLoop.eventLoopMetrics(metrics);
|
|
586
120
|
};
|
|
587
121
|
},
|
|
588
122
|
deps: {
|
|
@@ -593,7 +127,7 @@ exports.MetricsModule = tslib.__decorate([
|
|
|
593
127
|
core.provide({
|
|
594
128
|
provide: tokensCorePrivate.COMMAND_LINE_EXECUTION_END_TOKEN,
|
|
595
129
|
useFactory: ({ metrics }) => {
|
|
596
|
-
return commandLineMetrics(metrics);
|
|
130
|
+
return commandLine.commandLineMetrics(metrics);
|
|
597
131
|
},
|
|
598
132
|
deps: {
|
|
599
133
|
metrics: tokensMetrics.METRICS_MODULE_TOKEN,
|
|
@@ -603,7 +137,7 @@ exports.MetricsModule = tslib.__decorate([
|
|
|
603
137
|
})
|
|
604
138
|
], exports.MetricsModule);
|
|
605
139
|
|
|
606
|
-
exports.getUrlAndOptions = getUrlAndOptions;
|
|
140
|
+
exports.getUrlAndOptions = createRequestWithMetrics.getUrlAndOptions;
|
|
607
141
|
Object.keys(tokensMetrics).forEach(function (k) {
|
|
608
142
|
if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
|
|
609
143
|
enumerable: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tramvai/module-metrics",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.72.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"browser": "lib/browser.js",
|
|
6
6
|
"main": "lib/server.js",
|
|
@@ -14,29 +14,28 @@
|
|
|
14
14
|
"url": "git@github.com:Tinkoff/tramvai.git"
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
|
-
"build": "tramvai-build --
|
|
18
|
-
"watch": "tsc -w"
|
|
19
|
-
"build-for-publish": "true"
|
|
17
|
+
"build": "tramvai-build --forPublish --preserveModules",
|
|
18
|
+
"watch": "tsc -w"
|
|
20
19
|
},
|
|
21
20
|
"dependencies": {
|
|
22
|
-
"@tramvai/core": "2.
|
|
23
|
-
"@tramvai/tokens-common": "2.
|
|
24
|
-
"@tramvai/tokens-core-private": "2.
|
|
25
|
-
"@tramvai/tokens-server": "2.
|
|
26
|
-
"@tramvai/tokens-server-private": "2.
|
|
27
|
-
"@tramvai/tokens-metrics": "2.
|
|
28
|
-
"@tramvai/tokens-router": "2.
|
|
29
|
-
"@tramvai/tokens-http-client": "2.
|
|
30
|
-
"@tramvai/state": "2.
|
|
31
|
-
"@tramvai/papi": "2.
|
|
32
|
-
"@tinkoff/measure-fastify-requests": "0.1.
|
|
33
|
-
"@tinkoff/monkeypatch": "2.0.
|
|
34
|
-
"@tinkoff/url": "0.8.
|
|
21
|
+
"@tramvai/core": "2.72.0",
|
|
22
|
+
"@tramvai/tokens-common": "2.72.0",
|
|
23
|
+
"@tramvai/tokens-core-private": "2.72.0",
|
|
24
|
+
"@tramvai/tokens-server": "2.72.0",
|
|
25
|
+
"@tramvai/tokens-server-private": "2.72.0",
|
|
26
|
+
"@tramvai/tokens-metrics": "2.72.0",
|
|
27
|
+
"@tramvai/tokens-router": "2.72.0",
|
|
28
|
+
"@tramvai/tokens-http-client": "2.72.0",
|
|
29
|
+
"@tramvai/state": "2.72.0",
|
|
30
|
+
"@tramvai/papi": "2.72.0",
|
|
31
|
+
"@tinkoff/measure-fastify-requests": "0.1.7",
|
|
32
|
+
"@tinkoff/monkeypatch": "2.0.4",
|
|
33
|
+
"@tinkoff/url": "0.8.5",
|
|
35
34
|
"@tinkoff/utils": "^2.1.2",
|
|
36
35
|
"prom-client": "^12.0.0",
|
|
37
|
-
"@tinkoff/logger": "0.10.
|
|
38
|
-
"@tinkoff/metrics-noop": "2.0.
|
|
39
|
-
"@tinkoff/browser-timings": "0.10.
|
|
36
|
+
"@tinkoff/logger": "0.10.58",
|
|
37
|
+
"@tinkoff/metrics-noop": "2.0.5",
|
|
38
|
+
"@tinkoff/browser-timings": "0.10.5"
|
|
40
39
|
},
|
|
41
40
|
"peerDependencies": {
|
|
42
41
|
"tslib": "^2.4.0"
|