@linagora/rabbitmq-client 0.1.2 → 0.2.1

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
@@ -37,9 +37,10 @@ Only `url` is required. Everything else has defaults.
37
37
  | `connectionRetryDelay` | `5000` | Milliseconds between reconnection attempts |
38
38
  | `initMaxAttempts` | `5` | Connection attempts on startup before giving up |
39
39
  | `publishMaxAttempts` | `5` | Publish retries (exponential backoff, capped at 60s) |
40
- | `prefetch` | `10` | Channel prefetch count |
40
+ | `prefetch` | `10` | Channel prefetch: how many messages the broker delivers into consumer memory before waiting for acks |
41
+ | `concurrency` | = `prefetch` | How many handlers run at once per subscription. Prefetch buffers messages in memory; concurrency bounds how many are processed simultaneously. Only throttles when set below `prefetch` (the broker never delivers more than `prefetch` unacked at a time); `0` means no limit. Override per queue via `subscribe(..., { concurrency })` |
41
42
  | `closeTimeout` | `5000` | Milliseconds to wait for in-flight messages when closing |
42
- | `logger` | -- | Parent [pino](https://github.com/pinojs/pino) instance (the library creates its own if omitted) |
43
+ | `logger` | console (warn/error) | Any `ILogger` (`console`, Winston, pino, Bunyan). Omitted: logs warn/error to the console, stays quiet otherwise. Pass `silentLogger` for no output. |
43
44
  | `hooks` | -- | Observability callbacks (see [Hooks](#hooks)) |
44
45
 
45
46
  ## How it works
@@ -79,6 +80,15 @@ await client.subscribe('events', 'order.placed', 'order-queue', handler, {
79
80
 
80
81
  Custom arguments are merged with the DLQ wiring defaults, so you can swap the queue type or add a max-length without losing dead-letter routing.
81
82
 
83
+ To limit how many messages this queue processes at once (for example, to protect a downstream database from a burst), set `concurrency` on the subscription. It defaults to the client-level `concurrency`, which in turn defaults to `prefetch`:
84
+
85
+ ```typescript
86
+ // Buffer up to 50 messages in memory, but process at most 4 at a time.
87
+ await client.subscribe('events', 'order.placed', 'order-queue', handler, {
88
+ concurrency: 4,
89
+ })
90
+ ```
91
+
82
92
  ### Unsubscribing
83
93
 
84
94
  Cancel a consumer and remove it from the auto-restoration list:
package/dist/index.cjs CHANGED
@@ -30,7 +30,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
- RabbitMQClient: () => RabbitMQClient
33
+ RabbitMQClient: () => RabbitMQClient,
34
+ defaultLogger: () => defaultLogger,
35
+ silentLogger: () => silentLogger
34
36
  });
35
37
  module.exports = __toCommonJS(src_exports);
36
38
 
@@ -38,12 +40,69 @@ module.exports = __toCommonJS(src_exports);
38
40
  var import_amqplib = __toESM(require("amqplib"), 1);
39
41
 
40
42
  // src/logger.ts
41
- var import_pino = __toESM(require("pino"), 1);
42
- var createLogger = (parent) => {
43
- if (parent) {
44
- return parent.child({ service: "rabbitmq-client" });
43
+ var defaultLogger = {
44
+ debug: () => {
45
+ },
46
+ info: () => {
47
+ },
48
+ warn: (message, ...args) => console.warn("[rabbitmq-client]", message, ...args),
49
+ error: (message, ...args) => console.error("[rabbitmq-client]", message, ...args)
50
+ };
51
+ var silentLogger = {
52
+ debug: () => {
53
+ },
54
+ info: () => {
55
+ },
56
+ warn: () => {
57
+ },
58
+ error: () => {
59
+ }
60
+ };
61
+
62
+ // src/semaphore.ts
63
+ var Semaphore = class {
64
+ permits;
65
+ waiters = [];
66
+ constructor(permits) {
67
+ if (!Number.isInteger(permits) || permits < 1) {
68
+ throw new Error(`Semaphore requires a positive integer of permits, got ${permits}`);
69
+ }
70
+ this.permits = permits;
71
+ }
72
+ /**
73
+ * Acquires a permit, resolving immediately when one is free or queueing
74
+ * (FIFO) until another holder releases. Always pair with `release()` in a
75
+ * `finally` so a throwing handler never leaks a permit.
76
+ */
77
+ acquire() {
78
+ if (this.permits > 0) {
79
+ this.permits--;
80
+ return Promise.resolve();
81
+ }
82
+ return new Promise((resolve) => {
83
+ this.waiters.push(resolve);
84
+ });
85
+ }
86
+ /**
87
+ * Releases a permit. Hands it directly to the next waiter if any are queued,
88
+ * otherwise returns it to the pool.
89
+ */
90
+ release() {
91
+ const next = this.waiters.shift();
92
+ if (next) {
93
+ next();
94
+ } else {
95
+ this.permits++;
96
+ }
97
+ }
98
+ /** Permits currently available. Exposed for tests and introspection. */
99
+ get available() {
100
+ return this.permits;
101
+ }
102
+ /** Number of callers currently queued waiting for a permit. */
103
+ get waiting() {
104
+ return this.waiters.length;
45
105
  }
46
- return (0, import_pino.default)({ name: "rabbitmq-client" });
47
106
  };
48
107
 
49
108
  // src/client.ts
@@ -70,6 +129,11 @@ var RabbitMQClient = class {
70
129
  reconnectionPromise = null;
71
130
  assertedExchanges = /* @__PURE__ */ new Set();
72
131
  consumerTags = /* @__PURE__ */ new Map();
132
+ // Per-queue concurrency limiter, keyed by queue name. Persisted across
133
+ // reconnects (unlike consumerTags) so handlers still running when a
134
+ // connection drops keep holding their permits, and the new consumer cannot
135
+ // exceed the ceiling while the old pipeline drains. `null` = no limit.
136
+ subscriptionSemaphores = /* @__PURE__ */ new Map();
73
137
  inflightCount = 0;
74
138
  drainResolve = null;
75
139
  constructor(options) {
@@ -81,9 +145,14 @@ var RabbitMQClient = class {
81
145
  initMaxAttempts: options.initMaxAttempts ?? DEFAULTS.initMaxAttempts,
82
146
  publishMaxAttempts: options.publishMaxAttempts ?? DEFAULTS.publishMaxAttempts,
83
147
  prefetch: options.prefetch ?? DEFAULTS.prefetch,
148
+ // Default concurrency tracks prefetch, preserving the previous behaviour
149
+ // (handlers ran fire-and-forget, so up to `prefetch` ran at once) while
150
+ // making the ceiling explicit and independently tunable. A value <= 0
151
+ // (including an unlimited `prefetch: 0`) means "no concurrency limit".
152
+ concurrency: options.concurrency ?? options.prefetch ?? DEFAULTS.prefetch,
84
153
  closeTimeout: options.closeTimeout ?? DEFAULTS.closeTimeout
85
154
  };
86
- this.logger = createLogger(options.logger);
155
+ this.logger = options.logger ?? defaultLogger;
87
156
  this.hooks = options.hooks ?? {};
88
157
  }
89
158
  /**
@@ -117,10 +186,10 @@ var RabbitMQClient = class {
117
186
  this.channel = await this.connection.createConfirmChannel();
118
187
  this.logger.info("Confirm channel created");
119
188
  await this.channel.prefetch(this.options.prefetch);
120
- this.logger.info({ prefetch: this.options.prefetch }, "Channel prefetch set");
189
+ this.logger.info("Channel prefetch set", { prefetch: this.options.prefetch });
121
190
  this.connection.on("error", (error) => {
122
191
  this.connected = false;
123
- this.logger.error({ error }, "Connection error");
192
+ this.logger.error("Connection error", { error });
124
193
  });
125
194
  this.connection.on("close", () => {
126
195
  this.connected = false;
@@ -128,7 +197,7 @@ var RabbitMQClient = class {
128
197
  this.reconnectWithRetry();
129
198
  });
130
199
  this.channel.on("error", (error) => {
131
- this.logger.error({ error }, "Channel error");
200
+ this.logger.error("Channel error", { error });
132
201
  this.handleChannelFailure();
133
202
  });
134
203
  this.channel.on("close", () => {
@@ -141,20 +210,17 @@ var RabbitMQClient = class {
141
210
  attempts++;
142
211
  this.connected = false;
143
212
  if (maxAttempts !== void 0 && attempts >= maxAttempts) {
144
- this.logger.error({ error, attempts, maxAttempts }, "Connection failed after maximum attempts");
213
+ this.logger.error("Connection failed after maximum attempts", { error, attempts, maxAttempts });
145
214
  throw new Error(
146
215
  `Failed to connect to RabbitMQ after ${attempts} attempts. Check RABBITMQ_URL configuration and RabbitMQ server availability.`
147
216
  );
148
217
  }
149
- this.logger.warn(
150
- {
151
- error,
152
- attempt: attempts,
153
- maxAttempts: maxAttempts ?? "unlimited",
154
- retryDelayMs: this.options.connectionRetryDelay
155
- },
156
- "Connection attempt failed, retrying..."
157
- );
218
+ this.logger.warn("Connection attempt failed, retrying...", {
219
+ error,
220
+ attempt: attempts,
221
+ maxAttempts: maxAttempts ?? "unlimited",
222
+ retryDelayMs: this.options.connectionRetryDelay
223
+ });
158
224
  await this.sleep(this.options.connectionRetryDelay);
159
225
  }
160
226
  }
@@ -202,21 +268,18 @@ var RabbitMQClient = class {
202
268
  });
203
269
  await this.channel.waitForConfirms();
204
270
  if (attempts > 0) {
205
- this.logger.info({ exchange, routingKey, messageSize: content.length, attempts }, "Published message after retries");
271
+ this.logger.info("Published message after retries", { exchange, routingKey, messageSize: content.length, attempts });
206
272
  } else {
207
- this.logger.info({ exchange, routingKey, messageSize: content.length }, "Published message");
273
+ this.logger.info("Published message", { exchange, routingKey, messageSize: content.length });
208
274
  }
209
- this.logger.debug({ exchange, routingKey, payload: message }, "Published message payload");
275
+ this.logger.debug("Published message payload", { exchange, routingKey, payload: message });
210
276
  this.callHook(this.hooks.onPublish, { exchange, routingKey, attempts: attempts + 1 });
211
277
  return;
212
278
  } catch (error) {
213
279
  attempts++;
214
280
  this.connected = false;
215
281
  if (attempts >= maxAttempts) {
216
- this.logger.error(
217
- { error, exchange, routingKey, attempts, maxAttempts },
218
- "Publish failed after max attempts"
219
- );
282
+ this.logger.error("Publish failed after max attempts", { error, exchange, routingKey, attempts, maxAttempts });
220
283
  throw new Error(
221
284
  `Failed to publish to ${exchange}/${routingKey} after ${attempts} attempts: ${error instanceof Error ? error.message : String(error)}`
222
285
  );
@@ -225,10 +288,7 @@ var RabbitMQClient = class {
225
288
  baseDelay * Math.pow(2, attempts - 1),
226
289
  MAX_PUBLISH_RETRY_DELAY_MS
227
290
  );
228
- this.logger.warn(
229
- { error, exchange, routingKey, attempt: attempts, maxAttempts, retryDelayMs: retryDelay },
230
- "Publish attempt failed, retrying"
231
- );
291
+ this.logger.warn("Publish attempt failed, retrying", { error, exchange, routingKey, attempt: attempts, maxAttempts, retryDelayMs: retryDelay });
232
292
  await this.sleep(retryDelay);
233
293
  }
234
294
  }
@@ -242,7 +302,7 @@ var RabbitMQClient = class {
242
302
  async close(clearSubscriptions = true) {
243
303
  try {
244
304
  if (this.inflightCount > 0) {
245
- this.logger.info({ inflightCount: this.inflightCount }, "Waiting for in-flight messages to drain");
305
+ this.logger.info("Waiting for in-flight messages to drain", { inflightCount: this.inflightCount });
246
306
  await this.waitForDrain(this.options.closeTimeout);
247
307
  }
248
308
  await this.channel?.close();
@@ -256,10 +316,11 @@ var RabbitMQClient = class {
256
316
  this.consumerTags.clear();
257
317
  if (clearSubscriptions) {
258
318
  this.subscriptions = [];
319
+ this.subscriptionSemaphores.clear();
259
320
  }
260
321
  this.logger.info("Connection closed");
261
322
  } catch (error) {
262
- this.logger.error({ error }, "Error closing connection");
323
+ this.logger.error("Error closing connection", { error });
263
324
  throw error;
264
325
  }
265
326
  }
@@ -285,7 +346,7 @@ var RabbitMQClient = class {
285
346
  if (this.subscriptions.length === 0) {
286
347
  return;
287
348
  }
288
- this.logger.info({ count: this.subscriptions.length }, "Re-establishing subscriptions");
349
+ this.logger.info("Re-establishing subscriptions", { count: this.subscriptions.length });
289
350
  const subs = [...this.subscriptions];
290
351
  const results = await Promise.allSettled(
291
352
  subs.map(async (sub) => {
@@ -301,14 +362,14 @@ var RabbitMQClient = class {
301
362
  } else {
302
363
  const queue = subs[index].queue;
303
364
  failed.push(queue);
304
- this.logger.error({ error: result.reason, queue }, "Failed to re-subscribe to queue");
365
+ this.logger.error("Failed to re-subscribe to queue", { error: result.reason, queue });
305
366
  }
306
367
  });
307
368
  if (succeeded.length > 0) {
308
- this.logger.info({ count: succeeded.length, queues: succeeded }, "Successfully re-subscribed to queues");
369
+ this.logger.info("Successfully re-subscribed to queues", { count: succeeded.length, queues: succeeded });
309
370
  }
310
371
  if (failed.length > 0) {
311
- this.logger.warn({ count: failed.length, queues: failed }, "Some subscriptions failed to restore");
372
+ this.logger.warn("Some subscriptions failed to restore", { count: failed.length, queues: failed });
312
373
  }
313
374
  this.callHook(this.hooks.onReconnect, { subscriptionsRestored: succeeded.length, subscriptionsFailed: failed.length });
314
375
  }
@@ -328,6 +389,7 @@ var RabbitMQClient = class {
328
389
  this.subscriptions.push(sub);
329
390
  } else {
330
391
  this.subscriptions[existingIndex] = sub;
392
+ this.subscriptionSemaphores.delete(queue);
331
393
  }
332
394
  await this.setupSubscription(sub);
333
395
  }
@@ -340,22 +402,24 @@ var RabbitMQClient = class {
340
402
  await this.channel.cancel(tag);
341
403
  }
342
404
  this.consumerTags.delete(queue);
405
+ this.subscriptionSemaphores.delete(queue);
343
406
  this.subscriptions = this.subscriptions.filter((s) => s.queue !== queue);
344
- this.logger.info({ queue }, "Unsubscribed from queue");
407
+ this.logger.info("Unsubscribed from queue", { queue });
345
408
  }
346
409
  async setupSubscription(sub) {
347
410
  if (!this.channel) {
348
411
  throw new Error("Channel not available");
349
412
  }
413
+ const channel = this.channel;
350
414
  const { exchange, routingKey, queue, handler, options } = sub;
351
415
  const dlxExchange = `${exchange}.dlx`;
352
416
  const dlqQueue = `${queue}.dlq`;
353
417
  const dlqRoutingKey = `${routingKey}.dead`;
354
- await this.channel.assertExchange(dlxExchange, "topic", { durable: true });
355
- await this.channel.assertQueue(dlqQueue, { durable: true });
356
- await this.channel.bindQueue(dlqQueue, dlxExchange, dlqRoutingKey);
418
+ await channel.assertExchange(dlxExchange, "topic", { durable: true });
419
+ await channel.assertQueue(dlqQueue, { durable: true });
420
+ await channel.bindQueue(dlqQueue, dlxExchange, dlqRoutingKey);
357
421
  if (!this.assertedExchanges.has(exchange)) {
358
- await this.channel.assertExchange(exchange, "topic", { durable: true });
422
+ await channel.assertExchange(exchange, "topic", { durable: true });
359
423
  this.assertedExchanges.add(exchange);
360
424
  }
361
425
  const queueType = options?.queueArguments?.["x-queue-type"] ?? "quorum";
@@ -366,101 +430,102 @@ var RabbitMQClient = class {
366
430
  if (queueType === "quorum") {
367
431
  queueArgs["x-dead-letter-strategy"] = "at-least-once";
368
432
  }
369
- await this.channel.assertQueue(queue, {
433
+ await channel.assertQueue(queue, {
370
434
  durable: true,
371
435
  deadLetterExchange: dlxExchange,
372
436
  deadLetterRoutingKey: dlqRoutingKey,
373
437
  arguments: { ...queueArgs, ...options?.queueArguments }
374
438
  });
375
- await this.channel.bindQueue(queue, exchange, routingKey);
376
- const { consumerTag } = await this.channel.consume(
439
+ await channel.bindQueue(queue, exchange, routingKey);
440
+ if (!this.subscriptionSemaphores.has(queue)) {
441
+ const concurrency = options?.concurrency ?? this.options.concurrency;
442
+ this.subscriptionSemaphores.set(queue, concurrency > 0 ? new Semaphore(concurrency) : null);
443
+ }
444
+ const semaphore = this.subscriptionSemaphores.get(queue) ?? null;
445
+ const { consumerTag } = await channel.consume(
377
446
  queue,
378
447
  (message) => {
379
448
  if (message) {
380
- this.handleWithRetry(message, handler).catch((error) => {
381
- this.logger.error({ error }, "Unhandled error in message handler");
382
- });
449
+ this.dispatch(message, handler, semaphore, channel);
383
450
  }
384
451
  },
385
452
  { noAck: false }
386
453
  );
387
454
  this.consumerTags.set(queue, consumerTag);
388
- this.logger.info({ exchange, routingKey, queue }, "Subscribed to queue");
455
+ this.logger.info("Subscribed to queue", { exchange, routingKey, queue });
389
456
  }
390
- async handleWithRetry(message, handler) {
391
- const channel = this.channel;
392
- if (!channel) {
393
- this.logger.warn("Cannot process message -- channel unavailable");
457
+ /**
458
+ * Gates a delivered message on the subscription's concurrency semaphore, then
459
+ * processes it. A message counts as in-flight from delivery until its handler
460
+ * settles (including time spent waiting for a permit), so graceful `close()`
461
+ * drains queued messages as well as actively-processing ones.
462
+ */
463
+ dispatch(message, handler, semaphore, channel) {
464
+ this.inflightCount++;
465
+ const acquire = semaphore ? semaphore.acquire() : Promise.resolve();
466
+ acquire.then(() => this.handleWithRetry(message, handler, channel)).catch((error) => {
467
+ this.logger.error("Unhandled error in message handler", { error });
468
+ }).finally(() => {
469
+ semaphore?.release();
470
+ this.inflightCount--;
471
+ if (this.inflightCount === 0 && this.drainResolve) {
472
+ this.drainResolve();
473
+ }
474
+ });
475
+ }
476
+ async handleWithRetry(message, handler, channel) {
477
+ if (channel !== this.channel) {
478
+ this.logger.warn("Skipping message from a superseded channel; it will be redelivered");
394
479
  return;
395
480
  }
396
- this.inflightCount++;
481
+ const startTime = Date.now();
482
+ let attempts = 0;
483
+ const routingKey = message.fields.routingKey;
484
+ const exchange = message.fields.exchange;
485
+ let content;
397
486
  try {
398
- const startTime = Date.now();
399
- let attempts = 0;
400
- const routingKey = message.fields.routingKey;
401
- const exchange = message.fields.exchange;
402
- let content;
487
+ content = JSON.parse(message.content.toString());
488
+ } catch (parseError) {
489
+ const rawContent = message.content.toString();
490
+ const rawPreview = rawContent.substring(0, 100);
491
+ this.logger.error("Failed to parse message JSON, sending to DLQ", {
492
+ error: parseError,
493
+ exchange,
494
+ routingKey,
495
+ rawContentPreview: rawPreview + (rawContent.length > 100 ? "..." : "")
496
+ });
497
+ channel.nack(message, false, false);
498
+ this.callHook(this.hooks.onMessageDlq, { exchange, routingKey, duration: 0, reason: "invalid_json" });
499
+ return;
500
+ }
501
+ this.logger.debug("Message received, processing", { exchange, routingKey, payload: content });
502
+ while (attempts < this.options.maxRetries) {
403
503
  try {
404
- content = JSON.parse(message.content.toString());
405
- } catch (parseError) {
406
- const rawContent = message.content.toString();
407
- const rawPreview = rawContent.substring(0, 100);
408
- this.logger.error(
409
- {
410
- error: parseError,
411
- exchange,
412
- routingKey,
413
- rawContentPreview: rawPreview + (rawContent.length > 100 ? "..." : "")
414
- },
415
- "Failed to parse message JSON, sending to DLQ"
416
- );
417
- channel.nack(message, false, false);
418
- this.callHook(this.hooks.onMessageDlq, { exchange, routingKey, duration: 0, reason: "invalid_json" });
504
+ await handler(content);
505
+ const duration2 = Date.now() - startTime;
506
+ this.logger.info("Message processed successfully", { exchange, routingKey, duration: duration2, attempts: attempts + 1 });
507
+ channel.ack(message);
508
+ this.callHook(this.hooks.onMessageProcessed, { exchange, routingKey, duration: duration2, attempts: attempts + 1 });
419
509
  return;
420
- }
421
- this.logger.debug({ exchange, routingKey, payload: content }, "Message received, processing");
422
- while (attempts < this.options.maxRetries) {
423
- try {
424
- await handler(content);
425
- const duration2 = Date.now() - startTime;
426
- this.logger.info(
427
- { exchange, routingKey, duration: duration2, attempts: attempts + 1 },
428
- "Message processed successfully"
429
- );
430
- channel.ack(message);
431
- this.callHook(this.hooks.onMessageProcessed, { exchange, routingKey, duration: duration2, attempts: attempts + 1 });
432
- return;
433
- } catch (error) {
434
- attempts++;
435
- this.logger.error(
436
- {
437
- error: error instanceof Error ? error.message : error,
438
- stack: error instanceof Error ? error.stack : void 0,
439
- exchange,
440
- routingKey,
441
- attempt: attempts,
442
- maxRetries: this.options.maxRetries
443
- },
444
- "Handler failed"
445
- );
446
- if (attempts < this.options.maxRetries) {
447
- await this.sleep(this.options.retryDelay);
448
- }
510
+ } catch (error) {
511
+ attempts++;
512
+ this.logger.error("Handler failed", {
513
+ error: error instanceof Error ? error.message : error,
514
+ stack: error instanceof Error ? error.stack : void 0,
515
+ exchange,
516
+ routingKey,
517
+ attempt: attempts,
518
+ maxRetries: this.options.maxRetries
519
+ });
520
+ if (attempts < this.options.maxRetries) {
521
+ await this.sleep(this.options.retryDelay);
449
522
  }
450
523
  }
451
- const duration = Date.now() - startTime;
452
- this.logger.error(
453
- { exchange, routingKey, maxRetries: this.options.maxRetries, duration },
454
- "Message failed after max retries, sending to DLQ"
455
- );
456
- channel.nack(message, false, false);
457
- this.callHook(this.hooks.onMessageDlq, { exchange, routingKey, duration, reason: "max_retries_exhausted" });
458
- } finally {
459
- this.inflightCount--;
460
- if (this.inflightCount === 0 && this.drainResolve) {
461
- this.drainResolve();
462
- }
463
524
  }
525
+ const duration = Date.now() - startTime;
526
+ this.logger.error("Message failed after max retries, sending to DLQ", { exchange, routingKey, maxRetries: this.options.maxRetries, duration });
527
+ channel.nack(message, false, false);
528
+ this.callHook(this.hooks.onMessageDlq, { exchange, routingKey, duration, reason: "max_retries_exhausted" });
464
529
  }
465
530
  /**
466
531
  * Lightweight liveness probe: creates and immediately deletes a temporary
@@ -478,7 +543,7 @@ var RabbitMQClient = class {
478
543
  await this.channel.deleteQueue(queue);
479
544
  return true;
480
545
  } catch (error) {
481
- this.logger.warn({ error }, "Health check failed");
546
+ this.logger.warn("Health check failed", { error });
482
547
  return false;
483
548
  }
484
549
  }
@@ -486,10 +551,7 @@ var RabbitMQClient = class {
486
551
  if (this.inflightCount === 0) return Promise.resolve();
487
552
  return new Promise((resolve) => {
488
553
  const timer = setTimeout(() => {
489
- this.logger.warn(
490
- { inflightCount: this.inflightCount },
491
- "Close timeout reached with messages still in flight"
492
- );
554
+ this.logger.warn("Close timeout reached with messages still in flight", { inflightCount: this.inflightCount });
493
555
  this.drainResolve = null;
494
556
  resolve();
495
557
  }, timeout);
@@ -505,7 +567,7 @@ var RabbitMQClient = class {
505
567
  try {
506
568
  hook?.(info);
507
569
  } catch (error) {
508
- this.logger.debug({ error }, "Observability hook error");
570
+ this.logger.debug("Observability hook error", { error });
509
571
  }
510
572
  }
511
573
  sleep(ms) {
@@ -514,6 +576,8 @@ var RabbitMQClient = class {
514
576
  };
515
577
  // Annotate the CommonJS export names for ESM import in node:
516
578
  0 && (module.exports = {
517
- RabbitMQClient
579
+ RabbitMQClient,
580
+ defaultLogger,
581
+ silentLogger
518
582
  });
519
583
  //# sourceMappingURL=index.cjs.map