arnavmq 0.9.6 → 0.10.2
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/LICENSE +1 -0
- package/README.md +13 -4
- package/package.json +3 -3
- package/src/index.js +16 -5
- package/src/modules/connection.js +15 -3
- package/src/modules/consumer.js +25 -6
- package/src/modules/message-parsers.js +6 -4
- package/src/modules/producer.js +25 -8
- package/src/modules/utils.js +24 -14
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -115,8 +115,17 @@ You can specify a config object, properties and default values are:
|
|
|
115
115
|
// generate a hostname so we can track this connection on the broker (rabbitmq management plugin)
|
|
116
116
|
hostname: process.env.HOSTNAME || process.env.USER || uuid.v4(),
|
|
117
117
|
|
|
118
|
-
//
|
|
118
|
+
// Deprecated. Use 'logger' instead. The transport to use to debug. If provided, arnavmq will show some logs
|
|
119
119
|
transport: utils.emptyLogger
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* A logger object with a log function for each of the log levels ("debug", "info", "warn", or "error").
|
|
123
|
+
* Each log function receives one parameter containing a log event with the following fields:
|
|
124
|
+
* * message - A string message describing the event. Always present.
|
|
125
|
+
* * error - An 'Error' object in case one is present.
|
|
126
|
+
* * params - An optional object containing extra parameters that can provide extra context for the event.
|
|
127
|
+
*/
|
|
128
|
+
logger: utils.emptyLogger
|
|
120
129
|
});
|
|
121
130
|
```
|
|
122
131
|
|
|
@@ -128,9 +137,9 @@ You can override any or no of the property above.
|
|
|
128
137
|
|
|
129
138
|
Find more about RabbitMQ in the links below:
|
|
130
139
|
|
|
131
|
-
- http://www.rabbitmq.com/getstarted.html
|
|
132
|
-
- https://www.cloudamqp.com/blog/2015-05-18-part1-rabbitmq-for-beginners-what-is-rabbitmq.html
|
|
133
|
-
- http://spring.io/blog/2010/06/14/understanding-amqp-the-protocol-used-by-rabbitmq
|
|
140
|
+
- <http://www.rabbitmq.com/getstarted.html>
|
|
141
|
+
- <https://www.cloudamqp.com/blog/2015-05-18-part1-rabbitmq-for-beginners-what-is-rabbitmq.html>
|
|
142
|
+
- <http://spring.io/blog/2010/06/14/understanding-amqp-the-protocol-used-by-rabbitmq/>
|
|
134
143
|
|
|
135
144
|
## Tests
|
|
136
145
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arnavmq",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.2",
|
|
4
4
|
"description": "ArnavMQ is a RabbitMQ wrapper",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"rabbitmq",
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/bringg/node-arnavmq#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"amqplib": "^0.
|
|
31
|
+
"amqplib": "^0.10.0",
|
|
32
32
|
"p-defer": "^3.0.0",
|
|
33
33
|
"serialize-error": "^8.0.1",
|
|
34
|
-
"uuid": "^8.3.
|
|
34
|
+
"uuid": "^8.3.2"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"child-process-promise": "^2.2.1",
|
package/src/index.js
CHANGED
|
@@ -4,9 +4,7 @@ const connection = require('./modules/connection');
|
|
|
4
4
|
|
|
5
5
|
/* eslint global-require: "off" */
|
|
6
6
|
module.exports = (config) => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
configuration = {
|
|
7
|
+
const configuration = {
|
|
10
8
|
// amqp connection string
|
|
11
9
|
host: 'amqp://localhost',
|
|
12
10
|
|
|
@@ -32,12 +30,25 @@ module.exports = (config) => {
|
|
|
32
30
|
// generate a hostname so we can track this connection on the broker (rabbitmq management plugin)
|
|
33
31
|
hostname: process.env.HOSTNAME || process.env.USER || uuid.v4(),
|
|
34
32
|
|
|
35
|
-
//
|
|
33
|
+
// Deprecated. Use 'logger' instead. The transport to use to debug. If provided, arnavmq will show some logs
|
|
36
34
|
transport: utils.emptyLogger,
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
/**
|
|
37
|
+
* A logger object with a log function for each of the log levels ("debug", "info", "warn", or "error").
|
|
38
|
+
* Each log function receives one parameter containing a log event with the following fields:
|
|
39
|
+
* * message - A string message describing the event. Always present.
|
|
40
|
+
* * error - An 'Error' object in case one is present.
|
|
41
|
+
* * params - An optional object containing extra parameters that can provide extra context for the event.
|
|
42
|
+
*/
|
|
43
|
+
logger: utils.emptyLogger,
|
|
44
|
+
|
|
45
|
+
...config
|
|
39
46
|
};
|
|
40
47
|
|
|
48
|
+
if (configuration.transport !== utils.emptyLogger) {
|
|
49
|
+
process.emitWarning("The 'transport' configuration option is deprecated. Please use the 'logger' option instead.", 'DeprecationWarning');
|
|
50
|
+
}
|
|
51
|
+
|
|
41
52
|
configuration.prefetch = parseInt(configuration.prefetch, 10) || 0;
|
|
42
53
|
return require('./modules/arnavmq')(connection(configuration));
|
|
43
54
|
};
|
|
@@ -40,7 +40,7 @@ class Connection {
|
|
|
40
40
|
conn.on('close', () => {
|
|
41
41
|
delete connection.conn;
|
|
42
42
|
});
|
|
43
|
-
conn.on('error', this.
|
|
43
|
+
conn.on('error', this._onError.bind(this));
|
|
44
44
|
connection.conn = conn;
|
|
45
45
|
return conn;
|
|
46
46
|
}).catch((e) => {
|
|
@@ -71,7 +71,7 @@ class Connection {
|
|
|
71
71
|
|
|
72
72
|
// on error we remove the channel so the next call will recreate it (auto-reconnect are handled by connection users)
|
|
73
73
|
channel.on('close', () => { delete connection.chann; });
|
|
74
|
-
channel.on('error', this.
|
|
74
|
+
channel.on('error', this._onError.bind(this));
|
|
75
75
|
|
|
76
76
|
connection.chann = channel;
|
|
77
77
|
return channel;
|
|
@@ -79,6 +79,18 @@ class Connection {
|
|
|
79
79
|
return connection.chann;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Log errors from connection/channel error events.
|
|
84
|
+
* @param {Error} error
|
|
85
|
+
*/
|
|
86
|
+
_onError(error) {
|
|
87
|
+
this._config.transport.error(error);
|
|
88
|
+
this._config.logger.error({
|
|
89
|
+
message: error.message,
|
|
90
|
+
error
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
82
94
|
/**
|
|
83
95
|
* Connect to AMQP and create channel
|
|
84
96
|
* @return {Promise} A promise that resolve with an amqp.node channel object
|
|
@@ -89,7 +101,7 @@ class Connection {
|
|
|
89
101
|
|
|
90
102
|
/**
|
|
91
103
|
* Register an event on the amqp.node channel
|
|
92
|
-
* @param {string} on
|
|
104
|
+
* @param {string} on the channel event name to be bound with
|
|
93
105
|
* @param {function} func the callback function to execute when the event is called
|
|
94
106
|
*/
|
|
95
107
|
addListener(on, func) {
|
package/src/modules/consumer.js
CHANGED
|
@@ -32,7 +32,11 @@ class Consumer {
|
|
|
32
32
|
return (content) => {
|
|
33
33
|
if (msg.properties.replyTo) {
|
|
34
34
|
const options = { correlationId: msg.properties.correlationId, persistent: true, durable: true };
|
|
35
|
-
this._connection.config.transport.
|
|
35
|
+
this._connection.config.transport.debug(loggerAlias, `[${queue}][${msg.properties.replyTo}] >`, content);
|
|
36
|
+
this._connection.config.logger.debug({
|
|
37
|
+
message: `${loggerAlias} [${queue}][${msg.properties.replyTo}] > ${content}`,
|
|
38
|
+
params: { content }
|
|
39
|
+
});
|
|
36
40
|
this.channel.sendToQueue(msg.properties.replyTo, parsers.out(content, options), options);
|
|
37
41
|
}
|
|
38
42
|
|
|
@@ -42,7 +46,7 @@ class Consumer {
|
|
|
42
46
|
|
|
43
47
|
/**
|
|
44
48
|
* Create a durable queue on RabbitMQ and consumes messages from it - executing a callback function.
|
|
45
|
-
*
|
|
49
|
+
* Automatically answers with the callback response (can be a Promise)
|
|
46
50
|
* @param {string} queue The RabbitMQ queue name
|
|
47
51
|
* @param {object} options (Optional) Options for the queue (durable, persistent, etc.)
|
|
48
52
|
* @param {Function} callback Callback function executed when a message is received on the queue name, can return a promise
|
|
@@ -73,10 +77,19 @@ class Consumer {
|
|
|
73
77
|
});
|
|
74
78
|
|
|
75
79
|
return this.channel.assertQueue(suffixedQueue, options).then((q) => {
|
|
76
|
-
this._connection.config.transport.
|
|
80
|
+
this._connection.config.transport.debug(loggerAlias, 'init', q.queue);
|
|
81
|
+
this._connection.config.logger.debug({
|
|
82
|
+
message: `${loggerAlias} init ${q.queue}`,
|
|
83
|
+
params: { queue: q.queue }
|
|
84
|
+
});
|
|
77
85
|
|
|
78
86
|
this.channel.consume(q.queue, (msg) => {
|
|
79
|
-
|
|
87
|
+
const messageString = msg.content.toString();
|
|
88
|
+
this._connection.config.transport.debug(loggerAlias, `[${q.queue}] < ${messageString}`);
|
|
89
|
+
this._connection.config.logger.debug({
|
|
90
|
+
message: `${loggerAlias} [${q.queue}] < ${messageString}`,
|
|
91
|
+
params: { queue: q.queue, message: messageString }
|
|
92
|
+
});
|
|
80
93
|
|
|
81
94
|
// main answer management chaining
|
|
82
95
|
// receive message, parse it, execute callback, check if should answer, ack/reject message
|
|
@@ -86,9 +99,15 @@ class Consumer {
|
|
|
86
99
|
.then(() => {
|
|
87
100
|
this.channel.ack(msg);
|
|
88
101
|
})
|
|
89
|
-
.catch((
|
|
102
|
+
.catch((error) => {
|
|
90
103
|
// if something bad happened in the callback, reject the message so we can requeue it (or not)
|
|
91
|
-
this._connection.config.transport.error(loggerAlias,
|
|
104
|
+
this._connection.config.transport.error(loggerAlias, error);
|
|
105
|
+
this._connection.config.logger.error({
|
|
106
|
+
message: `${loggerAlias} Failed processing message from queue ${q.queue}: ${error.message}`,
|
|
107
|
+
error,
|
|
108
|
+
params: { queue: q.queue, message: messageString }
|
|
109
|
+
});
|
|
110
|
+
|
|
92
111
|
this.channel.reject(msg, this._connection.config.requeue);
|
|
93
112
|
});
|
|
94
113
|
}, { noAck: false });
|
|
@@ -35,16 +35,18 @@ module.exports.in = (msg) => {
|
|
|
35
35
|
*/
|
|
36
36
|
/* eslint no-param-reassign: "off" */
|
|
37
37
|
module.exports.out = (content, options) => {
|
|
38
|
-
|
|
39
|
-
if (
|
|
38
|
+
// If falsy
|
|
39
|
+
if (content == null) {
|
|
40
|
+
return Buffer.from([]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (typeof content !== 'string') {
|
|
40
44
|
if (content.error instanceof Error) {
|
|
41
45
|
content.error = serializeError(content.error);
|
|
42
46
|
}
|
|
43
47
|
// if content is not a string, we JSONify it (JSON.parse can handle numbers, etc. so we can skip all the checks)
|
|
44
48
|
content = JSON.stringify(content);
|
|
45
49
|
options.contentType = 'application/json';
|
|
46
|
-
} else if (falsie.includes(content)) {
|
|
47
|
-
return Buffer.from([]);
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
return Buffer.from(content, 'utf-8');
|
package/src/modules/producer.js
CHANGED
|
@@ -49,12 +49,16 @@ class Producer {
|
|
|
49
49
|
const responsePromise = rpcQueue[correlationId];
|
|
50
50
|
|
|
51
51
|
if (responsePromise === undefined) {
|
|
52
|
-
|
|
52
|
+
const error = new Error(`Receiving RPC message from previous session: callback no more in memory. ${queue}`);
|
|
53
|
+
this._connection.config.transport.warn(
|
|
53
54
|
loggerAlias,
|
|
54
|
-
|
|
55
|
-
`Receiving RPC message from previous session: callback no more in memory. ${queue}`
|
|
56
|
-
)
|
|
55
|
+
error
|
|
57
56
|
);
|
|
57
|
+
this._connection.config.logger.warn({
|
|
58
|
+
message: `${loggerAlias} ${error.message}`,
|
|
59
|
+
error,
|
|
60
|
+
params: { queue, rpcQueue }
|
|
61
|
+
});
|
|
58
62
|
|
|
59
63
|
return;
|
|
60
64
|
}
|
|
@@ -64,6 +68,10 @@ class Producer {
|
|
|
64
68
|
loggerAlias,
|
|
65
69
|
`[${queue}] < answer`
|
|
66
70
|
);
|
|
71
|
+
this._connection.config.logger.debug({
|
|
72
|
+
message: `${loggerAlias} [${queue}] < answer`,
|
|
73
|
+
params: { queue }
|
|
74
|
+
});
|
|
67
75
|
|
|
68
76
|
try {
|
|
69
77
|
responsePromise.resolve(parsers.in(msg));
|
|
@@ -233,16 +241,25 @@ class Producer {
|
|
|
233
241
|
`[${queue}] > `,
|
|
234
242
|
message
|
|
235
243
|
);
|
|
244
|
+
this._connection.config.logger.debug({
|
|
245
|
+
message: `${loggerAlias} [${queue}] > ${message}`,
|
|
246
|
+
params: { queue, message }
|
|
247
|
+
});
|
|
236
248
|
|
|
237
249
|
return this.checkRpc(queue, parsers.out(message, settings), settings);
|
|
238
250
|
})
|
|
239
|
-
.catch((
|
|
240
|
-
if (!this._shouldRetry(
|
|
241
|
-
throw
|
|
251
|
+
.catch((error) => {
|
|
252
|
+
if (!this._shouldRetry(error, currentRetryNumber)) {
|
|
253
|
+
throw error;
|
|
242
254
|
}
|
|
243
255
|
|
|
244
256
|
// add timeout between retries because we don't want to overflow the CPU
|
|
245
|
-
this._connection.config.transport.error(loggerAlias,
|
|
257
|
+
this._connection.config.transport.error(loggerAlias, error);
|
|
258
|
+
this._connection.config.logger.error({
|
|
259
|
+
message: `${loggerAlias} Failed sending message to queue ${queue}: ${error.message}`,
|
|
260
|
+
error,
|
|
261
|
+
params: { queue, message }
|
|
262
|
+
});
|
|
246
263
|
return utils
|
|
247
264
|
.timeoutPromise(this._connection.config.timeout)
|
|
248
265
|
.then(() => this._sendToQueue(queue, message, settings, currentRetryNumber + 1));
|
package/src/modules/utils.js
CHANGED
|
@@ -1,22 +1,32 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A function to generate a pause in promise chaining
|
|
3
|
-
* @param {number} timer How much ws to wait
|
|
4
|
-
* @return {Promise} A Promise that will resolve when timer is expired
|
|
5
|
-
*/
|
|
6
|
-
module.exports.timeoutPromise = (timer) => new Promise((resolve) => {
|
|
7
|
-
setTimeout(resolve, timer);
|
|
8
|
-
});
|
|
9
|
-
|
|
10
1
|
function empty() {}
|
|
11
2
|
|
|
12
|
-
|
|
13
|
-
* Default logger to prevent any printing in the terminal
|
|
14
|
-
* @type {Object} - empty logger overwriting the console object methods
|
|
15
|
-
*/
|
|
16
|
-
module.exports.emptyLogger = {
|
|
3
|
+
const emptyLogger = {
|
|
17
4
|
info: empty,
|
|
18
5
|
debug: empty,
|
|
19
6
|
warn: empty,
|
|
20
7
|
error: empty,
|
|
21
8
|
log: empty
|
|
22
9
|
};
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
/**
|
|
13
|
+
* Default transport to prevent any printing in the terminal
|
|
14
|
+
* @type {Object} - empty logger overwriting the console object methods
|
|
15
|
+
*/
|
|
16
|
+
emptyLogger,
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @deprecated
|
|
20
|
+
* For backwards compatibility with the `transport` configuration.
|
|
21
|
+
*/
|
|
22
|
+
emptyTransport: emptyLogger,
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A function to generate a pause in promise chaining
|
|
26
|
+
* @param {number} timer How much ws to wait
|
|
27
|
+
* @return {Promise} A Promise that will resolve when timer is expired
|
|
28
|
+
*/
|
|
29
|
+
timeoutPromise: (timer) => new Promise((resolve) => {
|
|
30
|
+
setTimeout(resolve, timer);
|
|
31
|
+
})
|
|
32
|
+
};
|