querysub 0.373.0 → 0.375.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/package.json +2 -4
- package/src/-f-node-discovery/NodeDiscovery.ts +2 -2
- package/src/0-path-value-core/PathValueCommitter.ts +1 -1
- package/src/0-path-value-core/PathValueController.ts +2 -2
- package/src/0-path-value-core/archiveLocks/ArchiveLocks2.ts +12 -12
- package/src/0-path-value-core/auditLogs.ts +1 -1
- package/src/0-path-value-core/pathValueCore.ts +2 -2
- package/src/3-path-functions/PathFunctionRunner.ts +1 -1
- package/src/3-path-functions/PathFunctionRunnerMain.ts +1 -1
- package/src/4-dom/qreact.tsx +2 -2
- package/src/4-querysub/QuerysubController.ts +1 -1
- package/src/5-diagnostics/diskValueAudit.ts +1 -1
- package/src/deployManager/components/MachineDetailPage.tsx +2 -5
- package/src/deployManager/components/ServiceDetailPage.tsx +2 -5
- package/src/deployManager/machineApplyMainCode.ts +7 -0
- package/src/diagnostics/NodeViewer.tsx +4 -5
- package/src/diagnostics/logs/IndexedLogs/BufferIndexHelpers.ts +1 -1
- package/src/diagnostics/logs/IndexedLogs/IndexedLogs.ts +7 -7
- package/src/diagnostics/logs/IndexedLogs/LogViewer3.tsx +221 -220
- package/src/diagnostics/logs/IndexedLogs/LogViewerParams.ts +21 -0
- package/src/diagnostics/logs/IndexedLogs/bufferMatcher.ts +3 -3
- package/src/diagnostics/logs/diskLogger.ts +1 -39
- package/src/diagnostics/logs/diskShimConsoleLogs.ts +2 -0
- package/src/diagnostics/logs/errorNotifications2/errorNotifications2.ts +3 -0
- package/src/diagnostics/logs/injectFileLocationToConsole.ts +3 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycles.tsx +32 -22
- package/src/diagnostics/managementPages.tsx +0 -18
- package/src/diagnostics/watchdog.ts +1 -1
- package/src/user-implementation/userData.ts +3 -3
- package/test.ts +0 -5
- package/bin/error-email.js +0 -8
- package/bin/error-im.js +0 -8
- package/src/diagnostics/logs/FastArchiveAppendable.ts +0 -843
- package/src/diagnostics/logs/FastArchiveController.ts +0 -573
- package/src/diagnostics/logs/FastArchiveViewer.tsx +0 -1090
- package/src/diagnostics/logs/LogViewer2.tsx +0 -552
- package/src/diagnostics/logs/errorNotifications/ErrorDigestPage.tsx +0 -409
- package/src/diagnostics/logs/errorNotifications/ErrorNotificationController.ts +0 -756
- package/src/diagnostics/logs/errorNotifications/ErrorSuppressionUI.tsx +0 -280
- package/src/diagnostics/logs/errorNotifications/ErrorWarning.tsx +0 -254
- package/src/diagnostics/logs/errorNotifications/errorDigestEmail.tsx +0 -233
- package/src/diagnostics/logs/errorNotifications/errorDigestEntry.tsx +0 -14
- package/src/diagnostics/logs/errorNotifications/errorDigests.tsx +0 -292
- package/src/diagnostics/logs/errorNotifications/errorWatchEntry.tsx +0 -209
- package/src/diagnostics/logs/importLogsEntry.ts +0 -38
- package/src/diagnostics/logs/lifeCycleAnalysis/LifeCyclePages.tsx +0 -150
- package/src/diagnostics/logs/logViewerExtractField.ts +0 -36
|
@@ -1,552 +0,0 @@
|
|
|
1
|
-
module.hotreload = true;
|
|
2
|
-
import { qreact } from "../../4-dom/qreact";
|
|
3
|
-
import { css } from "../../4-dom/css";
|
|
4
|
-
import { URLParam } from "../../library-components/URLParam";
|
|
5
|
-
import { InputLabelURL } from "../../library-components/InputLabel";
|
|
6
|
-
import { Button } from "../../library-components/Button";
|
|
7
|
-
import { formatDateTime, formatDateTimeDetailed, formatNiceDateTime, formatNumber, formatTime } from "socket-function/src/formatting/format";
|
|
8
|
-
import { TimeRangeSelector, endTimeParam, getTimeRange, startTimeParam } from "./TimeRangeSelector";
|
|
9
|
-
import { FastArchiveAppendable } from "./FastArchiveAppendable";
|
|
10
|
-
import { t } from "../../2-proxy/schema2";
|
|
11
|
-
import { measureFnc } from "socket-function/src/profiling/measure";
|
|
12
|
-
import { logErrors } from "../../errors";
|
|
13
|
-
import { batchFunction, delay, runInSerial } from "socket-function/src/batching";
|
|
14
|
-
import { Querysub } from "../../4-querysub/QuerysubController";
|
|
15
|
-
import { sort, timeInDay, timeInHour } from "socket-function/src/misc";
|
|
16
|
-
import { FastArchiveViewer, cacheBustParam, filterParam, filterParam2 } from "./FastArchiveViewer";
|
|
17
|
-
import { LogDatum, getLoggers, LOG_LIMIT_FLAG, LOG_LINE_LIMIT_FLAG } from "./diskLogger";
|
|
18
|
-
import { ColumnType, Table, TableType } from "../../5-diagnostics/Table";
|
|
19
|
-
import { formatDateJSX } from "../../misc/formatJSX";
|
|
20
|
-
import { InputPicker } from "../../library-components/InputPicker";
|
|
21
|
-
import { JSXFormatter } from "../../5-diagnostics/GenericFormat";
|
|
22
|
-
import { atomic } from "../../2-proxy/PathValueProxyWatcher";
|
|
23
|
-
import { ObjectDisplay } from "./ObjectDisplay";
|
|
24
|
-
import { endTime } from "../misc-pages/archiveViewerShared";
|
|
25
|
-
import { ErrorSuppressionUI } from "./errorNotifications/ErrorSuppressionUI";
|
|
26
|
-
import { FileMetadata } from "./FastArchiveController";
|
|
27
|
-
import { RecentErrorsController, SuppressionListController, getSuppressEntryChecker, getSuppressionFull } from "./errorNotifications/ErrorNotificationController";
|
|
28
|
-
import { SocketFunction } from "socket-function/SocketFunction";
|
|
29
|
-
import { throttleRender } from "../../functional/throttleRender";
|
|
30
|
-
import { isNode } from "typesafecss";
|
|
31
|
-
import { createLogViewerExtractField } from "./logViewerExtractField";
|
|
32
|
-
import { isPublic } from "../../config";
|
|
33
|
-
|
|
34
|
-
const RENDER_INTERVAL = 1000;
|
|
35
|
-
|
|
36
|
-
export const testLogs = new FastArchiveAppendable("test/");
|
|
37
|
-
|
|
38
|
-
const enableLogsURL = new URLParam("enableLogs", false);
|
|
39
|
-
const enableInfosURL = new URLParam("enableInfos", true);
|
|
40
|
-
const enableWarningsURL = new URLParam("enableWarnings", true);
|
|
41
|
-
const enableErrorsURL = new URLParam("enableErrors", true);
|
|
42
|
-
const selectedFieldsURL = new URLParam("selectedFields", {} as Record<string, boolean>);
|
|
43
|
-
const useRelativeTimeURL = new URLParam("useRelativeTime", true);
|
|
44
|
-
|
|
45
|
-
export const errorNotifyToggleURL = new URLParam("errorNotifyToggle", false);
|
|
46
|
-
const forceGetPublicURL = new URLParam("forceGetPublic", false);
|
|
47
|
-
|
|
48
|
-
const defaultSelectedFields = {
|
|
49
|
-
param0: true,
|
|
50
|
-
time: true,
|
|
51
|
-
__NAME__: true,
|
|
52
|
-
|
|
53
|
-
//__machineId: true,
|
|
54
|
-
__threadId: true,
|
|
55
|
-
__entry: true,
|
|
56
|
-
//__DIR__: true,
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export class LogViewer2 extends qreact.Component {
|
|
60
|
-
state = t.state({
|
|
61
|
-
datumsSeqNum: t.atomic<number>(0),
|
|
62
|
-
showCountByFile: t.boolean,
|
|
63
|
-
showSizeByFile: t.boolean,
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
private example: string | undefined = undefined;
|
|
67
|
-
private operationSequenceNum = 0;
|
|
68
|
-
private datumCount = 0;
|
|
69
|
-
private matchedSize = 0;
|
|
70
|
-
private notMatchedSize = 0;
|
|
71
|
-
private errors = 0;
|
|
72
|
-
private notMatchedCount = 0;
|
|
73
|
-
private datums: LogDatum[] = [];
|
|
74
|
-
private countsPerName = new Map<string, number>();
|
|
75
|
-
private sizePerName = new Map<string, number>();
|
|
76
|
-
|
|
77
|
-
private lastRenderTime = 0;
|
|
78
|
-
private suppressionCounts = new Map<string, number>();
|
|
79
|
-
private expiredSuppressionCounts = new Map<string, number>();
|
|
80
|
-
private fastArchiveViewer: FastArchiveViewer<LogDatum> | undefined = undefined;
|
|
81
|
-
|
|
82
|
-
rerun() {
|
|
83
|
-
Querysub.onCommitFinished(async () => {
|
|
84
|
-
void this.fastArchiveViewer?.synchronizeDataThrottled();
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
getLoggers = () => {
|
|
88
|
-
let logs: FastArchiveAppendable<LogDatum>[] = [];
|
|
89
|
-
let loggers = getLoggers();
|
|
90
|
-
if (!loggers) {
|
|
91
|
-
throw new Error("Loggers not available?");
|
|
92
|
-
}
|
|
93
|
-
let { logLogs, warnLogs, infoLogs, errorLogs } = loggers;
|
|
94
|
-
if (errorNotifyToggleURL.value) {
|
|
95
|
-
logs.push(errorLogs);
|
|
96
|
-
logs.push(warnLogs);
|
|
97
|
-
} else {
|
|
98
|
-
if (enableLogsURL.value) {
|
|
99
|
-
logs.push(logLogs);
|
|
100
|
-
}
|
|
101
|
-
if (enableInfosURL.value) {
|
|
102
|
-
logs.push(infoLogs);
|
|
103
|
-
}
|
|
104
|
-
if (enableWarningsURL.value) {
|
|
105
|
-
logs.push(warnLogs);
|
|
106
|
-
}
|
|
107
|
-
if (enableErrorsURL.value) {
|
|
108
|
-
logs.push(errorLogs);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
return logs;
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
render() {
|
|
115
|
-
//if (throttleRender({ key: "LogViewer2", frameDelay: 30 })) return undefined;
|
|
116
|
-
|
|
117
|
-
this.state.datumsSeqNum;
|
|
118
|
-
|
|
119
|
-
let suppressionController = SuppressionListController(SocketFunction.browserNodeId());
|
|
120
|
-
let suppressionList = suppressionController.getSuppressionList();
|
|
121
|
-
let hasErrorNotifyToggle = errorNotifyToggleURL.value;
|
|
122
|
-
|
|
123
|
-
const timeRange = getTimeRange();
|
|
124
|
-
return (
|
|
125
|
-
<div className={css.vbox(20).pad2(20).fillBoth}>
|
|
126
|
-
<div className={css.hbox(10)}>
|
|
127
|
-
<div className={css.fontSize(14)}>
|
|
128
|
-
Log Viewer
|
|
129
|
-
</div>
|
|
130
|
-
<InputLabelURL
|
|
131
|
-
label="Error Notification Mode" checkbox url={errorNotifyToggleURL}
|
|
132
|
-
onChangeValue={(newValue) => {
|
|
133
|
-
if (newValue) {
|
|
134
|
-
let now = Date.now();
|
|
135
|
-
startTimeParam.value = now - timeInDay * 7;
|
|
136
|
-
endTimeParam.value = now + timeInHour * 2;
|
|
137
|
-
}
|
|
138
|
-
this.rerun();
|
|
139
|
-
}}
|
|
140
|
-
/>
|
|
141
|
-
{errorNotifyToggleURL.value && <div className={css.hbox(10)}>
|
|
142
|
-
<Button onClick={() => {
|
|
143
|
-
void RecentErrorsController(SocketFunction.browserNodeId()).raiseTestError.promise("Test error notification");
|
|
144
|
-
}}>
|
|
145
|
-
Test Error Notification (won't rerun search, but it should show up in the title)
|
|
146
|
-
</Button>
|
|
147
|
-
</div>}
|
|
148
|
-
{!isPublic() && <InputLabelURL
|
|
149
|
-
label="Force Public Logs" checkbox url={forceGetPublicURL}
|
|
150
|
-
onChangeValue={(newValue) => {
|
|
151
|
-
this.rerun();
|
|
152
|
-
}}
|
|
153
|
-
/>}
|
|
154
|
-
</div>
|
|
155
|
-
|
|
156
|
-
{!errorNotifyToggleURL.value && <div className={css.hbox(10)}>
|
|
157
|
-
<Button hue={enableLogsURL.value ? 120 : undefined} onClick={() => { enableLogsURL.value = !enableLogsURL.value; this.rerun(); }}>
|
|
158
|
-
Logs
|
|
159
|
-
</Button>
|
|
160
|
-
<Button hue={enableInfosURL.value ? 120 : undefined} onClick={() => { enableInfosURL.value = !enableInfosURL.value; this.rerun(); }}>
|
|
161
|
-
Infos
|
|
162
|
-
</Button>
|
|
163
|
-
<Button hue={enableWarningsURL.value ? 120 : undefined} onClick={() => { enableWarningsURL.value = !enableWarningsURL.value; this.rerun(); }}>
|
|
164
|
-
Warnings
|
|
165
|
-
</Button>
|
|
166
|
-
<Button hue={enableErrorsURL.value ? 120 : undefined} onClick={() => { enableErrorsURL.value = !enableErrorsURL.value; this.rerun(); }}>
|
|
167
|
-
Errors
|
|
168
|
-
</Button>
|
|
169
|
-
</div>}
|
|
170
|
-
|
|
171
|
-
<FastArchiveViewer
|
|
172
|
-
ref2={x => this.fastArchiveViewer = x}
|
|
173
|
-
fastArchives={this.getLoggers}
|
|
174
|
-
runOnLoad={errorNotifyToggleURL.value}
|
|
175
|
-
forceGetPublic={forceGetPublicURL.value}
|
|
176
|
-
onStart={async () => {
|
|
177
|
-
this.datumCount = 0;
|
|
178
|
-
this.notMatchedCount = 0;
|
|
179
|
-
this.errors = 0;
|
|
180
|
-
this.notMatchedSize = 0;
|
|
181
|
-
this.matchedSize = 0;
|
|
182
|
-
this.example = undefined;
|
|
183
|
-
this.datums = [];
|
|
184
|
-
this.suppressionCounts = new Map();
|
|
185
|
-
this.expiredSuppressionCounts = new Map();
|
|
186
|
-
this.countsPerName = new Map();
|
|
187
|
-
this.operationSequenceNum++;
|
|
188
|
-
void (async () => {
|
|
189
|
-
const currentSequenceNum = this.operationSequenceNum;
|
|
190
|
-
while (true) {
|
|
191
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
192
|
-
if (this.operationSequenceNum !== currentSequenceNum) {
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
Querysub.commit(() => {
|
|
196
|
-
this.state.datumsSeqNum++;
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
})();
|
|
200
|
-
// ALWAYS update it, as the synchronous one might be out of date, and if we use an outdated one extra errors show up.
|
|
201
|
-
suppressionList = await suppressionController.getSuppressionList.promise();
|
|
202
|
-
|
|
203
|
-
}}
|
|
204
|
-
getScanFnc={() => {
|
|
205
|
-
if (!this.state.showCountByFile && !this.state.showSizeByFile) {
|
|
206
|
-
return () => false;
|
|
207
|
-
}
|
|
208
|
-
return createLogViewerExtractField("__NAME__", (value, size) => {
|
|
209
|
-
let prevCount = this.countsPerName.get(value) ?? 0;
|
|
210
|
-
prevCount++;
|
|
211
|
-
this.countsPerName.set(value, prevCount);
|
|
212
|
-
let prevSize = this.sizePerName.get(value) ?? 0;
|
|
213
|
-
this.sizePerName.set(value, prevSize + size);
|
|
214
|
-
});
|
|
215
|
-
}}
|
|
216
|
-
getWantData={async (file) => {
|
|
217
|
-
if (!hasErrorNotifyToggle) return undefined;
|
|
218
|
-
// By defaulting to the synchronous one, the list will be updated if there's any changes. However, we will also just get it asynchronously if the list hasn't been updated by the time we call get1data. And because we assign it back to the variable, it'll be cached.
|
|
219
|
-
suppressionList = suppressionList || await suppressionController.getSuppressionList.promise();
|
|
220
|
-
|
|
221
|
-
let suppressionFull = getSuppressionFull({
|
|
222
|
-
entries: suppressionList,
|
|
223
|
-
blockTimeRange: {
|
|
224
|
-
startTime: file.startTime,
|
|
225
|
-
endTime: file.endTime,
|
|
226
|
-
},
|
|
227
|
-
suppressionCounts: this.suppressionCounts,
|
|
228
|
-
expiredSuppressionCounts: this.expiredSuppressionCounts,
|
|
229
|
-
});
|
|
230
|
-
return suppressionFull;
|
|
231
|
-
}}
|
|
232
|
-
onDatums={(source, datums, file) => {
|
|
233
|
-
this.datumCount += datums.length;
|
|
234
|
-
if (!this.example && datums.length > 0) {
|
|
235
|
-
this.example = JSON.stringify(datums[0]);
|
|
236
|
-
}
|
|
237
|
-
for (let datum of datums) {
|
|
238
|
-
let time = datum.time;
|
|
239
|
-
if (time && (time < timeRange.startTime || time > timeRange.endTime)) {
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
datum.__metadata = file;
|
|
243
|
-
this.datums.push(datum);
|
|
244
|
-
}
|
|
245
|
-
}}
|
|
246
|
-
onStats={(source, stats, file) => {
|
|
247
|
-
this.matchedSize += stats.matchedSize;
|
|
248
|
-
this.notMatchedSize += stats.notMatchedSize;
|
|
249
|
-
this.errors += stats.errors;
|
|
250
|
-
this.notMatchedCount += stats.notMatchedCount;
|
|
251
|
-
|
|
252
|
-
let now = Date.now();
|
|
253
|
-
if (now - this.lastRenderTime > RENDER_INTERVAL) {
|
|
254
|
-
this.lastRenderTime = now;
|
|
255
|
-
Querysub.commit(() => {
|
|
256
|
-
this.state.datumsSeqNum++;
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
}}
|
|
260
|
-
onFinish={() => {
|
|
261
|
-
this.operationSequenceNum++;
|
|
262
|
-
Querysub.commit(() => {
|
|
263
|
-
sort(this.datums, x => -(x.time || 0));
|
|
264
|
-
this.state.datumsSeqNum++;
|
|
265
|
-
});
|
|
266
|
-
}}
|
|
267
|
-
>
|
|
268
|
-
{errorNotifyToggleURL.value && <ErrorSuppressionUI
|
|
269
|
-
dataSeqNum={this.state.datumsSeqNum}
|
|
270
|
-
suppressionCounts={this.suppressionCounts}
|
|
271
|
-
expiredSuppressionCounts={this.expiredSuppressionCounts}
|
|
272
|
-
datums={this.datums}
|
|
273
|
-
rerunFilters={() => this.rerun()}
|
|
274
|
-
/>}
|
|
275
|
-
{(() => {
|
|
276
|
-
let fieldNames = new Set<string>();
|
|
277
|
-
let anyLimited = false;
|
|
278
|
-
for (let i = 0; i < Math.min(1000, this.datums.length); i++) {
|
|
279
|
-
let datum = this.datums[i];
|
|
280
|
-
for (let key of Object.keys(datum)) {
|
|
281
|
-
fieldNames.add(key);
|
|
282
|
-
}
|
|
283
|
-
if (datum[LOG_LIMIT_FLAG]) {
|
|
284
|
-
anyLimited = true;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
let lineLimited = false;
|
|
288
|
-
for (let i = 0; i < Math.min(1000, this.datums.length); i++) {
|
|
289
|
-
let datum = this.datums[i];
|
|
290
|
-
if (datum[LOG_LINE_LIMIT_FLAG]) {
|
|
291
|
-
lineLimited = true;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
let selectedFields: string[] = [];
|
|
295
|
-
for (let field of Object.keys(defaultSelectedFields)) {
|
|
296
|
-
if (atomic(selectedFieldsURL.value[field]) === undefined) {
|
|
297
|
-
selectedFields.push(field);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
for (let [key, value] of Object.entries(selectedFieldsURL.value)) {
|
|
301
|
-
if (value && !selectedFields.includes(key)) {
|
|
302
|
-
selectedFields.splice(1, 0, key);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
let columns: TableType<LogDatum>["columns"] = {};
|
|
306
|
-
columns["log"] = {
|
|
307
|
-
title: "Log",
|
|
308
|
-
formatter: (x, context) => {
|
|
309
|
-
return <Button onClick={() => console.log(context?.row)}>
|
|
310
|
-
Log
|
|
311
|
-
</Button>;
|
|
312
|
-
}
|
|
313
|
-
};
|
|
314
|
-
if (anyLimited) {
|
|
315
|
-
columns[LOG_LIMIT_FLAG] = {
|
|
316
|
-
title: "Limited",
|
|
317
|
-
formatter: x => x && <div className={css.hsl(0, 50, 50).colorhsl(0, 50, 95).boldStyle.pad2(10).ellipsis}
|
|
318
|
-
title="File throttled (other line may be lost lost!)">
|
|
319
|
-
FILE throttled
|
|
320
|
-
</div> || undefined
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
if (lineLimited) {
|
|
324
|
-
columns[LOG_LINE_LIMIT_FLAG] = {
|
|
325
|
-
title: "Limited",
|
|
326
|
-
formatter: x => x && <div className={css.hsl(60, 50, 50).colorhsl(60, 50, 95).boldStyle.pad2(10).ellipsis}
|
|
327
|
-
title="Line throttled (no lines lost, just count for this line is lower than the actual count)">
|
|
328
|
-
Line throttled
|
|
329
|
-
</div> || undefined
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
for (let field of selectedFields) {
|
|
333
|
-
let column: ColumnType<unknown, LogDatum> = {};
|
|
334
|
-
if (field === "time") {
|
|
335
|
-
column.formatter = (x: unknown) => useRelativeTimeURL.value ? formatDateJSX(Number(x)) : <span title={formatDateTimeDetailed(Number(x))}>{formatDateTime(Number(x))}</span>;
|
|
336
|
-
}
|
|
337
|
-
if (!column.formatter) {
|
|
338
|
-
column.formatter = (x: unknown) => <ObjectDisplay value={x} />;
|
|
339
|
-
}
|
|
340
|
-
columns[field] = column;
|
|
341
|
-
}
|
|
342
|
-
columns["fields"] = {
|
|
343
|
-
title: "Fields",
|
|
344
|
-
formatter: (x, context) => {
|
|
345
|
-
if (!context?.row) return undefined;
|
|
346
|
-
let datum = context.row;
|
|
347
|
-
let fields = Object.keys(datum);
|
|
348
|
-
fields = fields.filter(x => !(x in columns) && !x.startsWith("_"));
|
|
349
|
-
return <div className={css.hbox(4, 4).wrap}>
|
|
350
|
-
{fields.map(x => <div
|
|
351
|
-
className={css.pad2(4, 0).hsl(0, 0, 80).button}
|
|
352
|
-
onClick={() => {
|
|
353
|
-
let newValues = { ...selectedFieldsURL.value };
|
|
354
|
-
newValues[x] = true;
|
|
355
|
-
selectedFieldsURL.value = newValues;
|
|
356
|
-
}}
|
|
357
|
-
>
|
|
358
|
-
{x}
|
|
359
|
-
</div>)}
|
|
360
|
-
</div>;
|
|
361
|
-
}
|
|
362
|
-
};
|
|
363
|
-
let includedFiles = filterParam2.value.split("|").map(x => x.trim());
|
|
364
|
-
return <>
|
|
365
|
-
<div className={css.hbox(10)}>
|
|
366
|
-
<InputPicker
|
|
367
|
-
label={<div className={css.hbox(10)}>
|
|
368
|
-
<div className={css.flexShrink0}>
|
|
369
|
-
Selected Fields
|
|
370
|
-
</div>
|
|
371
|
-
<Button onClick={() => {
|
|
372
|
-
let newValues = { ...selectedFieldsURL.value };
|
|
373
|
-
for (let key of Object.keys(newValues)) {
|
|
374
|
-
newValues[key] = key in defaultSelectedFields;
|
|
375
|
-
}
|
|
376
|
-
selectedFieldsURL.value = newValues;
|
|
377
|
-
}}>
|
|
378
|
-
Reset
|
|
379
|
-
</Button>
|
|
380
|
-
</div>}
|
|
381
|
-
picked={selectedFields}
|
|
382
|
-
options={Array.from(fieldNames).map(x => ({ value: x, label: x }))}
|
|
383
|
-
addPicked={x => {
|
|
384
|
-
let newValues = { ...selectedFieldsURL.value };
|
|
385
|
-
newValues[x] = true;
|
|
386
|
-
selectedFieldsURL.value = newValues;
|
|
387
|
-
}}
|
|
388
|
-
removePicked={x => {
|
|
389
|
-
let newValues = { ...selectedFieldsURL.value };
|
|
390
|
-
newValues[x] = false;
|
|
391
|
-
selectedFieldsURL.value = newValues;
|
|
392
|
-
}}
|
|
393
|
-
/>
|
|
394
|
-
<InputLabelURL
|
|
395
|
-
label="Use Relative Time"
|
|
396
|
-
checkbox
|
|
397
|
-
url={useRelativeTimeURL}
|
|
398
|
-
onChangeValue={() => {
|
|
399
|
-
Querysub.commit(() => {
|
|
400
|
-
this.state.datumsSeqNum++;
|
|
401
|
-
});
|
|
402
|
-
}}
|
|
403
|
-
/>
|
|
404
|
-
</div>
|
|
405
|
-
{this.datums.length === 0 && <div className={css.hsl(40, 50, 50).colorhsl(60, 50, 100).boldStyle.pad2(10).ellipsis}>
|
|
406
|
-
No logs matched, either increase the time range or decrease the filter specificity.
|
|
407
|
-
</div>}
|
|
408
|
-
{includedFiles.length > 0 && includedFiles[0] && <div className={css.hbox(20)}>
|
|
409
|
-
<div className={css.hbox(10)}>
|
|
410
|
-
<div className={css.fontSize(18).hsl(0, 50, 50).colorhsl(0, 50, 95).pad2(8, 4)}>
|
|
411
|
-
Only showing {includedFiles.length} files:
|
|
412
|
-
</div>
|
|
413
|
-
<Button onClick={() => {
|
|
414
|
-
filterParam2.value = "";
|
|
415
|
-
this.rerun();
|
|
416
|
-
}}>
|
|
417
|
-
Clear All Filters
|
|
418
|
-
</Button>
|
|
419
|
-
</div>
|
|
420
|
-
<div className={css.hbox(10, 10).wrap}>
|
|
421
|
-
{includedFiles.filter(x => x).map((fileName) => (
|
|
422
|
-
<div
|
|
423
|
-
key={fileName}
|
|
424
|
-
className={css.pad2(8, 4).hsl(120, 40, 80).bord2(120, 40, 60, 1).button}
|
|
425
|
-
onClick={() => {
|
|
426
|
-
let newFiles = includedFiles.filter(x => x !== fileName);
|
|
427
|
-
filterParam2.value = newFiles.join("|");
|
|
428
|
-
}}
|
|
429
|
-
title={`Click to remove ${fileName} from filter`}
|
|
430
|
-
>
|
|
431
|
-
{fileName} ✕
|
|
432
|
-
</div>
|
|
433
|
-
))}
|
|
434
|
-
</div>
|
|
435
|
-
</div>}
|
|
436
|
-
|
|
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);
|
|
443
|
-
|
|
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;
|
|
450
|
-
|
|
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>
|
|
530
|
-
<Table
|
|
531
|
-
rows={this.datums}
|
|
532
|
-
columns={columns}
|
|
533
|
-
lineLimit={4}
|
|
534
|
-
initialLimit={10}
|
|
535
|
-
characterLimit={400}
|
|
536
|
-
getRowAttributes={row => {
|
|
537
|
-
let hue = -1;
|
|
538
|
-
if (row.__LOG_TYPE === "warn") hue = 40;
|
|
539
|
-
if (row.__LOG_TYPE === "error") hue = 0;
|
|
540
|
-
if (row.__LOG_TYPE === "info") hue = 200;
|
|
541
|
-
return {
|
|
542
|
-
className: hue !== -1 && css.hsl(hue, 40, 50).colorhsl(hue, 0, 100) || undefined,
|
|
543
|
-
};
|
|
544
|
-
}}
|
|
545
|
-
/>
|
|
546
|
-
</>;
|
|
547
|
-
})()}
|
|
548
|
-
</FastArchiveViewer>
|
|
549
|
-
</div>
|
|
550
|
-
);
|
|
551
|
-
}
|
|
552
|
-
}
|