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 CHANGED
@@ -1,6 +1,7 @@
1
1
  The MIT License (MIT)
2
2
 
3
3
  Copyright (c) 2016 Dial Once
4
+ Copyright (c) 2020 Bringg
4
5
 
5
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
7
  of this software and associated documentation files (the "Software"), to deal
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
- // the transport to use to debug. if provided, arnavmq will show some logs
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.9.6",
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.8.0",
31
+ "amqplib": "^0.10.0",
32
32
  "p-defer": "^3.0.0",
33
33
  "serialize-error": "^8.0.1",
34
- "uuid": "^8.3.1"
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
- let configuration = { ...config };
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
- // the transport to use to debug. if provided, arnavmq will show some logs
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
- ...configuration
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._config.transport.error);
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._config.transport.error);
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 the channel event name to be binded with
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) {
@@ -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.info(loggerAlias, `[${queue}][${msg.properties.replyTo}] >`, content);
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
- * Automaticaly answers with the callback response (can be a Promise)
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.info(loggerAlias, 'init', q.queue);
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
- this._connection.config.transport.info(loggerAlias, `[${q.queue}] < ${msg.content.toString()}`);
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((err) => {
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, err);
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
- const falsie = [undefined, null];
39
- if (!falsie.includes(content) && typeof content !== 'string') {
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');
@@ -49,12 +49,16 @@ class Producer {
49
49
  const responsePromise = rpcQueue[correlationId];
50
50
 
51
51
  if (responsePromise === undefined) {
52
- this._connection.config.transport.error(
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
- new Error(
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((err) => {
240
- if (!this._shouldRetry(err, currentRetryNumber)) {
241
- throw err;
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, err);
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));
@@ -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
+ };