@tramvai/module-metrics 2.70.1 → 2.72.3

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 (33) hide show
  1. package/lib/browser.js +5 -156
  2. package/lib/constants.es.js +3 -0
  3. package/lib/constants.js +7 -0
  4. package/lib/instantMetrics/browser.browser.js +70 -0
  5. package/lib/instantMetrics/server.es.js +97 -0
  6. package/lib/instantMetrics/server.js +103 -0
  7. package/lib/instantMetrics/shared.browser.js +12 -0
  8. package/lib/instantMetrics/shared.es.js +12 -0
  9. package/lib/instantMetrics/shared.js +16 -0
  10. package/lib/instantMetrics/store.browser.js +8 -0
  11. package/lib/instantMetrics/store.es.js +8 -0
  12. package/lib/instantMetrics/store.js +13 -0
  13. package/lib/metrics/commandLine.es.js +22 -0
  14. package/lib/metrics/commandLine.js +26 -0
  15. package/lib/metrics/eventLoop.es.js +23 -0
  16. package/lib/metrics/eventLoop.js +27 -0
  17. package/lib/performance-devtools/PerfMetrics.browser.js +56 -0
  18. package/lib/performance-devtools/startMeasure.browser.js +14 -0
  19. package/lib/performance-devtools/supportsUserTiming.browser.js +7 -0
  20. package/lib/performance-devtools/uniqueId.browser.js +7 -0
  21. package/lib/request/MetricsServicesRegistry.es.js +56 -0
  22. package/lib/request/MetricsServicesRegistry.js +64 -0
  23. package/lib/request/PrefixTree.es.js +48 -0
  24. package/lib/request/PrefixTree.js +52 -0
  25. package/lib/request/createRequestWithMetrics.es.js +123 -0
  26. package/lib/request/createRequestWithMetrics.js +128 -0
  27. package/lib/request/index.es.js +57 -0
  28. package/lib/request/index.js +65 -0
  29. package/lib/request/initRequestsMetrics.es.js +53 -0
  30. package/lib/request/initRequestsMetrics.js +61 -0
  31. package/lib/server.es.js +9 -469
  32. package/lib/server.js +9 -475
  33. package/package.json +19 -20
@@ -0,0 +1,56 @@
1
+ import { Histogram, Summary, Gauge } from '@tinkoff/metrics-noop';
2
+ import { startMeasure } from './startMeasure.browser.js';
3
+ import { supportsUserTiming } from './supportsUserTiming.browser.js';
4
+ import { uniqueId } from './uniqueId.browser.js';
5
+
6
+ function startMeasurePerformance(name, startLabels = {}) {
7
+ if (!supportsUserTiming) {
8
+ return () => { };
9
+ }
10
+ const endMeasure = startMeasure(name, uniqueId());
11
+ return (endLabels = {}) => {
12
+ const labels = { ...startLabels, ...endLabels };
13
+ const labelsNames = Object.keys(labels)
14
+ .map((key) => `${key}="${labels[key]}"`)
15
+ .join(',');
16
+ endMeasure(labelsNames && `${name}{${labelsNames}}`);
17
+ };
18
+ }
19
+ class PerfHistogram extends Histogram {
20
+ constructor(configurator) {
21
+ super(configurator);
22
+ this.name = configurator.name;
23
+ }
24
+ startTimer(startLabels) {
25
+ const endMeasure = startMeasurePerformance(this.name, startLabels);
26
+ return (endLabels) => {
27
+ endMeasure(endLabels);
28
+ };
29
+ }
30
+ }
31
+ class PerfSummary extends Summary {
32
+ constructor(configurator) {
33
+ super(configurator);
34
+ this.name = configurator.name;
35
+ }
36
+ startTimer() {
37
+ const endMeasure = startMeasurePerformance(this.name);
38
+ return (endLabels) => {
39
+ endMeasure(endLabels);
40
+ };
41
+ }
42
+ }
43
+ class PerfGauge extends Gauge {
44
+ constructor(configurator) {
45
+ super(configurator);
46
+ this.name = configurator.name;
47
+ }
48
+ startTimer(startLabels) {
49
+ const endMeasure = startMeasurePerformance(this.name, startLabels);
50
+ return (endLabels) => {
51
+ endMeasure(endLabels);
52
+ };
53
+ }
54
+ }
55
+
56
+ export { PerfGauge, PerfHistogram, PerfSummary, startMeasurePerformance };
@@ -0,0 +1,14 @@
1
+ function startMeasure(markName, uniqueMarkId) {
2
+ const uniqueMarkName = `${markName}:${uniqueMarkId}`;
3
+ performance.mark(uniqueMarkName);
4
+ function endMeasure(measureName) {
5
+ try {
6
+ performance.measure(measureName || markName, uniqueMarkName);
7
+ }
8
+ catch (e) { }
9
+ performance.clearMarks(uniqueMarkName);
10
+ }
11
+ return endMeasure;
12
+ }
13
+
14
+ export { startMeasure };
@@ -0,0 +1,7 @@
1
+ const supportsUserTiming = typeof performance !== 'undefined' &&
2
+ typeof performance.mark === 'function' &&
3
+ typeof performance.clearMarks === 'function' &&
4
+ typeof performance.measure === 'function' &&
5
+ typeof performance.clearMeasures === 'function';
6
+
7
+ export { supportsUserTiming };
@@ -0,0 +1,7 @@
1
+ let idCounter = 0;
2
+ function uniqueId() {
3
+ // eslint-disable-next-line no-plusplus
4
+ return ++idCounter;
5
+ }
6
+
7
+ export { uniqueId };
@@ -0,0 +1,56 @@
1
+ import isString from '@tinkoff/utils/is/string';
2
+ import { PrefixTree } from './PrefixTree.es.js';
3
+
4
+ const NO_PROTOCOL = 'NO PROTOCOL';
5
+ const PROTOCOL_REGEX = /^([a-z0-9.+-]+:)/i;
6
+ const splitProtocolAndUrl = (url) => {
7
+ // Регулярка используется потому что протокол обязателен и класс URL не распарсит такую строку
8
+ const urlMatches = url.match(PROTOCOL_REGEX);
9
+ const protocol = urlMatches && urlMatches[0];
10
+ // Под + 2 тут подразумевается два слеша после протокола например для http: обрежется http://
11
+ const urlWOProtocol = protocol ? url.slice(protocol.length + 2) : url;
12
+ return [protocol, urlWOProtocol];
13
+ };
14
+ class MetricsServicesRegistry {
15
+ constructor() {
16
+ this.registryPrefixTree = new PrefixTree({
17
+ delimiter: '/',
18
+ });
19
+ }
20
+ registerEnv(env) {
21
+ Object.keys(env).forEach((name) => {
22
+ // В env могут быть undefined, и number, т.к. это явно не урлы, просто пропускаем
23
+ if (isString(env[name])) {
24
+ this.register(env[name], name);
25
+ }
26
+ });
27
+ }
28
+ register(url, serviceName) {
29
+ if (!url) {
30
+ return;
31
+ }
32
+ const [protocol, urlWOProtocol] = splitProtocolAndUrl(url);
33
+ // Некоторые модули не хранят инфу о протоколе, потому что tinkoff-request сам подставляет протокол.
34
+ // В результате при поиске префиксное дерево не отрабатывает.
35
+ // предусматриваем этот случай добавляя инфу о том для какого протокола храним имя сервиса.
36
+ this.registryPrefixTree.set(urlWOProtocol, (value) => ({
37
+ ...(value || {}),
38
+ [protocol || NO_PROTOCOL]: serviceName,
39
+ }));
40
+ }
41
+ getServiceName(url) {
42
+ if (!url) {
43
+ return undefined;
44
+ }
45
+ const [protocol, urlWOProtocol] = splitProtocolAndUrl(url);
46
+ const treeValue = this.registryPrefixTree.get(urlWOProtocol);
47
+ if (!treeValue) {
48
+ return undefined;
49
+ }
50
+ // Когда урл запросен из регистри, мы не знаем был ли он добавлен с протоколом, поэтому
51
+ // проверяем оба варианта
52
+ return treeValue[protocol] || treeValue[NO_PROTOCOL];
53
+ }
54
+ }
55
+
56
+ export { MetricsServicesRegistry };
@@ -0,0 +1,64 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var isString = require('@tinkoff/utils/is/string');
6
+ var PrefixTree = require('./PrefixTree.js');
7
+
8
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
9
+
10
+ var isString__default = /*#__PURE__*/_interopDefaultLegacy(isString);
11
+
12
+ const NO_PROTOCOL = 'NO PROTOCOL';
13
+ const PROTOCOL_REGEX = /^([a-z0-9.+-]+:)/i;
14
+ const splitProtocolAndUrl = (url) => {
15
+ // Регулярка используется потому что протокол обязателен и класс URL не распарсит такую строку
16
+ const urlMatches = url.match(PROTOCOL_REGEX);
17
+ const protocol = urlMatches && urlMatches[0];
18
+ // Под + 2 тут подразумевается два слеша после протокола например для http: обрежется http://
19
+ const urlWOProtocol = protocol ? url.slice(protocol.length + 2) : url;
20
+ return [protocol, urlWOProtocol];
21
+ };
22
+ class MetricsServicesRegistry {
23
+ constructor() {
24
+ this.registryPrefixTree = new PrefixTree.PrefixTree({
25
+ delimiter: '/',
26
+ });
27
+ }
28
+ registerEnv(env) {
29
+ Object.keys(env).forEach((name) => {
30
+ // В env могут быть undefined, и number, т.к. это явно не урлы, просто пропускаем
31
+ if (isString__default["default"](env[name])) {
32
+ this.register(env[name], name);
33
+ }
34
+ });
35
+ }
36
+ register(url, serviceName) {
37
+ if (!url) {
38
+ return;
39
+ }
40
+ const [protocol, urlWOProtocol] = splitProtocolAndUrl(url);
41
+ // Некоторые модули не хранят инфу о протоколе, потому что tinkoff-request сам подставляет протокол.
42
+ // В результате при поиске префиксное дерево не отрабатывает.
43
+ // предусматриваем этот случай добавляя инфу о том для какого протокола храним имя сервиса.
44
+ this.registryPrefixTree.set(urlWOProtocol, (value) => ({
45
+ ...(value || {}),
46
+ [protocol || NO_PROTOCOL]: serviceName,
47
+ }));
48
+ }
49
+ getServiceName(url) {
50
+ if (!url) {
51
+ return undefined;
52
+ }
53
+ const [protocol, urlWOProtocol] = splitProtocolAndUrl(url);
54
+ const treeValue = this.registryPrefixTree.get(urlWOProtocol);
55
+ if (!treeValue) {
56
+ return undefined;
57
+ }
58
+ // Когда урл запросен из регистри, мы не знаем был ли он добавлен с протоколом, поэтому
59
+ // проверяем оба варианта
60
+ return treeValue[protocol] || treeValue[NO_PROTOCOL];
61
+ }
62
+ }
63
+
64
+ exports.MetricsServicesRegistry = MetricsServicesRegistry;
@@ -0,0 +1,48 @@
1
+ // Дерево использует подход структуры данных trie для прямого доступа к serviceName, но в отличие от
2
+ // trie возвращает не все значения соответствующие префиксу, а значение соответствующее самому
3
+ // длинному ключу в дереве который является подстрокой входной строки
4
+ class PrefixTree {
5
+ constructor(options = {}) {
6
+ this.index = Object.create(null);
7
+ this.delimiter = options.delimiter || '';
8
+ }
9
+ // На случай если value не примитивные значения есть возможность передать функцию и там как угодно
10
+ // модифицировать объект
11
+ set(key, valueOrUpdateFn) {
12
+ let tree = this.index;
13
+ const parts = key.split(this.delimiter);
14
+ do {
15
+ const letter = parts.shift();
16
+ if (letter === '')
17
+ continue;
18
+ if (!tree[letter]) {
19
+ tree[letter] = Object.create(null);
20
+ }
21
+ tree = tree[letter];
22
+ } while (parts.length);
23
+ if (valueOrUpdateFn instanceof Function) {
24
+ tree.value = valueOrUpdateFn(tree.value);
25
+ }
26
+ else {
27
+ tree.value = valueOrUpdateFn;
28
+ }
29
+ }
30
+ get(key) {
31
+ let tree = this.index;
32
+ const parts = key.split(this.delimiter);
33
+ let prevTree;
34
+ do {
35
+ const letter = parts.shift();
36
+ if (letter === '')
37
+ continue;
38
+ prevTree = tree;
39
+ tree = tree[letter];
40
+ } while (parts.length && tree);
41
+ if (tree) {
42
+ return tree.value;
43
+ }
44
+ return prevTree.value;
45
+ }
46
+ }
47
+
48
+ export { PrefixTree };
@@ -0,0 +1,52 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ // Дерево использует подход структуры данных trie для прямого доступа к serviceName, но в отличие от
6
+ // trie возвращает не все значения соответствующие префиксу, а значение соответствующее самому
7
+ // длинному ключу в дереве который является подстрокой входной строки
8
+ class PrefixTree {
9
+ constructor(options = {}) {
10
+ this.index = Object.create(null);
11
+ this.delimiter = options.delimiter || '';
12
+ }
13
+ // На случай если value не примитивные значения есть возможность передать функцию и там как угодно
14
+ // модифицировать объект
15
+ set(key, valueOrUpdateFn) {
16
+ let tree = this.index;
17
+ const parts = key.split(this.delimiter);
18
+ do {
19
+ const letter = parts.shift();
20
+ if (letter === '')
21
+ continue;
22
+ if (!tree[letter]) {
23
+ tree[letter] = Object.create(null);
24
+ }
25
+ tree = tree[letter];
26
+ } while (parts.length);
27
+ if (valueOrUpdateFn instanceof Function) {
28
+ tree.value = valueOrUpdateFn(tree.value);
29
+ }
30
+ else {
31
+ tree.value = valueOrUpdateFn;
32
+ }
33
+ }
34
+ get(key) {
35
+ let tree = this.index;
36
+ const parts = key.split(this.delimiter);
37
+ let prevTree;
38
+ do {
39
+ const letter = parts.shift();
40
+ if (letter === '')
41
+ continue;
42
+ prevTree = tree;
43
+ tree = tree[letter];
44
+ } while (parts.length && tree);
45
+ if (tree) {
46
+ return tree.value;
47
+ }
48
+ return prevTree.value;
49
+ }
50
+ }
51
+
52
+ exports.PrefixTree = PrefixTree;
@@ -0,0 +1,123 @@
1
+ import { format } from '@tinkoff/url';
2
+
3
+ // https://nodejs.org/api/errors.html#nodejs-error-codes - Common system errors possible for net/http/dns
4
+ const POSSIBLE_ERRORS = [
5
+ 'EADDRINUSE',
6
+ 'ECONNREFUSED',
7
+ 'ECONNRESET',
8
+ 'ENOTFOUND',
9
+ 'EPIPE',
10
+ 'ETIMEDOUT',
11
+ ];
12
+ const getUrlAndOptions = (args) => {
13
+ let url;
14
+ let options;
15
+ // У request первый аргумент либо урл либо объект опций, кейс когда первого аргумента нет не валиден
16
+ const isUrlStringFirst = args[0].constructor === String;
17
+ const isUrlObjectFirst = args[0].constructor === URL;
18
+ const isOptionsFirst = !isUrlStringFirst && !isUrlObjectFirst;
19
+ const isOptionsSecond = !isOptionsFirst && !(args[0] instanceof Function);
20
+ if (isUrlStringFirst) {
21
+ [url] = args;
22
+ }
23
+ if (isUrlObjectFirst) {
24
+ url = format(args[0]);
25
+ }
26
+ if (isOptionsFirst) {
27
+ [options] = args;
28
+ // Тут учитываем случай если передаётся не href в options, а отдельно protocol, host, port, path
29
+ if (options.href) {
30
+ url = options.href;
31
+ }
32
+ else {
33
+ const urlString = format({
34
+ protocol: options.protocol,
35
+ host: options.hostname || options.host,
36
+ port: options.port,
37
+ pathname: options.path,
38
+ });
39
+ // format где-то внутри делает encodeURIComponent и из-за этого потом не может обрезать query
40
+ try {
41
+ url = decodeURIComponent(urlString);
42
+ }
43
+ catch {
44
+ url = urlString;
45
+ }
46
+ }
47
+ }
48
+ if (isOptionsSecond) {
49
+ [, options] = args;
50
+ }
51
+ const parsedUrl = new URL(url);
52
+ const urlWOQuery = parsedUrl.origin + parsedUrl.pathname;
53
+ return [urlWOQuery, options || {}, parsedUrl];
54
+ };
55
+ // in seconds
56
+ const getDuration = (current, prev) =>
57
+ // max to avoid negative values and turn that into zero
58
+ prev === 0 ? 0 : Math.max((current - prev) / 1000, 0);
59
+ const createRequestWithMetrics = ({ metricsInstances: { requestsTotal, requestsErrors, requestsDuration, dnsResolveDuration, tcpConnectDuration, tlsHandshakeDuration, }, getServiceName, config, }) => {
60
+ const socketSet = new WeakSet();
61
+ return function requestWithMetrics(originalRequest, ...args) {
62
+ const [url, options] = getUrlAndOptions(args);
63
+ const serviceName = getServiceName(url);
64
+ const req = originalRequest.apply(this, args);
65
+ const timerDone = requestsDuration.startTimer();
66
+ const labelsValues = {
67
+ method: options.method || 'unknown',
68
+ service: serviceName || new URL(url).origin || 'unknown',
69
+ status: 'unknown',
70
+ };
71
+ req.on('response', (res) => {
72
+ labelsValues.status = res.statusCode.toString();
73
+ if (res.statusCode >= 400) {
74
+ requestsErrors.inc(labelsValues);
75
+ }
76
+ requestsTotal.inc(labelsValues);
77
+ timerDone(labelsValues);
78
+ });
79
+ req.on('error', (e) => {
80
+ if (POSSIBLE_ERRORS.includes(e === null || e === void 0 ? void 0 : e.code)) {
81
+ labelsValues.status = req.aborted ? 'aborted' : e.code;
82
+ }
83
+ requestsTotal.inc(labelsValues);
84
+ requestsErrors.inc(labelsValues);
85
+ timerDone(labelsValues);
86
+ });
87
+ if (config.enableConnectionResolveMetrics) {
88
+ req.on('socket', (socket) => {
89
+ // due to keep-alive tcp option sockets might be reused
90
+ // ignore them because they have already emitted events we are interested in
91
+ if (socketSet.has(socket)) {
92
+ return;
93
+ }
94
+ socketSet.add(socket);
95
+ const timings = {
96
+ start: Date.now(),
97
+ lookupEnd: 0,
98
+ connectEnd: 0,
99
+ secureConnectEnd: 0,
100
+ };
101
+ const { service } = labelsValues;
102
+ socket.on('lookup', () => {
103
+ timings.lookupEnd = Date.now();
104
+ dnsResolveDuration.observe({ service }, getDuration(timings.lookupEnd, timings.start));
105
+ });
106
+ socket.on('connect', () => {
107
+ timings.connectEnd = Date.now();
108
+ tcpConnectDuration.observe({ service }, getDuration(timings.connectEnd, timings.lookupEnd));
109
+ });
110
+ socket.on('secureConnect', () => {
111
+ timings.secureConnectEnd = Date.now();
112
+ tlsHandshakeDuration.observe({ service }, getDuration(timings.secureConnectEnd, timings.connectEnd));
113
+ });
114
+ socket.on('close', () => {
115
+ socketSet.delete(socket);
116
+ });
117
+ });
118
+ }
119
+ return req;
120
+ };
121
+ };
122
+
123
+ export { createRequestWithMetrics, getUrlAndOptions };
@@ -0,0 +1,128 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var url = require('@tinkoff/url');
6
+
7
+ // https://nodejs.org/api/errors.html#nodejs-error-codes - Common system errors possible for net/http/dns
8
+ const POSSIBLE_ERRORS = [
9
+ 'EADDRINUSE',
10
+ 'ECONNREFUSED',
11
+ 'ECONNRESET',
12
+ 'ENOTFOUND',
13
+ 'EPIPE',
14
+ 'ETIMEDOUT',
15
+ ];
16
+ const getUrlAndOptions = (args) => {
17
+ let url$1;
18
+ let options;
19
+ // У request первый аргумент либо урл либо объект опций, кейс когда первого аргумента нет не валиден
20
+ const isUrlStringFirst = args[0].constructor === String;
21
+ const isUrlObjectFirst = args[0].constructor === URL;
22
+ const isOptionsFirst = !isUrlStringFirst && !isUrlObjectFirst;
23
+ const isOptionsSecond = !isOptionsFirst && !(args[0] instanceof Function);
24
+ if (isUrlStringFirst) {
25
+ [url$1] = args;
26
+ }
27
+ if (isUrlObjectFirst) {
28
+ url$1 = url.format(args[0]);
29
+ }
30
+ if (isOptionsFirst) {
31
+ [options] = args;
32
+ // Тут учитываем случай если передаётся не href в options, а отдельно protocol, host, port, path
33
+ if (options.href) {
34
+ url$1 = options.href;
35
+ }
36
+ else {
37
+ const urlString = url.format({
38
+ protocol: options.protocol,
39
+ host: options.hostname || options.host,
40
+ port: options.port,
41
+ pathname: options.path,
42
+ });
43
+ // format где-то внутри делает encodeURIComponent и из-за этого потом не может обрезать query
44
+ try {
45
+ url$1 = decodeURIComponent(urlString);
46
+ }
47
+ catch {
48
+ url$1 = urlString;
49
+ }
50
+ }
51
+ }
52
+ if (isOptionsSecond) {
53
+ [, options] = args;
54
+ }
55
+ const parsedUrl = new URL(url$1);
56
+ const urlWOQuery = parsedUrl.origin + parsedUrl.pathname;
57
+ return [urlWOQuery, options || {}, parsedUrl];
58
+ };
59
+ // in seconds
60
+ const getDuration = (current, prev) =>
61
+ // max to avoid negative values and turn that into zero
62
+ prev === 0 ? 0 : Math.max((current - prev) / 1000, 0);
63
+ const createRequestWithMetrics = ({ metricsInstances: { requestsTotal, requestsErrors, requestsDuration, dnsResolveDuration, tcpConnectDuration, tlsHandshakeDuration, }, getServiceName, config, }) => {
64
+ const socketSet = new WeakSet();
65
+ return function requestWithMetrics(originalRequest, ...args) {
66
+ const [url, options] = getUrlAndOptions(args);
67
+ const serviceName = getServiceName(url);
68
+ const req = originalRequest.apply(this, args);
69
+ const timerDone = requestsDuration.startTimer();
70
+ const labelsValues = {
71
+ method: options.method || 'unknown',
72
+ service: serviceName || new URL(url).origin || 'unknown',
73
+ status: 'unknown',
74
+ };
75
+ req.on('response', (res) => {
76
+ labelsValues.status = res.statusCode.toString();
77
+ if (res.statusCode >= 400) {
78
+ requestsErrors.inc(labelsValues);
79
+ }
80
+ requestsTotal.inc(labelsValues);
81
+ timerDone(labelsValues);
82
+ });
83
+ req.on('error', (e) => {
84
+ if (POSSIBLE_ERRORS.includes(e === null || e === void 0 ? void 0 : e.code)) {
85
+ labelsValues.status = req.aborted ? 'aborted' : e.code;
86
+ }
87
+ requestsTotal.inc(labelsValues);
88
+ requestsErrors.inc(labelsValues);
89
+ timerDone(labelsValues);
90
+ });
91
+ if (config.enableConnectionResolveMetrics) {
92
+ req.on('socket', (socket) => {
93
+ // due to keep-alive tcp option sockets might be reused
94
+ // ignore them because they have already emitted events we are interested in
95
+ if (socketSet.has(socket)) {
96
+ return;
97
+ }
98
+ socketSet.add(socket);
99
+ const timings = {
100
+ start: Date.now(),
101
+ lookupEnd: 0,
102
+ connectEnd: 0,
103
+ secureConnectEnd: 0,
104
+ };
105
+ const { service } = labelsValues;
106
+ socket.on('lookup', () => {
107
+ timings.lookupEnd = Date.now();
108
+ dnsResolveDuration.observe({ service }, getDuration(timings.lookupEnd, timings.start));
109
+ });
110
+ socket.on('connect', () => {
111
+ timings.connectEnd = Date.now();
112
+ tcpConnectDuration.observe({ service }, getDuration(timings.connectEnd, timings.lookupEnd));
113
+ });
114
+ socket.on('secureConnect', () => {
115
+ timings.secureConnectEnd = Date.now();
116
+ tlsHandshakeDuration.observe({ service }, getDuration(timings.secureConnectEnd, timings.connectEnd));
117
+ });
118
+ socket.on('close', () => {
119
+ socketSet.delete(socket);
120
+ });
121
+ });
122
+ }
123
+ return req;
124
+ };
125
+ };
126
+
127
+ exports.createRequestWithMetrics = createRequestWithMetrics;
128
+ exports.getUrlAndOptions = getUrlAndOptions;
@@ -0,0 +1,57 @@
1
+ import { __decorate } from 'tslib';
2
+ import { Module, commandLineListTokens, Scope } from '@tramvai/core';
3
+ import { METRICS_MODULE_TOKEN, METRICS_SERVICES_REGISTRY_TOKEN, METRICS_MODULE_CONFIG_TOKEN } from '@tramvai/tokens-metrics';
4
+ import { ENV_MANAGER_TOKEN } from '@tramvai/tokens-common';
5
+ import noop from '@tinkoff/utils/function/noop';
6
+ import https from 'https';
7
+ import http from 'http';
8
+ import { createRequestWithMetrics } from './createRequestWithMetrics.es.js';
9
+ import { initRequestsMetrics } from './initRequestsMetrics.es.js';
10
+ import { MetricsServicesRegistry } from './MetricsServicesRegistry.es.js';
11
+
12
+ let RequestModule = class RequestModule {
13
+ };
14
+ RequestModule = __decorate([
15
+ Module({
16
+ providers: [
17
+ {
18
+ provide: commandLineListTokens.init,
19
+ multi: true,
20
+ useFactory: ({ metrics, envManager, metricsServicesRegistry, metricsModuleConfig, }) => {
21
+ if (!metrics) {
22
+ return noop;
23
+ }
24
+ return () => {
25
+ const env = envManager.getAll();
26
+ metricsServicesRegistry.registerEnv(env);
27
+ const getServiceName = metricsServicesRegistry.getServiceName.bind(metricsServicesRegistry);
28
+ initRequestsMetrics({
29
+ metrics,
30
+ getServiceName,
31
+ http,
32
+ https,
33
+ createRequestWithMetrics,
34
+ config: metricsModuleConfig,
35
+ });
36
+ };
37
+ },
38
+ deps: {
39
+ metrics: {
40
+ token: METRICS_MODULE_TOKEN,
41
+ optional: true,
42
+ },
43
+ metricsServicesRegistry: METRICS_SERVICES_REGISTRY_TOKEN,
44
+ envManager: ENV_MANAGER_TOKEN,
45
+ metricsModuleConfig: METRICS_MODULE_CONFIG_TOKEN,
46
+ },
47
+ },
48
+ {
49
+ provide: METRICS_SERVICES_REGISTRY_TOKEN,
50
+ useClass: MetricsServicesRegistry,
51
+ scope: Scope.SINGLETON,
52
+ },
53
+ ],
54
+ })
55
+ ], RequestModule);
56
+
57
+ export { RequestModule };