@mimik/request-retry 2.0.11 → 2.1.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/README.md CHANGED
@@ -29,6 +29,10 @@ The following properties are added to the `options` object under the `retry` pro
29
29
  - *responseName*: name to associate with the response. The default is `response`or is the value is not a string or and empty string it is set to `response`,
30
30
  - timeout `{int}`: in seconds the timeout to be set for the request and retries. If the timeout is reached, a TimeoutError will be generated. The default is `50 seconds`. If the value is less than `10 seconds` the value is set to `10 seconds`, and if the value is more than `120 seconds` the value is set to `120 seconds`,
31
31
  - retryStrategy `{function}`: function describing the retry strategy. The parameters are (`nbRetry`, `err`, `options`, `correlationId`). Must return a `boolean`, if not the function strategy is ignored.
32
+ The following properties are added to the `options` object under the 'metrics' property:
33
+ - HTTPRequestDuration `function`: prom-client function to measure the delay of the request,
34
+ - url: optional url to be added for labelling the metric. If not present the url of the request will be used.
35
+
32
36
  The `default` retryStategy is:
33
37
 
34
38
  ``` javascript
package/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  const Promise = require('bluebird');
2
- const axios = require('axios');
3
2
 
4
3
  const logger = require('@mimik/sumologic-winston-logger');
5
4
  const { getRichError } = require('@mimik/response-helper');
@@ -50,6 +49,10 @@ Promise.config({ cancellation: true });
50
49
  * - *responseName*: name to associate with the response. The default is `response`or is the value is not a string or and empty string it is set to `response`,
51
50
  * - timeout `{int}`: in seconds the timeout to be set for the request and retries. If the timeout is reached, a TimeoutError will be generated. The default is `50 seconds`. If the value is less than `10 seconds` the value is set to `10 seconds`, and if the value is more than `120 seconds` the value is set to `120 seconds`,
52
51
  * - retryStrategy `{function}`: function describing the retry strategy. The parameters are (`nbRetry`, `err`, `options`, `correlationId`). Must return a `boolean`, if not the function strategy is ignored.
52
+ * The following properties are added to the `options` object under the 'metrics' property:
53
+ * - HTTPRequestDuration `function`: prom-client function to measure the delay of the request,
54
+ * - url: optional url to be added for labelling the metric. If not present the url of the request will be used.
55
+ *
53
56
  * The `default` retryStategy is:
54
57
  *
55
58
  *``` javascript
@@ -69,9 +72,10 @@ Promise.config({ cancellation: true });
69
72
  * - `qs` is used to assign to `params`
70
73
  */
71
74
  const rpRetry = (origOptions) => {
72
- const { CancelToken } = axios;
73
- const source = CancelToken.source();
75
+ const startHrTime = process.hrtime();
74
76
  const options = origOptions;
77
+ const { metrics } = options;
78
+ const enteredUrl = options.uri || options.url;
75
79
  const correlationId = (options.headers && options.headers['x-correlation-id']) ? options.headers['x-correlation-id'] : getCorrelationId();
76
80
  const errors = [];
77
81
  const delayPromises = [];
@@ -80,14 +84,25 @@ const rpRetry = (origOptions) => {
80
84
  const logLevelLength = Object.keys(logLevel).length;
81
85
  let nbRetries = 0;
82
86
  let mainTimeout;
87
+ const measure = (statusCode) => {
88
+ if (metrics && metrics.HTTPRequestDuration) {
89
+ const elapsedHrTime = process.hrtime(startHrTime);
90
+ const elapsedTimeInMs = elapsedHrTime[0] * 1000 + elapsedHrTime[1] / 1e6;
91
+
92
+ metrics.HTTPRequestDuration
93
+ .labels('rpRetry', options.method, metrics.url || enteredUrl, enteredUrl.includes('?'), statusCode)
94
+ .observe(elapsedTimeInMs);
95
+ }
96
+ };
83
97
 
84
98
  if (!options.headers) options.headers = { 'user-agent': setUserAgent() };
85
99
  else if (!options.headers['user-agent']) options.headers['user-agent'] = setUserAgent();
86
- options.cancelToken = source.token;
100
+ const optionsInfo = { ...options };
101
+ delete optionsInfo.metrics;
87
102
 
88
103
  const retryProcess = (nbRetry) => rp(options)
89
104
  .then((response) => {
90
- const info = { options, nbRetries, errors };
105
+ const info = { options: optionsInfo, nbRetries, errors };
91
106
 
92
107
  if (logLevel.response) {
93
108
  info[logLevel.responseName] = setResponse(response, logLevel.responseDetails);
@@ -130,8 +145,7 @@ const rpRetry = (origOptions) => {
130
145
  const retryPromise = retryProcess(0);
131
146
  const mainTimeoutPromise = new Promise((resolve, reject) => {
132
147
  mainTimeout = setTimeout(() => {
133
- delayPromises.forEach((delayPromise) => delayPromise.cancel(`retry timeout, ${criteria.timeout}, ${nbRetries}`));
134
- source.cancel(`retry timeout, ${criteria.timeout}, ${nbRetries}`);
148
+ delayPromises.forEach((delayPromise) => delayPromise.cancel(`retry timeout delay, ${criteria.timeout}, ${nbRetries}`));
135
149
  let error = new Error('retry timeout');
136
150
 
137
151
  if (nbRetries !== 0) {
@@ -141,7 +155,7 @@ const rpRetry = (origOptions) => {
141
155
  error = getRichError(
142
156
  'System',
143
157
  'request error response',
144
- { options },
158
+ { options: optionsInfo },
145
159
  error,
146
160
  logLevel.error || ((logLevelLength === 0) ? DEFAULT_LOGLEVEL_ERROR : undefined),
147
161
  correlationId,
@@ -150,17 +164,19 @@ const rpRetry = (origOptions) => {
150
164
  }, criteria.timeout * 1000);
151
165
  });
152
166
 
153
- if (logLevel.request) logger[logLevel.request]('making a request', { options, criteria }, correlationId);
154
- else if (logLevelLength === 0) logger[DEFAULT_LOGLEVEL_REQUEST]('making a request', { options, criteria }, correlationId);
167
+ if (logLevel.request) logger[logLevel.request]('making a request', { options: optionsInfo, criteria }, correlationId);
168
+ else if (logLevelLength === 0) logger[DEFAULT_LOGLEVEL_REQUEST]('making a request', { options: optionsInfo, criteria }, correlationId);
155
169
  return Promise.race([retryPromise, mainTimeoutPromise])
156
170
  .then((result) => {
157
171
  mainTimeoutPromise.cancel('main timeout');
158
172
  clearTimeout(mainTimeout);
173
+ measure(200);
159
174
  return result;
160
175
  })
161
176
  .catch((err) => {
162
177
  mainTimeoutPromise.cancel(`main timeout error: ${err.message}`);
163
178
  clearTimeout(mainTimeout);
179
+ measure(err.statusCode || 500);
164
180
  throw err;
165
181
  });
166
182
  };
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable no-console */
2
- require('../test/testEnv');
2
+ require('./testEnv');
3
3
 
4
4
  const { rpRetry } = require('../index');
5
5
 
@@ -15,7 +15,7 @@ const options = {
15
15
  retry: {},
16
16
  };
17
17
 
18
- options.retry.delayStrategy = () => 10000;
18
+ options.retry.delayStrategy = () => 1000;
19
19
  options.retry.timeout = 10;
20
20
  options.retry.retries = 2;
21
21
 
@@ -11,6 +11,7 @@ const config = {
11
11
  nbRetry: 400,
12
12
  success: 200,
13
13
  error: 500,
14
+ timeout: 4000,
14
15
  },
15
16
  post: {
16
17
  success: 201,
@@ -37,9 +38,10 @@ app.get(`${config.base}${config.path}`, (req, res) => {
37
38
  return;
38
39
  }
39
40
  nbRequest += 1;
40
- res.statusCode = config.get.error;
41
-
42
- res.send({ statusCode: config.get.error });
41
+ setTimeout(() => {
42
+ res.statusCode = config.get.error;
43
+ res.send({ statusCode: config.get.error });
44
+ }, config.get.timeout);
43
45
  });
44
46
  app.post(`${config.base}${config.path}`, (req, res) => {
45
47
  console.log('----->', 'Received a POST request');
@@ -0,0 +1,23 @@
1
+ /* eslint no-process-env: "off" */
2
+
3
+ /**
4
+ * The following environment variables are set for the test:
5
+ *
6
+ * | Env variable name | Description | Default | Comments |
7
+ * | ----------------- | ----------- | ------- | -------- |
8
+ * | SUMO_LOGIC_ENDPOINT | endpoint to use to log on sumologic | null
9
+ * | SUMO_LOGIC_COLLECTOR_CODE | code to use to log on sumologic | null
10
+ * | NO_STACK | flag to have a stack associated with the log | yes
11
+ * | LOG_LEVEL | log level to log | error
12
+ * | CONSOLE_LEVEL | log level to diplay | debug
13
+ * | LOG_MODE | log mode | none
14
+ */
15
+
16
+ // process.env.SUMO_LOGIC_ENDPOINT = 'http://localhost:9080/logs/';
17
+ // process.env.SUMO_LOGIC_COLLECTOR_CODE = '1234';
18
+ process.env.SUMO_LOGIC_ENDPOINT = null;
19
+ process.env.SUMO_LOGIC_COLLECTOR_CODE = null;
20
+ process.env.NO_STACK = 'yes';
21
+ process.env.LOG_LEVEL = 'debug';
22
+ process.env.CONSOLE_LEVEL = 'debug';
23
+ process.env.LOG_MODE = 'none';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mimik/request-retry",
3
- "version": "2.0.11",
3
+ "version": "2.1.0",
4
4
  "description": "Request retry wrapping axios",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -31,8 +31,8 @@
31
31
  "dependencies": {
32
32
  "@mimik/request-helper": "^1.7.7",
33
33
  "@mimik/response-helper": "^2.6.2",
34
- "@mimik/sumologic-winston-logger": "^1.6.11",
35
- "axios": "1.1.3",
34
+ "@mimik/sumologic-winston-logger": "^1.6.13",
35
+ "axios": "1.2.1",
36
36
  "bluebird": "3.7.2",
37
37
  "lodash": "4.17.21"
38
38
  },
@@ -42,7 +42,7 @@
42
42
  "acorn": "8.8.1",
43
43
  "body-parser": "1.20.1",
44
44
  "chai": "4.3.7",
45
- "eslint": "8.28.0",
45
+ "eslint": "8.30.0",
46
46
  "eslint-config-airbnb": "19.0.4",
47
47
  "eslint-plugin-import": "2.26.0",
48
48
  "eslint-plugin-jsx-a11y": "6.6.1",
@@ -51,10 +51,10 @@
51
51
  "express": "4.18.2",
52
52
  "husky": "8.0.2",
53
53
  "jsdoc-to-markdown": "8.0.0",
54
- "mocha": "10.1.0",
54
+ "mocha": "10.2.0",
55
55
  "mochawesome": "7.1.3",
56
56
  "nyc": "15.1.0",
57
- "sinon": "15.0.0",
57
+ "sinon": "15.0.1",
58
58
  "verror": "1.10.1"
59
59
  }
60
60
  }
package/test/testEnv.js CHANGED
@@ -10,6 +10,7 @@
10
10
  * | NO_STACK | flag to have a stack associated with the log | yes
11
11
  * | LOG_LEVEL | log level to log | error
12
12
  * | CONSOLE_LEVEL | log level to diplay | debug
13
+ * | LOG_MODE | log mode | none
13
14
  */
14
15
 
15
16
  // process.env.SUMO_LOGIC_ENDPOINT = 'http://localhost:9080/logs/';
@@ -19,3 +20,4 @@ process.env.SUMO_LOGIC_COLLECTOR_CODE = null;
19
20
  process.env.NO_STACK = 'yes';
20
21
  process.env.LOG_LEVEL = 'debug';
21
22
  process.env.CONSOLE_LEVEL = 'debug';
23
+ process.env.LOG_MODE = 'none';