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
|
@@ -26,11 +26,10 @@ import { IndexedLogResults, createEmptyIndexedLogResults, mergeIndexedLogResults
|
|
|
26
26
|
import { TimeFilePath } from "./TimeFileTree";
|
|
27
27
|
import { isPublic } from "../../../config";
|
|
28
28
|
import { Zip } from "../../../zip";
|
|
29
|
+
import { readProductionLogsURL, searchTextURL } from "./LogViewerParams";
|
|
29
30
|
|
|
30
|
-
let searchText = new URLParam("searchText", "");
|
|
31
31
|
let excludePendingResults = new URLParam("excludePendingResults", false);
|
|
32
32
|
let limitURL = new URLParam("limit", 100);
|
|
33
|
-
let readPublicLogs = new URLParam("readPublicLogs", false);
|
|
34
33
|
|
|
35
34
|
let savedPathsURL = new URLParam("savedPaths", "");
|
|
36
35
|
let selectedFieldsURL = new URLParam("selectedFields", {} as Record<string, boolean>);
|
|
@@ -105,6 +104,218 @@ export class LogViewer3 extends qreact.Component {
|
|
|
105
104
|
await this.loadPaths();
|
|
106
105
|
}
|
|
107
106
|
|
|
107
|
+
async loadPaths() {
|
|
108
|
+
if (savedPathsURL.value) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
Querysub.commitLocal(() => {
|
|
113
|
+
this.state.loadingPaths = true;
|
|
114
|
+
this.state.results = [];
|
|
115
|
+
this.state.stats = undefined;
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
let loggers = await getLoggers2Async();
|
|
119
|
+
let selectedLoggers: typeof loggers.logLogs[] = [];
|
|
120
|
+
Querysub.localRead(() => {
|
|
121
|
+
if (this.state.enableLogs) selectedLoggers.push(loggers.logLogs);
|
|
122
|
+
if (this.state.enableInfos) selectedLoggers.push(loggers.infoLogs);
|
|
123
|
+
if (this.state.enableWarnings) selectedLoggers.push(loggers.warnLogs);
|
|
124
|
+
if (this.state.enableErrors) selectedLoggers.push(loggers.errorLogs);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (selectedLoggers.length === 0) {
|
|
128
|
+
console.error("No log sources selected");
|
|
129
|
+
Querysub.commitLocal(() => {
|
|
130
|
+
this.state.loadingPaths = false;
|
|
131
|
+
});
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let allPaths: TimeFilePathWithSize[] = [];
|
|
136
|
+
let range = Querysub.localRead(() => getTimeRange());
|
|
137
|
+
|
|
138
|
+
await Promise.all(selectedLoggers.map(async (logger) => {
|
|
139
|
+
let paths = await logger.clientGetPaths({
|
|
140
|
+
startTime: range.startTime,
|
|
141
|
+
endTime: range.endTime,
|
|
142
|
+
only: excludePendingResults.value ? "public" : undefined,
|
|
143
|
+
forceReadProduction: readProductionLogsURL.value,
|
|
144
|
+
});
|
|
145
|
+
allPaths.push(...paths);
|
|
146
|
+
}));
|
|
147
|
+
sort(allPaths, x => -x.startTime);
|
|
148
|
+
|
|
149
|
+
Querysub.commitLocal(() => {
|
|
150
|
+
this.state.paths = allPaths;
|
|
151
|
+
this.state.loadingPaths = false;
|
|
152
|
+
});
|
|
153
|
+
return allPaths;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async updateToLatestResults() {
|
|
157
|
+
let prevPaths = this.getPaths();
|
|
158
|
+
let newPaths = await this.loadPaths();
|
|
159
|
+
Querysub.commitLocal(() => {
|
|
160
|
+
function getHash(path: TimeFilePath): string {
|
|
161
|
+
return path.threadId + "_" + path.machineId;
|
|
162
|
+
}
|
|
163
|
+
let keep = new Set(prevPaths.map(getHash));
|
|
164
|
+
this.state.paths = newPaths?.filter(x => keep.has(getHash(x))) || [];
|
|
165
|
+
});
|
|
166
|
+
await this.search();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
cancel = async () => {
|
|
170
|
+
this.searchSequenceNumber++;
|
|
171
|
+
let loggers = await getLoggers2Async();
|
|
172
|
+
for (let logger of Object.values(loggers)) {
|
|
173
|
+
logger.clientCancelAllCallbacks();
|
|
174
|
+
}
|
|
175
|
+
Querysub.commitLocal(() => {
|
|
176
|
+
this.state.searching = false;
|
|
177
|
+
this.state.hasSearched = false;
|
|
178
|
+
});
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
search = async () => {
|
|
183
|
+
await this.cancel();
|
|
184
|
+
Querysub.commitLocal(() => {
|
|
185
|
+
this.state.searching = true;
|
|
186
|
+
this.state.results = [];
|
|
187
|
+
this.state.stats = undefined;
|
|
188
|
+
this.state.searchingLogs = false;
|
|
189
|
+
this.state.searchingInfos = false;
|
|
190
|
+
this.state.searchingWarnings = false;
|
|
191
|
+
this.state.searchingErrors = false;
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
let loggers = await getLoggers2Async();
|
|
196
|
+
|
|
197
|
+
let selectedLoggers: typeof loggers.logLogs[] = [];
|
|
198
|
+
Querysub.localRead(() => {
|
|
199
|
+
if (this.state.enableLogs) {
|
|
200
|
+
selectedLoggers.push(loggers.logLogs);
|
|
201
|
+
this.state.searchingLogs = true;
|
|
202
|
+
}
|
|
203
|
+
if (this.state.enableInfos) {
|
|
204
|
+
selectedLoggers.push(loggers.infoLogs);
|
|
205
|
+
this.state.searchingInfos = true;
|
|
206
|
+
}
|
|
207
|
+
if (this.state.enableWarnings) {
|
|
208
|
+
selectedLoggers.push(loggers.warnLogs);
|
|
209
|
+
this.state.searchingWarnings = true;
|
|
210
|
+
}
|
|
211
|
+
if (this.state.enableErrors) {
|
|
212
|
+
selectedLoggers.push(loggers.errorLogs);
|
|
213
|
+
this.state.searchingErrors = true;
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
this.searchSequenceNumber++;
|
|
218
|
+
let currentSequenceNumber = this.searchSequenceNumber;
|
|
219
|
+
|
|
220
|
+
let startTime = Date.now();
|
|
221
|
+
|
|
222
|
+
let hasPaths = Querysub.localRead(() => this.getPaths().length > 0);
|
|
223
|
+
let getFilesTime = 0;
|
|
224
|
+
if (!hasPaths) {
|
|
225
|
+
let startTime = Date.now();
|
|
226
|
+
await this.loadPaths();
|
|
227
|
+
getFilesTime = Date.now() - startTime;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
if (selectedLoggers.length === 0) {
|
|
232
|
+
console.error("No log sources selected");
|
|
233
|
+
Querysub.commitLocal(() => {
|
|
234
|
+
this.state.searching = false;
|
|
235
|
+
});
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
let searchBuffer = Querysub.localRead(() => Buffer.from(searchTextURL.value, "utf8"));
|
|
240
|
+
let results: LogDatum[] = [];
|
|
241
|
+
let stats: IndexedLogResults = createEmptyIndexedLogResults();
|
|
242
|
+
stats.fileFindTime += getFilesTime;
|
|
243
|
+
|
|
244
|
+
let paths = Querysub.localRead(() => this.getPaths());
|
|
245
|
+
|
|
246
|
+
let range = Querysub.localRead(() => getTimeRange());
|
|
247
|
+
|
|
248
|
+
let updateResults = throttleFunction(100, () => {
|
|
249
|
+
if (this.searchSequenceNumber !== currentSequenceNumber) return;
|
|
250
|
+
Querysub.commitLocal(() => {
|
|
251
|
+
this.state.results = results;
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
let loggerResults = new Map<typeof loggers.logLogs, IndexedLogResults>();
|
|
256
|
+
|
|
257
|
+
let updateStats = () => {
|
|
258
|
+
if (this.searchSequenceNumber !== currentSequenceNumber) return;
|
|
259
|
+
|
|
260
|
+
let mergedStats: IndexedLogResults = createEmptyIndexedLogResults();
|
|
261
|
+
|
|
262
|
+
for (let loggerResult of loggerResults.values()) {
|
|
263
|
+
mergedStats = mergeIndexedLogResults(mergedStats, loggerResult);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
mergedStats.totalSearchTime = Date.now() - startTime;
|
|
267
|
+
|
|
268
|
+
Querysub.commitLocal(() => {
|
|
269
|
+
this.state.stats = mergedStats;
|
|
270
|
+
});
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
await Promise.all(selectedLoggers.map(async (logger) => {
|
|
274
|
+
let done = false;
|
|
275
|
+
let result = await errorToUndefined(logger.clientFind({
|
|
276
|
+
params: {
|
|
277
|
+
startTime: range.startTime,
|
|
278
|
+
endTime: range.endTime,
|
|
279
|
+
limit: limitURL.value,
|
|
280
|
+
findBuffer: searchBuffer,
|
|
281
|
+
pathOverrides: paths,
|
|
282
|
+
only: excludePendingResults.value ? "local" : undefined,
|
|
283
|
+
forceReadProduction: readProductionLogsURL.value,
|
|
284
|
+
},
|
|
285
|
+
onResult: (match: LogDatum) => {
|
|
286
|
+
console.log("onResult", match);
|
|
287
|
+
results.push(match);
|
|
288
|
+
void updateResults();
|
|
289
|
+
},
|
|
290
|
+
onResults: (loggerStats: IndexedLogResults) => {
|
|
291
|
+
if (done) return;
|
|
292
|
+
loggerResults.set(logger, loggerStats);
|
|
293
|
+
updateStats();
|
|
294
|
+
},
|
|
295
|
+
}));
|
|
296
|
+
done = true;
|
|
297
|
+
|
|
298
|
+
if (result) {
|
|
299
|
+
loggerResults.set(logger, result);
|
|
300
|
+
updateStats();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
Querysub.commitLocal(() => {
|
|
304
|
+
if (logger === loggers.logLogs) this.state.searchingLogs = false;
|
|
305
|
+
if (logger === loggers.infoLogs) this.state.searchingInfos = false;
|
|
306
|
+
if (logger === loggers.warnLogs) this.state.searchingWarnings = false;
|
|
307
|
+
if (logger === loggers.errorLogs) this.state.searchingErrors = false;
|
|
308
|
+
});
|
|
309
|
+
}));
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
Querysub.commitLocal(() => {
|
|
313
|
+
this.state.results = results;
|
|
314
|
+
this.state.searching = false;
|
|
315
|
+
this.state.hasSearched = true;
|
|
316
|
+
});
|
|
317
|
+
};
|
|
318
|
+
|
|
108
319
|
renderSearchStats() {
|
|
109
320
|
const stats = this.state.stats;
|
|
110
321
|
if (!stats) return undefined;
|
|
@@ -288,7 +499,7 @@ export class LogViewer3 extends qreact.Component {
|
|
|
288
499
|
<span>is not moving logs to remote storage. {formatNumber(warning.count)} files ({formatNumber(warning.totalSize)}B) are {formatTime(now - warning.oldestTime)} old</span>
|
|
289
500
|
</div>
|
|
290
501
|
))}
|
|
291
|
-
{!isPublic() && !
|
|
502
|
+
{!isPublic() && !readProductionLogsURL.value && <Button
|
|
292
503
|
onClick={async () => {
|
|
293
504
|
Querysub.commitLocal(() => {
|
|
294
505
|
this.state.forceMoveStartTime = Date.now();
|
|
@@ -336,217 +547,6 @@ export class LogViewer3 extends qreact.Component {
|
|
|
336
547
|
);
|
|
337
548
|
}
|
|
338
549
|
|
|
339
|
-
async loadPaths() {
|
|
340
|
-
if (savedPathsURL.value) {
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
Querysub.commitLocal(() => {
|
|
345
|
-
this.state.loadingPaths = true;
|
|
346
|
-
this.state.results = [];
|
|
347
|
-
this.state.stats = undefined;
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
let loggers = await getLoggers2Async();
|
|
351
|
-
let selectedLoggers: typeof loggers.logLogs[] = [];
|
|
352
|
-
Querysub.localRead(() => {
|
|
353
|
-
if (this.state.enableLogs) selectedLoggers.push(loggers.logLogs);
|
|
354
|
-
if (this.state.enableInfos) selectedLoggers.push(loggers.infoLogs);
|
|
355
|
-
if (this.state.enableWarnings) selectedLoggers.push(loggers.warnLogs);
|
|
356
|
-
if (this.state.enableErrors) selectedLoggers.push(loggers.errorLogs);
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
if (selectedLoggers.length === 0) {
|
|
360
|
-
console.error("No log sources selected");
|
|
361
|
-
Querysub.commitLocal(() => {
|
|
362
|
-
this.state.loadingPaths = false;
|
|
363
|
-
});
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
let allPaths: TimeFilePathWithSize[] = [];
|
|
368
|
-
let range = Querysub.localRead(() => getTimeRange());
|
|
369
|
-
|
|
370
|
-
await Promise.all(selectedLoggers.map(async (logger) => {
|
|
371
|
-
let paths = await logger.clientGetPaths({
|
|
372
|
-
startTime: range.startTime,
|
|
373
|
-
endTime: range.endTime,
|
|
374
|
-
only: excludePendingResults.value ? "public" : undefined,
|
|
375
|
-
forceReadPublic: readPublicLogs.value,
|
|
376
|
-
});
|
|
377
|
-
allPaths.push(...paths);
|
|
378
|
-
}));
|
|
379
|
-
sort(allPaths, x => -x.startTime);
|
|
380
|
-
|
|
381
|
-
Querysub.commitLocal(() => {
|
|
382
|
-
this.state.paths = allPaths;
|
|
383
|
-
this.state.loadingPaths = false;
|
|
384
|
-
});
|
|
385
|
-
return allPaths;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
async updateToLatestResults() {
|
|
389
|
-
let prevPaths = this.getPaths();
|
|
390
|
-
let newPaths = await this.loadPaths();
|
|
391
|
-
Querysub.commitLocal(() => {
|
|
392
|
-
function getHash(path: TimeFilePath): string {
|
|
393
|
-
return path.threadId + "_" + path.machineId;
|
|
394
|
-
}
|
|
395
|
-
let keep = new Set(prevPaths.map(getHash));
|
|
396
|
-
this.state.paths = newPaths?.filter(x => keep.has(getHash(x))) || [];
|
|
397
|
-
});
|
|
398
|
-
await this.search();
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
cancel = async () => {
|
|
402
|
-
this.searchSequenceNumber++;
|
|
403
|
-
let loggers = await getLoggers2Async();
|
|
404
|
-
for (let logger of Object.values(loggers)) {
|
|
405
|
-
logger.clientCancelAllCallbacks();
|
|
406
|
-
}
|
|
407
|
-
Querysub.commitLocal(() => {
|
|
408
|
-
this.state.searching = false;
|
|
409
|
-
this.state.hasSearched = false;
|
|
410
|
-
});
|
|
411
|
-
};
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
search = async () => {
|
|
415
|
-
await this.cancel();
|
|
416
|
-
Querysub.commitLocal(() => {
|
|
417
|
-
this.state.searching = true;
|
|
418
|
-
this.state.results = [];
|
|
419
|
-
this.state.stats = undefined;
|
|
420
|
-
this.state.searchingLogs = false;
|
|
421
|
-
this.state.searchingInfos = false;
|
|
422
|
-
this.state.searchingWarnings = false;
|
|
423
|
-
this.state.searchingErrors = false;
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
let loggers = await getLoggers2Async();
|
|
428
|
-
|
|
429
|
-
let selectedLoggers: typeof loggers.logLogs[] = [];
|
|
430
|
-
Querysub.localRead(() => {
|
|
431
|
-
if (this.state.enableLogs) {
|
|
432
|
-
selectedLoggers.push(loggers.logLogs);
|
|
433
|
-
this.state.searchingLogs = true;
|
|
434
|
-
}
|
|
435
|
-
if (this.state.enableInfos) {
|
|
436
|
-
selectedLoggers.push(loggers.infoLogs);
|
|
437
|
-
this.state.searchingInfos = true;
|
|
438
|
-
}
|
|
439
|
-
if (this.state.enableWarnings) {
|
|
440
|
-
selectedLoggers.push(loggers.warnLogs);
|
|
441
|
-
this.state.searchingWarnings = true;
|
|
442
|
-
}
|
|
443
|
-
if (this.state.enableErrors) {
|
|
444
|
-
selectedLoggers.push(loggers.errorLogs);
|
|
445
|
-
this.state.searchingErrors = true;
|
|
446
|
-
}
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
this.searchSequenceNumber++;
|
|
450
|
-
let currentSequenceNumber = this.searchSequenceNumber;
|
|
451
|
-
|
|
452
|
-
let startTime = Date.now();
|
|
453
|
-
|
|
454
|
-
let hasPaths = Querysub.localRead(() => this.getPaths().length > 0);
|
|
455
|
-
let getFilesTime = 0;
|
|
456
|
-
if (!hasPaths) {
|
|
457
|
-
let startTime = Date.now();
|
|
458
|
-
await this.loadPaths();
|
|
459
|
-
getFilesTime = Date.now() - startTime;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
if (selectedLoggers.length === 0) {
|
|
464
|
-
console.error("No log sources selected");
|
|
465
|
-
Querysub.commitLocal(() => {
|
|
466
|
-
this.state.searching = false;
|
|
467
|
-
});
|
|
468
|
-
return;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
let searchBuffer = Querysub.localRead(() => Buffer.from(searchText.value, "utf8"));
|
|
472
|
-
let results: LogDatum[] = [];
|
|
473
|
-
let stats: IndexedLogResults = createEmptyIndexedLogResults();
|
|
474
|
-
stats.fileFindTime += getFilesTime;
|
|
475
|
-
|
|
476
|
-
let paths = Querysub.localRead(() => this.getPaths());
|
|
477
|
-
|
|
478
|
-
let range = Querysub.localRead(() => getTimeRange());
|
|
479
|
-
|
|
480
|
-
let updateResults = throttleFunction(100, () => {
|
|
481
|
-
if (this.searchSequenceNumber !== currentSequenceNumber) return;
|
|
482
|
-
Querysub.commitLocal(() => {
|
|
483
|
-
this.state.results = results;
|
|
484
|
-
});
|
|
485
|
-
});
|
|
486
|
-
|
|
487
|
-
let loggerResults = new Map<typeof loggers.logLogs, IndexedLogResults>();
|
|
488
|
-
|
|
489
|
-
let updateStats = () => {
|
|
490
|
-
if (this.searchSequenceNumber !== currentSequenceNumber) return;
|
|
491
|
-
|
|
492
|
-
let mergedStats: IndexedLogResults = createEmptyIndexedLogResults();
|
|
493
|
-
|
|
494
|
-
for (let loggerResult of loggerResults.values()) {
|
|
495
|
-
mergedStats = mergeIndexedLogResults(mergedStats, loggerResult);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
mergedStats.totalSearchTime = Date.now() - startTime;
|
|
499
|
-
|
|
500
|
-
Querysub.commitLocal(() => {
|
|
501
|
-
this.state.stats = mergedStats;
|
|
502
|
-
});
|
|
503
|
-
};
|
|
504
|
-
|
|
505
|
-
await Promise.all(selectedLoggers.map(async (logger) => {
|
|
506
|
-
let done = false;
|
|
507
|
-
let result = await errorToUndefined(logger.clientFind({
|
|
508
|
-
params: {
|
|
509
|
-
startTime: range.startTime,
|
|
510
|
-
endTime: range.endTime,
|
|
511
|
-
limit: limitURL.value,
|
|
512
|
-
findBuffer: searchBuffer,
|
|
513
|
-
pathOverrides: paths,
|
|
514
|
-
only: excludePendingResults.value ? "local" : undefined,
|
|
515
|
-
forceReadPublic: readPublicLogs.value,
|
|
516
|
-
},
|
|
517
|
-
onResult: (match: LogDatum) => {
|
|
518
|
-
console.log("onResult", match);
|
|
519
|
-
results.push(match);
|
|
520
|
-
void updateResults();
|
|
521
|
-
},
|
|
522
|
-
onResults: (loggerStats: IndexedLogResults) => {
|
|
523
|
-
if (done) return;
|
|
524
|
-
loggerResults.set(logger, loggerStats);
|
|
525
|
-
updateStats();
|
|
526
|
-
},
|
|
527
|
-
}));
|
|
528
|
-
done = true;
|
|
529
|
-
|
|
530
|
-
if (result) {
|
|
531
|
-
loggerResults.set(logger, result);
|
|
532
|
-
updateStats();
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
Querysub.commitLocal(() => {
|
|
536
|
-
if (logger === loggers.logLogs) this.state.searchingLogs = false;
|
|
537
|
-
if (logger === loggers.infoLogs) this.state.searchingInfos = false;
|
|
538
|
-
if (logger === loggers.warnLogs) this.state.searchingWarnings = false;
|
|
539
|
-
if (logger === loggers.errorLogs) this.state.searchingErrors = false;
|
|
540
|
-
});
|
|
541
|
-
}));
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
Querysub.commitLocal(() => {
|
|
545
|
-
this.state.results = results;
|
|
546
|
-
this.state.searching = false;
|
|
547
|
-
this.state.hasSearched = true;
|
|
548
|
-
});
|
|
549
|
-
};
|
|
550
550
|
|
|
551
551
|
renderResults() {
|
|
552
552
|
let fieldNames = new Set<string>();
|
|
@@ -695,8 +695,8 @@ export class LogViewer3 extends qreact.Component {
|
|
|
695
695
|
/>
|
|
696
696
|
{!isPublic() && <InputLabelURL
|
|
697
697
|
checkbox
|
|
698
|
-
label="Read
|
|
699
|
-
url={
|
|
698
|
+
label="Read Production Logs"
|
|
699
|
+
url={readProductionLogsURL}
|
|
700
700
|
onChangeValue={(newValue) => {
|
|
701
701
|
if (newValue) {
|
|
702
702
|
savedPathsURL.value = "";
|
|
@@ -754,7 +754,7 @@ export class LogViewer3 extends qreact.Component {
|
|
|
754
754
|
</Button>
|
|
755
755
|
<TimeRangeSelector />
|
|
756
756
|
</div>
|
|
757
|
-
{!isPublic() &&
|
|
757
|
+
{!isPublic() && readProductionLogsURL.value && <div className={css.fontSize(40).pad2(12, 6).hsl(280, 40, 50).colorhsl(0, 0, 100).boldStyle}>
|
|
758
758
|
Reading public logs
|
|
759
759
|
</div>}
|
|
760
760
|
|
|
@@ -763,10 +763,10 @@ export class LogViewer3 extends qreact.Component {
|
|
|
763
763
|
flavor="large"
|
|
764
764
|
fillWidth
|
|
765
765
|
focusOnMount
|
|
766
|
-
url={
|
|
766
|
+
url={searchTextURL}
|
|
767
767
|
onKeyDown={(e) => {
|
|
768
768
|
if (e.key === "Enter") {
|
|
769
|
-
|
|
769
|
+
searchTextURL.value = e.currentTarget.value;
|
|
770
770
|
void this.search();
|
|
771
771
|
}
|
|
772
772
|
}}
|
|
@@ -776,6 +776,7 @@ export class LogViewer3 extends qreact.Component {
|
|
|
776
776
|
flavor="large"
|
|
777
777
|
onClick={() => void this.loadPaths()}
|
|
778
778
|
hue={this.state.paths.length ? 200 : 120}
|
|
779
|
+
className={css + (this.state.loadingPaths && css.opacity(0.5))}
|
|
779
780
|
>
|
|
780
781
|
{this.getPaths().length && "Reset Files" || "Preview Files"}
|
|
781
782
|
</Button>
|
|
@@ -835,7 +836,7 @@ export class LogViewer3 extends qreact.Component {
|
|
|
835
836
|
>
|
|
836
837
|
{savedPathsURL.value ? `Clear Frozen Files (${this.getPaths().length})` : `Freeze Files (${this.state.paths.length})`}
|
|
837
838
|
</Button>
|
|
838
|
-
{
|
|
839
|
+
{savedPathsURL.value && (
|
|
839
840
|
<div className={css.pad2(8, 4).hsl(40, 60, 50).colorhsl(40, 0, 100)}>
|
|
840
841
|
Files frozen in memory. Click Preview Files to refresh.
|
|
841
842
|
</div>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { URLParam } from "../../../library-components/URLParam";
|
|
2
|
+
import { managementPageURL } from "../../managementPages";
|
|
3
|
+
import { LogDatum } from "../diskLogger";
|
|
4
|
+
|
|
5
|
+
export let searchTextURL = new URLParam("searchText", "");
|
|
6
|
+
export let readProductionLogsURL = new URLParam("readProductionLogs", false);
|
|
7
|
+
|
|
8
|
+
export function formatSearchString(obj: Record<string, unknown>): string {
|
|
9
|
+
return Object.entries(obj).map(([key, value]) => {
|
|
10
|
+
return `${JSON.stringify(key)}:${JSON.stringify(value)}`;
|
|
11
|
+
}).join("&");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getLogViewerParams(logObj: Partial<LogDatum>) {
|
|
15
|
+
return [
|
|
16
|
+
managementPageURL.getOverride("LogViewer3"),
|
|
17
|
+
searchTextURL.getOverride(formatSearchString(logObj)),
|
|
18
|
+
// If we're linking to the logs, it's almost certainly for production logs.
|
|
19
|
+
readProductionLogsURL.getOverride(true),
|
|
20
|
+
];
|
|
21
|
+
}
|
|
@@ -26,7 +26,7 @@ export type MatchStructure = {
|
|
|
26
26
|
function parseMatchStructure(pattern: Buffer): MatchStructure {
|
|
27
27
|
let text = pattern.toString("utf-8");
|
|
28
28
|
|
|
29
|
-
let orParts = text.split(SEARCH_OR_CHAR);
|
|
29
|
+
let orParts = text.split(SEARCH_OR_CHAR).map(x => x.trim()).filter(x => x.length > 0);
|
|
30
30
|
if (orParts.length > 1) {
|
|
31
31
|
return {
|
|
32
32
|
type: "or",
|
|
@@ -34,7 +34,7 @@ function parseMatchStructure(pattern: Buffer): MatchStructure {
|
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
let andParts = text.split(SEARCH_AND_CHAR);
|
|
37
|
+
let andParts = text.split(SEARCH_AND_CHAR).map(x => x.trim()).filter(x => x.length > 0);
|
|
38
38
|
if (andParts.length > 1) {
|
|
39
39
|
return {
|
|
40
40
|
type: "and",
|
|
@@ -42,7 +42,7 @@ function parseMatchStructure(pattern: Buffer): MatchStructure {
|
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
let wildcardParts = text.split(WILD_CARD_CHAR).filter(
|
|
45
|
+
let wildcardParts = text.split(WILD_CARD_CHAR).map(x => x.trim()).filter(x => x.length > 0);
|
|
46
46
|
if (wildcardParts.length > 1) {
|
|
47
47
|
return {
|
|
48
48
|
type: "wildcard",
|
|
@@ -5,7 +5,6 @@ import { lazy } from "socket-function/src/caching";
|
|
|
5
5
|
import { timeInMinute } from "socket-function/src/misc";
|
|
6
6
|
import { formatTime } from "socket-function/src/formatting/format";
|
|
7
7
|
import { addEpsilons } from "../../bits";
|
|
8
|
-
import { FileMetadata } from "./FastArchiveController";
|
|
9
8
|
import { getPathStr2 } from "../../path";
|
|
10
9
|
import { isPublic } from "../../config";
|
|
11
10
|
// IMPORTANT! We can't have any real imports here, because we are depended on so early in startup!
|
|
@@ -32,8 +31,6 @@ export type LogDatum = Record<string, unknown> & {
|
|
|
32
31
|
__DIR__?: string;
|
|
33
32
|
__NAME__?: string;
|
|
34
33
|
__LINE__?: string;
|
|
35
|
-
/** Dynamically set at runtime in the frontend. */
|
|
36
|
-
__metadata?: FileMetadata;
|
|
37
34
|
/** Dynamically set when matching recent errors only. */
|
|
38
35
|
__matchedOutdatedSuppressionKey?: string;
|
|
39
36
|
};
|
|
@@ -68,36 +65,6 @@ export const LOG_LINE_LIMIT_FLAG = String.fromCharCode(44534) + "LOGS_LINE_LIMIT
|
|
|
68
65
|
/** If this key exists in the logged object, as in a key in one of the objects logged, then we will use the value of it as the limit ID. This is useful as it allows us to either override a limit or limit something independently from other logs in the file. */
|
|
69
66
|
export const LOG_LINE_LIMIT_ID = "LIMIT_LINE_ID";
|
|
70
67
|
|
|
71
|
-
export const getLoggers = lazy(function () {
|
|
72
|
-
const { FastArchiveAppendable } = require("./FastArchiveAppendable") as typeof import("./FastArchiveAppendable");
|
|
73
|
-
if (!FastArchiveAppendable) {
|
|
74
|
-
setImmediate(() => {
|
|
75
|
-
getLoggers.reset();
|
|
76
|
-
});
|
|
77
|
-
return undefined;
|
|
78
|
-
}
|
|
79
|
-
return {
|
|
80
|
-
logLogs: new FastArchiveAppendable<LogDatum>("logs-log/"),
|
|
81
|
-
warnLogs: new FastArchiveAppendable<LogDatum>("logs-warn/"),
|
|
82
|
-
infoLogs: new FastArchiveAppendable<LogDatum>("logs-info/"),
|
|
83
|
-
errorLogs: new FastArchiveAppendable<LogDatum>("logs-error/"),
|
|
84
|
-
};
|
|
85
|
-
});
|
|
86
|
-
setImmediate(() => {
|
|
87
|
-
// If we don't import it at all, then it doesn't work client-side.
|
|
88
|
-
require("./FastArchiveAppendable") as typeof import("./FastArchiveAppendable");
|
|
89
|
-
});
|
|
90
|
-
const getNotifyErrors = lazy(function () {
|
|
91
|
-
const { notifyWatchersOfError: notifyErrors } = require("./errorNotifications/ErrorNotificationController") as typeof import("./errorNotifications/ErrorNotificationController");
|
|
92
|
-
if (typeof notifyErrors !== "function") {
|
|
93
|
-
setImmediate(() => {
|
|
94
|
-
getNotifyErrors.reset();
|
|
95
|
-
});
|
|
96
|
-
return undefined;
|
|
97
|
-
}
|
|
98
|
-
return notifyErrors;
|
|
99
|
-
});
|
|
100
|
-
|
|
101
68
|
export const getLoggers2 = lazy(function () {
|
|
102
69
|
const { IndexedLogs } = require("./IndexedLogs/IndexedLogs") as typeof import("./IndexedLogs/IndexedLogs");
|
|
103
70
|
|
|
@@ -146,7 +113,7 @@ void Promise.resolve().then(() => {
|
|
|
146
113
|
|
|
147
114
|
|
|
148
115
|
const logDiskDontShim = logDisk;
|
|
149
|
-
/**
|
|
116
|
+
/** @deprecated, Don't call this directly, call console info instead, which our shim will prevent from logging to the console, but it will still call logDisk. */
|
|
150
117
|
export function logDisk(type: "log" | "warn" | "info" | "error", ...args: unknown[]) {
|
|
151
118
|
if (!isNode()) return;
|
|
152
119
|
try {
|
|
@@ -189,11 +156,6 @@ export function logDisk(type: "log" | "warn" | "info" | "error", ...args: unknow
|
|
|
189
156
|
}
|
|
190
157
|
}
|
|
191
158
|
|
|
192
|
-
if (type === "warn" || type === "error") {
|
|
193
|
-
// Dropping notifies is fine, as long as they get added to the logs, we'll see them eventually...
|
|
194
|
-
void getNotifyErrors()?.(logObj);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
159
|
} catch (e: any) {
|
|
198
160
|
process.stdout.write(`Error writing to disk logs: ${e.stack || e}\n\t${String(args[0])}\n`);
|
|
199
161
|
}
|
|
@@ -55,6 +55,8 @@ export function shimConsoleLogs() {
|
|
|
55
55
|
// Some arguments might throw if accessed (as they might be proxies), so
|
|
56
56
|
// catch and ignore errors
|
|
57
57
|
}
|
|
58
|
+
// NOTE: Info really has absolutely no purpose. There's no reason to use info instead of log, so we're going to give it a purpose. Infos are not going to be shown in the console and are only going to be logged to disk. This helps fix our shimming issue, where when we call log disk directly, we lose out on the source file information.
|
|
59
|
+
if (fncName === "info") return;
|
|
58
60
|
return originalFnc(...args);
|
|
59
61
|
};
|
|
60
62
|
}
|
|
@@ -41,4 +41,7 @@ if (isNode()) {
|
|
|
41
41
|
|
|
42
42
|
let diskShimConsoleLogs = require("./diskShimConsoleLogs") as typeof import("./diskShimConsoleLogs");
|
|
43
43
|
diskShimConsoleLogs.shimConsoleLogs();
|
|
44
|
+
} else {
|
|
45
|
+
// NOTE: Disable console.info, as serverside we use it for disk only logs
|
|
46
|
+
console.info = () => { };
|
|
44
47
|
}
|