querysub 0.374.0 → 0.376.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/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/BufferIndex.ts +10 -5
- package/src/diagnostics/logs/IndexedLogs/BufferIndexCPP.cpp +20 -0
- package/src/diagnostics/logs/IndexedLogs/BufferIndexHelpers.ts +29 -2
- package/src/diagnostics/logs/IndexedLogs/BufferUnitIndex.ts +61 -20
- package/src/diagnostics/logs/IndexedLogs/BufferUnitSet.ts +2 -2
- package/src/diagnostics/logs/IndexedLogs/IndexedLogs.ts +7 -7
- package/src/diagnostics/logs/IndexedLogs/LogViewer3.tsx +250 -243
- package/src/diagnostics/logs/IndexedLogs/LogViewerParams.ts +21 -0
- package/src/diagnostics/logs/IndexedLogs/{bufferMatcher.ts → bufferSearchFindMatcher.ts} +9 -4
- package/src/diagnostics/logs/IndexedLogs/moveIndexLogsToPublic.ts +3 -3
- package/src/diagnostics/logs/diskLogger.ts +0 -38
- package/src/diagnostics/logs/errorNotifications2/errorNotifications2.ts +9 -0
- package/src/diagnostics/logs/injectFileLocationToConsole.ts +3 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycles.tsx +24 -22
- package/src/diagnostics/managementPages.tsx +0 -18
- 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,1090 +0,0 @@
|
|
|
1
|
-
import { measureFnc } from "socket-function/src/profiling/measure";
|
|
2
|
-
import { t } from "../../2-proxy/schema2";
|
|
3
|
-
import { qreact } from "../../4-dom/qreact";
|
|
4
|
-
import { DatumStats, FastArchiveAppendable } from "./FastArchiveAppendable";
|
|
5
|
-
import { Querysub } from "../../4-querysub/Querysub";
|
|
6
|
-
import { URLParam } from "../../library-components/URLParam";
|
|
7
|
-
import { TimeRangeSelector, getTimeRange } from "./TimeRangeSelector";
|
|
8
|
-
import { formatNumber, formatTime, formatVeryNiceDateTime, formatDateTime } from "socket-function/src/formatting/format";
|
|
9
|
-
import { list, sort, throttleFunction, timeInHour } from "socket-function/src/misc";
|
|
10
|
-
import { css } from "typesafecss";
|
|
11
|
-
import { logErrors } from "../../errors";
|
|
12
|
-
import { InputLabelURL } from "../../library-components/InputLabel";
|
|
13
|
-
import { clamp } from "../../misc";
|
|
14
|
-
import { ButtonSelector } from "../../library-components/ButtonSelector";
|
|
15
|
-
import { Button } from "../../library-components/Button";
|
|
16
|
-
import { lazy } from "socket-function/src/caching";
|
|
17
|
-
import { LOG_LIMIT_FLAG } from "./diskLogger";
|
|
18
|
-
import { MaybePromise, canHaveChildren } from "socket-function/src/types";
|
|
19
|
-
import { niceParse } from "../../niceStringify";
|
|
20
|
-
import { FileMetadata } from "./FastArchiveController";
|
|
21
|
-
import { throttleRender } from "../../functional/throttleRender";
|
|
22
|
-
|
|
23
|
-
const RENDER_INTERVAL = 1000;
|
|
24
|
-
|
|
25
|
-
const HISTOGRAM_RERENDER_INTERVAL = 10000;
|
|
26
|
-
|
|
27
|
-
export const filterParam = new URLParam("filter", "");
|
|
28
|
-
// If filter2 is set, we also need it. Not great (why not support arbitrary counts of these), but for now... this should be fine (and support arbitrary counts might slow down our incredibly optimized scan function, where most of our time is spent).
|
|
29
|
-
// NOTE: This supports less than filterParam (no object parsing, it only does text contains)
|
|
30
|
-
export const filterParam2 = new URLParam("filter2", "");
|
|
31
|
-
|
|
32
|
-
export const cacheBustParam = new URLParam("cacheBust", 0);
|
|
33
|
-
const caseInsensitiveParam = new URLParam("caseInsensitive", false);
|
|
34
|
-
const hideAllDataParam = new URLParam("hideAllData", false);
|
|
35
|
-
export type ScanFnc = (posStart: number, posEnd: number, data: Buffer) => boolean;
|
|
36
|
-
|
|
37
|
-
export class FastArchiveViewer<T> extends qreact.Component<{
|
|
38
|
-
fastArchives: () => FastArchiveAppendable<T>[];
|
|
39
|
-
forceGetPublic?: boolean;
|
|
40
|
-
runOnLoad?: boolean;
|
|
41
|
-
onStart: () => MaybePromise<void>;
|
|
42
|
-
getScanFnc?: () => ScanFnc;
|
|
43
|
-
getWantData?: (file: FileMetadata) => Promise<ScanFnc | undefined>;
|
|
44
|
-
onDatums: (source: FastArchiveAppendable<T>, datums: T[], metadata: FileMetadata) => void;
|
|
45
|
-
// Called after onData
|
|
46
|
-
onStats?: (source: FastArchiveAppendable<T>, stats: DatumStats, metadata: FileMetadata) => void;
|
|
47
|
-
onFinish?: () => void;
|
|
48
|
-
}> {
|
|
49
|
-
state = t.state({
|
|
50
|
-
runCount: t.atomic<number>(0),
|
|
51
|
-
// rootPath =>
|
|
52
|
-
fileMetadata: t.atomic<({
|
|
53
|
-
files: FileMetadata[];
|
|
54
|
-
createTime?: number;
|
|
55
|
-
} | undefined)[]>([]),
|
|
56
|
-
finished: t.atomic(true),
|
|
57
|
-
error: t.atomic<string | undefined>(undefined),
|
|
58
|
-
pendingSyncInitializations: t.atomic<number>(0),
|
|
59
|
-
|
|
60
|
-
// Current sync parameters - set before synchronizeData is called
|
|
61
|
-
currentSyncParams: t.atomic<{
|
|
62
|
-
filterString: string;
|
|
63
|
-
startTime: number;
|
|
64
|
-
endTime: number;
|
|
65
|
-
fastArchivePaths: string[];
|
|
66
|
-
} | undefined>(undefined),
|
|
67
|
-
|
|
68
|
-
renderSeqNum: t.atomic<number>(0),
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
// Histogram data arrays
|
|
72
|
-
private histogramAllDataCounts: Float64Array = new Float64Array(0);
|
|
73
|
-
private histogramSelectedDataCounts: Float64Array = new Float64Array(0);
|
|
74
|
-
private histogramAllDataSizes: Float64Array = new Float64Array(0);
|
|
75
|
-
private histogramSelectedDataSizes: Float64Array = new Float64Array(0);
|
|
76
|
-
private histogramStartTime: number = 0;
|
|
77
|
-
private histogramEndTime: number = 0;
|
|
78
|
-
private histogramBucketTime: number = timeInHour;
|
|
79
|
-
|
|
80
|
-
private allSize = 0;
|
|
81
|
-
private matchedSize = 0;
|
|
82
|
-
|
|
83
|
-
private limitedScanCount = 0;
|
|
84
|
-
private limitedMatchCount = 0;
|
|
85
|
-
|
|
86
|
-
private fileErrors: Array<{ file: FileMetadata; error: Error }> = [];
|
|
87
|
-
|
|
88
|
-
private progressBars: Record<string, {
|
|
89
|
-
section: string,
|
|
90
|
-
value: number,
|
|
91
|
-
max: number,
|
|
92
|
-
initialTime: number,
|
|
93
|
-
lastSetTime: number,
|
|
94
|
-
}> = {};
|
|
95
|
-
|
|
96
|
-
private lastRenderTime = 0;
|
|
97
|
-
|
|
98
|
-
private getHistogramBucketIndex(startTime: number): number {
|
|
99
|
-
const index = Math.floor((startTime - this.histogramStartTime) / this.histogramBucketTime);
|
|
100
|
-
return clamp(index, 0, this.histogramAllDataCounts.length - 1);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
componentWillMount(): void {
|
|
104
|
-
qreact.onDispose(() => {
|
|
105
|
-
this.cancelAllSynchronizes();
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
cancelAllSynchronizes() {
|
|
110
|
-
for (let fastArchive of this.props.fastArchives()) {
|
|
111
|
-
fastArchive.cancelAllSynchronizes();
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
private currentSequenceNumber = 0;
|
|
116
|
-
private latestSequenceNumber = 0;
|
|
117
|
-
|
|
118
|
-
@measureFnc
|
|
119
|
-
private async synchronizeData() {
|
|
120
|
-
console.log("synchronizeData");
|
|
121
|
-
let onStart = Querysub.fastRead(() => this.props.onStart);
|
|
122
|
-
let onDatums = Querysub.fastRead(() => this.props.onDatums);
|
|
123
|
-
let onStats = Querysub.fastRead(() => this.props.onStats);
|
|
124
|
-
let fastArchives = Querysub.fastRead(() => this.props.fastArchives);
|
|
125
|
-
let onFinish = Querysub.fastRead(() => this.props.onFinish);
|
|
126
|
-
let getWantData = Querysub.fastRead(() => this.props.getWantData);
|
|
127
|
-
// Increment sequence number for this new sync attempt
|
|
128
|
-
this.currentSequenceNumber++;
|
|
129
|
-
this.latestSequenceNumber = this.currentSequenceNumber;
|
|
130
|
-
const mySequenceNumber = this.currentSequenceNumber;
|
|
131
|
-
|
|
132
|
-
// Increment run count for each new run
|
|
133
|
-
Querysub.commit(() => {
|
|
134
|
-
this.state.runCount++;
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
// Helper function to check if this sequence number is still the latest
|
|
138
|
-
const isLatestSync = () => mySequenceNumber === this.latestSequenceNumber;
|
|
139
|
-
|
|
140
|
-
// Helper function to safely update state only if we're the latest sync
|
|
141
|
-
const ifLatest = (updater: () => void) => {
|
|
142
|
-
if (isLatestSync()) {
|
|
143
|
-
Querysub.commit(updater);
|
|
144
|
-
} else {
|
|
145
|
-
console.log(`Ignoring state update from sequence ${mySequenceNumber}, latest is ${this.latestSequenceNumber}`);
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
// Increment sync count before making the call
|
|
150
|
-
Querysub.commit(() => {
|
|
151
|
-
this.state.pendingSyncInitializations++;
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const timeRange = getTimeRange();
|
|
156
|
-
let filterString = filterParam.value;
|
|
157
|
-
let filterString2 = filterParam2.value;
|
|
158
|
-
let scannedValueCount = 0;
|
|
159
|
-
|
|
160
|
-
// Store current sync parameters BEFORE calling synchronizeData
|
|
161
|
-
ifLatest(() => {
|
|
162
|
-
this.state.currentSyncParams = {
|
|
163
|
-
filterString,
|
|
164
|
-
startTime: timeRange.startTime,
|
|
165
|
-
endTime: timeRange.endTime,
|
|
166
|
-
fastArchivePaths: fastArchives().map(archive => archive.rootPath),
|
|
167
|
-
};
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
ifLatest(() => {
|
|
171
|
-
this.state.error = undefined;
|
|
172
|
-
this.state.finished = false;
|
|
173
|
-
|
|
174
|
-
// Clear existing progress bars
|
|
175
|
-
for (let key of Object.keys(this.progressBars)) {
|
|
176
|
-
delete this.progressBars[key];
|
|
177
|
-
}
|
|
178
|
-
this.state.fileMetadata = [];
|
|
179
|
-
this.fileErrors = [];
|
|
180
|
-
this.limitedScanCount = 0;
|
|
181
|
-
this.limitedMatchCount = 0;
|
|
182
|
-
this.allSize = 0;
|
|
183
|
-
this.matchedSize = 0;
|
|
184
|
-
|
|
185
|
-
this.histogramBucketTime = Math.max(timeInHour, (timeRange.endTime - timeRange.startTime) / 100);
|
|
186
|
-
|
|
187
|
-
this.histogramStartTime = timeRange.startTime - this.histogramBucketTime * 2;
|
|
188
|
-
this.histogramEndTime = timeRange.endTime + this.histogramBucketTime * 2;
|
|
189
|
-
const bucketCount = clamp(Math.ceil((this.histogramEndTime - this.histogramStartTime) / this.histogramBucketTime), 1, 10000);
|
|
190
|
-
|
|
191
|
-
this.histogramAllDataCounts = new Float64Array(bucketCount);
|
|
192
|
-
this.histogramSelectedDataCounts = new Float64Array(bucketCount);
|
|
193
|
-
this.histogramAllDataSizes = new Float64Array(bucketCount);
|
|
194
|
-
this.histogramSelectedDataSizes = new Float64Array(bucketCount);
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
const onProgress = (progress: { section: string; value: number; max: number }) => {
|
|
198
|
-
if (!isLatestSync()) return;
|
|
199
|
-
const key = progress.section;
|
|
200
|
-
|
|
201
|
-
// Add or update progress bar
|
|
202
|
-
let existingBar = this.progressBars[key];
|
|
203
|
-
this.progressBars[key] = {
|
|
204
|
-
section: progress.section,
|
|
205
|
-
value: progress.value,
|
|
206
|
-
max: progress.max,
|
|
207
|
-
initialTime: existingBar?.initialTime || Date.now(),
|
|
208
|
-
lastSetTime: Date.now(),
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
let now = Date.now();
|
|
212
|
-
if (now - this.lastRenderTime > RENDER_INTERVAL) {
|
|
213
|
-
this.lastRenderTime = now;
|
|
214
|
-
ifLatest(() => {
|
|
215
|
-
this.state.renderSeqNum++;
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
try {
|
|
220
|
-
await onStart();
|
|
221
|
-
|
|
222
|
-
const caseInsensitive = Querysub.fastRead(() => caseInsensitiveParam.value);
|
|
223
|
-
let caseInsensitiveMapping = new Uint8Array(256);
|
|
224
|
-
for (let i = 0; i < 256; i++) {
|
|
225
|
-
caseInsensitiveMapping[i] = String.fromCharCode(i).toLowerCase().charCodeAt(0);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
type ScanMatch = (posStart: number, posEnd: number, buffer: Buffer) => boolean;
|
|
229
|
-
type DatumMatch = (datum: unknown) => boolean;
|
|
230
|
-
type MatchObj = {
|
|
231
|
-
scanMatch: ScanMatch,
|
|
232
|
-
datumMatch: DatumMatch,
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
function parsePart(filterString: string): MatchObj {
|
|
236
|
-
if (!filterString.trim()) {
|
|
237
|
-
return {
|
|
238
|
-
scanMatch: () => true,
|
|
239
|
-
datumMatch: () => true,
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
let datumMatch = (datum: unknown) => true;
|
|
243
|
-
if (filterString.includes("=")) {
|
|
244
|
-
let equalIndex = filterString.indexOf("=");
|
|
245
|
-
let key = filterString.slice(0, equalIndex).trim();
|
|
246
|
-
let value = niceParse(filterString.slice(equalIndex + 1).trim());
|
|
247
|
-
filterString = `${JSON.stringify(key)}:${JSON.stringify(value)}`;
|
|
248
|
-
datumMatch = (datum: unknown) => {
|
|
249
|
-
if (!canHaveChildren(datum)) return false;
|
|
250
|
-
let curKey = key;
|
|
251
|
-
if (!(curKey in datum)) {
|
|
252
|
-
curKey = curKey.toLowerCase();
|
|
253
|
-
let keys = Object.keys(datum);
|
|
254
|
-
curKey = keys.find(x => x.toLowerCase() === curKey) || "";
|
|
255
|
-
if (!curKey) return false;
|
|
256
|
-
}
|
|
257
|
-
return datum[curKey] === value;
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
if (caseInsensitive) {
|
|
261
|
-
filterString = filterString.toLowerCase();
|
|
262
|
-
}
|
|
263
|
-
const filterBuffer = Buffer.from(filterString);
|
|
264
|
-
const char0 = filterBuffer[0];
|
|
265
|
-
return {
|
|
266
|
-
scanMatch: (posStart: number, posEnd: number, buffer: Buffer) => {
|
|
267
|
-
for (let i = posStart; i < posEnd; i++) {
|
|
268
|
-
// NOTE: This is slow for repeated prefixes, but... in practice, even if we have prefixes, they won't repeat, so... this should be quite fast.
|
|
269
|
-
let ch = buffer[i];
|
|
270
|
-
if (caseInsensitive) {
|
|
271
|
-
ch = caseInsensitiveMapping[ch];
|
|
272
|
-
}
|
|
273
|
-
if (ch === char0) {
|
|
274
|
-
if (filterBuffer.length === 1) return true;
|
|
275
|
-
for (let j = 1; j < filterBuffer.length; j++) {
|
|
276
|
-
let ch2 = buffer[i + j];
|
|
277
|
-
if (caseInsensitive) {
|
|
278
|
-
ch2 = caseInsensitiveMapping[ch2];
|
|
279
|
-
}
|
|
280
|
-
if (ch2 !== filterBuffer[j]) {
|
|
281
|
-
break;
|
|
282
|
-
}
|
|
283
|
-
if (j === filterBuffer.length - 1) {
|
|
284
|
-
return true;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
return false;
|
|
290
|
-
},
|
|
291
|
-
datumMatch,
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
function joinMatches(matches: MatchObj[], condition: "||" | "&&"): MatchObj {
|
|
296
|
-
if (condition === "||") {
|
|
297
|
-
return {
|
|
298
|
-
scanMatch: (posStart: number, posEnd: number, buffer: Buffer) => {
|
|
299
|
-
return matches.some(match => match.scanMatch(posStart, posEnd, buffer));
|
|
300
|
-
},
|
|
301
|
-
datumMatch: (datum: unknown) => {
|
|
302
|
-
return matches.some(match => match.datumMatch(datum));
|
|
303
|
-
},
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
return {
|
|
307
|
-
scanMatch: (posStart: number, posEnd: number, buffer: Buffer) => {
|
|
308
|
-
return matches.every(match => match.scanMatch(posStart, posEnd, buffer));
|
|
309
|
-
},
|
|
310
|
-
datumMatch: (datum: unknown) => {
|
|
311
|
-
return matches.every(match => match.datumMatch(datum));
|
|
312
|
-
},
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
let andMatches = filterString.split("|").map(orParts => {
|
|
318
|
-
let andParts = orParts.split("&");
|
|
319
|
-
let andMatches = andParts.map(part => parsePart(part.trim()));
|
|
320
|
-
return joinMatches(andMatches, "&&");
|
|
321
|
-
});
|
|
322
|
-
let { scanMatch, datumMatch } = joinMatches(andMatches, "||");
|
|
323
|
-
|
|
324
|
-
let andMatches2 = filterString2.split("|").map(orParts => {
|
|
325
|
-
let andParts = orParts.split("&");
|
|
326
|
-
let andMatches = andParts.map(part => parsePart(part.trim()));
|
|
327
|
-
return joinMatches(andMatches, "&&");
|
|
328
|
-
});
|
|
329
|
-
let scanObj2 = joinMatches(andMatches2, "||");
|
|
330
|
-
let scanMatch2 = scanObj2.scanMatch;
|
|
331
|
-
let datumMatch2 = scanObj2.datumMatch;
|
|
332
|
-
let useScan2 = !!filterString2.trim();
|
|
333
|
-
|
|
334
|
-
const limitedBuffer = Buffer.from(LOG_LIMIT_FLAG);
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
await Promise.all(fastArchives().map(async (fastArchive, index) => {
|
|
338
|
-
const result = await fastArchive.synchronizeData({
|
|
339
|
-
range: timeRange,
|
|
340
|
-
cacheBust: Querysub.fastRead(() => cacheBustParam.value),
|
|
341
|
-
scanFnc: Querysub.fastRead(() => this.props.getScanFnc?.()),
|
|
342
|
-
getWantData: async (file) => {
|
|
343
|
-
let wantData = await getWantData?.(file);
|
|
344
|
-
return (posStart: number, posEnd: number, data: Buffer) => {
|
|
345
|
-
let isLimited = false;
|
|
346
|
-
for (let i = posStart; i < posEnd && !isLimited; i++) {
|
|
347
|
-
if (data[i] === limitedBuffer[0]) {
|
|
348
|
-
for (let j = 1; j < limitedBuffer.length; j++) {
|
|
349
|
-
if (data[i + j] !== limitedBuffer[j]) {
|
|
350
|
-
break;
|
|
351
|
-
}
|
|
352
|
-
if (j === limitedBuffer.length - 1) {
|
|
353
|
-
isLimited = true;
|
|
354
|
-
break;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
if (isLimited) {
|
|
360
|
-
this.limitedScanCount++;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// scanMatch is faster than wantData (generally), so run it first
|
|
364
|
-
let matched = scanMatch(posStart, posEnd, data);
|
|
365
|
-
if (matched && useScan2) {
|
|
366
|
-
matched = scanMatch2(posStart, posEnd, data);
|
|
367
|
-
}
|
|
368
|
-
if (matched && wantData) {
|
|
369
|
-
matched = wantData(posStart, posEnd, data);
|
|
370
|
-
}
|
|
371
|
-
if (isLimited && matched) {
|
|
372
|
-
this.limitedMatchCount++;
|
|
373
|
-
}
|
|
374
|
-
return matched;
|
|
375
|
-
};
|
|
376
|
-
},
|
|
377
|
-
onData: (datums, file) => {
|
|
378
|
-
if (!isLatestSync()) return;
|
|
379
|
-
let keptDatums: T[] = [];
|
|
380
|
-
for (let i = 0; i < datums.length; i++) {
|
|
381
|
-
let datum = datums[i];
|
|
382
|
-
if (datumMatch(datum)) {
|
|
383
|
-
keptDatums.push(datum);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
onDatums(fastArchive, keptDatums, file);
|
|
387
|
-
},
|
|
388
|
-
onStats: onStats && ((stats, file) => {
|
|
389
|
-
if (!isLatestSync()) return;
|
|
390
|
-
|
|
391
|
-
onStats!(fastArchive, stats, file);
|
|
392
|
-
this.allSize += stats.matchedSize + stats.notMatchedSize;
|
|
393
|
-
this.matchedSize += stats.matchedSize;
|
|
394
|
-
// Update scanned count and sequence number for rendering
|
|
395
|
-
scannedValueCount += stats.notMatchedCount + stats.matchedCount;
|
|
396
|
-
// Update all data histogram
|
|
397
|
-
const bucketIndex = this.getHistogramBucketIndex(file.startTime);
|
|
398
|
-
this.histogramAllDataCounts[bucketIndex] += stats.notMatchedCount + stats.matchedCount;
|
|
399
|
-
this.histogramSelectedDataCounts[bucketIndex] += stats.matchedCount;
|
|
400
|
-
this.histogramAllDataSizes[bucketIndex] += stats.matchedSize + stats.notMatchedSize;
|
|
401
|
-
this.histogramSelectedDataSizes[bucketIndex] += stats.matchedSize;
|
|
402
|
-
if (scannedValueCount >= HISTOGRAM_RERENDER_INTERVAL) {
|
|
403
|
-
scannedValueCount = 0;
|
|
404
|
-
let now = Date.now();
|
|
405
|
-
if (now - this.lastRenderTime > RENDER_INTERVAL) {
|
|
406
|
-
this.lastRenderTime = now;
|
|
407
|
-
ifLatest(() => {
|
|
408
|
-
this.state.renderSeqNum++;
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
}),
|
|
413
|
-
onError: (error, file) => {
|
|
414
|
-
if (!isLatestSync()) return;
|
|
415
|
-
this.fileErrors.push({ file, error });
|
|
416
|
-
},
|
|
417
|
-
onProgress,
|
|
418
|
-
onFinish: () => {
|
|
419
|
-
ifLatest(() => {
|
|
420
|
-
this.state.renderSeqNum++;
|
|
421
|
-
});
|
|
422
|
-
setTimeout(() => {
|
|
423
|
-
ifLatest(() => {
|
|
424
|
-
this.state.finished = true;
|
|
425
|
-
});
|
|
426
|
-
}, 2500);
|
|
427
|
-
onFinish?.();
|
|
428
|
-
},
|
|
429
|
-
});
|
|
430
|
-
if (result !== "cancelled") {
|
|
431
|
-
ifLatest(() => {
|
|
432
|
-
this.state.fileMetadata[index] = result.metadata;
|
|
433
|
-
});
|
|
434
|
-
}
|
|
435
|
-
return result;
|
|
436
|
-
}));
|
|
437
|
-
|
|
438
|
-
} catch (error: any) {
|
|
439
|
-
ifLatest(() => {
|
|
440
|
-
this.state.error = error.stack || String(error);
|
|
441
|
-
});
|
|
442
|
-
} finally {
|
|
443
|
-
// Decrement sync count when call finishes (regardless of sequence number)
|
|
444
|
-
Querysub.commit(() => {
|
|
445
|
-
this.state.pendingSyncInitializations--;
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
private renderProgressBars() {
|
|
450
|
-
let progressEntries = Object.entries(this.progressBars);
|
|
451
|
-
|
|
452
|
-
if (progressEntries.length === 0) {
|
|
453
|
-
return null;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
const getNowTime = lazy(() => Querysub.timeDelayed(1000));
|
|
457
|
-
if (progressEntries.some(x => x[0].startsWith("Error"))) {
|
|
458
|
-
progressEntries = progressEntries.filter(x => x[0].startsWith("Error"));
|
|
459
|
-
}
|
|
460
|
-
sort(progressEntries, x => x[1].initialTime);
|
|
461
|
-
|
|
462
|
-
// Group progress bars by the text after the pipe sign
|
|
463
|
-
const groupedProgress = new Map<string, {
|
|
464
|
-
displayName: string;
|
|
465
|
-
entries: Array<[string, { section: string, value: number, max: number, initialTime: number, lastSetTime: number }]>;
|
|
466
|
-
totalValue: number;
|
|
467
|
-
totalMax: number;
|
|
468
|
-
earliestTime: number;
|
|
469
|
-
latestTime: number;
|
|
470
|
-
}>();
|
|
471
|
-
|
|
472
|
-
for (const [key, progress] of progressEntries) {
|
|
473
|
-
const pipeIndex = progress.section.indexOf("|");
|
|
474
|
-
const displayName = pipeIndex >= 0 ? progress.section.substring(pipeIndex + 1) : progress.section;
|
|
475
|
-
|
|
476
|
-
if (!groupedProgress.has(displayName)) {
|
|
477
|
-
groupedProgress.set(displayName, {
|
|
478
|
-
displayName,
|
|
479
|
-
entries: [],
|
|
480
|
-
totalValue: 0,
|
|
481
|
-
totalMax: 0,
|
|
482
|
-
earliestTime: progress.initialTime,
|
|
483
|
-
latestTime: progress.lastSetTime,
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
const group = groupedProgress.get(displayName)!;
|
|
488
|
-
group.entries.push([key, progress]);
|
|
489
|
-
group.totalValue += progress.value;
|
|
490
|
-
group.totalMax += progress.max;
|
|
491
|
-
group.earliestTime = Math.min(group.earliestTime, progress.initialTime);
|
|
492
|
-
group.latestTime = Math.max(group.latestTime, progress.lastSetTime);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
return (
|
|
496
|
-
<div className={css.hbox(10).wrap.alignItems("start").fillWidth}>
|
|
497
|
-
{!this.state.finished && (
|
|
498
|
-
<div
|
|
499
|
-
onClick={() => {
|
|
500
|
-
this.cancelAllSynchronizes();
|
|
501
|
-
}}
|
|
502
|
-
className={css.pad2(12).bord2(200, 50, 50).hsl(200, 50, 95).colorhsl(200, 50, 40).button}
|
|
503
|
-
>
|
|
504
|
-
Cancel
|
|
505
|
-
</div>
|
|
506
|
-
)}
|
|
507
|
-
{Array.from(groupedProgress.values()).map((group) => {
|
|
508
|
-
const fraction = clamp(group.totalMax > 0 ? (group.totalValue / group.totalMax) * 1 : 1, 0, 1);
|
|
509
|
-
let progressTime = 0;
|
|
510
|
-
if (group.totalValue >= group.totalMax) {
|
|
511
|
-
progressTime = group.latestTime;
|
|
512
|
-
} else {
|
|
513
|
-
progressTime = getNowTime();
|
|
514
|
-
}
|
|
515
|
-
const elapsedTime = progressTime - group.earliestTime;
|
|
516
|
-
|
|
517
|
-
// Create tooltip showing breakdown by original prefix
|
|
518
|
-
const tooltipContent = group.entries.map(([key, progress]) => {
|
|
519
|
-
const pipeIndex = progress.section.indexOf("|");
|
|
520
|
-
const prefix = pipeIndex >= 0 ? progress.section.substring(0, pipeIndex) : key;
|
|
521
|
-
return `${prefix}: ${formatNumber(progress.value)}/${formatNumber(progress.max)}`;
|
|
522
|
-
}).join("\n");
|
|
523
|
-
|
|
524
|
-
return (
|
|
525
|
-
<div key={group.displayName} className={
|
|
526
|
-
// NOTE: There is no point making this proportionally to the time, because we stream the data, so the time overlaps, making everything take most of the time.
|
|
527
|
-
css.vbox(4)
|
|
528
|
-
}
|
|
529
|
-
title={tooltipContent}
|
|
530
|
-
>
|
|
531
|
-
<div className={css.vbox(2)}>
|
|
532
|
-
<div className={css.fontSize(14).colorhsl(0, 0, 20)}>
|
|
533
|
-
{group.displayName}
|
|
534
|
-
</div>
|
|
535
|
-
<div className={css.fontSize(12).colorhsl(0, 0, 40).width(150)}>
|
|
536
|
-
{formatNumber(group.totalValue)} {group.totalValue < group.totalMax && `/ ${formatNumber(group.totalMax)}`} ({formatTime(elapsedTime)})
|
|
537
|
-
</div>
|
|
538
|
-
</div>
|
|
539
|
-
<div className={css.fillWidth.height(8).bord2(200, 20, 80).hsl(200, 10, 95)}>
|
|
540
|
-
<div
|
|
541
|
-
className={css.height(8).hsl(200, 50, 70).width(`${fraction * 100}%`).transition("width 200ms")}
|
|
542
|
-
/>
|
|
543
|
-
</div>
|
|
544
|
-
</div>
|
|
545
|
-
);
|
|
546
|
-
})}
|
|
547
|
-
</div>
|
|
548
|
-
);
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
private renderHistogram() {
|
|
552
|
-
this.state.renderSeqNum;
|
|
553
|
-
|
|
554
|
-
if (this.histogramAllDataCounts.length === 0) {
|
|
555
|
-
return null;
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
let countsArray = Array.from(this.histogramAllDataCounts);
|
|
559
|
-
let selectedArray = Array.from(this.histogramSelectedDataCounts);
|
|
560
|
-
const maxAllCount = countsArray.reduce((a, b) => Math.max(a, b), 0);
|
|
561
|
-
const maxSelectedCount = selectedArray.reduce((a, b) => Math.max(a, b), 0);
|
|
562
|
-
const maxCountForScale = hideAllDataParam.value ? maxSelectedCount : maxAllCount;
|
|
563
|
-
const selected = css.hsla(120, 40, 50, 1);
|
|
564
|
-
const all = css.hsla(200, 40, 70, 1);
|
|
565
|
-
|
|
566
|
-
return (
|
|
567
|
-
<div className={css.vbox(10).fillWidth}>
|
|
568
|
-
<div
|
|
569
|
-
className={css.relative.fillWidth.height(100).bord2(0, 0, 40).hbox0.overflowHidden}
|
|
570
|
-
>
|
|
571
|
-
{Array.from(this.histogramAllDataCounts).map((count, index) => {
|
|
572
|
-
const selectedCount = this.histogramSelectedDataCounts[index] || 0;
|
|
573
|
-
const allSize = this.histogramAllDataSizes[index] || 0;
|
|
574
|
-
const selectedSize = this.histogramSelectedDataSizes[index] || 0;
|
|
575
|
-
return (
|
|
576
|
-
<div
|
|
577
|
-
key={index}
|
|
578
|
-
className={css.relative.fillHeight.fillWidth.filter("brightness(1.1)", "hover")}
|
|
579
|
-
title={`${formatNumber(selectedCount)} filtered (${formatNumber(selectedSize)}B)\n${formatNumber(count)} all (${formatNumber(allSize)}B)\n${formatVeryNiceDateTime(this.histogramStartTime + index * this.histogramBucketTime)}`}
|
|
580
|
-
>
|
|
581
|
-
{count > 0 && !hideAllDataParam.value && <div
|
|
582
|
-
className={
|
|
583
|
-
all.absolute.bottom(0).left(0).right(1)
|
|
584
|
-
.transition("height 200ms")
|
|
585
|
-
.height(`calc(max(3px, ${(count / maxCountForScale) * 100}%))`)
|
|
586
|
-
}
|
|
587
|
-
/>}
|
|
588
|
-
|
|
589
|
-
{selectedCount > 0 && <div
|
|
590
|
-
className={
|
|
591
|
-
selected.absolute.bottom(0).left(0).right(1)
|
|
592
|
-
.transition("height 200ms")
|
|
593
|
-
.height(`calc(max(3px, ${(selectedCount / maxCountForScale) * 100}%))`)
|
|
594
|
-
}
|
|
595
|
-
/>}
|
|
596
|
-
</div>
|
|
597
|
-
);
|
|
598
|
-
})}
|
|
599
|
-
</div>
|
|
600
|
-
<div className={css.hbox(20).fontSize(12)}>
|
|
601
|
-
{!hideAllDataParam.value && (
|
|
602
|
-
<div className={css.hbox(5)}>
|
|
603
|
-
<div className={all.size(16, 16)} />
|
|
604
|
-
<span>All data (max {formatNumber(maxAllCount)} | {formatNumber(countsArray.reduce((a, b) => a + b, 0))} | {formatNumber(this.allSize)}B)</span>
|
|
605
|
-
</div>
|
|
606
|
-
)}
|
|
607
|
-
<div className={css.hbox(5)}>
|
|
608
|
-
<div className={selected.size(16, 16)} />
|
|
609
|
-
<span>Filtered data (max {formatNumber(maxSelectedCount)} | {formatNumber(selectedArray.reduce((a, b) => a + b, 0))} | {formatNumber(this.matchedSize)}B)</span>
|
|
610
|
-
</div>
|
|
611
|
-
<InputLabelURL
|
|
612
|
-
label="Only show filtered data"
|
|
613
|
-
checkbox
|
|
614
|
-
url={hideAllDataParam}
|
|
615
|
-
flavor="small"
|
|
616
|
-
/>
|
|
617
|
-
</div>
|
|
618
|
-
</div>
|
|
619
|
-
);
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
private getOutdatedInfo(): string[] {
|
|
623
|
-
const currentTimeRange = getTimeRange();
|
|
624
|
-
const currentFilterString = filterParam.value;
|
|
625
|
-
const currentArchivePaths = this.props.fastArchives().map(archive => archive.rootPath);
|
|
626
|
-
|
|
627
|
-
// Get stored sync parameters
|
|
628
|
-
const storedParams = this.state.currentSyncParams;
|
|
629
|
-
|
|
630
|
-
// If we have no stored params yet, not outdated (first run or loading)
|
|
631
|
-
if (!storedParams) {
|
|
632
|
-
return [];
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
let warnings: string[] = [];
|
|
636
|
-
|
|
637
|
-
// Check for parameter differences
|
|
638
|
-
if (storedParams.filterString !== currentFilterString) {
|
|
639
|
-
warnings.push("filter");
|
|
640
|
-
}
|
|
641
|
-
if (storedParams.startTime !== currentTimeRange.startTime) {
|
|
642
|
-
warnings.push("start time");
|
|
643
|
-
}
|
|
644
|
-
if (storedParams.endTime !== currentTimeRange.endTime) {
|
|
645
|
-
warnings.push("end time");
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
// Check for missing and extra archives
|
|
649
|
-
const currentArchivePathsSet = new Set(currentArchivePaths);
|
|
650
|
-
const storedArchivePathsSet = new Set(storedParams.fastArchivePaths);
|
|
651
|
-
|
|
652
|
-
for (let storedPath of storedParams.fastArchivePaths) {
|
|
653
|
-
if (!currentArchivePathsSet.has(storedPath)) {
|
|
654
|
-
warnings.push(`Missing ${storedPath}`);
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
for (let currentPath of currentArchivePaths) {
|
|
658
|
-
if (!storedArchivePathsSet.has(currentPath)) {
|
|
659
|
-
warnings.push(`Extra ${currentPath}`);
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
return warnings;
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
public synchronizeDataThrottled = throttleFunction(500, () => {
|
|
667
|
-
Querysub.onCommitFinished(() => {
|
|
668
|
-
logErrors(this.synchronizeData());
|
|
669
|
-
});
|
|
670
|
-
});
|
|
671
|
-
render() {
|
|
672
|
-
if (throttleRender({ key: "FastArchiveViewer", frameDelay: 30 })) return undefined;
|
|
673
|
-
|
|
674
|
-
let totalFileCount = 0;
|
|
675
|
-
let totalBackblazeByteCount = 0;
|
|
676
|
-
let totalLocalByteCount = 0;
|
|
677
|
-
let totalLocalFileCount = 0;
|
|
678
|
-
|
|
679
|
-
let readLocalTimes: number[] = [];
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
for (let fileMetadata of this.state.fileMetadata) {
|
|
683
|
-
if (!fileMetadata) continue;
|
|
684
|
-
totalFileCount += fileMetadata.files.length;
|
|
685
|
-
let backblazeFiles = fileMetadata.files.filter(file => !file.nodeId);
|
|
686
|
-
let localFiles = fileMetadata.files.filter(file => file.nodeId);
|
|
687
|
-
if (localFiles.length > 0) {
|
|
688
|
-
readLocalTimes.push(fileMetadata.createTime || 0);
|
|
689
|
-
}
|
|
690
|
-
totalBackblazeByteCount += backblazeFiles.reduce((acc, file) => acc + file.size, 0);
|
|
691
|
-
totalLocalByteCount += localFiles.reduce((acc, file) => acc + file.size, 0);
|
|
692
|
-
totalLocalFileCount += localFiles.length;
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
const infoDisplay = (hue: number) => css.pad2(12).bord2(hue, 50, 50).hsl(hue, 50, 95).colorhsl(hue, 50, 40);
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
return (
|
|
700
|
-
<div className={css.vbox(20).pad2(20).fillBoth}>
|
|
701
|
-
<div className={css.hbox(20).fillWidth}>
|
|
702
|
-
<TimeRangeSelector />
|
|
703
|
-
<div className={infoDisplay(280)}>
|
|
704
|
-
TODO: Filtering for machineId / threadId, if we find the log loading is too slow
|
|
705
|
-
</div>
|
|
706
|
-
</div>
|
|
707
|
-
|
|
708
|
-
<div className={css.vbox(12).fillWidth.paddingBottom(20)}>
|
|
709
|
-
<InputLabelURL
|
|
710
|
-
label={
|
|
711
|
-
<div className={css.hbox(10)}>
|
|
712
|
-
<Button onClick={() => {
|
|
713
|
-
void this.synchronizeDataThrottled();
|
|
714
|
-
}}>
|
|
715
|
-
Run
|
|
716
|
-
</Button>
|
|
717
|
-
<span>Filter</span>
|
|
718
|
-
</div>
|
|
719
|
-
}
|
|
720
|
-
url={filterParam}
|
|
721
|
-
hot
|
|
722
|
-
flavor="large"
|
|
723
|
-
fillWidth
|
|
724
|
-
onKeyUp={this.synchronizeDataThrottled}
|
|
725
|
-
ref2={() => {
|
|
726
|
-
if (this.props.runOnLoad) {
|
|
727
|
-
void this.synchronizeDataThrottled();
|
|
728
|
-
}
|
|
729
|
-
}}
|
|
730
|
-
noEnterKeyBlur
|
|
731
|
-
placeholder="Filter terms, ex x | y & z"
|
|
732
|
-
/>
|
|
733
|
-
<InputLabelURL
|
|
734
|
-
label="Case Insensitive"
|
|
735
|
-
checkbox
|
|
736
|
-
url={caseInsensitiveParam}
|
|
737
|
-
onChange={() => {
|
|
738
|
-
void this.synchronizeDataThrottled();
|
|
739
|
-
}}
|
|
740
|
-
flavor="small"
|
|
741
|
-
/>
|
|
742
|
-
<div className={css.vbox(10)}>
|
|
743
|
-
{this.state.runCount > 0 && (() => {
|
|
744
|
-
const outdatedWarnings = this.getOutdatedInfo();
|
|
745
|
-
const errorCount = this.fileErrors.length;
|
|
746
|
-
|
|
747
|
-
// Group errors by file path
|
|
748
|
-
const errorsByFile = new Map<string, number>();
|
|
749
|
-
for (let errorInfo of this.fileErrors) {
|
|
750
|
-
const path = errorInfo.file.path;
|
|
751
|
-
errorsByFile.set(path, (errorsByFile.get(path) || 0) + 1);
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
const errorTooltip = Array.from(errorsByFile.entries())
|
|
755
|
-
.map(([path, count]) => `${path}: ${count} errors`)
|
|
756
|
-
.join("\n");
|
|
757
|
-
|
|
758
|
-
return (
|
|
759
|
-
<div className={css.hbox(10).wrap}>
|
|
760
|
-
<div
|
|
761
|
-
className={infoDisplay(120)}
|
|
762
|
-
title={this.state.fileMetadata.map(x => x?.files || []).flat().map(x =>
|
|
763
|
-
`${x.path} (${formatNumber(x.size)})`
|
|
764
|
-
).join("\n")}
|
|
765
|
-
>
|
|
766
|
-
File count: {formatNumber(totalFileCount)}{errorCount > 0 && ` (${errorCount} erred)`}, Backblaze size: {formatNumber(totalBackblazeByteCount)}B (compressed), Disk size: {formatNumber(totalLocalByteCount)}B (uncompressed)
|
|
767
|
-
</div>
|
|
768
|
-
{errorCount > 0 && (
|
|
769
|
-
<div
|
|
770
|
-
className={infoDisplay(45)}
|
|
771
|
-
title={errorTooltip}
|
|
772
|
-
>
|
|
773
|
-
{errorCount} file{errorCount > 1 ? "s" : ""} failed to load
|
|
774
|
-
</div>
|
|
775
|
-
)}
|
|
776
|
-
{outdatedWarnings.length > 0 && (
|
|
777
|
-
<div
|
|
778
|
-
className={infoDisplay(30).button}
|
|
779
|
-
onClick={() => {
|
|
780
|
-
void this.synchronizeDataThrottled();
|
|
781
|
-
}}
|
|
782
|
-
title={outdatedWarnings.join(", ")}
|
|
783
|
-
>
|
|
784
|
-
<div className={css.vbox(4)}>
|
|
785
|
-
<div className={css.boldStyle}>Search parameters outdated - Click to run updated search. Outdated:</div>
|
|
786
|
-
<div className={css.fontSize(12)}>
|
|
787
|
-
{outdatedWarnings.join(" | ")}
|
|
788
|
-
</div>
|
|
789
|
-
</div>
|
|
790
|
-
</div>
|
|
791
|
-
)}
|
|
792
|
-
</div>
|
|
793
|
-
);
|
|
794
|
-
})()}
|
|
795
|
-
{this.state.runCount === 0 && (
|
|
796
|
-
<div className={infoDisplay(200).button} onClick={() => {
|
|
797
|
-
void this.synchronizeDataThrottled();
|
|
798
|
-
}}>
|
|
799
|
-
No data downloaded yet. Click here to download data.
|
|
800
|
-
</div>
|
|
801
|
-
)}
|
|
802
|
-
{this.state.finished && (() => {
|
|
803
|
-
|
|
804
|
-
const timeRange = getTimeRange();
|
|
805
|
-
if (timeRange.endTime && readLocalTimes.length === 0) return null;
|
|
806
|
-
const earliestTime = Math.min(...readLocalTimes);
|
|
807
|
-
|
|
808
|
-
if (earliestTime >= timeRange.endTime) return null;
|
|
809
|
-
|
|
810
|
-
return (
|
|
811
|
-
<div
|
|
812
|
-
className={infoDisplay(0).button}
|
|
813
|
-
onClick={() => {
|
|
814
|
-
Querysub.commit(() => {
|
|
815
|
-
cacheBustParam.value = Date.now();
|
|
816
|
-
});
|
|
817
|
-
void this.synchronizeDataThrottled();
|
|
818
|
-
}}
|
|
819
|
-
>
|
|
820
|
-
<div className={css.hbox(8).alignItems("center").wrap}>
|
|
821
|
-
<span>Future data has been read</span>
|
|
822
|
-
<span className={css.fontSize(18).boldStyle}>{formatTime(timeRange.endTime - earliestTime)} into the future</span>
|
|
823
|
-
<span>({formatDateTime(earliestTime)})</span>
|
|
824
|
-
<span>Clear here to load possibly missing data data.</span>
|
|
825
|
-
</div>
|
|
826
|
-
</div>
|
|
827
|
-
);
|
|
828
|
-
})()}
|
|
829
|
-
{!this.state.finished && <LoaderAurora />}
|
|
830
|
-
{this.limitedScanCount > 0 && (
|
|
831
|
-
<div className={infoDisplay(60).boldStyle.button}
|
|
832
|
-
onClick={() => {
|
|
833
|
-
filterParam.value = LOG_LIMIT_FLAG;
|
|
834
|
-
void this.synchronizeDataThrottled();
|
|
835
|
-
}}
|
|
836
|
-
>
|
|
837
|
-
Click here to see {formatNumber(this.limitedScanCount)} scanned logs were rate limited at a file level. This means lines might be missing. If this happens a lot, use pass {`{ [LOG_LINE_LIMIT_ID]: "INSERT RANDOM GUID HERE" }`} in the error/warn message to limit only the spamming line.
|
|
838
|
-
</div>
|
|
839
|
-
)}
|
|
840
|
-
{this.limitedMatchCount > 0 && filterParam.value && (
|
|
841
|
-
<div className={infoDisplay(0).boldStyle.button}
|
|
842
|
-
onClick={() => {
|
|
843
|
-
filterParam.value += " & " + LOG_LIMIT_FLAG;
|
|
844
|
-
void this.synchronizeDataThrottled();
|
|
845
|
-
}}
|
|
846
|
-
>
|
|
847
|
-
Click here to see {formatNumber(this.limitedMatchCount)} matched logs were rate limited at a file level. This means lines might be missing. If this happens a lot, use pass {`{ [LOG_LINE_LIMIT_ID]: "INSERT RANDOM GUID HERE" }`} in the error/warn message to limit only the spamming line.
|
|
848
|
-
</div>
|
|
849
|
-
)}
|
|
850
|
-
{this.state.pendingSyncInitializations > 0 && (
|
|
851
|
-
<div className={infoDisplay(200)}>
|
|
852
|
-
<strong>Pending sync initializations:</strong> {formatNumber(this.state.pendingSyncInitializations)}
|
|
853
|
-
</div>
|
|
854
|
-
)}
|
|
855
|
-
</div>
|
|
856
|
-
|
|
857
|
-
{this.state.error && (
|
|
858
|
-
<div className={infoDisplay(0).whiteSpace("pre-wrap")}>
|
|
859
|
-
<strong>Error:</strong> {this.state.error}
|
|
860
|
-
</div>
|
|
861
|
-
)}
|
|
862
|
-
|
|
863
|
-
{this.renderProgressBars()}
|
|
864
|
-
|
|
865
|
-
{this.renderHistogram()}
|
|
866
|
-
|
|
867
|
-
{this.props.children}
|
|
868
|
-
</div>
|
|
869
|
-
</div>
|
|
870
|
-
);
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
class LoaderAurora extends qreact.Component {
|
|
875
|
-
render() {
|
|
876
|
-
return (
|
|
877
|
-
<>
|
|
878
|
-
<style>{`
|
|
879
|
-
.aurora-container-2 {
|
|
880
|
-
position: relative;
|
|
881
|
-
width: 800px;
|
|
882
|
-
height: 40px;
|
|
883
|
-
overflow: hidden;
|
|
884
|
-
background: linear-gradient(180deg, hsl(280, 30%, 12%), hsl(320, 35%, 15%));
|
|
885
|
-
}
|
|
886
|
-
.aurora-ribbon-2 {
|
|
887
|
-
position: absolute;
|
|
888
|
-
width: 1420px;
|
|
889
|
-
height: 40px;
|
|
890
|
-
filter: blur(12px);
|
|
891
|
-
opacity: 0.9;
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
/* Different gradient patterns for variety */
|
|
895
|
-
.aurora-ribbon-2.gradient1 {
|
|
896
|
-
background: linear-gradient(90deg,
|
|
897
|
-
hsla(160, 80%, 50%, 0) 0%,
|
|
898
|
-
hsla(160, 80%, 50%, 0.7) 30%,
|
|
899
|
-
hsla(280, 80%, 60%, 0.7) 60%,
|
|
900
|
-
hsla(40, 80%, 55%, 0) 100%);
|
|
901
|
-
}
|
|
902
|
-
.aurora-ribbon-2.gradient2 {
|
|
903
|
-
background: linear-gradient(90deg,
|
|
904
|
-
hsla(200, 70%, 60%, 0) 0%,
|
|
905
|
-
hsla(200, 70%, 60%, 0.6) 40%,
|
|
906
|
-
hsla(320, 70%, 55%, 0.6) 70%,
|
|
907
|
-
hsla(60, 70%, 50%, 0) 100%);
|
|
908
|
-
}
|
|
909
|
-
.aurora-ribbon-2.gradient3 {
|
|
910
|
-
background: linear-gradient(90deg,
|
|
911
|
-
hsla(300, 80%, 65%, 0) 0%,
|
|
912
|
-
hsla(300, 80%, 65%, 0.8) 25%,
|
|
913
|
-
hsla(180, 80%, 55%, 0.8) 65%,
|
|
914
|
-
hsla(240, 80%, 60%, 0) 100%);
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
/* Left to right movement */
|
|
918
|
-
.aurora-ribbon-2.r1 {
|
|
919
|
-
top: 0;
|
|
920
|
-
transform: rotate(-10deg);
|
|
921
|
-
animation: auroraSlideLeft-2 3s ease-in-out infinite, auroraRotate1-2 6s linear infinite;
|
|
922
|
-
opacity: 0.7;
|
|
923
|
-
}
|
|
924
|
-
.aurora-ribbon-2.r2 {
|
|
925
|
-
top: 20px;
|
|
926
|
-
transform: rotate(12deg);
|
|
927
|
-
animation: auroraSlideRight-2 2.5s ease-in-out infinite, auroraRotate2-2 8s linear infinite reverse;
|
|
928
|
-
opacity: 0.8;
|
|
929
|
-
}
|
|
930
|
-
.aurora-ribbon-2.r3 {
|
|
931
|
-
top: 40px;
|
|
932
|
-
transform: rotate(18deg);
|
|
933
|
-
animation: auroraSlideDiagonal1-2 4s ease-in-out infinite;
|
|
934
|
-
opacity: 0.6;
|
|
935
|
-
}
|
|
936
|
-
.aurora-ribbon-2.r4 {
|
|
937
|
-
top: -10px;
|
|
938
|
-
transform: rotate(-15deg);
|
|
939
|
-
animation: auroraSlideLeft-2 3.5s ease-in-out infinite, auroraRotate3-2 10s linear infinite;
|
|
940
|
-
opacity: 0.5;
|
|
941
|
-
}
|
|
942
|
-
.aurora-ribbon-2.r5 {
|
|
943
|
-
top: 30px;
|
|
944
|
-
transform: rotate(8deg);
|
|
945
|
-
animation: auroraSlideDiagonal2-2 2.8s ease-in-out infinite;
|
|
946
|
-
opacity: 0.9;
|
|
947
|
-
}
|
|
948
|
-
.aurora-ribbon-2.r6 {
|
|
949
|
-
top: 10px;
|
|
950
|
-
transform: rotate(-5deg);
|
|
951
|
-
animation: auroraSlideRight-2 4.2s ease-in-out infinite, auroraRotate1-2 7s linear infinite;
|
|
952
|
-
opacity: 0.4;
|
|
953
|
-
}
|
|
954
|
-
.aurora-ribbon-2.r7 {
|
|
955
|
-
top: 50px;
|
|
956
|
-
transform: rotate(22deg);
|
|
957
|
-
animation: auroraSlideLeft-2 2.2s ease-in-out infinite;
|
|
958
|
-
opacity: 0.8;
|
|
959
|
-
}
|
|
960
|
-
.aurora-ribbon-2.r8 {
|
|
961
|
-
top: 15px;
|
|
962
|
-
transform: rotate(-8deg);
|
|
963
|
-
animation: auroraSlideDiagonal3-2 3.7s ease-in-out infinite, auroraRotate2-2 9s linear infinite;
|
|
964
|
-
opacity: 0.6;
|
|
965
|
-
}
|
|
966
|
-
.aurora-ribbon-2.r9 {
|
|
967
|
-
top: 35px;
|
|
968
|
-
transform: rotate(15deg);
|
|
969
|
-
animation: auroraSlideRight-2 3.1s ease-in-out infinite, auroraRotate1-2 8.5s linear infinite;
|
|
970
|
-
opacity: 0.7;
|
|
971
|
-
}
|
|
972
|
-
.aurora-ribbon-2.r10 {
|
|
973
|
-
top: -5px;
|
|
974
|
-
transform: rotate(-12deg);
|
|
975
|
-
animation: auroraSlideDiagonal1-2 3.8s ease-in-out infinite;
|
|
976
|
-
opacity: 0.5;
|
|
977
|
-
}
|
|
978
|
-
.aurora-ribbon-2.r11 {
|
|
979
|
-
top: 25px;
|
|
980
|
-
transform: rotate(6deg);
|
|
981
|
-
animation: auroraSlideLeft-2 2.9s ease-in-out infinite, auroraRotate3-2 11s linear infinite;
|
|
982
|
-
opacity: 0.8;
|
|
983
|
-
}
|
|
984
|
-
.aurora-ribbon-2.r12 {
|
|
985
|
-
top: 45px;
|
|
986
|
-
transform: rotate(-20deg);
|
|
987
|
-
animation: auroraSlideDiagonal2-2 4.1s ease-in-out infinite, auroraRotate2-2 7.5s linear infinite reverse;
|
|
988
|
-
opacity: 0.6;
|
|
989
|
-
}
|
|
990
|
-
.aurora-ribbon-2.r13 {
|
|
991
|
-
top: 5px;
|
|
992
|
-
transform: rotate(10deg);
|
|
993
|
-
animation: auroraSlideRight-2 2.7s ease-in-out infinite;
|
|
994
|
-
opacity: 0.9;
|
|
995
|
-
}
|
|
996
|
-
.aurora-ribbon-2.r14 {
|
|
997
|
-
top: 38px;
|
|
998
|
-
transform: rotate(-7deg);
|
|
999
|
-
animation: auroraSlideDiagonal3-2 3.3s ease-in-out infinite, auroraRotate1-2 9.5s linear infinite;
|
|
1000
|
-
opacity: 0.4;
|
|
1001
|
-
}
|
|
1002
|
-
.aurora-ribbon-2.r15 {
|
|
1003
|
-
top: 18px;
|
|
1004
|
-
transform: rotate(14deg);
|
|
1005
|
-
animation: auroraSlideLeft-2 4.3s ease-in-out infinite, auroraRotate3-2 8.2s linear infinite reverse;
|
|
1006
|
-
opacity: 0.7;
|
|
1007
|
-
}
|
|
1008
|
-
.aurora-ribbon-2.r16 {
|
|
1009
|
-
top: 12px;
|
|
1010
|
-
transform: rotate(-18deg);
|
|
1011
|
-
animation: auroraSlideDiagonal1-2 2.6s ease-in-out infinite, auroraRotate2-2 10.5s linear infinite;
|
|
1012
|
-
opacity: 0.8;
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
/* Left to right slide */
|
|
1016
|
-
@keyframes auroraSlideLeft-2 {
|
|
1017
|
-
0% { left: -920px; }
|
|
1018
|
-
100% { left: 900px; }
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
/* Right to left slide */
|
|
1022
|
-
@keyframes auroraSlideRight-2 {
|
|
1023
|
-
0% { left: 900px; }
|
|
1024
|
-
100% { left: -920px; }
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
/* Diagonal movements */
|
|
1028
|
-
@keyframes auroraSlideDiagonal1-2 {
|
|
1029
|
-
0% { left: -920px; top: 40px; }
|
|
1030
|
-
50% { left: 400px; top: -5px; }
|
|
1031
|
-
100% { left: 900px; top: 40px; }
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
@keyframes auroraSlideDiagonal2-2 {
|
|
1035
|
-
0% { left: 900px; top: 30px; }
|
|
1036
|
-
50% { left: 400px; top: 45px; }
|
|
1037
|
-
100% { left: -920px; top: 30px; }
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
@keyframes auroraSlideDiagonal3-2 {
|
|
1041
|
-
0% { left: -920px; top: 15px; }
|
|
1042
|
-
30% { left: -200px; top: 5px; }
|
|
1043
|
-
70% { left: 600px; top: 25px; }
|
|
1044
|
-
100% { left: 900px; top: 15px; }
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
/* Rotation animations */
|
|
1048
|
-
@keyframes auroraRotate1-2 {
|
|
1049
|
-
0% { transform: rotate(-10deg); }
|
|
1050
|
-
25% { transform: rotate(-5deg); }
|
|
1051
|
-
50% { transform: rotate(-15deg); }
|
|
1052
|
-
75% { transform: rotate(-8deg); }
|
|
1053
|
-
100% { transform: rotate(-10deg); }
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
@keyframes auroraRotate2-2 {
|
|
1057
|
-
0% { transform: rotate(12deg); }
|
|
1058
|
-
33% { transform: rotate(18deg); }
|
|
1059
|
-
66% { transform: rotate(8deg); }
|
|
1060
|
-
100% { transform: rotate(12deg); }
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
@keyframes auroraRotate3-2 {
|
|
1064
|
-
0% { transform: rotate(-15deg); }
|
|
1065
|
-
50% { transform: rotate(-20deg); }
|
|
1066
|
-
100% { transform: rotate(-15deg); }
|
|
1067
|
-
}
|
|
1068
|
-
`}</style>
|
|
1069
|
-
<div className="aurora-container-2">
|
|
1070
|
-
<div className="aurora-ribbon-2 r1 gradient1"></div>
|
|
1071
|
-
<div className="aurora-ribbon-2 r2 gradient2"></div>
|
|
1072
|
-
<div className="aurora-ribbon-2 r3 gradient3"></div>
|
|
1073
|
-
<div className="aurora-ribbon-2 r4 gradient1"></div>
|
|
1074
|
-
<div className="aurora-ribbon-2 r5 gradient3"></div>
|
|
1075
|
-
<div className="aurora-ribbon-2 r6 gradient2"></div>
|
|
1076
|
-
<div className="aurora-ribbon-2 r7 gradient1"></div>
|
|
1077
|
-
<div className="aurora-ribbon-2 r8 gradient3"></div>
|
|
1078
|
-
<div className="aurora-ribbon-2 r9 gradient2"></div>
|
|
1079
|
-
<div className="aurora-ribbon-2 r10 gradient1"></div>
|
|
1080
|
-
<div className="aurora-ribbon-2 r11 gradient3"></div>
|
|
1081
|
-
<div className="aurora-ribbon-2 r12 gradient2"></div>
|
|
1082
|
-
<div className="aurora-ribbon-2 r13 gradient1"></div>
|
|
1083
|
-
<div className="aurora-ribbon-2 r14 gradient3"></div>
|
|
1084
|
-
<div className="aurora-ribbon-2 r15 gradient2"></div>
|
|
1085
|
-
<div className="aurora-ribbon-2 r16 gradient1"></div>
|
|
1086
|
-
</div>
|
|
1087
|
-
</>
|
|
1088
|
-
);
|
|
1089
|
-
}
|
|
1090
|
-
}
|