@sentienguard/apm 1.0.0 → 1.0.3

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/package.json CHANGED
@@ -1,11 +1,15 @@
1
1
  {
2
2
  "name": "@sentienguard/apm",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "description": "SentienGuard APM SDK - Minimal, production-safe application performance monitoring",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
7
+ "browser": "./src/browser.js",
7
8
  "exports": {
8
- ".": "./src/index.js"
9
+ ".": {
10
+ "browser": "./src/browser.js",
11
+ "default": "./src/index.js"
12
+ }
9
13
  },
10
14
  "scripts": {
11
15
  "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
@@ -16,7 +20,10 @@
16
20
  "monitoring",
17
21
  "performance",
18
22
  "metrics",
19
- "sentienguard"
23
+ "sentienguard",
24
+ "mongodb",
25
+ "circuit-breaker",
26
+ "observability"
20
27
  ],
21
28
  "author": "SentienGuard",
22
29
  "license": "MIT",
@@ -26,7 +33,18 @@
26
33
  "engines": {
27
34
  "node": ">=16.0.0"
28
35
  },
29
- "peerDependencies": {},
36
+ "peerDependencies": {
37
+ "mongoose": ">=6.0.0",
38
+ "opossum": ">=8.0.0"
39
+ },
40
+ "peerDependenciesMeta": {
41
+ "mongoose": {
42
+ "optional": true
43
+ },
44
+ "opossum": {
45
+ "optional": true
46
+ }
47
+ },
30
48
  "devDependencies": {
31
49
  "jest": "^29.7.0"
32
50
  },
package/src/aggregator.js CHANGED
@@ -5,6 +5,7 @@
5
5
  *
6
6
  * Aggregation key: (service, method, route) for requests
7
7
  * Aggregation key: (service, name, type) for dependencies
8
+ * Aggregation key: (collection, operation) for MongoDB operations
8
9
  */
9
10
 
10
11
  import { RouteRegistry } from './normalizer.js';
@@ -24,6 +25,20 @@ function createDependencyKey(name, type) {
24
25
  return `${name}:${type}`;
25
26
  }
26
27
 
28
+ /**
29
+ * Create an aggregation key for MongoDB operation metrics
30
+ */
31
+ function createMongoKey(collection, operation) {
32
+ return `${collection}:${operation}`;
33
+ }
34
+
35
+ /**
36
+ * Create an aggregation key for OpenAI operation metrics
37
+ */
38
+ function createOpenAIKey(operation, model) {
39
+ return `${operation}:${model}`;
40
+ }
41
+
27
42
  /**
28
43
  * Metrics Aggregator class
29
44
  * Maintains in-memory aggregated metrics that are flushed periodically
@@ -44,6 +59,28 @@ export class MetricsAggregator {
44
59
  // Dependency metrics: Map<key, {name, type, count, errorCount, latency: {sum, min, max}}>
45
60
  this.dependencies = new Map();
46
61
 
62
+ // MongoDB operation metrics: Map<key, {collection, operation, count, errorCount, latency: {sum, min, max}}>
63
+ this.mongodbOperations = new Map();
64
+
65
+ // MongoDB slow queries: Array<{collection, operation, latency, timestamp}>
66
+ this.slowQueries = [];
67
+
68
+ // OpenAI operation metrics: Map<key, {operation, model, count, errorCount, latency, tokens, cost}>
69
+ this.openaiOperations = new Map();
70
+
71
+ // MongoDB connection pool stats (updated periodically, not reset)
72
+ if (!this.mongodbPoolStats) {
73
+ this.mongodbPoolStats = {
74
+ active: 0,
75
+ idle: 0,
76
+ waitQueueSize: 0,
77
+ lastUpdated: null
78
+ };
79
+ }
80
+
81
+ // Circuit breaker state changes: Array<{service, state, timestamp}>
82
+ this.circuitBreakerStates = [];
83
+
47
84
  // Error counter for unhandled exceptions
48
85
  this.unhandledErrors = 0;
49
86
  }
@@ -131,11 +168,162 @@ export class MetricsAggregator {
131
168
  this.unhandledErrors++;
132
169
  }
133
170
 
171
+ /**
172
+ * Record a MongoDB operation
173
+ *
174
+ * @param {string} collection - Collection name
175
+ * @param {string} operation - Operation type (find, insert, update, delete, aggregate)
176
+ * @param {number} latency - Response time in milliseconds
177
+ * @param {boolean} isError - Whether the operation resulted in an error
178
+ */
179
+ recordMongoOperation(collection, operation, latency, isError = false) {
180
+ const key = createMongoKey(collection, operation);
181
+
182
+ let metric = this.mongodbOperations.get(key);
183
+ if (!metric) {
184
+ metric = {
185
+ collection,
186
+ operation,
187
+ count: 0,
188
+ errorCount: 0,
189
+ latency: {
190
+ sum: 0,
191
+ min: Infinity,
192
+ max: 0
193
+ }
194
+ };
195
+ this.mongodbOperations.set(key, metric);
196
+ }
197
+
198
+ metric.count++;
199
+ if (isError) {
200
+ metric.errorCount++;
201
+ }
202
+
203
+ metric.latency.sum += latency;
204
+ metric.latency.min = Math.min(metric.latency.min, latency);
205
+ metric.latency.max = Math.max(metric.latency.max, latency);
206
+ }
207
+
208
+ /**
209
+ * Record a slow MongoDB query
210
+ *
211
+ * @param {string} collection - Collection name
212
+ * @param {string} operation - Operation type
213
+ * @param {number} latency - Response time in milliseconds
214
+ */
215
+ recordSlowQuery(collection, operation, latency) {
216
+ // Keep only the most recent slow queries (max 100 per interval)
217
+ if (this.slowQueries.length >= 100) {
218
+ this.slowQueries.shift();
219
+ }
220
+
221
+ this.slowQueries.push({
222
+ collection,
223
+ operation,
224
+ latency: Math.round(latency),
225
+ timestamp: Date.now()
226
+ });
227
+ }
228
+
229
+ /**
230
+ * Record an OpenAI API operation
231
+ *
232
+ * @param {Object} data - Operation data
233
+ * @param {string} data.operation - Operation type (chat.completions, embeddings, images.generate, etc.)
234
+ * @param {string} data.model - Model used (gpt-4, gpt-3.5-turbo, etc.)
235
+ * @param {number} data.latency - Response time in milliseconds
236
+ * @param {number} data.promptTokens - Number of prompt tokens used
237
+ * @param {number} data.completionTokens - Number of completion tokens used
238
+ * @param {number} data.totalTokens - Total tokens used
239
+ * @param {number} data.cost - Estimated cost in USD
240
+ * @param {string|null} data.error - Error message if failed
241
+ * @param {number} data.statusCode - HTTP status code
242
+ */
243
+ recordOpenAIOperation(data) {
244
+ const key = createOpenAIKey(data.operation, data.model);
245
+
246
+ let metric = this.openaiOperations.get(key);
247
+ if (!metric) {
248
+ metric = {
249
+ operation: data.operation,
250
+ model: data.model,
251
+ count: 0,
252
+ errorCount: 0,
253
+ latency: {
254
+ sum: 0,
255
+ min: Infinity,
256
+ max: 0
257
+ },
258
+ tokens: {
259
+ prompt: 0,
260
+ completion: 0,
261
+ total: 0
262
+ },
263
+ cost: 0
264
+ };
265
+ this.openaiOperations.set(key, metric);
266
+ }
267
+
268
+ metric.count++;
269
+ if (data.error) {
270
+ metric.errorCount++;
271
+ }
272
+
273
+ metric.latency.sum += data.latency;
274
+ metric.latency.min = Math.min(metric.latency.min, data.latency);
275
+ metric.latency.max = Math.max(metric.latency.max, data.latency);
276
+
277
+ metric.tokens.prompt += data.promptTokens || 0;
278
+ metric.tokens.completion += data.completionTokens || 0;
279
+ metric.tokens.total += data.totalTokens || 0;
280
+
281
+ metric.cost += data.cost || 0;
282
+ }
283
+
284
+ /**
285
+ * Update MongoDB connection pool statistics
286
+ *
287
+ * @param {Object} stats - Pool statistics
288
+ * @param {number} stats.active - Active connections (checked out)
289
+ * @param {number} stats.idle - Idle connections (available in pool)
290
+ * @param {number} stats.waitQueueSize - Wait queue size
291
+ * @param {number} stats.total - Total connections in pool
292
+ */
293
+ updatePoolStats(stats) {
294
+ this.mongodbPoolStats = {
295
+ active: stats.active || 0,
296
+ idle: stats.idle || 0,
297
+ waitQueueSize: stats.waitQueueSize || 0,
298
+ total: stats.total || (stats.active || 0) + (stats.idle || 0),
299
+ lastUpdated: Date.now()
300
+ };
301
+ }
302
+
303
+ /**
304
+ * Record a circuit breaker state change
305
+ *
306
+ * @param {string} service - Service/dependency name (e.g., 'mongodb')
307
+ * @param {string} state - New state ('open', 'halfOpen', 'close')
308
+ */
309
+ recordCircuitState(service, state) {
310
+ this.circuitBreakerStates.push({
311
+ service,
312
+ state,
313
+ timestamp: Date.now()
314
+ });
315
+ }
316
+
134
317
  /**
135
318
  * Check if there's data to flush
136
319
  */
137
320
  hasData() {
138
- return this.requests.size > 0 || this.dependencies.size > 0;
321
+ return this.requests.size > 0 ||
322
+ this.dependencies.size > 0 ||
323
+ this.mongodbOperations.size > 0 ||
324
+ this.slowQueries.length > 0 ||
325
+ this.circuitBreakerStates.length > 0 ||
326
+ this.openaiOperations.size > 0;
139
327
  }
140
328
 
141
329
  /**
@@ -148,7 +336,12 @@ export class MetricsAggregator {
148
336
  service: config.service,
149
337
  environment: config.environment,
150
338
  requests: [],
151
- dependencies: []
339
+ dependencies: [],
340
+ mongodb: [],
341
+ mongodbPool: null,
342
+ slowQueries: [],
343
+ circuitBreaker: [],
344
+ openai: []
152
345
  };
153
346
 
154
347
  // Convert request metrics to array
@@ -181,6 +374,53 @@ export class MetricsAggregator {
181
374
  });
182
375
  }
183
376
 
377
+ // Convert MongoDB operation metrics to array
378
+ for (const metric of this.mongodbOperations.values()) {
379
+ payload.mongodb.push({
380
+ collection: metric.collection,
381
+ operation: metric.operation,
382
+ count: metric.count,
383
+ errorCount: metric.errorCount,
384
+ latency: {
385
+ sum: Math.round(metric.latency.sum),
386
+ min: metric.latency.min === Infinity ? 0 : Math.round(metric.latency.min),
387
+ max: Math.round(metric.latency.max)
388
+ }
389
+ });
390
+ }
391
+
392
+ // Include MongoDB pool stats if available
393
+ if (this.mongodbPoolStats && this.mongodbPoolStats.lastUpdated) {
394
+ payload.mongodbPool = { ...this.mongodbPoolStats };
395
+ }
396
+
397
+ // Include slow queries
398
+ payload.slowQueries = [...this.slowQueries];
399
+
400
+ // Include circuit breaker state changes
401
+ payload.circuitBreaker = [...this.circuitBreakerStates];
402
+
403
+ // Convert OpenAI operation metrics to array
404
+ for (const metric of this.openaiOperations.values()) {
405
+ payload.openai.push({
406
+ operation: metric.operation,
407
+ model: metric.model,
408
+ count: metric.count,
409
+ errorCount: metric.errorCount,
410
+ latency: {
411
+ sum: Math.round(metric.latency.sum),
412
+ min: metric.latency.min === Infinity ? 0 : Math.round(metric.latency.min),
413
+ max: Math.round(metric.latency.max)
414
+ },
415
+ tokens: {
416
+ prompt: metric.tokens.prompt,
417
+ completion: metric.tokens.completion,
418
+ total: metric.tokens.total
419
+ },
420
+ cost: Math.round(metric.cost * 1000000) / 1000000 // 6 decimal places
421
+ });
422
+ }
423
+
184
424
  // Reset for next interval
185
425
  this.reset();
186
426
 
@@ -194,6 +434,10 @@ export class MetricsAggregator {
194
434
  return {
195
435
  requestMetrics: this.requests.size,
196
436
  dependencyMetrics: this.dependencies.size,
437
+ mongodbMetrics: this.mongodbOperations.size,
438
+ openaiMetrics: this.openaiOperations.size,
439
+ slowQueries: this.slowQueries.length,
440
+ circuitBreakerEvents: this.circuitBreakerStates.length,
197
441
  uniqueRoutes: this.routeRegistry.size,
198
442
  unhandledErrors: this.unhandledErrors
199
443
  };
package/src/browser.js ADDED
@@ -0,0 +1,107 @@
1
+ /**
2
+ * SentienGuard APM SDK - Browser No-Op
3
+ *
4
+ * This module is automatically used when the SDK is imported in a browser
5
+ * environment (via bundlers like Webpack, Vite, Rsbuild, etc.).
6
+ *
7
+ * The APM SDK only works in Node.js (it patches http/https modules),
8
+ * so in the browser we export no-op stubs to avoid build errors.
9
+ */
10
+
11
+ const noop = () => {};
12
+ const asyncNoop = async () => {};
13
+ const noopMiddleware = (_req, _res, next) => next?.();
14
+
15
+ function initialize() {}
16
+ async function shutdown() {}
17
+ function getStatus() {
18
+ return {
19
+ enabled: false,
20
+ initialized: false,
21
+ config: { service: '', environment: '', flushInterval: 0 },
22
+ stats: {}
23
+ };
24
+ }
25
+ function flush() {}
26
+ function getConfig() {
27
+ return {};
28
+ }
29
+ function isEnabled() {
30
+ return false;
31
+ }
32
+ function normalizeRoute(route) {
33
+ return route;
34
+ }
35
+ function extractRoute(req) {
36
+ return req?.url || '/';
37
+ }
38
+ function getAggregator() {
39
+ return {
40
+ getStats: () => ({}),
41
+ recordRequest: noop,
42
+ recordDependency: noop,
43
+ recordError: noop
44
+ };
45
+ }
46
+ function instrumentMongoDB() {}
47
+ function instrumentOpenAI() {}
48
+ function createBreaker(fn) {
49
+ return fn;
50
+ }
51
+ function wrapMongoOperation(fn) {
52
+ return fn;
53
+ }
54
+ function getBreakerStats() {
55
+ return {};
56
+ }
57
+
58
+ const RouteRegistry = {
59
+ register: noop,
60
+ match: () => null
61
+ };
62
+
63
+ const expressMiddleware = noopMiddleware;
64
+ const expressErrorMiddleware = (_err, _req, _res, next) => next?.();
65
+ const fastifyPlugin = noop;
66
+ const fastifyErrorHandler = noop;
67
+
68
+ export {
69
+ initialize,
70
+ shutdown,
71
+ getStatus,
72
+ flush,
73
+ getConfig,
74
+ isEnabled,
75
+ expressMiddleware,
76
+ expressErrorMiddleware,
77
+ fastifyPlugin,
78
+ fastifyErrorHandler,
79
+ normalizeRoute,
80
+ extractRoute,
81
+ RouteRegistry,
82
+ getAggregator,
83
+ instrumentMongoDB,
84
+ instrumentOpenAI,
85
+ createBreaker,
86
+ wrapMongoOperation,
87
+ getBreakerStats
88
+ };
89
+
90
+ export default {
91
+ initialize,
92
+ shutdown,
93
+ getStatus,
94
+ flush,
95
+ expressMiddleware,
96
+ expressErrorMiddleware,
97
+ fastifyPlugin,
98
+ fastifyErrorHandler,
99
+ normalizeRoute,
100
+ extractRoute,
101
+ getAggregator,
102
+ instrumentMongoDB,
103
+ instrumentOpenAI,
104
+ createBreaker,
105
+ wrapMongoOperation,
106
+ getBreakerStats
107
+ };
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Circuit Breaker Module
3
+ *
4
+ * Provides circuit breaker functionality using Opossum library.
5
+ * Wraps operations (especially database calls) to prevent cascade failures.
6
+ *
7
+ * Circuit States:
8
+ * - CLOSED: Normal operation, requests pass through
9
+ * - OPEN: Circuit tripped due to failures, requests fail fast
10
+ * - HALF-OPEN: Testing if service recovered, limited requests allowed
11
+ *
12
+ * Usage:
13
+ * import { createBreaker, wrapMongoOperation } from './circuitBreaker.js';
14
+ *
15
+ * // Wrap an async operation
16
+ * const breaker = createBreaker(async () => await db.query(), { name: 'db-query' });
17
+ * const result = await breaker.fire();
18
+ *
19
+ * // Or use the MongoDB helper
20
+ * const findUsers = wrapMongoOperation('User.find', async () => User.find());
21
+ * const users = await findUsers();
22
+ */
23
+
24
+ import config, { debug, warn } from './config.js';
25
+ import { getAggregator } from './aggregator.js';
26
+
27
+ // Store active circuit breakers
28
+ const breakers = new Map();
29
+
30
+ // Check if opossum is available
31
+ let CircuitBreaker = null;
32
+
33
+ /**
34
+ * Initialize Opossum (lazy load)
35
+ */
36
+ async function initOpossum() {
37
+ if (CircuitBreaker) return true;
38
+
39
+ try {
40
+ const opossum = await import('opossum');
41
+ CircuitBreaker = opossum.default || opossum;
42
+ debug('Opossum circuit breaker loaded');
43
+ return true;
44
+ } catch (err) {
45
+ debug('Opossum not installed. Circuit breaker disabled. Install with: npm install opossum');
46
+ return false;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Default circuit breaker options
52
+ */
53
+ function getDefaultOptions() {
54
+ return {
55
+ timeout: config.circuitBreaker.timeout,
56
+ errorThresholdPercentage: config.circuitBreaker.errorThresholdPercentage,
57
+ resetTimeout: config.circuitBreaker.resetTimeout,
58
+ volumeThreshold: config.circuitBreaker.volumeThreshold
59
+ };
60
+ }
61
+
62
+ /**
63
+ * Create a circuit breaker for an async operation
64
+ *
65
+ * @param {Function} asyncFn - The async function to wrap
66
+ * @param {Object} options - Circuit breaker options
67
+ * @param {string} options.name - Name for the circuit (used for metrics)
68
+ * @param {number} options.timeout - Timeout in ms
69
+ * @param {number} options.errorThresholdPercentage - Error threshold to trip
70
+ * @param {number} options.resetTimeout - Time before attempting recovery
71
+ * @param {Function} options.fallback - Fallback function when circuit is open
72
+ * @returns {Object} Circuit breaker instance or wrapped function if opossum unavailable
73
+ */
74
+ export async function createBreaker(asyncFn, options = {}) {
75
+ if (!config.circuitBreaker.enabled) {
76
+ debug('Circuit breaker disabled, returning passthrough');
77
+ return createPassthrough(asyncFn);
78
+ }
79
+
80
+ const initialized = await initOpossum();
81
+ if (!initialized) {
82
+ return createPassthrough(asyncFn);
83
+ }
84
+
85
+ const name = options.name || 'unnamed';
86
+ const breakerOptions = {
87
+ ...getDefaultOptions(),
88
+ ...options,
89
+ name
90
+ };
91
+
92
+ const breaker = new CircuitBreaker(asyncFn, breakerOptions);
93
+ const aggregator = getAggregator();
94
+
95
+ // Attach event listeners for metrics
96
+ breaker.on('open', () => {
97
+ aggregator.recordCircuitState(name, 'open');
98
+ warn(`Circuit breaker OPEN: ${name}`);
99
+ });
100
+
101
+ breaker.on('halfOpen', () => {
102
+ aggregator.recordCircuitState(name, 'halfOpen');
103
+ debug(`Circuit breaker HALF-OPEN: ${name}`);
104
+ });
105
+
106
+ breaker.on('close', () => {
107
+ aggregator.recordCircuitState(name, 'close');
108
+ debug(`Circuit breaker CLOSED: ${name}`);
109
+ });
110
+
111
+ breaker.on('fallback', (result) => {
112
+ debug(`Circuit breaker fallback executed: ${name}`);
113
+ });
114
+
115
+ breaker.on('timeout', () => {
116
+ debug(`Circuit breaker timeout: ${name}`);
117
+ });
118
+
119
+ breaker.on('reject', () => {
120
+ debug(`Circuit breaker rejected (circuit open): ${name}`);
121
+ });
122
+
123
+ // Register fallback if provided
124
+ if (options.fallback) {
125
+ breaker.fallback(options.fallback);
126
+ }
127
+
128
+ // Store reference
129
+ breakers.set(name, breaker);
130
+
131
+ return breaker;
132
+ }
133
+
134
+ /**
135
+ * Create a passthrough wrapper when circuit breaker is disabled
136
+ */
137
+ function createPassthrough(asyncFn) {
138
+ return {
139
+ fire: (...args) => asyncFn(...args),
140
+ fallback: () => {},
141
+ on: () => {},
142
+ isOpen: () => false,
143
+ isClosed: () => true,
144
+ stats: { fires: 0, failures: 0, successes: 0 }
145
+ };
146
+ }
147
+
148
+ /**
149
+ * Create a circuit breaker specifically for MongoDB operations
150
+ *
151
+ * @param {string} operationName - Name of the operation (e.g., 'User.find')
152
+ * @param {Function} mongoOperation - The MongoDB operation function
153
+ * @param {Object} options - Additional options
154
+ * @returns {Function} Wrapped function that uses circuit breaker
155
+ */
156
+ export async function wrapMongoOperation(operationName, mongoOperation, options = {}) {
157
+ const breaker = await createBreaker(mongoOperation, {
158
+ name: `mongodb:${operationName}`,
159
+ // MongoDB-specific defaults
160
+ timeout: options.timeout || config.circuitBreaker.timeout,
161
+ errorThresholdPercentage: options.errorThresholdPercentage || 50,
162
+ resetTimeout: options.resetTimeout || 30000,
163
+ ...options
164
+ });
165
+
166
+ // Return a function that fires the breaker
167
+ return async function wrappedOperation(...args) {
168
+ return breaker.fire(...args);
169
+ };
170
+ }
171
+
172
+ /**
173
+ * Get a registered circuit breaker by name
174
+ *
175
+ * @param {string} name - Circuit breaker name
176
+ * @returns {Object|null} Circuit breaker instance or null
177
+ */
178
+ export function getBreaker(name) {
179
+ return breakers.get(name) || null;
180
+ }
181
+
182
+ /**
183
+ * Get all registered circuit breakers
184
+ *
185
+ * @returns {Map} Map of circuit breaker instances
186
+ */
187
+ export function getAllBreakers() {
188
+ return new Map(breakers);
189
+ }
190
+
191
+ /**
192
+ * Get circuit breaker statistics
193
+ *
194
+ * @param {string} name - Circuit breaker name (optional, returns all if not provided)
195
+ * @returns {Object} Statistics object
196
+ */
197
+ export function getBreakerStats(name) {
198
+ if (name) {
199
+ const breaker = breakers.get(name);
200
+ if (!breaker) return null;
201
+
202
+ return {
203
+ name,
204
+ state: breaker.opened ? 'open' : (breaker.halfOpen ? 'halfOpen' : 'closed'),
205
+ stats: breaker.stats
206
+ };
207
+ }
208
+
209
+ // Return stats for all breakers
210
+ const allStats = {};
211
+ for (const [breakerName, breaker] of breakers) {
212
+ allStats[breakerName] = {
213
+ state: breaker.opened ? 'open' : (breaker.halfOpen ? 'halfOpen' : 'closed'),
214
+ stats: breaker.stats
215
+ };
216
+ }
217
+ return allStats;
218
+ }
219
+
220
+ /**
221
+ * Reset a circuit breaker (close it)
222
+ *
223
+ * @param {string} name - Circuit breaker name
224
+ */
225
+ export function resetBreaker(name) {
226
+ const breaker = breakers.get(name);
227
+ if (breaker && typeof breaker.close === 'function') {
228
+ breaker.close();
229
+ debug(`Circuit breaker reset: ${name}`);
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Shutdown all circuit breakers
235
+ */
236
+ export function shutdownBreakers() {
237
+ for (const [name, breaker] of breakers) {
238
+ if (typeof breaker.shutdown === 'function') {
239
+ breaker.shutdown();
240
+ }
241
+ }
242
+ breakers.clear();
243
+ debug('All circuit breakers shutdown');
244
+ }
245
+
246
+ export default {
247
+ createBreaker,
248
+ wrapMongoOperation,
249
+ getBreaker,
250
+ getAllBreakers,
251
+ getBreakerStats,
252
+ resetBreaker,
253
+ shutdownBreakers
254
+ };