@jsreport/jsreport-core 3.11.1 → 3.11.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/README.md +15 -0
- package/lib/main/optionsSchema.js +15 -0
- package/lib/main/profiler.js +67 -7
- package/lib/main/reporter.js +1 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -282,6 +282,21 @@ jsreport.documentStore.collection('templates')
|
|
|
282
282
|
|
|
283
283
|
## Changelog
|
|
284
284
|
|
|
285
|
+
### 3.11.3
|
|
286
|
+
|
|
287
|
+
- update vm2 to fix security issue
|
|
288
|
+
- automatically disable full profiling after some time to avoid performance degradation
|
|
289
|
+
- improvements to full profile serialization (prevent blocking)
|
|
290
|
+
- fix profiles cleaning and calculate timeout in beforeRender
|
|
291
|
+
|
|
292
|
+
### 3.11.2
|
|
293
|
+
|
|
294
|
+
- add `options.onReqReady` to be able to receive the parsed req values
|
|
295
|
+
|
|
296
|
+
### 3.11.1
|
|
297
|
+
|
|
298
|
+
- fix error when trying to read `req.options` on reporter main code when `enableRequestReportTimeout` is enabled
|
|
299
|
+
|
|
285
300
|
### 3.11.0
|
|
286
301
|
|
|
287
302
|
- log when worker returns bad res.content
|
|
@@ -176,6 +176,16 @@ module.exports.getRootSchemaOptions = () => ({
|
|
|
176
176
|
type: 'string',
|
|
177
177
|
default: 'standard'
|
|
178
178
|
},
|
|
179
|
+
fullModeDurationCheckInterval: {
|
|
180
|
+
type: ['string', 'number'],
|
|
181
|
+
'$jsreport-acceptsDuration': true,
|
|
182
|
+
default: '10m'
|
|
183
|
+
},
|
|
184
|
+
fullModeDuration: {
|
|
185
|
+
type: ['string', 'number'],
|
|
186
|
+
'$jsreport-acceptsDuration': true,
|
|
187
|
+
default: '4h'
|
|
188
|
+
},
|
|
179
189
|
maxProfilesHistory: {
|
|
180
190
|
type: 'number',
|
|
181
191
|
default: 1000
|
|
@@ -185,6 +195,11 @@ module.exports.getRootSchemaOptions = () => ({
|
|
|
185
195
|
'$jsreport-acceptsDuration': true,
|
|
186
196
|
default: '1m'
|
|
187
197
|
},
|
|
198
|
+
maxUnallocatedProfileAge: {
|
|
199
|
+
type: ['string', 'number'],
|
|
200
|
+
'$jsreport-acceptsDuration': true,
|
|
201
|
+
default: '24h'
|
|
202
|
+
},
|
|
188
203
|
maxDiffSize: {
|
|
189
204
|
type: ['string', 'number'],
|
|
190
205
|
'$jsreport-acceptsSize': true,
|
package/lib/main/profiler.js
CHANGED
|
@@ -4,6 +4,9 @@ const extend = require('node.extend.without.arrays')
|
|
|
4
4
|
const generateRequestId = require('../shared/generateRequestId')
|
|
5
5
|
const fs = require('fs/promises')
|
|
6
6
|
const { SPLAT } = require('triple-beam')
|
|
7
|
+
const promisify = require('util').promisify
|
|
8
|
+
const stringifyAsync = promisify(require('yieldable-json').stringifyAsync)
|
|
9
|
+
|
|
7
10
|
module.exports = (reporter) => {
|
|
8
11
|
reporter.documentStore.registerEntityType('ProfileType', {
|
|
9
12
|
templateShortid: { type: 'Edm.String', referenceTo: 'templates' },
|
|
@@ -104,8 +107,11 @@ module.exports = (reporter) => {
|
|
|
104
107
|
req.context.profiling.lastOperation = lastOperation
|
|
105
108
|
}
|
|
106
109
|
|
|
107
|
-
runInProfilerChain(() => {
|
|
108
|
-
|
|
110
|
+
runInProfilerChain(async () => {
|
|
111
|
+
const stringifiedMessages = req.context.mode === 'full'
|
|
112
|
+
? await Promise.all(events.map(m => stringifyAsync(m)))
|
|
113
|
+
: events.map(m => JSON.stringify(m))
|
|
114
|
+
await fs.appendFile(req.context.profiling.logFilePath, Buffer.from(stringifiedMessages.join('\n') + '\n'))
|
|
109
115
|
}, req)
|
|
110
116
|
}
|
|
111
117
|
|
|
@@ -167,8 +173,7 @@ module.exports = (reporter) => {
|
|
|
167
173
|
_id: reporter.documentStore.generateId(),
|
|
168
174
|
timestamp: new Date(),
|
|
169
175
|
state: 'queued',
|
|
170
|
-
mode: req.context.profiling.mode
|
|
171
|
-
timeout: reporter.options.enableRequestReportTimeout && req.options.timeout ? req.options.timeout : reporter.options.reportTimeout
|
|
176
|
+
mode: req.context.profiling.mode
|
|
172
177
|
}
|
|
173
178
|
|
|
174
179
|
const { pathToFile } = await reporter.writeTempFile((uuid) => `${uuid}.log`, '')
|
|
@@ -204,7 +209,9 @@ module.exports = (reporter) => {
|
|
|
204
209
|
|
|
205
210
|
reporter.beforeRenderListeners.add('profiler', async (req, res) => {
|
|
206
211
|
const update = {
|
|
207
|
-
state: 'running'
|
|
212
|
+
state: 'running',
|
|
213
|
+
// the timeout needs to be calculated later here, because the req.options.timeout isnt yet parsed in beforeRenderWorkerAllocatedListeners
|
|
214
|
+
timeout: reporter.options.enableRequestReportTimeout && req.options.timeout ? req.options.timeout : reporter.options.reportTimeout
|
|
208
215
|
}
|
|
209
216
|
|
|
210
217
|
// we set the request here because this listener will container the req which
|
|
@@ -365,6 +372,7 @@ module.exports = (reporter) => {
|
|
|
365
372
|
}
|
|
366
373
|
|
|
367
374
|
let profilesCleanupInterval
|
|
375
|
+
let fullModeDurationCheckInterval
|
|
368
376
|
|
|
369
377
|
reporter.initializeListeners.add('profiler', async () => {
|
|
370
378
|
reporter.documentStore.collection('profiles').beforeRemoveListeners.add('profiles', async (query, req) => {
|
|
@@ -377,13 +385,21 @@ module.exports = (reporter) => {
|
|
|
377
385
|
}
|
|
378
386
|
})
|
|
379
387
|
|
|
388
|
+
// exposing it to jo for override
|
|
380
389
|
function profilesCleanupExec () {
|
|
381
390
|
return reporter._profilesCleanup()
|
|
382
391
|
}
|
|
383
392
|
|
|
393
|
+
function fullModeDurationCheckExec () {
|
|
394
|
+
return reporter._profilesFullModeDurationCheck()
|
|
395
|
+
}
|
|
396
|
+
|
|
384
397
|
profilesCleanupInterval = setInterval(profilesCleanupExec, reporter.options.profiler.cleanupInterval)
|
|
385
398
|
profilesCleanupInterval.unref()
|
|
386
399
|
|
|
400
|
+
fullModeDurationCheckInterval = setInterval(fullModeDurationCheckExec, reporter.options.profiler.fullModeDurationCheckInterval)
|
|
401
|
+
fullModeDurationCheckInterval.unref()
|
|
402
|
+
|
|
387
403
|
await reporter._profilesCleanup()
|
|
388
404
|
})
|
|
389
405
|
|
|
@@ -392,6 +408,10 @@ module.exports = (reporter) => {
|
|
|
392
408
|
clearInterval(profilesCleanupInterval)
|
|
393
409
|
}
|
|
394
410
|
|
|
411
|
+
if (fullModeDurationCheckInterval) {
|
|
412
|
+
clearInterval(fullModeDurationCheckInterval)
|
|
413
|
+
}
|
|
414
|
+
|
|
395
415
|
for (const key of profilerOperationsChainsMap.keys()) {
|
|
396
416
|
const profileAppendPromise = profilerOperationsChainsMap.get(key)
|
|
397
417
|
if (profileAppendPromise) {
|
|
@@ -445,11 +465,28 @@ module.exports = (reporter) => {
|
|
|
445
465
|
}
|
|
446
466
|
|
|
447
467
|
if (!profile.timeout) {
|
|
468
|
+
// we can calculate profile timeout only after worker parses request and req.options.timeout is calculated
|
|
469
|
+
// if the timeout isnt calculated we error orphans that hangs for very long time before worker gets allocated and parses req
|
|
470
|
+
if ((profile.timestamp.getTime() + reporter.options.profiler.maxUnallocatedProfileAge) < new Date().getTime()) {
|
|
471
|
+
try {
|
|
472
|
+
await reporter.documentStore.collection('profiles').update({
|
|
473
|
+
_id: profile._id
|
|
474
|
+
}, {
|
|
475
|
+
$set: {
|
|
476
|
+
state: 'error',
|
|
477
|
+
finishedOn: new Date(),
|
|
478
|
+
error: `The request wasn't parsed before ${reporter.options.profiler.maxUnallocatedProfileAge}ms. This can happen when the server is unexpectedly stopped.`
|
|
479
|
+
}
|
|
480
|
+
})
|
|
481
|
+
} catch (e) {
|
|
482
|
+
lastError = e
|
|
483
|
+
}
|
|
484
|
+
}
|
|
448
485
|
continue
|
|
449
486
|
}
|
|
450
487
|
|
|
451
|
-
const whenShouldBeFinished = profile.timestamp + profile.timeout + reporter.options.reportTimeoutMargin * 2
|
|
452
|
-
if (whenShouldBeFinished
|
|
488
|
+
const whenShouldBeFinished = profile.timestamp.getTime() + profile.timeout + reporter.options.reportTimeoutMargin * 2
|
|
489
|
+
if (whenShouldBeFinished > new Date().getTime()) {
|
|
453
490
|
continue
|
|
454
491
|
}
|
|
455
492
|
|
|
@@ -478,6 +515,29 @@ module.exports = (reporter) => {
|
|
|
478
515
|
}
|
|
479
516
|
}
|
|
480
517
|
|
|
518
|
+
reporter._profilesFullModeDurationCheck = async function () {
|
|
519
|
+
try {
|
|
520
|
+
if (reporter.options.profiler.defaultMode === 'full') {
|
|
521
|
+
return
|
|
522
|
+
}
|
|
523
|
+
const profiler = await reporter.documentStore.collection('settings').findOne({ key: 'profiler' })
|
|
524
|
+
if (profiler == null || (profiler.modificationDate.getTime() + reporter.options.profiler.fullModeDuration) > new Date().getTime()) {
|
|
525
|
+
return
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const profilerValue = JSON.parse(profiler.value)
|
|
529
|
+
|
|
530
|
+
if (profilerValue.mode !== 'full') {
|
|
531
|
+
return
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
reporter.logger.info('Switching full mode profiling back to standard to avoid performance degradation.')
|
|
535
|
+
await reporter.settings.addOrSet('profiler', { mode: 'standard' })
|
|
536
|
+
} catch (e) {
|
|
537
|
+
reporter.logger.warn('Failed to change profiling mode', e)
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
481
541
|
return function cleanProfileInRequest (req) {
|
|
482
542
|
// - req.context.profiling is empty only on an early error
|
|
483
543
|
// that happens before setting the profiler.
|
package/lib/main/reporter.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsreport/jsreport-core",
|
|
3
|
-
"version": "3.11.
|
|
3
|
+
"version": "3.11.3",
|
|
4
4
|
"description": "javascript based business reporting",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"report",
|
|
@@ -71,9 +71,10 @@
|
|
|
71
71
|
"triple-beam": "1.3.0",
|
|
72
72
|
"unset-value": "1.0.0",
|
|
73
73
|
"uuid": "8.3.2",
|
|
74
|
-
"vm2": "3.9.
|
|
74
|
+
"vm2": "3.9.17",
|
|
75
75
|
"winston": "3.8.1",
|
|
76
|
-
"winston-transport": "4.5.0"
|
|
76
|
+
"winston-transport": "4.5.0",
|
|
77
|
+
"yieldable-json": "2.0.1"
|
|
77
78
|
},
|
|
78
79
|
"devDependencies": {
|
|
79
80
|
"mocha": "9.2.2",
|