@tramvai/module-metrics 1.61.0 → 1.64.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.
@@ -5,7 +5,7 @@ import type httpsType from 'https';
5
5
  import type { Counter, Histogram } from 'prom-client';
6
6
  import type { METRICS_MODULE_TOKEN, METRICS_SERVICES_REGISTRY_TOKEN } from '@tramvai/tokens-metrics';
7
7
  export declare type ModuleConfig = {
8
- enableDnsResolveMetric: boolean;
8
+ enableConnectionResolveMetrics: boolean;
9
9
  };
10
10
  export declare type MetricsModule = typeof METRICS_MODULE_TOKEN;
11
11
  export declare type HttpModule = typeof httpType;
@@ -16,6 +16,8 @@ export declare type MetricsInstances = {
16
16
  requestsErrors: Counter<'status' | 'method' | 'service'>;
17
17
  requestsDuration: Histogram<'status' | 'method' | 'service'>;
18
18
  dnsResolveDuration: Histogram<'service'>;
19
+ tcpConnectDuration: Histogram<'service'>;
20
+ tlsHandshakeDuration: Histogram<'service'>;
19
21
  };
20
22
  export declare type GetServiceName = typeof METRICS_SERVICES_REGISTRY_TOKEN['getServiceName'];
21
23
  export declare type Args = [RequestOptions | string | URL, (res: IncomingMessage) => void] | [string | URL, RequestOptions, (res: IncomingMessage) => void];
package/lib/server.es.js CHANGED
@@ -68,43 +68,75 @@ const getUrlAndOptions = (args) => {
68
68
  const urlWOQuery = parsedUrl.origin + parsedUrl.pathname;
69
69
  return [urlWOQuery, options || {}];
70
70
  };
71
- const createRequestWithMetrics = ({ metricsInstances: { requestsTotal, requestsErrors, requestsDuration, dnsResolveDuration }, getServiceName, config, }) => function requestWithMetrics(originalRequest, ...args) {
72
- const [url, options] = getUrlAndOptions(args);
73
- const serviceName = getServiceName(url);
74
- const req = originalRequest.apply(this, args);
75
- const timerDone = requestsDuration.startTimer();
76
- const labelsValues = {
77
- method: options.method || 'unknown',
78
- service: serviceName || new URL(url).origin || 'unknown',
79
- status: 'unknown',
80
- };
81
- req.on('response', (res) => {
82
- labelsValues.status = res.statusCode.toString();
83
- if (res.statusCode >= 400) {
71
+ // in seconds
72
+ const getDuration = (current, prev) =>
73
+ // max to avoid negative values and turn that into zero
74
+ prev === 0 ? 0 : Math.max((current - prev) / 1000, 0);
75
+ const createRequestWithMetrics = ({ metricsInstances: { requestsTotal, requestsErrors, requestsDuration, dnsResolveDuration, tcpConnectDuration, tlsHandshakeDuration, }, getServiceName, config, }) => {
76
+ const socketSet = new WeakSet();
77
+ return function requestWithMetrics(originalRequest, ...args) {
78
+ const [url, options] = getUrlAndOptions(args);
79
+ const serviceName = getServiceName(url);
80
+ const req = originalRequest.apply(this, args);
81
+ const timerDone = requestsDuration.startTimer();
82
+ const labelsValues = {
83
+ method: options.method || 'unknown',
84
+ service: serviceName || new URL(url).origin || 'unknown',
85
+ status: 'unknown',
86
+ };
87
+ req.on('response', (res) => {
88
+ labelsValues.status = res.statusCode.toString();
89
+ if (res.statusCode >= 400) {
90
+ requestsErrors.inc(labelsValues);
91
+ }
92
+ requestsTotal.inc(labelsValues);
93
+ timerDone(labelsValues);
94
+ });
95
+ req.on('error', (e) => {
96
+ if (POSSIBLE_ERRORS.includes(e === null || e === void 0 ? void 0 : e.code)) {
97
+ labelsValues.status = e.code;
98
+ }
99
+ requestsTotal.inc(labelsValues);
84
100
  requestsErrors.inc(labelsValues);
85
- }
86
- requestsTotal.inc(labelsValues);
87
- timerDone(labelsValues);
88
- });
89
- req.on('error', (e) => {
90
- if (POSSIBLE_ERRORS.includes(e === null || e === void 0 ? void 0 : e.code)) {
91
- labelsValues.status = e.code;
92
- }
93
- requestsTotal.inc(labelsValues);
94
- requestsErrors.inc(labelsValues);
95
- timerDone(labelsValues);
96
- });
97
- if (config.enableDnsResolveMetric) {
98
- req.on('socket', (socket) => {
99
- const dnsTimerDone = dnsResolveDuration.startTimer();
100
- socket.on('lookup', () => {
101
- dnsTimerDone({ service: labelsValues.service });
102
- });
101
+ timerDone(labelsValues);
103
102
  });
104
- }
105
- return req;
103
+ if (config.enableConnectionResolveMetrics) {
104
+ req.on('socket', (socket) => {
105
+ // due to keep-alive tcp option sockets might be reused
106
+ // ignore them because they have already emitted events we are interested in
107
+ if (socketSet.has(socket)) {
108
+ return;
109
+ }
110
+ socketSet.add(socket);
111
+ const timings = {
112
+ start: Date.now(),
113
+ lookupEnd: 0,
114
+ connectEnd: 0,
115
+ secureConnectEnd: 0,
116
+ };
117
+ const { service } = labelsValues;
118
+ socket.on('lookup', () => {
119
+ timings.lookupEnd = Date.now();
120
+ dnsResolveDuration.observe({ service }, getDuration(timings.lookupEnd, timings.start));
121
+ });
122
+ socket.on('connect', () => {
123
+ timings.connectEnd = Date.now();
124
+ tcpConnectDuration.observe({ service }, getDuration(timings.connectEnd, timings.lookupEnd));
125
+ });
126
+ socket.on('secureConnect', () => {
127
+ timings.secureConnectEnd = Date.now();
128
+ tlsHandshakeDuration.observe({ service }, getDuration(timings.secureConnectEnd, timings.connectEnd));
129
+ });
130
+ socket.on('close', () => {
131
+ socketSet.delete(socket);
132
+ });
133
+ });
134
+ }
135
+ return req;
136
+ };
106
137
  };
107
138
 
139
+ const DEFAULT_BUCKETS = [0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 20, 40, 60];
108
140
  const initRequestsMetrics = ({ metrics, getServiceName, http, https, createRequestWithMetrics, config, }) => {
109
141
  const metricsInstances = {
110
142
  requestsTotal: metrics.counter({
@@ -121,11 +153,25 @@ const initRequestsMetrics = ({ metrics, getServiceName, http, https, createReque
121
153
  name: 'http_sent_requests_duration',
122
154
  help: 'Execution time of the sent requests',
123
155
  labelNames: ['status', 'method', 'service'],
156
+ buckets: DEFAULT_BUCKETS,
124
157
  }),
125
158
  dnsResolveDuration: metrics.histogram({
126
159
  name: 'dns_resolve_duration',
127
160
  help: 'Time for dns resolve of the outhgoing requests',
128
161
  labelNames: ['service'],
162
+ buckets: DEFAULT_BUCKETS,
163
+ }),
164
+ tcpConnectDuration: metrics.histogram({
165
+ name: 'tcp_connect_duration',
166
+ help: 'Duration of tcp connect of the outgoing requests',
167
+ labelNames: ['service'],
168
+ buckets: DEFAULT_BUCKETS,
169
+ }),
170
+ tlsHandshakeDuration: metrics.histogram({
171
+ name: 'tls_handshake_duration',
172
+ help: 'Duration of tls handshake of the outgoing requests',
173
+ labelNames: ['service'],
174
+ buckets: DEFAULT_BUCKETS,
129
175
  }),
130
176
  };
131
177
  monkeypatch({
@@ -402,7 +448,7 @@ const eventLoopMetrics = (metrics) => {
402
448
  const histogram = metrics.histogram({
403
449
  name: NODEJS_EVENTLOOP_LAG,
404
450
  help: 'Lag of event loop in seconds (setInterval based).',
405
- buckets: [0.02, 0.1, 0.2, 0.5, 1, 3, 5],
451
+ buckets: [0.02, 0.1, 0.2, 0.5, 1, 3, 5, 7.5, 10],
406
452
  });
407
453
  startEventLoopLagMeasure(histogram);
408
454
  };
@@ -416,7 +462,7 @@ MetricsModule = __decorate([
416
462
  provide({
417
463
  provide: METRICS_MODULE_CONFIG_TOKEN,
418
464
  useValue: {
419
- enableDnsResolveMetric: false,
465
+ enableConnectionResolveMetrics: false,
420
466
  },
421
467
  }),
422
468
  provide({
package/lib/server.js CHANGED
@@ -82,43 +82,75 @@ const getUrlAndOptions = (args) => {
82
82
  const urlWOQuery = parsedUrl.origin + parsedUrl.pathname;
83
83
  return [urlWOQuery, options || {}];
84
84
  };
85
- const createRequestWithMetrics = ({ metricsInstances: { requestsTotal, requestsErrors, requestsDuration, dnsResolveDuration }, getServiceName, config, }) => function requestWithMetrics(originalRequest, ...args) {
86
- const [url, options] = getUrlAndOptions(args);
87
- const serviceName = getServiceName(url);
88
- const req = originalRequest.apply(this, args);
89
- const timerDone = requestsDuration.startTimer();
90
- const labelsValues = {
91
- method: options.method || 'unknown',
92
- service: serviceName || new URL(url).origin || 'unknown',
93
- status: 'unknown',
94
- };
95
- req.on('response', (res) => {
96
- labelsValues.status = res.statusCode.toString();
97
- if (res.statusCode >= 400) {
85
+ // in seconds
86
+ const getDuration = (current, prev) =>
87
+ // max to avoid negative values and turn that into zero
88
+ prev === 0 ? 0 : Math.max((current - prev) / 1000, 0);
89
+ const createRequestWithMetrics = ({ metricsInstances: { requestsTotal, requestsErrors, requestsDuration, dnsResolveDuration, tcpConnectDuration, tlsHandshakeDuration, }, getServiceName, config, }) => {
90
+ const socketSet = new WeakSet();
91
+ return function requestWithMetrics(originalRequest, ...args) {
92
+ const [url, options] = getUrlAndOptions(args);
93
+ const serviceName = getServiceName(url);
94
+ const req = originalRequest.apply(this, args);
95
+ const timerDone = requestsDuration.startTimer();
96
+ const labelsValues = {
97
+ method: options.method || 'unknown',
98
+ service: serviceName || new URL(url).origin || 'unknown',
99
+ status: 'unknown',
100
+ };
101
+ req.on('response', (res) => {
102
+ labelsValues.status = res.statusCode.toString();
103
+ if (res.statusCode >= 400) {
104
+ requestsErrors.inc(labelsValues);
105
+ }
106
+ requestsTotal.inc(labelsValues);
107
+ timerDone(labelsValues);
108
+ });
109
+ req.on('error', (e) => {
110
+ if (POSSIBLE_ERRORS.includes(e === null || e === void 0 ? void 0 : e.code)) {
111
+ labelsValues.status = e.code;
112
+ }
113
+ requestsTotal.inc(labelsValues);
98
114
  requestsErrors.inc(labelsValues);
99
- }
100
- requestsTotal.inc(labelsValues);
101
- timerDone(labelsValues);
102
- });
103
- req.on('error', (e) => {
104
- if (POSSIBLE_ERRORS.includes(e === null || e === void 0 ? void 0 : e.code)) {
105
- labelsValues.status = e.code;
106
- }
107
- requestsTotal.inc(labelsValues);
108
- requestsErrors.inc(labelsValues);
109
- timerDone(labelsValues);
110
- });
111
- if (config.enableDnsResolveMetric) {
112
- req.on('socket', (socket) => {
113
- const dnsTimerDone = dnsResolveDuration.startTimer();
114
- socket.on('lookup', () => {
115
- dnsTimerDone({ service: labelsValues.service });
116
- });
115
+ timerDone(labelsValues);
117
116
  });
118
- }
119
- return req;
117
+ if (config.enableConnectionResolveMetrics) {
118
+ req.on('socket', (socket) => {
119
+ // due to keep-alive tcp option sockets might be reused
120
+ // ignore them because they have already emitted events we are interested in
121
+ if (socketSet.has(socket)) {
122
+ return;
123
+ }
124
+ socketSet.add(socket);
125
+ const timings = {
126
+ start: Date.now(),
127
+ lookupEnd: 0,
128
+ connectEnd: 0,
129
+ secureConnectEnd: 0,
130
+ };
131
+ const { service } = labelsValues;
132
+ socket.on('lookup', () => {
133
+ timings.lookupEnd = Date.now();
134
+ dnsResolveDuration.observe({ service }, getDuration(timings.lookupEnd, timings.start));
135
+ });
136
+ socket.on('connect', () => {
137
+ timings.connectEnd = Date.now();
138
+ tcpConnectDuration.observe({ service }, getDuration(timings.connectEnd, timings.lookupEnd));
139
+ });
140
+ socket.on('secureConnect', () => {
141
+ timings.secureConnectEnd = Date.now();
142
+ tlsHandshakeDuration.observe({ service }, getDuration(timings.secureConnectEnd, timings.connectEnd));
143
+ });
144
+ socket.on('close', () => {
145
+ socketSet.delete(socket);
146
+ });
147
+ });
148
+ }
149
+ return req;
150
+ };
120
151
  };
121
152
 
153
+ const DEFAULT_BUCKETS = [0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 20, 40, 60];
122
154
  const initRequestsMetrics = ({ metrics, getServiceName, http, https, createRequestWithMetrics, config, }) => {
123
155
  const metricsInstances = {
124
156
  requestsTotal: metrics.counter({
@@ -135,11 +167,25 @@ const initRequestsMetrics = ({ metrics, getServiceName, http, https, createReque
135
167
  name: 'http_sent_requests_duration',
136
168
  help: 'Execution time of the sent requests',
137
169
  labelNames: ['status', 'method', 'service'],
170
+ buckets: DEFAULT_BUCKETS,
138
171
  }),
139
172
  dnsResolveDuration: metrics.histogram({
140
173
  name: 'dns_resolve_duration',
141
174
  help: 'Time for dns resolve of the outhgoing requests',
142
175
  labelNames: ['service'],
176
+ buckets: DEFAULT_BUCKETS,
177
+ }),
178
+ tcpConnectDuration: metrics.histogram({
179
+ name: 'tcp_connect_duration',
180
+ help: 'Duration of tcp connect of the outgoing requests',
181
+ labelNames: ['service'],
182
+ buckets: DEFAULT_BUCKETS,
183
+ }),
184
+ tlsHandshakeDuration: metrics.histogram({
185
+ name: 'tls_handshake_duration',
186
+ help: 'Duration of tls handshake of the outgoing requests',
187
+ labelNames: ['service'],
188
+ buckets: DEFAULT_BUCKETS,
143
189
  }),
144
190
  };
145
191
  monkeypatch__default["default"]({
@@ -416,7 +462,7 @@ const eventLoopMetrics = (metrics) => {
416
462
  const histogram = metrics.histogram({
417
463
  name: NODEJS_EVENTLOOP_LAG,
418
464
  help: 'Lag of event loop in seconds (setInterval based).',
419
- buckets: [0.02, 0.1, 0.2, 0.5, 1, 3, 5],
465
+ buckets: [0.02, 0.1, 0.2, 0.5, 1, 3, 5, 7.5, 10],
420
466
  });
421
467
  startEventLoopLagMeasure(histogram);
422
468
  };
@@ -430,7 +476,7 @@ exports.MetricsModule = tslib.__decorate([
430
476
  core.provide({
431
477
  provide: METRICS_MODULE_CONFIG_TOKEN,
432
478
  useValue: {
433
- enableDnsResolveMetric: false,
479
+ enableConnectionResolveMetrics: false,
434
480
  },
435
481
  }),
436
482
  core.provide({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tramvai/module-metrics",
3
- "version": "1.61.0",
3
+ "version": "1.64.0",
4
4
  "description": "",
5
5
  "browser": "lib/browser.js",
6
6
  "main": "lib/server.js",
@@ -19,14 +19,14 @@
19
19
  "build-for-publish": "true"
20
20
  },
21
21
  "dependencies": {
22
- "@tramvai/core": "1.61.0",
23
- "@tramvai/tokens-server": "1.61.0",
24
- "@tramvai/tokens-metrics": "1.61.0",
25
- "@tramvai/module-common": "1.61.0",
26
- "@tramvai/tokens-http-client": "1.61.0",
27
- "@tramvai/state": "1.61.0",
28
- "@tramvai/papi": "1.61.0",
29
- "@tinkoff/measure-express-requests": "1.4.2",
22
+ "@tramvai/core": "1.64.0",
23
+ "@tramvai/tokens-server": "1.64.0",
24
+ "@tramvai/tokens-metrics": "1.64.0",
25
+ "@tramvai/module-common": "1.64.0",
26
+ "@tramvai/tokens-http-client": "1.64.0",
27
+ "@tramvai/state": "1.64.0",
28
+ "@tramvai/papi": "1.64.0",
29
+ "@tinkoff/measure-express-requests": "1.4.3",
30
30
  "@tinkoff/monkeypatch": "1.3.3",
31
31
  "@tinkoff/url": "0.7.37",
32
32
  "@tinkoff/utils": "^2.1.2",