querysub 0.355.0 → 0.357.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.
- package/.cursorrules +8 -0
- package/bin/movelogs.js +4 -0
- package/package.json +12 -6
- package/scripts/postinstall.js +23 -0
- package/src/-a-archives/archiveCache.ts +10 -12
- package/src/-a-archives/archives.ts +29 -0
- package/src/-a-archives/archivesBackBlaze.ts +60 -12
- package/src/-a-archives/archivesDisk.ts +27 -8
- package/src/-a-archives/archivesLimitedCache.ts +21 -0
- package/src/-a-archives/archivesMemoryCache.ts +350 -0
- package/src/-a-archives/archivesPrivateFileSystem.ts +22 -0
- package/src/-g-core-values/NodeCapabilities.ts +3 -0
- package/src/0-path-value-core/auditLogs.ts +5 -1
- package/src/0-path-value-core/pathValueCore.ts +7 -7
- package/src/4-dom/qreact.tsx +1 -0
- package/src/4-querysub/Querysub.ts +1 -5
- package/src/config.ts +5 -0
- package/src/diagnostics/MachineThreadInfo.tsx +235 -0
- package/src/diagnostics/NodeViewer.tsx +3 -2
- package/src/diagnostics/logs/FastArchiveAppendable.ts +79 -42
- package/src/diagnostics/logs/FastArchiveController.ts +102 -63
- package/src/diagnostics/logs/FastArchiveViewer.tsx +36 -8
- package/src/diagnostics/logs/IndexedLogs/BufferIndex.ts +461 -0
- package/src/diagnostics/logs/IndexedLogs/BufferIndexCPP.cpp +327 -0
- package/src/diagnostics/logs/IndexedLogs/BufferIndexCPP.d.ts +18 -0
- package/src/diagnostics/logs/IndexedLogs/BufferIndexCPP.js +1 -0
- package/src/diagnostics/logs/IndexedLogs/BufferIndexHelpers.ts +140 -0
- package/src/diagnostics/logs/IndexedLogs/BufferIndexLogsOptimizationConstants.ts +22 -0
- package/src/diagnostics/logs/IndexedLogs/BufferIndexWAT.wat +1145 -0
- package/src/diagnostics/logs/IndexedLogs/BufferIndexWAT.wat.d.ts +178 -0
- package/src/diagnostics/logs/IndexedLogs/BufferListStreamer.ts +206 -0
- package/src/diagnostics/logs/IndexedLogs/BufferUnitIndex.ts +719 -0
- package/src/diagnostics/logs/IndexedLogs/BufferUnitSet.ts +146 -0
- package/src/diagnostics/logs/IndexedLogs/FilePathSelector.tsx +408 -0
- package/src/diagnostics/logs/IndexedLogs/FindProgressTracker.ts +45 -0
- package/src/diagnostics/logs/IndexedLogs/IndexedLogs.ts +598 -0
- package/src/diagnostics/logs/IndexedLogs/LogStreamer.ts +47 -0
- package/src/diagnostics/logs/IndexedLogs/LogViewer3.tsx +702 -0
- package/src/diagnostics/logs/IndexedLogs/TimeFileTree.ts +236 -0
- package/src/diagnostics/logs/IndexedLogs/binding.gyp +23 -0
- package/src/diagnostics/logs/IndexedLogs/moveIndexLogsToPublic.ts +221 -0
- package/src/diagnostics/logs/IndexedLogs/moveLogsEntry.ts +10 -0
- package/src/diagnostics/logs/LogViewer2.tsx +120 -55
- package/src/diagnostics/logs/TimeRangeSelector.tsx +5 -2
- package/src/diagnostics/logs/diskLogger.ts +32 -48
- package/src/diagnostics/logs/errorNotifications/ErrorNotificationController.ts +3 -2
- package/src/diagnostics/logs/errorNotifications/errorDigests.tsx +1 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/LifeCyclePages.tsx +150 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycles.tsx +133 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/test.ts +180 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/test.wat +106 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/test.wat.d.ts +2 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/testHoist.ts +5 -0
- package/src/diagnostics/logs/logViewerExtractField.ts +2 -3
- package/src/diagnostics/managementPages.tsx +11 -1
- package/src/diagnostics/trackResources.ts +1 -1
- package/src/misc/lz4_wasm_nodejs.d.ts +34 -0
- package/src/misc/lz4_wasm_nodejs.js +178 -0
- package/src/misc/lz4_wasm_nodejs_bg.js +94 -0
- package/src/misc/lz4_wasm_nodejs_bg.wasm +0 -0
- package/src/misc/lz4_wasm_nodejs_bg.wasm.d.ts +15 -0
- package/src/storage/CompressedStream.ts +13 -0
- package/src/storage/LZ4.ts +32 -0
- package/src/storage/ZSTD.ts +10 -0
- package/src/wat/watCompiler.ts +1716 -0
- package/src/wat/watGrammar.pegjs +93 -0
- package/src/wat/watHandler.ts +179 -0
- package/src/wat/watInstructions.txt +707 -0
- package/src/zip.ts +3 -89
- package/src/diagnostics/logs/lifeCycleAnalysis/spec.md +0 -125
|
@@ -10,7 +10,7 @@ import { FastArchiveAppendable } from "./FastArchiveAppendable";
|
|
|
10
10
|
import { t } from "../../2-proxy/schema2";
|
|
11
11
|
import { measureFnc } from "socket-function/src/profiling/measure";
|
|
12
12
|
import { logErrors } from "../../errors";
|
|
13
|
-
import { batchFunction, runInSerial } from "socket-function/src/batching";
|
|
13
|
+
import { batchFunction, delay, runInSerial } from "socket-function/src/batching";
|
|
14
14
|
import { Querysub } from "../../4-querysub/QuerysubController";
|
|
15
15
|
import { sort, timeInDay, timeInHour } from "socket-function/src/misc";
|
|
16
16
|
import { FastArchiveViewer, cacheBustParam, filterParam, filterParam2 } from "./FastArchiveViewer";
|
|
@@ -29,6 +29,7 @@ import { SocketFunction } from "socket-function/SocketFunction";
|
|
|
29
29
|
import { throttleRender } from "../../functional/throttleRender";
|
|
30
30
|
import { isNode } from "typesafecss";
|
|
31
31
|
import { createLogViewerExtractField } from "./logViewerExtractField";
|
|
32
|
+
import { isPublic } from "../../config";
|
|
32
33
|
|
|
33
34
|
const RENDER_INTERVAL = 1000;
|
|
34
35
|
|
|
@@ -42,6 +43,7 @@ const selectedFieldsURL = new URLParam("selectedFields", {} as Record<string, bo
|
|
|
42
43
|
const useRelativeTimeURL = new URLParam("useRelativeTime", true);
|
|
43
44
|
|
|
44
45
|
export const errorNotifyToggleURL = new URLParam("errorNotifyToggle", false);
|
|
46
|
+
const forceGetPublicURL = new URLParam("forceGetPublic", false);
|
|
45
47
|
|
|
46
48
|
const defaultSelectedFields = {
|
|
47
49
|
param0: true,
|
|
@@ -58,6 +60,7 @@ export class LogViewer2 extends qreact.Component {
|
|
|
58
60
|
state = t.state({
|
|
59
61
|
datumsSeqNum: t.atomic<number>(0),
|
|
60
62
|
showCountByFile: t.boolean,
|
|
63
|
+
showSizeByFile: t.boolean,
|
|
61
64
|
});
|
|
62
65
|
|
|
63
66
|
private example: string | undefined = undefined;
|
|
@@ -69,6 +72,7 @@ export class LogViewer2 extends qreact.Component {
|
|
|
69
72
|
private notMatchedCount = 0;
|
|
70
73
|
private datums: LogDatum[] = [];
|
|
71
74
|
private countsPerName = new Map<string, number>();
|
|
75
|
+
private sizePerName = new Map<string, number>();
|
|
72
76
|
|
|
73
77
|
private lastRenderTime = 0;
|
|
74
78
|
private suppressionCounts = new Map<string, number>();
|
|
@@ -76,19 +80,15 @@ export class LogViewer2 extends qreact.Component {
|
|
|
76
80
|
private fastArchiveViewer: FastArchiveViewer<LogDatum> | undefined = undefined;
|
|
77
81
|
|
|
78
82
|
rerun() {
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
Querysub.onCommitFinished(async () => {
|
|
84
|
+
void this.fastArchiveViewer?.synchronizeDataThrottled();
|
|
85
|
+
});
|
|
81
86
|
}
|
|
82
|
-
|
|
83
|
-
render() {
|
|
84
|
-
if (throttleRender({ key: "LogViewer2", frameDelay: 30 })) return undefined;
|
|
85
|
-
|
|
86
|
-
this.state.datumsSeqNum;
|
|
87
|
-
|
|
87
|
+
getLoggers = () => {
|
|
88
88
|
let logs: FastArchiveAppendable<LogDatum>[] = [];
|
|
89
89
|
let loggers = getLoggers();
|
|
90
90
|
if (!loggers) {
|
|
91
|
-
|
|
91
|
+
throw new Error("Loggers not available?");
|
|
92
92
|
}
|
|
93
93
|
let { logLogs, warnLogs, infoLogs, errorLogs } = loggers;
|
|
94
94
|
if (errorNotifyToggleURL.value) {
|
|
@@ -108,6 +108,13 @@ export class LogViewer2 extends qreact.Component {
|
|
|
108
108
|
logs.push(errorLogs);
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
|
+
return logs;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
render() {
|
|
115
|
+
//if (throttleRender({ key: "LogViewer2", frameDelay: 30 })) return undefined;
|
|
116
|
+
|
|
117
|
+
this.state.datumsSeqNum;
|
|
111
118
|
|
|
112
119
|
let suppressionController = SuppressionListController(SocketFunction.browserNodeId());
|
|
113
120
|
let suppressionList = suppressionController.getSuppressionList();
|
|
@@ -138,6 +145,12 @@ export class LogViewer2 extends qreact.Component {
|
|
|
138
145
|
Test Error Notification (won't rerun search, but it should show up in the title)
|
|
139
146
|
</Button>
|
|
140
147
|
</div>}
|
|
148
|
+
{!isPublic() && <InputLabelURL
|
|
149
|
+
label="Force Public Logs" checkbox url={forceGetPublicURL}
|
|
150
|
+
onChangeValue={(newValue) => {
|
|
151
|
+
this.rerun();
|
|
152
|
+
}}
|
|
153
|
+
/>}
|
|
141
154
|
</div>
|
|
142
155
|
|
|
143
156
|
{!errorNotifyToggleURL.value && <div className={css.hbox(10)}>
|
|
@@ -157,8 +170,9 @@ export class LogViewer2 extends qreact.Component {
|
|
|
157
170
|
|
|
158
171
|
<FastArchiveViewer
|
|
159
172
|
ref2={x => this.fastArchiveViewer = x}
|
|
160
|
-
fastArchives={
|
|
173
|
+
fastArchives={this.getLoggers}
|
|
161
174
|
runOnLoad={errorNotifyToggleURL.value}
|
|
175
|
+
forceGetPublic={forceGetPublicURL.value}
|
|
162
176
|
onStart={async () => {
|
|
163
177
|
this.datumCount = 0;
|
|
164
178
|
this.notMatchedCount = 0;
|
|
@@ -188,13 +202,15 @@ export class LogViewer2 extends qreact.Component {
|
|
|
188
202
|
|
|
189
203
|
}}
|
|
190
204
|
getScanFnc={() => {
|
|
191
|
-
if (!this.state.showCountByFile) {
|
|
205
|
+
if (!this.state.showCountByFile && !this.state.showSizeByFile) {
|
|
192
206
|
return () => false;
|
|
193
207
|
}
|
|
194
|
-
return createLogViewerExtractField("
|
|
208
|
+
return createLogViewerExtractField("__NAME__", (value, size) => {
|
|
195
209
|
let prevCount = this.countsPerName.get(value) ?? 0;
|
|
196
210
|
prevCount++;
|
|
197
211
|
this.countsPerName.set(value, prevCount);
|
|
212
|
+
let prevSize = this.sizePerName.get(value) ?? 0;
|
|
213
|
+
this.sizePerName.set(value, prevSize + size);
|
|
198
214
|
});
|
|
199
215
|
}}
|
|
200
216
|
getWantData={async (file) => {
|
|
@@ -418,50 +434,99 @@ export class LogViewer2 extends qreact.Component {
|
|
|
418
434
|
</div>
|
|
419
435
|
</div>}
|
|
420
436
|
|
|
421
|
-
{
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
437
|
+
<div className={css.hbox(10)}>
|
|
438
|
+
{this.state.showCountByFile && (() => {
|
|
439
|
+
let counts = Array.from(this.countsPerName.entries());
|
|
440
|
+
sort(counts, x => -x[1]);
|
|
441
|
+
let topCounts = counts.slice(0, 20);
|
|
442
|
+
let totalCount = counts.reduce((sum, [, count]) => sum + count, 0);
|
|
426
443
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
444
|
+
return <div className={css.vbox(10)}>
|
|
445
|
+
<div className={css.hbox(10, 10).wrap}>
|
|
446
|
+
{topCounts.map(([name, count]) => {
|
|
447
|
+
let percentage = totalCount > 0 ? (count / totalCount) * 100 : 0;
|
|
448
|
+
let isFiltered = includedFiles.includes(name);
|
|
449
|
+
let size = this.sizePerName.get(name) ?? 0;
|
|
432
450
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
451
|
+
return <div
|
|
452
|
+
key={name}
|
|
453
|
+
className={css.relative.pad2(8, 4).bord2(210, 30, 60, 1).ellipsis.button}
|
|
454
|
+
title={isFiltered ? `Click to remove ${name} from filter` : `Click to add ${name} to filter`}
|
|
455
|
+
onClick={() => {
|
|
456
|
+
if (isFiltered) {
|
|
457
|
+
// Remove from filter
|
|
458
|
+
let newFiles = includedFiles.filter(x => x !== name);
|
|
459
|
+
filterParam2.value = newFiles.join("|");
|
|
460
|
+
} else {
|
|
461
|
+
// Add to filter
|
|
462
|
+
let newFiles = [...includedFiles.filter(x => x), name];
|
|
463
|
+
filterParam2.value = newFiles.join("|");
|
|
464
|
+
}
|
|
465
|
+
}}
|
|
466
|
+
>
|
|
467
|
+
<div
|
|
468
|
+
className={css.absolute.pos(0, 0).height("100%").width(`${percentage}%`).hsl(isFiltered ? 120 : 210, 40, 80)}
|
|
469
|
+
/>
|
|
470
|
+
<div className={css.relative.maxWidth(200).ellipsis}>
|
|
471
|
+
{formatNumber(count)} ({formatNumber(size)}B) | {name || "(no name present)"} {isFiltered && "✓" || ""}
|
|
472
|
+
</div>
|
|
473
|
+
</div>;
|
|
474
|
+
})}
|
|
475
|
+
</div>
|
|
476
|
+
</div>;
|
|
477
|
+
})() || <Button onClick={() => {
|
|
478
|
+
this.state.showCountByFile = true;
|
|
479
|
+
this.rerun();
|
|
480
|
+
}}>
|
|
481
|
+
Show Count by File
|
|
482
|
+
</Button>}
|
|
483
|
+
|
|
484
|
+
{this.state.showSizeByFile && (() => {
|
|
485
|
+
let sizes = Array.from(this.sizePerName.entries());
|
|
486
|
+
sort(sizes, x => -x[1]);
|
|
487
|
+
let topSizes = sizes.slice(0, 20);
|
|
488
|
+
let totalSize = sizes.reduce((sum, [, size]) => sum + size, 0);
|
|
489
|
+
|
|
490
|
+
return <div className={css.vbox(10)}>
|
|
491
|
+
<div className={css.hbox(10, 10).wrap}>
|
|
492
|
+
{topSizes.map(([name, size]) => {
|
|
493
|
+
let percentage = totalSize > 0 ? (size / totalSize) * 100 : 0;
|
|
494
|
+
let isFiltered = includedFiles.includes(name);
|
|
495
|
+
let count = this.countsPerName.get(name) ?? 0;
|
|
496
|
+
|
|
497
|
+
return <div
|
|
498
|
+
key={name}
|
|
499
|
+
className={css.relative.pad2(8, 4).bord2(280, 30, 60, 1).ellipsis.button}
|
|
500
|
+
title={isFiltered ? `Click to remove ${name} from filter` : `Click to add ${name} to filter`}
|
|
501
|
+
onClick={() => {
|
|
502
|
+
if (isFiltered) {
|
|
503
|
+
// Remove from filter
|
|
504
|
+
let newFiles = includedFiles.filter(x => x !== name);
|
|
505
|
+
filterParam2.value = newFiles.join("|");
|
|
506
|
+
} else {
|
|
507
|
+
// Add to filter
|
|
508
|
+
let newFiles = [...includedFiles.filter(x => x), name];
|
|
509
|
+
filterParam2.value = newFiles.join("|");
|
|
510
|
+
}
|
|
511
|
+
}}
|
|
512
|
+
>
|
|
513
|
+
<div
|
|
514
|
+
className={css.absolute.pos(0, 0).height("100%").width(`${percentage}%`).hsl(isFiltered ? 120 : 280, 40, 80)}
|
|
515
|
+
/>
|
|
516
|
+
<div className={css.relative.maxWidth(200).ellipsis}>
|
|
517
|
+
{formatNumber(size)}B ({formatNumber(count)}) | {name || "(no name present)"} {isFiltered && "✓" || ""}
|
|
518
|
+
</div>
|
|
519
|
+
</div>;
|
|
520
|
+
})}
|
|
521
|
+
</div>
|
|
522
|
+
</div>;
|
|
523
|
+
})() || <Button onClick={() => {
|
|
524
|
+
this.state.showSizeByFile = true;
|
|
525
|
+
this.rerun();
|
|
526
|
+
}}>
|
|
527
|
+
Show Size by File
|
|
528
|
+
</Button>}
|
|
529
|
+
</div>
|
|
465
530
|
<Table
|
|
466
531
|
rows={this.datums}
|
|
467
532
|
columns={columns}
|
|
@@ -20,8 +20,8 @@ export function getTimeRange(): { startTime: number; endTime: number } {
|
|
|
20
20
|
const defaultEnd = now + timeInHour;
|
|
21
21
|
|
|
22
22
|
return {
|
|
23
|
-
startTime: startTimeParam.value
|
|
24
|
-
endTime: endTimeParam.value
|
|
23
|
+
startTime: startTimeParam.value || defaultStart,
|
|
24
|
+
endTime: endTimeParam.value || defaultEnd,
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -73,6 +73,9 @@ export class TimeRangeSelector extends qreact.Component {
|
|
|
73
73
|
}}
|
|
74
74
|
outerClass={!endTimeParam.value && css.opacity(0.5) || ""}
|
|
75
75
|
/>
|
|
76
|
+
</div>
|
|
77
|
+
<div className={css.hbox(12).wrap}>
|
|
78
|
+
|
|
76
79
|
<Button
|
|
77
80
|
hue={110} onClick={() => {
|
|
78
81
|
let now = Date.now();
|
|
@@ -98,6 +98,33 @@ const getNotifyErrors = lazy(function () {
|
|
|
98
98
|
return notifyErrors;
|
|
99
99
|
});
|
|
100
100
|
|
|
101
|
+
export const getLoggers2 = lazy(function () {
|
|
102
|
+
const { IndexedLogs } = require("./IndexedLogs/IndexedLogs") as typeof import("./IndexedLogs/IndexedLogs");
|
|
103
|
+
|
|
104
|
+
if (!IndexedLogs) {
|
|
105
|
+
setImmediate(() => {
|
|
106
|
+
getLoggers2.reset();
|
|
107
|
+
});
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
logLogs: new IndexedLogs<LogDatum>({ name: "logs/log", getTime: x => x.time }),
|
|
113
|
+
warnLogs: new IndexedLogs<LogDatum>({ name: "logs/warn", getTime: x => x.time }),
|
|
114
|
+
infoLogs: new IndexedLogs<LogDatum>({ name: "logs/info", getTime: x => x.time }),
|
|
115
|
+
errorLogs: new IndexedLogs<LogDatum>({ name: "logs/error", getTime: x => x.time }),
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
export const getLoggers2Async = lazy(async () => {
|
|
119
|
+
const { IndexedLogs } = await import("./IndexedLogs/IndexedLogs");
|
|
120
|
+
return {
|
|
121
|
+
logLogs: new IndexedLogs<LogDatum>({ name: "logs/log", getTime: x => x.time }),
|
|
122
|
+
warnLogs: new IndexedLogs<LogDatum>({ name: "logs/warn", getTime: x => x.time }),
|
|
123
|
+
infoLogs: new IndexedLogs<LogDatum>({ name: "logs/info", getTime: x => x.time }),
|
|
124
|
+
errorLogs: new IndexedLogs<LogDatum>({ name: "logs/error", getTime: x => x.time }),
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
|
|
101
128
|
// NOTE: If any message (first param) starts with this, we don't log it to the disk. VERY useful for multi-line logging where it wouldn't make sense in the logs
|
|
102
129
|
// NOTE: This is visible, otherwise it's easy to accidentally copy it, and not know why the text is behaving strangely (not === other seemingly equal text, etc).
|
|
103
130
|
// NOTE: Also hardcoded in measure.ts (in socket-function)
|
|
@@ -116,15 +143,7 @@ void Promise.resolve().then(() => {
|
|
|
116
143
|
startupDone = true;
|
|
117
144
|
});
|
|
118
145
|
|
|
119
|
-
let logLimitLookup: {
|
|
120
|
-
resetTime: number;
|
|
121
|
-
counts: Map<string, number>;
|
|
122
|
-
} | undefined;
|
|
123
146
|
|
|
124
|
-
const LIMIT_PERIOD = timeInMinute * 15;
|
|
125
|
-
const LIMIT_THRESHOLD = 1000;
|
|
126
|
-
const WARN_LIMIT = 100;
|
|
127
|
-
const ERROR_LIMIT = 100;
|
|
128
147
|
|
|
129
148
|
const logDiskDontShim = logDisk;
|
|
130
149
|
/** NOTE: Calling this directly means we lose __FILE__ tracking. But... that's probably fine... */
|
|
@@ -147,47 +166,12 @@ export function logDisk(type: "log" | "warn" | "info" | "error", ...args: unknow
|
|
|
147
166
|
hasLineLimit = true;
|
|
148
167
|
}
|
|
149
168
|
|
|
150
|
-
if (
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
if (!logLimitLookup) {
|
|
156
|
-
logLimitLookup = {
|
|
157
|
-
resetTime: logObj.time + LIMIT_PERIOD,
|
|
158
|
-
counts: new Map(),
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
let count = logLimitLookup.counts.get(logType) || 0;
|
|
163
|
-
count++;
|
|
164
|
-
logLimitLookup.counts.set(logType, count);
|
|
165
|
-
let limit = LIMIT_THRESHOLD;
|
|
166
|
-
if (type === "warn") {
|
|
167
|
-
limit = WARN_LIMIT;
|
|
168
|
-
} else if (type === "error") {
|
|
169
|
-
limit = ERROR_LIMIT;
|
|
170
|
-
}
|
|
171
|
-
if (count > limit) {
|
|
172
|
-
let timeUntilReset = logLimitLookup.resetTime - logObj.time;
|
|
173
|
-
if (hasLineLimit) {
|
|
174
|
-
process.stdout.write(`Log type hit limit, not writing log type to disk for ~${formatTime(timeUntilReset)}: ${logType}\n`);
|
|
175
|
-
}
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
if (count >= limit) {
|
|
179
|
-
if (hasLineLimit) {
|
|
180
|
-
logObj[LOG_LINE_LIMIT_FLAG] = true;
|
|
181
|
-
} else {
|
|
182
|
-
logObj[LOG_LIMIT_FLAG] = true;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// We don't want developer errors clogging up the error logs. However, they can still notify errors, Because this will only notify nodes that are able to access us (It uses a reverse connection scheme, so instead of talking to nodes that we can access, we only talk to nodes that can access us), Which will mean it will only notify for local services, so the developer still gets error notifications, But our errors won't be spread to all developers. BUT, we will still watch global errors, because we can contact the global server, so developers will still get errors about production issues, even while developing!
|
|
187
|
-
if (isPublic()) {
|
|
188
|
-
let loggers = startupDone ? getLoggers() : undefined;
|
|
169
|
+
// NOTE: Local logs now log to their local disk instead of backblaze, so we can log even if in development (they just won't show up on the remote server).
|
|
170
|
+
//if (isPublic())
|
|
171
|
+
{
|
|
172
|
+
let loggers = startupDone ? getLoggers2() : undefined;
|
|
189
173
|
if (!loggers) {
|
|
190
|
-
|
|
174
|
+
getLoggers2.reset();
|
|
191
175
|
setImmediate(() => {
|
|
192
176
|
logDiskDontShim(type, ...args);
|
|
193
177
|
});
|
|
@@ -505,7 +505,7 @@ class URLCache {
|
|
|
505
505
|
}
|
|
506
506
|
});
|
|
507
507
|
}
|
|
508
|
-
const urlCache = new URLCache();
|
|
508
|
+
export const urlCache = new URLCache();
|
|
509
509
|
|
|
510
510
|
const limitRecentErrors = measureWrap(function limitRecentErrors(objs: LogDatum[]) {
|
|
511
511
|
sort(objs, x => x.time);
|
|
@@ -575,7 +575,7 @@ export class RecentErrors {
|
|
|
575
575
|
void this.broadcastUpdate(undefined);
|
|
576
576
|
}
|
|
577
577
|
});
|
|
578
|
-
private broadcastUpdate = batchFunction({ delay: NOTIFICATION_BROADCAST_BATCH }, () => {
|
|
578
|
+
private broadcastUpdate = batchFunction({ delay: NOTIFICATION_BROADCAST_BATCH, noMeasure: true }, () => {
|
|
579
579
|
recentErrorsChannel.broadcast(true);
|
|
580
580
|
});
|
|
581
581
|
|
|
@@ -637,6 +637,7 @@ export class RecentErrors {
|
|
|
637
637
|
},
|
|
638
638
|
rootPath: appendable.rootPath,
|
|
639
639
|
noLocalFiles: config.noLocalFiles,
|
|
640
|
+
forceGetPublic: true,
|
|
640
641
|
});
|
|
641
642
|
// Filter again, as new suppressions change the errors
|
|
642
643
|
await this.updateRecentErrors();
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
module.hotreload = true;
|
|
2
|
+
import { qreact } from "../../../4-dom/qreact";
|
|
3
|
+
import { css } from "../../../4-dom/css";
|
|
4
|
+
import { t } from "../../../2-proxy/schema2";
|
|
5
|
+
import { Querysub } from "../../../4-querysub/QuerysubController";
|
|
6
|
+
import { sort } from "socket-function/src/misc";
|
|
7
|
+
import { FastArchiveViewer } from "../FastArchiveViewer";
|
|
8
|
+
import { LogDatum, getLoggers } from "../diskLogger";
|
|
9
|
+
import { Table } from "../../../5-diagnostics/Table";
|
|
10
|
+
import { formatDateTime } from "socket-function/src/formatting/format";
|
|
11
|
+
import { FastArchiveAppendable } from "../FastArchiveAppendable";
|
|
12
|
+
import { getTimeRange } from "../TimeRangeSelector";
|
|
13
|
+
import { URLParam } from "../../../library-components/URLParam";
|
|
14
|
+
import { InputLabelURL } from "../../../library-components/InputLabel";
|
|
15
|
+
import { isPublic } from "../../../config";
|
|
16
|
+
|
|
17
|
+
const forceGetPublicURL = new URLParam("forceGetPublic", false);
|
|
18
|
+
|
|
19
|
+
export class LifeCyclePages extends qreact.Component {
|
|
20
|
+
state = t.state({
|
|
21
|
+
dataSeqNum: t.atomic<number>(0),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
private datums: LogDatum[] = [];
|
|
25
|
+
private latestByName = new Map<string, LogDatum>();
|
|
26
|
+
private fastArchiveViewer: FastArchiveViewer<LogDatum> | undefined = undefined;
|
|
27
|
+
|
|
28
|
+
rerun() {
|
|
29
|
+
Querysub.onCommitFinished(async () => {
|
|
30
|
+
void this.fastArchiveViewer?.synchronizeDataThrottled();
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getLoggers = () => {
|
|
35
|
+
let logs: FastArchiveAppendable<LogDatum>[] = [];
|
|
36
|
+
let loggers = getLoggers();
|
|
37
|
+
if (!loggers) {
|
|
38
|
+
throw new Error("Loggers not available?");
|
|
39
|
+
}
|
|
40
|
+
let { logLogs, warnLogs, infoLogs, errorLogs } = loggers;
|
|
41
|
+
logs.push(logLogs);
|
|
42
|
+
logs.push(infoLogs);
|
|
43
|
+
logs.push(warnLogs);
|
|
44
|
+
logs.push(errorLogs);
|
|
45
|
+
return logs;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
renderContent() {
|
|
49
|
+
let latestEntries = Array.from(this.latestByName.entries());
|
|
50
|
+
sort(latestEntries, x => -(x[1].time || 0));
|
|
51
|
+
|
|
52
|
+
let rows = latestEntries.map(([name, datum]) => ({
|
|
53
|
+
name,
|
|
54
|
+
time: datum.time,
|
|
55
|
+
datum,
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
return <>
|
|
59
|
+
<div className={css.hbox(10)}>
|
|
60
|
+
<div>
|
|
61
|
+
Total unique __NAME__ values: {this.latestByName.size}
|
|
62
|
+
</div>
|
|
63
|
+
<div>
|
|
64
|
+
Total logs processed: {this.datums.length}
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
{rows.length === 0 && <div className={css.hsl(40, 50, 50).colorhsl(60, 50, 100).boldStyle.pad2(10).ellipsis}>
|
|
69
|
+
No logs matched, either increase the time range or run the search.
|
|
70
|
+
</div>}
|
|
71
|
+
|
|
72
|
+
<Table
|
|
73
|
+
rows={rows}
|
|
74
|
+
columns={{
|
|
75
|
+
name: {
|
|
76
|
+
title: "Name",
|
|
77
|
+
formatter: x => <div>{String(x)}</div>
|
|
78
|
+
},
|
|
79
|
+
time: {
|
|
80
|
+
title: "Last Time",
|
|
81
|
+
formatter: x => <div>{formatDateTime(Number(x))}</div>
|
|
82
|
+
},
|
|
83
|
+
}}
|
|
84
|
+
lineLimit={4}
|
|
85
|
+
initialLimit={20}
|
|
86
|
+
characterLimit={400}
|
|
87
|
+
/>
|
|
88
|
+
</>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
render() {
|
|
92
|
+
this.state.dataSeqNum;
|
|
93
|
+
|
|
94
|
+
const timeRange = getTimeRange();
|
|
95
|
+
return (
|
|
96
|
+
<div className={css.vbox(20).pad2(20).fillBoth}>
|
|
97
|
+
<div className={css.hbox(10)}>
|
|
98
|
+
<div className={css.fontSize(14)}>
|
|
99
|
+
Life Cycle Analysis
|
|
100
|
+
</div>
|
|
101
|
+
{!isPublic() && <InputLabelURL
|
|
102
|
+
label="Force Public Logs" checkbox url={forceGetPublicURL}
|
|
103
|
+
onChangeValue={(newValue) => {
|
|
104
|
+
this.rerun();
|
|
105
|
+
}}
|
|
106
|
+
/>}
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<FastArchiveViewer
|
|
110
|
+
ref2={x => this.fastArchiveViewer = x}
|
|
111
|
+
fastArchives={this.getLoggers}
|
|
112
|
+
runOnLoad={false}
|
|
113
|
+
forceGetPublic={forceGetPublicURL.value}
|
|
114
|
+
onStart={async () => {
|
|
115
|
+
this.datums = [];
|
|
116
|
+
this.latestByName = new Map();
|
|
117
|
+
}}
|
|
118
|
+
onDatums={(source, datums, file) => {
|
|
119
|
+
for (let datum of datums) {
|
|
120
|
+
let time = datum.time;
|
|
121
|
+
if (time && (time < timeRange.startTime || time > timeRange.endTime)) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
this.datums.push(datum);
|
|
125
|
+
}
|
|
126
|
+
}}
|
|
127
|
+
onFinish={() => {
|
|
128
|
+
// Sort all datums by time
|
|
129
|
+
sort(this.datums, x => x.time || 0);
|
|
130
|
+
|
|
131
|
+
// Get the latest value (last time) for every __NAME__
|
|
132
|
+
this.latestByName.clear();
|
|
133
|
+
for (let datum of this.datums) {
|
|
134
|
+
const name = datum.__NAME__;
|
|
135
|
+
if (name) {
|
|
136
|
+
this.latestByName.set(name, datum);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
Querysub.commit(() => {
|
|
141
|
+
this.state.dataSeqNum++;
|
|
142
|
+
});
|
|
143
|
+
}}
|
|
144
|
+
>
|
|
145
|
+
{this.renderContent()}
|
|
146
|
+
</FastArchiveViewer>
|
|
147
|
+
</div>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|