@jsreport/jsreport-core 3.10.0 → 3.11.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
|
@@ -282,6 +282,14 @@ jsreport.documentStore.collection('templates')
|
|
|
282
282
|
|
|
283
283
|
## Changelog
|
|
284
284
|
|
|
285
|
+
### 3.11.0
|
|
286
|
+
|
|
287
|
+
- log when worker returns bad res.content
|
|
288
|
+
- fix profiler leaks
|
|
289
|
+
- remove settings sync API and avoid loading all items to memory
|
|
290
|
+
- throw weak error when validating duplicated entity
|
|
291
|
+
- ensure we end with profiles with error state when there is server or req timeout
|
|
292
|
+
|
|
285
293
|
### 3.10.0
|
|
286
294
|
|
|
287
295
|
- `mainReporter.executeWorkerAction` now supports cancellation with `AbortController.signal`
|
|
@@ -74,6 +74,7 @@ async function validateDuplicatedName (reporter, c, doc, originalIdValue, req) {
|
|
|
74
74
|
|
|
75
75
|
throw reporter.createError(msg, {
|
|
76
76
|
statusCode: 400,
|
|
77
|
+
weak: true,
|
|
77
78
|
code: 'DUPLICATED_ENTITY',
|
|
78
79
|
existingEntity: existingEntity.entity,
|
|
79
80
|
existingEntityEntitySet: existingEntity.entitySet
|
package/lib/main/profiler.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const EventEmitter = require('events')
|
|
2
|
-
const
|
|
2
|
+
const Transport = require('winston-transport')
|
|
3
3
|
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
7
|
module.exports = (reporter) => {
|
|
8
8
|
reporter.documentStore.registerEntityType('ProfileType', {
|
|
9
9
|
templateShortid: { type: 'Edm.String', referenceTo: 'templates' },
|
|
@@ -12,7 +12,8 @@ module.exports = (reporter) => {
|
|
|
12
12
|
state: { type: 'Edm.String' },
|
|
13
13
|
error: { type: 'Edm.String' },
|
|
14
14
|
mode: { type: 'Edm.String', schema: { enum: ['full', 'standard', 'disabled'] } },
|
|
15
|
-
blobName: { type: 'Edm.String' }
|
|
15
|
+
blobName: { type: 'Edm.String' },
|
|
16
|
+
timeout: { type: 'Edm.Int32' }
|
|
16
17
|
})
|
|
17
18
|
|
|
18
19
|
reporter.documentStore.registerEntitySet('profiles', {
|
|
@@ -23,7 +24,6 @@ module.exports = (reporter) => {
|
|
|
23
24
|
const profilersMap = new Map()
|
|
24
25
|
const profilerOperationsChainsMap = new Map()
|
|
25
26
|
const profilerRequestMap = new Map()
|
|
26
|
-
const profilerLogRequestMap = new Map()
|
|
27
27
|
|
|
28
28
|
function runInProfilerChain (fnOrOptions, req) {
|
|
29
29
|
if (req.context.profiling.mode === 'disabled') {
|
|
@@ -89,7 +89,7 @@ module.exports = (reporter) => {
|
|
|
89
89
|
for (const m of events) {
|
|
90
90
|
if (m.type === 'log') {
|
|
91
91
|
if (log) {
|
|
92
|
-
reporter.logger[m.level](m.message, { ...req, ...m.meta, timestamp: m.timestamp,
|
|
92
|
+
reporter.logger[m.level](m.message, { ...req, ...m.meta, timestamp: m.timestamp, logged: true })
|
|
93
93
|
}
|
|
94
94
|
} else {
|
|
95
95
|
lastOperation = m
|
|
@@ -167,7 +167,8 @@ module.exports = (reporter) => {
|
|
|
167
167
|
_id: reporter.documentStore.generateId(),
|
|
168
168
|
timestamp: new Date(),
|
|
169
169
|
state: 'queued',
|
|
170
|
-
mode: req.context.profiling.mode
|
|
170
|
+
mode: req.context.profiling.mode,
|
|
171
|
+
timeout: reporter.options.enableRequestReportTimeout && req.options.timeout ? req.options.timeout : reporter.options.reportTimeout
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
const { pathToFile } = await reporter.writeTempFile((uuid) => `${uuid}.log`, '')
|
|
@@ -325,49 +326,40 @@ module.exports = (reporter) => {
|
|
|
325
326
|
}
|
|
326
327
|
})
|
|
327
328
|
|
|
329
|
+
// we want to add to profiles also log messages from the main
|
|
328
330
|
const configuredPreviously = reporter.logger.__profilerConfigured__ === true
|
|
329
|
-
|
|
330
331
|
if (!configuredPreviously) {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
332
|
+
// we emit from winston transport, so winston formatters can still format message
|
|
333
|
+
class EmittingProfilesTransport extends Transport {
|
|
334
|
+
log (info, callback) {
|
|
335
|
+
setImmediate(() => {
|
|
336
|
+
this.emit('logged', info)
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
if (info[SPLAT]) {
|
|
340
|
+
const [req] = info[SPLAT]
|
|
341
|
+
|
|
342
|
+
if (req && req.context && req.logged !== true) {
|
|
343
|
+
emitProfiles({
|
|
344
|
+
events: [createProfileMessage({
|
|
345
|
+
type: 'log',
|
|
346
|
+
level: info.level,
|
|
347
|
+
message: info.message,
|
|
348
|
+
previousOperationId: req.context.profiling.lastOperationId
|
|
349
|
+
}, req)],
|
|
350
|
+
log: false
|
|
351
|
+
}, req)
|
|
352
|
+
}
|
|
353
|
+
}
|
|
336
354
|
|
|
337
|
-
|
|
338
|
-
profilerLogRequestMap.set(meta.context.rootId, meta)
|
|
355
|
+
callback()
|
|
339
356
|
}
|
|
340
|
-
|
|
341
|
-
return originalLog.call(this, level, msg, ...splat)
|
|
342
357
|
}
|
|
343
358
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
emitProfiles({
|
|
350
|
-
events: [createProfileMessage({
|
|
351
|
-
type: 'log',
|
|
352
|
-
level: info.level,
|
|
353
|
-
message: info.message,
|
|
354
|
-
previousOperationId: req.context.profiling.lastOperationId
|
|
355
|
-
}, req)],
|
|
356
|
-
log: false
|
|
357
|
-
}, req)
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
if (info.fromEmitProfile != null) {
|
|
361
|
-
delete info.fromEmitProfile
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
return info
|
|
365
|
-
})
|
|
366
|
-
|
|
367
|
-
reporter.logger.format = winston.format.combine(
|
|
368
|
-
reporter.logger.format,
|
|
369
|
-
mainLogsToProfile()
|
|
370
|
-
)
|
|
359
|
+
reporter.logger.add(new EmittingProfilesTransport({
|
|
360
|
+
format: reporter.logger.format,
|
|
361
|
+
level: 'debug'
|
|
362
|
+
}))
|
|
371
363
|
|
|
372
364
|
reporter.logger.__profilerConfigured__ = true
|
|
373
365
|
}
|
|
@@ -410,7 +402,6 @@ module.exports = (reporter) => {
|
|
|
410
402
|
profilersMap.clear()
|
|
411
403
|
profilerOperationsChainsMap.clear()
|
|
412
404
|
profilerRequestMap.clear()
|
|
413
|
-
profilerLogRequestMap.clear()
|
|
414
405
|
})
|
|
415
406
|
|
|
416
407
|
let profilesCleanupRunning = false
|
|
@@ -422,11 +413,13 @@ module.exports = (reporter) => {
|
|
|
422
413
|
|
|
423
414
|
profilesCleanupRunning = true
|
|
424
415
|
|
|
425
|
-
let
|
|
416
|
+
let lastError
|
|
426
417
|
|
|
427
418
|
try {
|
|
428
|
-
const
|
|
429
|
-
|
|
419
|
+
const profilesToRemove = await reporter.documentStore.collection('profiles')
|
|
420
|
+
.find({}, { _id: 1 }).sort({ timestamp: -1 })
|
|
421
|
+
.skip(reporter.options.profiler.maxProfilesHistory)
|
|
422
|
+
.toArray()
|
|
430
423
|
|
|
431
424
|
for (const profile of profilesToRemove) {
|
|
432
425
|
if (reporter.closed || reporter.closing) {
|
|
@@ -438,7 +431,40 @@ module.exports = (reporter) => {
|
|
|
438
431
|
_id: profile._id
|
|
439
432
|
})
|
|
440
433
|
} catch (e) {
|
|
441
|
-
|
|
434
|
+
lastError = e
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const notFinishedProfiles = await reporter.documentStore.collection('profiles')
|
|
439
|
+
.find({ $or: [{ state: 'running' }, { state: 'queued' }] }, { _id: 1, timeout: 1, timestamp: 1 })
|
|
440
|
+
.toArray()
|
|
441
|
+
|
|
442
|
+
for (const profile of notFinishedProfiles) {
|
|
443
|
+
if (reporter.closed || reporter.closing) {
|
|
444
|
+
return
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (!profile.timeout) {
|
|
448
|
+
continue
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const whenShouldBeFinished = profile.timestamp + profile.timeout + reporter.options.reportTimeoutMargin * 2
|
|
452
|
+
if (whenShouldBeFinished < new Date().getTime()) {
|
|
453
|
+
continue
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
try {
|
|
457
|
+
await reporter.documentStore.collection('profiles').update({
|
|
458
|
+
_id: profile._id
|
|
459
|
+
}, {
|
|
460
|
+
$set: {
|
|
461
|
+
state: 'error',
|
|
462
|
+
finishedOn: new Date(),
|
|
463
|
+
error: 'The server did not update the report profile before its timeout. This can happen when the server is unexpectedly stopped.'
|
|
464
|
+
}
|
|
465
|
+
})
|
|
466
|
+
} catch (e) {
|
|
467
|
+
lastError = e
|
|
442
468
|
}
|
|
443
469
|
}
|
|
444
470
|
} catch (e) {
|
|
@@ -447,8 +473,8 @@ module.exports = (reporter) => {
|
|
|
447
473
|
profilesCleanupRunning = false
|
|
448
474
|
}
|
|
449
475
|
|
|
450
|
-
if (
|
|
451
|
-
reporter.logger.warn('Profile cleanup failed for some entities, last error:',
|
|
476
|
+
if (lastError) {
|
|
477
|
+
reporter.logger.warn('Profile cleanup failed for some entities, last error:', lastError)
|
|
452
478
|
}
|
|
453
479
|
}
|
|
454
480
|
|
|
@@ -461,7 +487,6 @@ module.exports = (reporter) => {
|
|
|
461
487
|
profilersMap.delete(req.context.rootId)
|
|
462
488
|
profilerOperationsChainsMap.delete(req.context.rootId)
|
|
463
489
|
profilerRequestMap.delete(req.context.rootId)
|
|
464
|
-
profilerLogRequestMap.delete(req.context.rootId)
|
|
465
490
|
return
|
|
466
491
|
}
|
|
467
492
|
|
|
@@ -471,7 +496,6 @@ module.exports = (reporter) => {
|
|
|
471
496
|
profilersMap.delete(req.context.rootId)
|
|
472
497
|
profilerOperationsChainsMap.delete(req.context.rootId)
|
|
473
498
|
profilerRequestMap.delete(req.context.rootId)
|
|
474
|
-
profilerLogRequestMap.delete(req.context.rootId)
|
|
475
499
|
}
|
|
476
500
|
}, req)
|
|
477
501
|
}
|
package/lib/main/reporter.js
CHANGED
|
@@ -378,6 +378,7 @@ class MainReporter extends Reporter {
|
|
|
378
378
|
req.context.id = req.context.rootId
|
|
379
379
|
req.context.reportCounter = ++reportCounter
|
|
380
380
|
req.context.startTimestamp = new Date().getTime()
|
|
381
|
+
req.options = Object.assign({}, req.options)
|
|
381
382
|
|
|
382
383
|
let worker
|
|
383
384
|
let workerAborted
|
|
@@ -449,6 +450,7 @@ class MainReporter extends Reporter {
|
|
|
449
450
|
} catch (err) {
|
|
450
451
|
await this._handleRenderError(req, res, err).catch((e) => {})
|
|
451
452
|
} finally {
|
|
453
|
+
this._cleanProfileInRequest(req)
|
|
452
454
|
if (!workerAborted) {
|
|
453
455
|
await worker.release(req)
|
|
454
456
|
}
|
|
@@ -477,6 +479,10 @@ class MainReporter extends Reporter {
|
|
|
477
479
|
|
|
478
480
|
await this.afterRenderListeners.fire(req, res)
|
|
479
481
|
|
|
482
|
+
if (!res.content) {
|
|
483
|
+
this.logger.error('Worker didnt return render res.content, returned:' + JSON.stringify(responseResult), req)
|
|
484
|
+
}
|
|
485
|
+
|
|
480
486
|
res.stream = Readable.from(res.content)
|
|
481
487
|
|
|
482
488
|
this._cleanProfileInRequest(req)
|
package/lib/main/settings.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
const Request = require('../shared/request')
|
|
7
7
|
|
|
8
8
|
const Settings = module.exports = function () {
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
Settings.prototype.add = function (key, value, req) {
|
|
@@ -15,15 +15,9 @@ Settings.prototype.add = function (key, value, req) {
|
|
|
15
15
|
value: typeof value !== 'string' ? JSON.stringify(value) : value
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
this._collection.push(settingItem)
|
|
19
|
-
|
|
20
18
|
return this.documentStore.collection('settings').insert(settingItem, localReqWithoutAuthorization(req))
|
|
21
19
|
}
|
|
22
20
|
|
|
23
|
-
Settings.prototype.get = function (key) {
|
|
24
|
-
return this._collection.find((s) => s.key === key)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
21
|
Settings.prototype.findValue = async function (key, req) {
|
|
28
22
|
const res = await this.documentStore.collection('settings').find({ key: key }, localReqWithoutAuthorization(req))
|
|
29
23
|
if (res.length !== 1) {
|
|
@@ -36,8 +30,6 @@ Settings.prototype.findValue = async function (key, req) {
|
|
|
36
30
|
Settings.prototype.set = function (key, avalue, req) {
|
|
37
31
|
const value = typeof avalue !== 'string' ? JSON.stringify(avalue) : avalue
|
|
38
32
|
|
|
39
|
-
this.get(key).value = value
|
|
40
|
-
|
|
41
33
|
return this.documentStore.collection('settings').update({
|
|
42
34
|
key: key
|
|
43
35
|
}, {
|
|
@@ -58,8 +50,6 @@ Settings.prototype.addOrSet = async function (key, avalue, req) {
|
|
|
58
50
|
Settings.prototype.init = async function (documentStore, authorization) {
|
|
59
51
|
this.documentStore = documentStore
|
|
60
52
|
|
|
61
|
-
const incompatibleSettingsToRemove = []
|
|
62
|
-
|
|
63
53
|
if (authorization != null) {
|
|
64
54
|
const col = documentStore.collection('settings')
|
|
65
55
|
|
|
@@ -95,30 +85,6 @@ Settings.prototype.init = async function (documentStore, authorization) {
|
|
|
95
85
|
}
|
|
96
86
|
})
|
|
97
87
|
}
|
|
98
|
-
|
|
99
|
-
const res = await documentStore.collection('settings').find({})
|
|
100
|
-
|
|
101
|
-
res.forEach((v) => {
|
|
102
|
-
if (typeof v.value !== 'string') {
|
|
103
|
-
return this._collection.push({
|
|
104
|
-
key: v.key,
|
|
105
|
-
value: v.value
|
|
106
|
-
})
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
try {
|
|
110
|
-
return this._collection.push({
|
|
111
|
-
key: v.key,
|
|
112
|
-
value: JSON.parse(v.value)
|
|
113
|
-
})
|
|
114
|
-
} catch (e) {
|
|
115
|
-
incompatibleSettingsToRemove.push(v._id)
|
|
116
|
-
}
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
if (incompatibleSettingsToRemove.length) {
|
|
120
|
-
return documentStore.collection('settings').remove({ _id: { $in: incompatibleSettingsToRemove } })
|
|
121
|
-
}
|
|
122
88
|
}
|
|
123
89
|
|
|
124
90
|
Settings.prototype.registerEntity = function (documentStore) {
|
|
@@ -13,7 +13,7 @@ module.exports = (level, msg, meta) => {
|
|
|
13
13
|
|
|
14
14
|
// TODO adding cancel looks bad, its before script is adding req.cancel()
|
|
15
15
|
// excluding non relevant properties for the log
|
|
16
|
-
const newMeta = Object.assign({}, omit(meta, ['rawContent', 'template', 'options', 'data', 'context', 'timestamp', 'cancel']))
|
|
16
|
+
const newMeta = Object.assign({}, omit(meta, ['logged', 'rawContent', 'template', 'options', 'data', 'context', 'timestamp', 'cancel']))
|
|
17
17
|
|
|
18
18
|
if (newMeta.rootId == null && meta.context.rootId != null) {
|
|
19
19
|
newMeta.rootId = meta.context.rootId
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsreport/jsreport-core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.11.1",
|
|
4
4
|
"description": "javascript based business reporting",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"report",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"@babel/parser": "7.14.4",
|
|
41
41
|
"@babel/traverse": "7.12.9",
|
|
42
42
|
"@colors/colors": "1.5.0",
|
|
43
|
-
"@jsreport/advanced-workers": "1.2.
|
|
43
|
+
"@jsreport/advanced-workers": "1.2.4",
|
|
44
44
|
"@jsreport/mingo": "2.4.1",
|
|
45
45
|
"@jsreport/reap": "0.1.0",
|
|
46
46
|
"ajv": "6.12.6",
|