@jsreport/jsreport-core 3.5.0 → 3.7.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.
@@ -5,10 +5,12 @@ const winston = require('winston')
5
5
  const Transport = require('winston-transport')
6
6
  const debug = require('debug')('jsreport')
7
7
  const createDefaultLoggerFormat = require('./createDefaultLoggerFormat')
8
- const normalizeMetaFromLogs = require('../shared/normalizeMetaFromLogs')
8
+ const createNormalizeMetaLoggerFormat = require('./createNormalizeMetaLoggerFormat')
9
+ const Request = require('./request')
9
10
 
10
11
  const defaultLoggerFormat = createDefaultLoggerFormat()
11
12
  const defaultLoggerFormatWithTimestamp = createDefaultLoggerFormat({ timestamp: true })
13
+ const normalizeMetaLoggerFormat = createNormalizeMetaLoggerFormat()
12
14
 
13
15
  function createLogger () {
14
16
  const logger = winston.createLogger(getConfigurationOptions())
@@ -166,6 +168,9 @@ function configureLogger (logger, _transports) {
166
168
  }
167
169
 
168
170
  for (const { TransportClass, options } of transportsToAdd) {
171
+ if (options.silent) {
172
+ continue
173
+ }
169
174
  const transportInstance = new TransportClass(options)
170
175
 
171
176
  const existingTransport = logger.transports.find((t) => t.name === transportInstance.name)
@@ -177,25 +182,30 @@ function configureLogger (logger, _transports) {
177
182
  logger.add(transportInstance)
178
183
  }
179
184
 
180
- logger.__configured__ = true
181
- }
185
+ const originalLog = logger.log
182
186
 
183
- function getConfigurationOptions () {
184
- const normalizeMeta = winston.format((info) => {
185
- const { level, message, ...meta } = info
186
- const newMeta = normalizeMetaFromLogs(level, message, meta)
187
-
188
- if (newMeta != null) {
189
- return {
190
- level,
191
- message,
192
- ...newMeta
193
- }
187
+ // we want to normalize the req has httpIncomingRequest early
188
+ // otherwise we will get serialization issues when trying to
189
+ // log http.IncomingRequest
190
+ logger.log = function (level, msg, ...splat) {
191
+ const [meta] = splat
192
+
193
+ if (
194
+ typeof meta === 'object' &&
195
+ meta !== null &&
196
+ meta.context != null &&
197
+ meta.socket != null
198
+ ) {
199
+ splat[0] = Request(meta)
194
200
  }
195
201
 
196
- return info
197
- })
202
+ return originalLog.call(this, level, msg, ...splat)
203
+ }
198
204
 
205
+ logger.__configured__ = true
206
+ }
207
+
208
+ function getConfigurationOptions () {
199
209
  return {
200
210
  levels: {
201
211
  error: 0,
@@ -204,7 +214,7 @@ function getConfigurationOptions () {
204
214
  debug: 3
205
215
  },
206
216
  format: winston.format.combine(
207
- normalizeMeta(),
217
+ normalizeMetaLoggerFormat(),
208
218
  defaultLoggerFormatWithTimestamp()
209
219
  ),
210
220
  transports: [new DebugTransport()]
@@ -228,6 +238,7 @@ class DebugTransport extends Transport {
228
238
 
229
239
  this.format = options.format || winston.format.combine(
230
240
  winston.format.colorize(),
241
+ normalizeMetaLoggerFormat(),
231
242
  defaultLoggerFormat()
232
243
  )
233
244
 
@@ -39,6 +39,7 @@ async function optionsLoad ({
39
39
  loadConfigResult = await loadConfig(defaults, options, false)
40
40
  }
41
41
 
42
+ const explicitOptions = loadConfigResult[0]
42
43
  const appliedConfigFile = loadConfigResult[1]
43
44
 
44
45
  options.loadConfig = shouldLoadExternalConfig
@@ -79,9 +80,16 @@ async function optionsLoad ({
79
80
  options.store = options.store || { provider: 'memory' }
80
81
 
81
82
  options.sandbox = options.sandbox || {}
82
- if (options.allowLocalFilesAccess === true) {
83
+
84
+ // NOTE: handling back-compatible introduction of "trustUserCode" option, "allowLocalFilesAccess" is deprecated
85
+ if (explicitOptions.allowLocalFilesAccess === true && explicitOptions.trustUserCode == null) {
86
+ options.trustUserCode = true
87
+ }
88
+
89
+ if (options.trustUserCode === true) {
83
90
  options.sandbox.allowedModules = '*'
84
91
  }
92
+
85
93
  options.sandbox.nativeModules = options.sandbox.nativeModules || []
86
94
  options.sandbox.modules = options.sandbox.modules || []
87
95
  options.sandbox.allowedModules = options.sandbox.allowedModules || []
@@ -98,7 +106,7 @@ async function optionsLoad ({
98
106
  fs.mkdirSync(options.tempCoreDirectory, { recursive: true })
99
107
  }
100
108
 
101
- return appliedConfigFile
109
+ return [explicitOptions, appliedConfigFile]
102
110
  }
103
111
 
104
112
  /**
@@ -59,6 +59,7 @@ module.exports.getRootSchemaOptions = () => ({
59
59
  default: '2s'
60
60
  },
61
61
  enableRequestReportTimeout: { type: 'boolean', default: false, description: 'option that enables passing a custom report timeout per request using req.options.timeout. this enables that the caller of the report generation control the report timeout so enable it only when you trust the caller' },
62
+ trustUserCode: { type: 'boolean', default: false, description: 'option that control whether code sandboxing is enabled or not, code sandboxing has an impact on performance when rendering large reports. when true code sandboxing will be disabled meaning that users can potentially penetrate the local system if you allow code from external users to be part of your reports' },
62
63
  allowLocalFilesAccess: { type: 'boolean', default: false },
63
64
  encryption: {
64
65
  type: 'object',
@@ -77,6 +78,7 @@ module.exports.getRootSchemaOptions = () => ({
77
78
  },
78
79
  sandbox: {
79
80
  type: 'object',
81
+ default: {},
80
82
  properties: {
81
83
  allowedModules: {
82
84
  anyOf: [{
@@ -89,9 +91,10 @@ module.exports.getRootSchemaOptions = () => ({
89
91
  },
90
92
  cache: {
91
93
  type: 'object',
94
+ default: {},
92
95
  properties: {
93
- max: { type: 'number' },
94
- enabled: { type: 'boolean' }
96
+ max: { type: 'number', default: 100 },
97
+ enabled: { type: 'boolean', default: true }
95
98
  }
96
99
  }
97
100
  }
@@ -182,7 +185,7 @@ module.exports.getRootSchemaOptions = () => ({
182
185
  '$jsreport-acceptsDuration': true,
183
186
  default: '1m'
184
187
  },
185
- maxResponseSize: {
188
+ maxDiffSize: {
186
189
  type: ['string', 'number'],
187
190
  '$jsreport-acceptsSize': true,
188
191
  default: '50mb'
@@ -1,4 +1,5 @@
1
1
  const EventEmitter = require('events')
2
+ const winston = require('winston')
2
3
  const extend = require('node.extend.without.arrays')
3
4
  const generateRequestId = require('../shared/generateRequestId')
4
5
  const fs = require('fs/promises')
@@ -20,20 +21,44 @@ module.exports = (reporter) => {
20
21
  })
21
22
 
22
23
  const profilersMap = new Map()
23
-
24
24
  const profilerOperationsChainsMap = new Map()
25
- function runInProfilerChain (fn, req) {
25
+ const profilerRequestMap = new Map()
26
+ const profilerLogRequestMap = new Map()
27
+
28
+ function runInProfilerChain (fnOrOptions, req) {
26
29
  if (req.context.profiling.mode === 'disabled') {
27
30
  return
28
31
  }
29
32
 
33
+ let fn
34
+ let cleanFn
35
+
36
+ if (typeof fnOrOptions === 'function') {
37
+ fn = fnOrOptions
38
+ } else {
39
+ fn = fnOrOptions.fn
40
+ cleanFn = fnOrOptions.cleanFn
41
+ }
42
+
43
+ // this only happens when rendering remote delegated requests on docker workers
44
+ // there won't be operations chain because the request started from another server
45
+ if (!profilerOperationsChainsMap.has(req.context.rootId)) {
46
+ return
47
+ }
48
+
30
49
  profilerOperationsChainsMap.set(req.context.rootId, profilerOperationsChainsMap.get(req.context.rootId).then(async () => {
50
+ if (cleanFn) {
51
+ cleanFn()
52
+ }
53
+
31
54
  if (req.context.profiling.chainFailed) {
32
55
  return
33
56
  }
34
57
 
35
58
  try {
36
- await fn()
59
+ if (fn) {
60
+ await fn()
61
+ }
37
62
  } catch (e) {
38
63
  reporter.logger.warn('Failed persist profile', e)
39
64
  req.context.profiling.chainFailed = true
@@ -54,7 +79,7 @@ module.exports = (reporter) => {
54
79
  return m
55
80
  }
56
81
 
57
- function emitProfiles (events, req) {
82
+ function emitProfiles ({ events, log = true }, req) {
58
83
  if (events.length === 0) {
59
84
  return
60
85
  }
@@ -63,7 +88,9 @@ module.exports = (reporter) => {
63
88
 
64
89
  for (const m of events) {
65
90
  if (m.type === 'log') {
66
- reporter.logger[m.level](m.message, { ...req, ...m.meta, timestamp: m.timestamp })
91
+ if (log) {
92
+ reporter.logger[m.level](m.message, { ...req, ...m.meta, timestamp: m.timestamp, fromEmitProfile: true })
93
+ }
67
94
  } else {
68
95
  lastOperation = m
69
96
  }
@@ -78,19 +105,37 @@ module.exports = (reporter) => {
78
105
  }
79
106
 
80
107
  runInProfilerChain(() => {
81
- if (req.context.profiling.logFilePath) {
82
- return fs.appendFile(req.context.profiling.logFilePath, Buffer.from(events.map(m => JSON.stringify(m)).join('\n') + '\n'))
83
- }
84
-
85
- return reporter.blobStorage.append(
86
- req.context.profiling.entity.blobName,
87
- Buffer.from(events.map(m => JSON.stringify(m)).join('\n') + '\n'), req
88
- )
108
+ return fs.appendFile(req.context.profiling.logFilePath, Buffer.from(events.map(m => JSON.stringify(m)).join('\n') + '\n'))
89
109
  }, req)
90
110
  }
91
111
 
92
- reporter.registerMainAction('profile', async (events, req) => {
93
- return emitProfiles(events, req)
112
+ reporter.registerMainAction('profile', async (eventsOrOptions, _req) => {
113
+ let req = _req
114
+
115
+ // if there is request stored here then take it, this is needed
116
+ // for docker workers remote requests, so the emitProfile can work
117
+ // with the real render request object
118
+ if (profilerRequestMap.has(req.context.rootId) && req.__isJsreportRequest__ == null) {
119
+ req = profilerRequestMap.get(req.context.rootId)
120
+ }
121
+
122
+ let events
123
+ let log
124
+
125
+ if (Array.isArray(eventsOrOptions)) {
126
+ events = eventsOrOptions
127
+ } else {
128
+ events = eventsOrOptions.events
129
+ log = eventsOrOptions.log
130
+ }
131
+
132
+ const params = { events }
133
+
134
+ if (log != null) {
135
+ params.log = log
136
+ }
137
+
138
+ return emitProfiles(params, req)
94
139
  })
95
140
 
96
141
  reporter.attachProfiler = (req, profileMode) => {
@@ -118,20 +163,15 @@ module.exports = (reporter) => {
118
163
 
119
164
  req.context.profiling.lastOperation = null
120
165
 
121
- const blobName = `profiles/${req.context.rootId}.log`
122
-
123
166
  const profile = {
124
167
  _id: reporter.documentStore.generateId(),
125
168
  timestamp: new Date(),
126
169
  state: 'queued',
127
- mode: req.context.profiling.mode,
128
- blobName
170
+ mode: req.context.profiling.mode
129
171
  }
130
172
 
131
- if (!reporter.blobStorage.supportsAppend) {
132
- const { pathToFile } = await reporter.writeTempFile((uuid) => `${uuid}.log`, '')
133
- req.context.profiling.logFilePath = pathToFile
134
- }
173
+ const { pathToFile } = await reporter.writeTempFile((uuid) => `${uuid}.log`, '')
174
+ req.context.profiling.logFilePath = pathToFile
135
175
 
136
176
  runInProfilerChain(async () => {
137
177
  req.context.skipValidationFor = profile
@@ -149,14 +189,16 @@ module.exports = (reporter) => {
149
189
 
150
190
  req.context.profiling.profileStartOperationId = profileStartOperation.operationId
151
191
 
152
- emitProfiles([profileStartOperation], req)
192
+ emitProfiles({ events: [profileStartOperation] }, req)
153
193
 
154
- emitProfiles([createProfileMessage({
155
- type: 'log',
156
- level: 'info',
157
- message: `Render request ${req.context.reportCounter} queued for execution and waiting for availible worker`,
158
- previousOperationId: profileStartOperation.operationId
159
- }, req)], req)
194
+ emitProfiles({
195
+ events: [createProfileMessage({
196
+ type: 'log',
197
+ level: 'info',
198
+ message: `Render request ${req.context.reportCounter} queued for execution and waiting for available worker`,
199
+ previousOperationId: profileStartOperation.operationId
200
+ }, req)]
201
+ }, req)
160
202
  })
161
203
 
162
204
  reporter.beforeRenderListeners.add('profiler', async (req, res) => {
@@ -164,28 +206,20 @@ module.exports = (reporter) => {
164
206
  state: 'running'
165
207
  }
166
208
 
209
+ // we set the request here because this listener will container the req which
210
+ // the .render() starts
211
+ profilerRequestMap.set(req.context.rootId, req)
212
+
167
213
  const template = await reporter.templates.resolveTemplate(req)
168
214
  if (template && template._id) {
169
215
  req.context.resolvedTemplate = extend(true, {}, template)
170
- const templatePath = await reporter.folders.resolveEntityPath(template, 'templates', req)
171
- const blobName = `profiles/${templatePath.substring(1)}/${req.context.rootId}.log`
172
- update.templateShortid = template.shortid
173
-
174
- const originalBlobName = req.context.profiling.entity.blobName
175
- // we want to store the profile into blobName path reflecting the template path so we need to copy the blob to new path now
176
- runInProfilerChain(async () => {
177
- if (req.context.profiling.logFilePath == null) {
178
- const content = await reporter.blobStorage.read(originalBlobName, req)
179
- await reporter.blobStorage.write(blobName, content, req)
180
- return reporter.blobStorage.remove(originalBlobName, req)
181
- }
182
- }, req)
183
216
 
184
- update.blobName = blobName
217
+ update.templateShortid = template.shortid
185
218
  }
186
219
 
187
220
  runInProfilerChain(() => {
188
221
  req.context.skipValidationFor = update
222
+
189
223
  return reporter.documentStore.collection('profiles').update({
190
224
  _id: req.context.profiling.entity._id
191
225
  }, {
@@ -197,28 +231,38 @@ module.exports = (reporter) => {
197
231
  })
198
232
 
199
233
  reporter.afterRenderListeners.add('profiler', async (req, res) => {
200
- emitProfiles([createProfileMessage({
201
- type: 'operationEnd',
202
- doDiffs: false,
203
- previousEventId: req.context.profiling.lastEventId,
204
- previousOperationId: req.context.profiling.lastOperationId,
205
- operationId: req.context.profiling.profileStartOperationId
206
- }, req)], req)
234
+ emitProfiles({
235
+ events: [createProfileMessage({
236
+ type: 'operationEnd',
237
+ doDiffs: false,
238
+ previousEventId: req.context.profiling.lastEventId,
239
+ previousOperationId: req.context.profiling.lastOperationId,
240
+ operationId: req.context.profiling.profileStartOperationId
241
+ }, req)]
242
+ }, req)
207
243
 
208
244
  res.meta.profileId = req.context.profiling?.entity?._id
209
245
 
210
246
  runInProfilerChain(async () => {
211
- if (req.context.profiling.logFilePath != null) {
212
- const content = await fs.readFile(req.context.profiling.logFilePath)
213
- await reporter.blobStorage.write(req.context.profiling.entity.blobName, content, req)
214
- await fs.unlink(req.context.profiling.logFilePath)
247
+ let blobName = `profiles/${req.context.rootId}.log`
248
+
249
+ if (req.context.resolvedTemplate) {
250
+ const templatePath = await reporter.folders.resolveEntityPath(req.context.resolvedTemplate, 'templates', req)
251
+ blobName = `profiles/${templatePath.substring(1)}/${req.context.rootId}.log`
215
252
  }
216
253
 
254
+ const content = await fs.readFile(req.context.profiling.logFilePath)
255
+ blobName = await reporter.blobStorage.write(blobName, content, req)
256
+ await fs.unlink(req.context.profiling.logFilePath)
257
+
217
258
  const update = {
218
259
  state: 'success',
219
- finishedOn: new Date()
260
+ finishedOn: new Date(),
261
+ blobName
220
262
  }
263
+
221
264
  req.context.skipValidationFor = update
265
+
222
266
  await reporter.documentStore.collection('profiles').update({
223
267
  _id: req.context.profiling.entity._id
224
268
  }, {
@@ -226,61 +270,128 @@ module.exports = (reporter) => {
226
270
  }, req)
227
271
  }, req)
228
272
 
229
- // we don't remove from profiler requests map, because the renderErrorListeners are invoked if the afterRenderListener fails
273
+ // we don't clean the profiler maps here, we do it later in main reporter .render,
274
+ // because the renderErrorListeners can be invoked if the afterRenderListener fails
230
275
  })
231
276
 
232
277
  reporter.renderErrorListeners.add('profiler', async (req, res, e) => {
233
- try {
234
- res.meta.profileId = req.context.profiling?.entity?._id
278
+ res.meta.profileId = req.context.profiling?.entity?._id
235
279
 
236
- if (req.context.profiling?.entity != null) {
237
- emitProfiles([{
280
+ if (req.context.profiling?.entity != null) {
281
+ emitProfiles({
282
+ events: [{
238
283
  type: 'error',
239
284
  timestamp: new Date().getTime(),
240
285
  ...e,
241
286
  id: generateRequestId(),
242
287
  stack: e.stack,
243
288
  message: e.message
244
- }], req)
245
- runInProfilerChain(async () => {
246
- if (req.context.profiling.logFilePath != null) {
247
- const content = await fs.readFile(req.context.profiling.logFilePath, 'utf8')
248
- await reporter.blobStorage.write(req.context.profiling.entity.blobName, content, req)
249
- await fs.unlink(req.context.profiling.logFilePath)
250
- }
289
+ }]
290
+ }, req)
291
+
292
+ runInProfilerChain(async () => {
293
+ const update = {
294
+ state: 'error',
295
+ finishedOn: new Date(),
296
+ error: e.toString()
297
+ }
298
+
299
+ if (req.context.profiling.logFilePath != null) {
300
+ let blobName = `profiles/${req.context.rootId}.log`
251
301
 
252
- const update = {
253
- state: 'error',
254
- finishedOn: new Date(),
255
- error: e.toString()
302
+ if (req.context.resolvedTemplate) {
303
+ const templatePath = await reporter.folders.resolveEntityPath(req.context.resolvedTemplate, 'templates', req)
304
+ blobName = `profiles/${templatePath.substring(1)}/${req.context.rootId}.log`
256
305
  }
257
- req.context.skipValidationFor = update
258
- await reporter.documentStore.collection('profiles').update({
259
- _id: req.context.profiling.entity._id
260
- }, {
261
- $set: update
262
- }, req)
306
+
307
+ const content = await fs.readFile(req.context.profiling.logFilePath)
308
+ blobName = await reporter.blobStorage.write(blobName, content, req)
309
+ await fs.unlink(req.context.profiling.logFilePath)
310
+ update.blobName = blobName
311
+ }
312
+
313
+ req.context.skipValidationFor = update
314
+
315
+ await reporter.documentStore.collection('profiles').update({
316
+ _id: req.context.profiling.entity._id
317
+ }, {
318
+ $set: update
263
319
  }, req)
264
- }
265
- } finally {
266
- profilersMap.delete(req.context.rootId)
267
- profilerOperationsChainsMap.delete(req.context.rootId)
320
+ }, req)
321
+
322
+ // we don't clean the profiler maps here, we do it later in main reporter .render,
323
+ // we do this to ensure a single and clear order
268
324
  }
269
325
  })
270
326
 
327
+ const configuredPreviously = reporter.logger.__profilerConfigured__ === true
328
+
329
+ if (!configuredPreviously) {
330
+ const originalLog = reporter.logger.log
331
+
332
+ // we want to catch the original request
333
+ reporter.logger.log = function (level, msg, ...splat) {
334
+ const [meta] = splat
335
+
336
+ if (typeof meta === 'object' && meta !== null && meta.context?.rootId != null) {
337
+ profilerLogRequestMap.set(meta.context.rootId, meta)
338
+ }
339
+
340
+ return originalLog.call(this, level, msg, ...splat)
341
+ }
342
+
343
+ const mainLogsToProfile = winston.format((info) => {
344
+ // propagate the request logs occurring on main to the profile
345
+ if (info.rootId != null && info.fromEmitProfile == null && profilerLogRequestMap.has(info.rootId)) {
346
+ const req = profilerLogRequestMap.get(info.rootId)
347
+
348
+ emitProfiles({
349
+ events: [createProfileMessage({
350
+ type: 'log',
351
+ level: info.level,
352
+ message: info.message,
353
+ previousOperationId: req.context.profiling.lastOperationId
354
+ }, req)],
355
+ log: false
356
+ }, req)
357
+ }
358
+
359
+ if (info.fromEmitProfile != null) {
360
+ delete info.fromEmitProfile
361
+ }
362
+
363
+ return info
364
+ })
365
+
366
+ reporter.logger.format = winston.format.combine(
367
+ reporter.logger.format,
368
+ mainLogsToProfile()
369
+ )
370
+
371
+ reporter.logger.__profilerConfigured__ = true
372
+ }
373
+
271
374
  let profilesCleanupInterval
375
+
272
376
  reporter.initializeListeners.add('profiler', async () => {
273
377
  reporter.documentStore.collection('profiles').beforeRemoveListeners.add('profiles', async (query, req) => {
274
378
  const profiles = await reporter.documentStore.collection('profiles').find(query, req)
275
379
 
276
380
  for (const profile of profiles) {
277
- await reporter.blobStorage.remove(profile.blobName)
381
+ if (profile.blobName != null) {
382
+ await reporter.blobStorage.remove(profile.blobName)
383
+ }
278
384
  }
279
385
  })
280
386
 
281
- profilesCleanupInterval = setInterval(profilesCleanup, reporter.options.profiler.cleanupInterval)
387
+ function profilesCleanupExec () {
388
+ return reporter._profilesCleanup()
389
+ }
390
+
391
+ profilesCleanupInterval = setInterval(profilesCleanupExec, reporter.options.profiler.cleanupInterval)
282
392
  profilesCleanupInterval.unref()
283
- await profilesCleanup()
393
+
394
+ await reporter._profilesCleanup()
284
395
  })
285
396
 
286
397
  reporter.closeListeners.add('profiler', async () => {
@@ -294,15 +405,24 @@ module.exports = (reporter) => {
294
405
  await profileAppendPromise
295
406
  }
296
407
  }
408
+
409
+ profilersMap.clear()
410
+ profilerOperationsChainsMap.clear()
411
+ profilerRequestMap.clear()
412
+ profilerLogRequestMap.clear()
297
413
  })
298
414
 
299
415
  let profilesCleanupRunning = false
300
- async function profilesCleanup () {
416
+
417
+ reporter._profilesCleanup = async function profilesCleanup () {
301
418
  if (profilesCleanupRunning) {
302
419
  return
303
420
  }
421
+
304
422
  profilesCleanupRunning = true
423
+
305
424
  let lastRemoveError
425
+
306
426
  try {
307
427
  const profiles = await reporter.documentStore.collection('profiles').find({}).sort({ timestamp: -1 })
308
428
  const profilesToRemove = profiles.slice(reporter.options.profiler.maxProfilesHistory)
@@ -330,4 +450,28 @@ module.exports = (reporter) => {
330
450
  reporter.logger.warn('Profile cleanup failed for some entities, last error:', lastRemoveError)
331
451
  }
332
452
  }
453
+
454
+ return function cleanProfileInRequest (req) {
455
+ // - req.context.profiling is empty only on an early error
456
+ // that happens before setting the profiler.
457
+ // - when profiling.mode is "disabled" there is no profiler chain to append
458
+ // in both cases we want the clean code to happen immediately
459
+ if (req.context.profiling?.entity == null || req.context.profiling?.mode === 'disabled') {
460
+ profilersMap.delete(req.context.rootId)
461
+ profilerOperationsChainsMap.delete(req.context.rootId)
462
+ profilerRequestMap.delete(req.context.rootId)
463
+ profilerLogRequestMap.delete(req.context.rootId)
464
+ return
465
+ }
466
+
467
+ // this will get executed always even if some fn in the chain fails
468
+ runInProfilerChain({
469
+ cleanFn: () => {
470
+ profilersMap.delete(req.context.rootId)
471
+ profilerOperationsChainsMap.delete(req.context.rootId)
472
+ profilerRequestMap.delete(req.context.rootId)
473
+ profilerLogRequestMap.delete(req.context.rootId)
474
+ }
475
+ }, req)
476
+ }
333
477
  }