querysub 0.398.0 → 0.400.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 +1 -1
- package/src/-a-archives/archivesBackBlaze.ts +1 -1
- package/src/0-path-value-core/archiveLocks/ArchiveLocks2.ts +9 -6
- package/src/0-path-value-core/pathValueCore.ts +17 -17
- package/src/deployManager/machineApplyMainCode.ts +3 -3
- package/src/diagnostics/logs/IndexedLogs/BufferIndex.ts +19 -4
- package/src/diagnostics/logs/IndexedLogs/BufferIndexHelpers.ts +4 -1
- package/src/diagnostics/logs/IndexedLogs/BufferIndexLogsOptimizationConstants.ts +3 -1
- package/src/diagnostics/logs/IndexedLogs/BufferUnitIndex.ts +4 -0
- package/src/diagnostics/logs/IndexedLogs/IndexedLogs.ts +33 -18
- package/src/diagnostics/logs/IndexedLogs/LogViewer3.tsx +2 -2
- package/src/diagnostics/logs/IndexedLogs/RenderSearchStats.tsx +8 -6
- package/src/diagnostics/logs/ObjectDisplay.tsx +4 -4
- package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleInstanceTableView.tsx +61 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/LifeCyclePage.tsx +23 -6
- package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleRenderer.tsx +33 -14
- package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycleSearch.tsx +4 -2
package/package.json
CHANGED
|
@@ -517,7 +517,7 @@ export class ArchivesBackblaze {
|
|
|
517
517
|
|
|
518
518
|
// If it's a 503 and it's been a minute since we last reset, then Wait and reset.
|
|
519
519
|
if (
|
|
520
|
-
(err.stack.includes(`503`)
|
|
520
|
+
(err.stack.includes(`"status": 503`)
|
|
521
521
|
|| err.stack.includes(`"service_unavailable"`)
|
|
522
522
|
|| err.stack.includes(`"internal_error"`)
|
|
523
523
|
|| err.stack.includes(`ENOBUFS`)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { list, sort, timeInDay, timeInHour, timeInMinute } from "socket-function/src/misc";
|
|
1
|
+
import { list, sort, timeInDay, timeInHour, timeInMinute, timeInSecond } from "socket-function/src/misc";
|
|
2
2
|
import { MaybePromise } from "socket-function/src/types";
|
|
3
3
|
import { decodeNodeId } from "../../-a-auth/certs";
|
|
4
4
|
import { getOwnNodeId, getOwnNodeIdAssert } from "../../-f-node-discovery/NodeDiscovery";
|
|
@@ -18,7 +18,7 @@ import { logDisk } from "../../diagnostics/logs/diskLogger";
|
|
|
18
18
|
|
|
19
19
|
/** Clean up old files after a while */
|
|
20
20
|
const DEAD_CREATE_THRESHOLD = timeInHour * 12;
|
|
21
|
-
const ARCHIVE_PROPAGATION_TIME =
|
|
21
|
+
const ARCHIVE_PROPAGATION_TIME = timeInSecond * 5;
|
|
22
22
|
// NOTE: There isn't a reason for transaction to not apply. If applying throws, we throw,
|
|
23
23
|
// and can throw infinite times, but if it doesn't throw, it SHOULD be applied. We retry
|
|
24
24
|
// a few times though, just in case the storage system was having some issues. After enough
|
|
@@ -374,6 +374,7 @@ class TransactionLocker {
|
|
|
374
374
|
}
|
|
375
375
|
|
|
376
376
|
private lastFilesRead: FileInfo[] | undefined;
|
|
377
|
+
private lastFilesReadTime: number | undefined;
|
|
377
378
|
private async readDataState(): Promise<{
|
|
378
379
|
rawDataFiles: FileInfo[];
|
|
379
380
|
/** Confirmed FileInfos are === the FileInfos in rawDataFiles */
|
|
@@ -387,14 +388,15 @@ class TransactionLocker {
|
|
|
387
388
|
}> {
|
|
388
389
|
let bufferCache = new Map<string, Buffer>();
|
|
389
390
|
const tryToRead = async () => {
|
|
391
|
+
let time = Date.now();
|
|
390
392
|
let files = await this.storage.getKeys();
|
|
391
|
-
if (this.lastFilesRead) {
|
|
393
|
+
if (this.lastFilesRead && this.lastFilesReadTime) {
|
|
392
394
|
let prevFiles = new Set(this.lastFilesRead.map(a => a.file));
|
|
395
|
+
let suspiciousThreshold = this.lastFilesReadTime - ARCHIVE_PROPAGATION_TIME * 10;
|
|
393
396
|
let newFiles = files.filter(a => !prevFiles.has(a.file));
|
|
394
|
-
let
|
|
395
|
-
let veryBadFiles = newFiles.filter(x => x.createTime < pastTime);
|
|
397
|
+
let veryBadFiles = newFiles.filter(x => x.createTime < suspiciousThreshold);
|
|
396
398
|
if (veryBadFiles.length > 0) {
|
|
397
|
-
console.error(`Old
|
|
399
|
+
console.error(`Old read was not exhaustive, because we just found files which existed well before it, but that it did not read. This likely means our propagation assumptions are wrong, and propagation is much slower than anticipated. OR, getKeys is flakey and randomly misses files? Either way, this will BREAK THE DATABASE! Any node experiencing this might start deleting valid database files, which is very bad!`, {
|
|
398
400
|
newlyAppearingOldFiles: veryBadFiles.map(x => x.file),
|
|
399
401
|
prevFiles: this.lastFilesRead.map(x => x.file),
|
|
400
402
|
newFiles: files.map(x => x.file),
|
|
@@ -402,6 +404,7 @@ class TransactionLocker {
|
|
|
402
404
|
}
|
|
403
405
|
}
|
|
404
406
|
this.lastFilesRead = files;
|
|
407
|
+
this.lastFilesReadTime = time;
|
|
405
408
|
|
|
406
409
|
let transactions: (Transaction & {
|
|
407
410
|
seqNum: number;
|
|
@@ -980,7 +980,6 @@ class AuthorityPathValueStorage {
|
|
|
980
980
|
if (!isCoreQuiet) {
|
|
981
981
|
console.log(`GCing everything older than ${debugPathValuePath(values[0])}`);
|
|
982
982
|
for (let value of removed) {
|
|
983
|
-
console.log(` ${debugPathValuePath(value)}`);
|
|
984
983
|
auditLog("GCing", { path: value.path });
|
|
985
984
|
}
|
|
986
985
|
}
|
|
@@ -990,7 +989,6 @@ class AuthorityPathValueStorage {
|
|
|
990
989
|
for (let i = values.length - 1; i > firstGoldenIndex; i--) {
|
|
991
990
|
let gced = values.pop();
|
|
992
991
|
if (!isCoreQuiet && gced) {
|
|
993
|
-
console.log(`GCing ${debugPathValuePath(gced)}`);
|
|
994
992
|
auditLog("GCing", { path: gced.path });
|
|
995
993
|
}
|
|
996
994
|
}
|
|
@@ -1935,21 +1933,22 @@ class WriteValidStorage {
|
|
|
1935
1933
|
debugger;
|
|
1936
1934
|
}
|
|
1937
1935
|
let timeToReject = now - valueGroup[0].time.time;
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1936
|
+
let message = `!!! VALUE REJECTED DUE TO USING MISSING / REJECTED READ!!!(rejected after ${timeToReject}ms at ${Date.now()})`;
|
|
1937
|
+
message += `\n${debugTime(valueGroup[0].time)} (write)`;
|
|
1938
|
+
message += `\n rejected as the server could not find: `;
|
|
1941
1939
|
if (lock.readIsTransparent) {
|
|
1942
|
-
|
|
1940
|
+
message += `\n${debugTime(lock.startTime)} to ${debugTime(lock.endTime)} ${getPathFromStr(lock.path).join(".")} `;
|
|
1943
1941
|
} else {
|
|
1944
|
-
|
|
1942
|
+
message += `\n${debugTime(lock.startTime)} ${getPathFromStr(lock.path).join(".")} `;
|
|
1945
1943
|
}
|
|
1946
1944
|
if (lock.readIsTransparent) {
|
|
1947
|
-
|
|
1945
|
+
message += `\n (read was undefined, so presumably a value exists which the writer missed)`;
|
|
1948
1946
|
}
|
|
1949
|
-
|
|
1947
|
+
message += `\nFull list of writes rejected: `;
|
|
1950
1948
|
for (let pathValue of valueGroup) {
|
|
1951
|
-
|
|
1949
|
+
message += `\n${debugPathValue(pathValue)}`;
|
|
1952
1950
|
}
|
|
1951
|
+
console.log(red(message));
|
|
1953
1952
|
|
|
1954
1953
|
if (debugRejections) {
|
|
1955
1954
|
debugbreak(2);
|
|
@@ -1978,17 +1977,18 @@ class WriteValidStorage {
|
|
|
1978
1977
|
let changed = valueGroup.filter(x => x.valid);
|
|
1979
1978
|
if (changed.length > 0) {
|
|
1980
1979
|
let timeToReject = now - changed[0].time.time;
|
|
1981
|
-
|
|
1980
|
+
let redMessage = `!!! LOCK CONTENTION FIXED VIA REJECTION OF VALUE!!!(rejected after ${timeToReject}ms)`;
|
|
1982
1981
|
for (let pathValue of changed) {
|
|
1983
|
-
|
|
1982
|
+
redMessage += `\n${debugPathValue(pathValue)}`;
|
|
1984
1983
|
}
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1984
|
+
redMessage += `\n (write rejected due to original read not noticing value at: ${lock.path})`;
|
|
1985
|
+
redMessage += `\n (original read from: ${debugTime(lock.startTime)}`;
|
|
1986
|
+
redMessage += `\n (at time: ${debugTime(lock.endTime)}`;
|
|
1987
|
+
redMessage += `\n (current time: ${Date.now()})`;
|
|
1989
1988
|
for (let lockFailed of inRange) {
|
|
1990
|
-
|
|
1989
|
+
redMessage += `\n (conflict write at: ${debugTime(lockFailed.time)}`;
|
|
1991
1990
|
}
|
|
1991
|
+
console.log(red(redMessage));
|
|
1992
1992
|
if (debugRejections) {
|
|
1993
1993
|
debugbreak(2);
|
|
1994
1994
|
debugger;
|
|
@@ -593,10 +593,10 @@ const ensureGitSynced = measureWrap(async function ensureGitSynced(config: {
|
|
|
593
593
|
repoUrl: string;
|
|
594
594
|
gitRef: string;
|
|
595
595
|
}) {
|
|
596
|
-
if (!await fsExistsAsync(config.gitFolder + ".git")) {
|
|
597
|
-
await runPromise(`git clone ${config.repoUrl} ${config.gitFolder}`);
|
|
598
|
-
}
|
|
599
596
|
try {
|
|
597
|
+
if (!await fsExistsAsync(config.gitFolder + ".git")) {
|
|
598
|
+
await runPromise(`git clone ${config.repoUrl} ${config.gitFolder}`);
|
|
599
|
+
}
|
|
600
600
|
await setGitRef(config);
|
|
601
601
|
} catch (e: any) {
|
|
602
602
|
console.warn(`Failed to set git ref, trying to delete and clone again (${config.gitFolder}): ${e.stack}`);
|
|
@@ -419,14 +419,26 @@ export class BufferIndex {
|
|
|
419
419
|
keepIterating: () => boolean;
|
|
420
420
|
onResult: (match: Buffer) => void;
|
|
421
421
|
results: IndexedLogResults;
|
|
422
|
-
}): Promise<
|
|
422
|
+
}): Promise<{
|
|
423
|
+
blockSearchTime: number;
|
|
424
|
+
indexSearchTime: number;
|
|
425
|
+
}> {
|
|
423
426
|
let { index, dataReader, params, results } = config;
|
|
427
|
+
let startTime = Date.now();
|
|
428
|
+
let indexEndTime = startTime;
|
|
429
|
+
function createResult() {
|
|
430
|
+
let endTime = Date.now();
|
|
431
|
+
return {
|
|
432
|
+
indexSearchTime: indexEndTime - startTime,
|
|
433
|
+
blockSearchTime: endTime - indexEndTime,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
424
436
|
|
|
425
437
|
const matchesPattern = createMatchesPattern(params.findBuffer, !!params.disableWildCards);
|
|
426
438
|
|
|
427
439
|
let allSearchUnits = getSearchUnits(params.findBuffer, !!params.disableWildCards);
|
|
428
440
|
if (allSearchUnits.length === 0) {
|
|
429
|
-
return;
|
|
441
|
+
return createResult();
|
|
430
442
|
}
|
|
431
443
|
|
|
432
444
|
let type = index[0];
|
|
@@ -441,12 +453,13 @@ export class BufferIndex {
|
|
|
441
453
|
if (index.length === 0) {
|
|
442
454
|
index = await BufferIndex.rebuildLocalIndexFromData(dataReader);
|
|
443
455
|
if (index.length === 0) {
|
|
444
|
-
return;
|
|
456
|
+
return createResult();
|
|
445
457
|
}
|
|
446
458
|
}
|
|
447
459
|
|
|
448
460
|
// Fix partial index before processing
|
|
449
461
|
index = await BufferIndex.fixPartialIndex({ index, dataReader, results });
|
|
462
|
+
indexEndTime = Date.now();
|
|
450
463
|
|
|
451
464
|
await BufferIndex.findLocal({ index, dataReader, params, keepIterating: config.keepIterating, onResult: config.onResult, results, allSearchUnits, matchesPattern });
|
|
452
465
|
} else if (type === BULK_TYPE) {
|
|
@@ -465,9 +478,11 @@ export class BufferIndex {
|
|
|
465
478
|
let dataLength = await dataReader.getLength();
|
|
466
479
|
if (dataLength < 32) {
|
|
467
480
|
// If it's a small file, nothing of value will be lost by ignoring it. And almost all of our errors are coming from extremely small files.
|
|
468
|
-
return;
|
|
481
|
+
return createResult();
|
|
469
482
|
}
|
|
470
483
|
throw new Error(`Unknown type in index file: ${type}`);
|
|
471
484
|
}
|
|
485
|
+
|
|
486
|
+
return createResult();
|
|
472
487
|
}
|
|
473
488
|
}
|
|
@@ -134,6 +134,8 @@ export type IndexedLogResults = {
|
|
|
134
134
|
totalBackblazeLogs: number;
|
|
135
135
|
backblazeLogsSearched: number;
|
|
136
136
|
|
|
137
|
+
backblazeUncompressedSize: number;
|
|
138
|
+
|
|
137
139
|
totalBlockCount: number;
|
|
138
140
|
blockCheckedCount: number;
|
|
139
141
|
|
|
@@ -165,7 +167,7 @@ export type IndexedLogResults = {
|
|
|
165
167
|
};
|
|
166
168
|
export function createEmptyIndexedLogResults(): IndexedLogResults {
|
|
167
169
|
return {
|
|
168
|
-
matchCount: 0, reads: [], totalLocalFiles: 0, totalBackblazeFiles: 0, localFilesSearched: 0, backblazeFilesSearched: 0, totalBackblazeLogs: 0, backblazeLogsSearched: 0, totalBlockCount: 0, blockCheckedCount: 0, remoteBlockCount: 0, localBlockCount: 0, remoteBlockCheckedCount: 0, localBlockCheckedCount: 0, blocksCheckedCompressedSize: 0, blocksCheckedDecompressedSize: 0, blockErrors: [], fileErrors: [], remoteIndexesSearched: 0, remoteIndexSize: 0, localIndexesSearched: 0, localIndexSize: 0, timeToFirstMatch: -1, fileFindTime: 0, indexSearchTime: 0, blockSearchTime: 0, totalSearchTime: 0, cancel: undefined, limitGroup: undefined,
|
|
170
|
+
matchCount: 0, reads: [], totalLocalFiles: 0, totalBackblazeFiles: 0, localFilesSearched: 0, backblazeFilesSearched: 0, totalBackblazeLogs: 0, backblazeLogsSearched: 0, totalBlockCount: 0, blockCheckedCount: 0, remoteBlockCount: 0, localBlockCount: 0, remoteBlockCheckedCount: 0, localBlockCheckedCount: 0, blocksCheckedCompressedSize: 0, blocksCheckedDecompressedSize: 0, backblazeUncompressedSize: 0, blockErrors: [], fileErrors: [], remoteIndexesSearched: 0, remoteIndexSize: 0, localIndexesSearched: 0, localIndexSize: 0, timeToFirstMatch: -1, fileFindTime: 0, indexSearchTime: 0, blockSearchTime: 0, totalSearchTime: 0, cancel: undefined, limitGroup: undefined,
|
|
169
171
|
};
|
|
170
172
|
}
|
|
171
173
|
|
|
@@ -211,6 +213,7 @@ export function mergeIndexedLogResults(existing: IndexedLogResults, incoming: In
|
|
|
211
213
|
blockCheckedCount: existing.blockCheckedCount + incoming.blockCheckedCount,
|
|
212
214
|
blocksCheckedCompressedSize: existing.blocksCheckedCompressedSize + incoming.blocksCheckedCompressedSize,
|
|
213
215
|
blocksCheckedDecompressedSize: existing.blocksCheckedDecompressedSize + incoming.blocksCheckedDecompressedSize,
|
|
216
|
+
backblazeUncompressedSize: existing.backblazeUncompressedSize + incoming.backblazeUncompressedSize,
|
|
214
217
|
blockErrors: [...existing.blockErrors, ...incoming.blockErrors],
|
|
215
218
|
fileErrors: [...existing.fileErrors, ...incoming.fileErrors],
|
|
216
219
|
remoteIndexesSearched: existing.remoteIndexesSearched + incoming.remoteIndexesSearched,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { timeInHour, timeInMinute, timeInSecond } from "socket-function/src/misc";
|
|
2
2
|
|
|
3
3
|
// NOTE: This is the block size BEFORE compression. The actual download size is probably going to be around 1/20th of this.
|
|
4
|
-
|
|
4
|
+
// BUT, This is also going to be the amount of data we need to scan generally per result, as the results are often, at least in the cases that we're optimizing for, spread out over many files.
|
|
5
|
+
// NOTE: This used to be 1024 * 1024, which in practice seemed to give us about a 170X factor from the full data to the index size. If this change to 1024 * 256 moves it below a 100X factor we should increase the size again (small block sizes mean more blocks, which means the index file has to store more data, making it less efficient).
|
|
6
|
+
export const DEFAULT_BLOCK_SIZE = 1024 * 256;
|
|
5
7
|
export const DEFAULT_TARGET_UNITS_PER_BUCKET = 16;
|
|
6
8
|
|
|
7
9
|
// Worst case scenario, the logs the user wants are very spread out, in which case having too high of a threshold will require decoding more data (the threshold amount per result).
|
|
@@ -512,7 +512,11 @@ export class BufferUnitIndex {
|
|
|
512
512
|
}): Promise<void> {
|
|
513
513
|
const { params, index, reader, keepIterating, results, allSearchUnits } = config;
|
|
514
514
|
|
|
515
|
+
let indexTime = Date.now();
|
|
515
516
|
let candidateBlocksList = allSearchUnits.map(units => this.findBlocks({ units, index })).flat();
|
|
517
|
+
indexTime = Date.now() - indexTime;
|
|
518
|
+
results.indexSearchTime += indexTime;
|
|
519
|
+
results.blockSearchTime -= indexTime;
|
|
516
520
|
let candidateBlocksSet = new Set(candidateBlocksList);
|
|
517
521
|
|
|
518
522
|
|
|
@@ -10,7 +10,7 @@ import { getArchivesBackblaze, getArchivesBackblazePrivateImmutable } from "../.
|
|
|
10
10
|
import { getMachineId, getOwnThreadId } from "../../../-a-auth/certs";
|
|
11
11
|
import { ArchivesMemoryCacheStats, createArchivesMemoryCache } from "../../../-a-archives/archivesMemoryCache";
|
|
12
12
|
import { registerShutdownHandler } from "../../periodic";
|
|
13
|
-
import { measureBlock, measureFnc } from "socket-function/src/profiling/measure";
|
|
13
|
+
import { measureBlock, measureFnc, measureWrap } from "socket-function/src/profiling/measure";
|
|
14
14
|
import { isNode } from "typesafecss";
|
|
15
15
|
import { getOwnMachineId, isNodeIdOnOwnMachineId, isOwnNodeId } from "../../../-f-node-discovery/NodeDiscovery";
|
|
16
16
|
import { TimeFilePath, TimeFileTree } from "./TimeFileTree";
|
|
@@ -27,7 +27,8 @@ import { getAllNodeIds } from "../../../-f-node-discovery/NodeDiscovery";
|
|
|
27
27
|
import { NodeCapabilitiesController } from "../../../-g-core-values/NodeCapabilities";
|
|
28
28
|
import { getLoggers2Async } from "../diskLogger";
|
|
29
29
|
import { watchAllValues } from "../errorNotifications2/logWatcher";
|
|
30
|
-
|
|
30
|
+
|
|
31
|
+
// Ensure it's available so that the controller is listening on all servers
|
|
31
32
|
watchAllValues;
|
|
32
33
|
|
|
33
34
|
export type TimeFilePathWithSize = TimeFilePath & {
|
|
@@ -92,11 +93,11 @@ export class IndexedLogs<T> {
|
|
|
92
93
|
});
|
|
93
94
|
return archives;
|
|
94
95
|
});
|
|
95
|
-
private
|
|
96
|
+
private getPublicLogsBase(loadPublic: boolean): Archives {
|
|
96
97
|
let basePublic: Archives = getArchivesHome(getDomain());
|
|
97
98
|
// NOTE: The local disk is so fast that reading in 10 megabytes is nothing, And if we read in too small of a value, the overhead per read ends up making this take forever.
|
|
98
99
|
let extraReadSize = 1024 * 1024 * 10;
|
|
99
|
-
if (
|
|
100
|
+
if (loadPublic) {
|
|
100
101
|
basePublic = getArchivesBackblaze(getDomain());
|
|
101
102
|
// NOTE: While the latency to back plays is high, now we're reading in parallel, so it shouldn't be as big of an issue.
|
|
102
103
|
extraReadSize = 1024 * 1024 * 1;
|
|
@@ -111,6 +112,12 @@ export class IndexedLogs<T> {
|
|
|
111
112
|
sizeCache: this.fileSizeCache,
|
|
112
113
|
});
|
|
113
114
|
return archives;
|
|
115
|
+
};
|
|
116
|
+
private getPublicLogs = lazy((): Archives => {
|
|
117
|
+
return this.getPublicLogsBase(isPublic());
|
|
118
|
+
});
|
|
119
|
+
private getRealPublicLogs = lazy((): Archives => {
|
|
120
|
+
return this.getPublicLogsBase(true);
|
|
114
121
|
});
|
|
115
122
|
|
|
116
123
|
private getTimeBlock(time: number): { startTime: number, endTime: number } {
|
|
@@ -374,7 +381,10 @@ export class IndexedLogs<T> {
|
|
|
374
381
|
onResults?: (results: IndexedLogResults) => Promise<boolean>;
|
|
375
382
|
}): Promise<IndexedLogResults> {
|
|
376
383
|
|
|
377
|
-
|
|
384
|
+
let { params } = config;
|
|
385
|
+
if (params.pathOverrides && config.params.only === "public") {
|
|
386
|
+
// Fine, if they provided path overrides, and they are all public, just read them, as they'll resolve the same no matter where we read from
|
|
387
|
+
} else if (config.params.forceReadProduction && !isPublic()) {
|
|
378
388
|
let machineNodes = await this.getMachineNodes();
|
|
379
389
|
if (machineNodes.length === 0) throw new Error(`Cannot find any public nodes to read from`);
|
|
380
390
|
return await this.clientFind({
|
|
@@ -391,13 +401,14 @@ export class IndexedLogs<T> {
|
|
|
391
401
|
for (let result of allResults.values()) {
|
|
392
402
|
results = mergeIndexedLogResults(results, result);
|
|
393
403
|
}
|
|
404
|
+
|
|
394
405
|
return results;
|
|
395
406
|
};
|
|
396
407
|
let allResults = new Map<string, IndexedLogResults>();
|
|
397
408
|
let allDone: Promise<void>[] = [];
|
|
398
409
|
|
|
399
410
|
// Read other nodes if on a public server
|
|
400
|
-
if (config.params.only !== "
|
|
411
|
+
if (config.params.only !== "public" && isPublic()) {
|
|
401
412
|
let machineNodes = await this.getMachineNodes();
|
|
402
413
|
allDone.push(...machineNodes.map(async (machineNode) => {
|
|
403
414
|
try {
|
|
@@ -501,9 +512,13 @@ export class IndexedLogs<T> {
|
|
|
501
512
|
onResult: (match: T) => void;
|
|
502
513
|
results: IndexedLogResults;
|
|
503
514
|
}): Promise<void> {
|
|
515
|
+
let results = config.results;
|
|
504
516
|
let startTime = Date.now();
|
|
505
517
|
let localLogs = this.getLocalLogs();
|
|
506
518
|
let backblazeLogs = this.getPublicLogs();
|
|
519
|
+
if (!isPublic() && config.params.forceReadProduction) {
|
|
520
|
+
backblazeLogs = this.getRealPublicLogs();
|
|
521
|
+
}
|
|
507
522
|
|
|
508
523
|
let fileFindTime = Date.now();
|
|
509
524
|
let paths = config.params.pathOverrides ?? await this.getPaths({
|
|
@@ -518,6 +533,10 @@ export class IndexedLogs<T> {
|
|
|
518
533
|
}
|
|
519
534
|
fileFindTime = Date.now() - fileFindTime;
|
|
520
535
|
|
|
536
|
+
for (let path of paths) {
|
|
537
|
+
results.backblazeUncompressedSize += path.uncompressedSize ?? 0;
|
|
538
|
+
}
|
|
539
|
+
|
|
521
540
|
// Sort by time based on search direction
|
|
522
541
|
if (config.params.searchFromStart) {
|
|
523
542
|
sort(paths, x => x.startTime);
|
|
@@ -525,7 +544,6 @@ export class IndexedLogs<T> {
|
|
|
525
544
|
sort(paths, x => - x.startTime);
|
|
526
545
|
}
|
|
527
546
|
|
|
528
|
-
let results = config.results;
|
|
529
547
|
let localPaths = paths.filter(x => x.logCount === undefined);
|
|
530
548
|
let remotePaths = paths.filter(x => x.logCount !== undefined);
|
|
531
549
|
results.totalLocalFiles = localPaths.length;
|
|
@@ -547,10 +565,10 @@ export class IndexedLogs<T> {
|
|
|
547
565
|
});
|
|
548
566
|
|
|
549
567
|
|
|
550
|
-
const searchPath = async (path: TimeFilePathWithSize) => {
|
|
568
|
+
const searchPath = measureWrap(async (path: TimeFilePathWithSize) => {
|
|
551
569
|
if (!progressTracker.isSourceRelevant(path)) return;
|
|
552
570
|
// Wait, so we don't lock up the main thread?
|
|
553
|
-
await delay(0);
|
|
571
|
+
//await delay(0);
|
|
554
572
|
|
|
555
573
|
let remote = path.logCount !== undefined;
|
|
556
574
|
let archives = remote ? backblazeLogs : localLogs;
|
|
@@ -578,9 +596,7 @@ export class IndexedLogs<T> {
|
|
|
578
596
|
return data;
|
|
579
597
|
},
|
|
580
598
|
};
|
|
581
|
-
let
|
|
582
|
-
let blockSearchTimeBefore = results.blockSearchTime;
|
|
583
|
-
await BufferIndex.find({
|
|
599
|
+
let timeResult = await BufferIndex.find({
|
|
584
600
|
index,
|
|
585
601
|
dataReader,
|
|
586
602
|
params: {
|
|
@@ -594,22 +610,21 @@ export class IndexedLogs<T> {
|
|
|
594
610
|
},
|
|
595
611
|
results,
|
|
596
612
|
});
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
results.indexSearchTime += totalSearchTime - blockSearchTimeAdded;
|
|
613
|
+
results.indexSearchTime += timeResult.indexSearchTime;
|
|
614
|
+
results.blockSearchTime += timeResult.blockSearchTime;
|
|
600
615
|
|
|
601
616
|
} catch (e: any) {
|
|
602
617
|
results.fileErrors.push({ error: String(e?.stack || e), path: path.fullPath });
|
|
603
618
|
console.warn(`Error in reading log file ${path.fullPath} logs: ${e}`);
|
|
604
619
|
}
|
|
605
|
-
};
|
|
620
|
+
});
|
|
621
|
+
|
|
606
622
|
let localDone = (async () => {
|
|
607
623
|
for (let path of localPaths) {
|
|
608
624
|
await searchPath(path);
|
|
609
625
|
}
|
|
610
626
|
})();
|
|
611
|
-
|
|
612
|
-
let parallelCount = isPublic() ? 32 : 1;
|
|
627
|
+
let parallelCount = 32;
|
|
613
628
|
let remoteParallel = runInParallel({ parallelCount }, searchPath);
|
|
614
629
|
await Promise.all(remotePaths.map(remoteParallel));
|
|
615
630
|
await localDone;
|
|
@@ -38,7 +38,7 @@ import { startTimeParam, endTimeParam } from "../TimeRangeSelector";
|
|
|
38
38
|
import { formatSearchString } from "./LogViewerParams";
|
|
39
39
|
|
|
40
40
|
let excludePendingResults = new URLParam("excludePendingResults", false);
|
|
41
|
-
let limitURL = new URLParam("limit",
|
|
41
|
+
let limitURL = new URLParam("limit", 16);
|
|
42
42
|
|
|
43
43
|
let savedPathsURL = new URLParam("savedPaths", "");
|
|
44
44
|
let selectedFieldsURL = new URLParam("selectedFields", {} as Record<string, boolean>);
|
|
@@ -298,7 +298,7 @@ export class LogViewer3 extends qreact.Component {
|
|
|
298
298
|
limit: limitURL.value,
|
|
299
299
|
findBuffer: searchBuffer,
|
|
300
300
|
pathOverrides: paths,
|
|
301
|
-
only: excludePendingResults.value ? "
|
|
301
|
+
only: excludePendingResults.value ? "public" : undefined,
|
|
302
302
|
forceReadProduction: readProductionLogsURL.value,
|
|
303
303
|
searchFromStart: range.searchFromStart,
|
|
304
304
|
},
|
|
@@ -67,21 +67,23 @@ export class RenderSearchStats extends qreact.Component<{
|
|
|
67
67
|
|
|
68
68
|
const fileItems = [
|
|
69
69
|
`${formatTime(stats.fileFindTime)} | ${formatNumber(stats.localFilesSearched + stats.backblazeFilesSearched)} / ${formatNumber(stats.totalLocalFiles + stats.totalBackblazeFiles)} | ${formatNumber(totalSize)}B`,
|
|
70
|
-
`remote ${formatNumber(stats.backblazeFilesSearched)} / ${formatNumber(stats.totalBackblazeFiles)} | ${formatNumber(
|
|
70
|
+
`remote ${formatNumber(stats.backblazeFilesSearched)} / ${formatNumber(stats.totalBackblazeFiles)} | ${formatNumber(stats.backblazeUncompressedSize)}B uncomp | ${formatNumber(stats.backblazeUncompressedSize / remoteTotalSize)}X | ${formatNumber(stats.backblazeLogsSearched)} logs`,
|
|
71
71
|
`pending ${formatNumber(stats.localFilesSearched)} / ${formatNumber(stats.totalLocalFiles)} | ${formatNumber(localTotalSize)}B`,
|
|
72
72
|
];
|
|
73
73
|
|
|
74
74
|
const totalIndexesSearched = stats.remoteIndexesSearched + stats.localIndexesSearched;
|
|
75
75
|
const totalIndexSize = stats.remoteIndexSize + stats.localIndexSize;
|
|
76
76
|
const indexItems = [
|
|
77
|
-
`${formatTime(stats.indexSearchTime)} | ${formatNumber(totalIndexesSearched)} | ${formatNumber(totalIndexSize)}B
|
|
78
|
-
`remote ${formatNumber(stats.
|
|
79
|
-
`local ${formatNumber(stats.localIndexesSearched)} | ${formatNumber(stats.localIndexSize)}B
|
|
77
|
+
`${formatTime(stats.indexSearchTime)} | ${formatNumber(totalIndexesSearched)} | ${formatNumber(totalIndexSize)}B`,
|
|
78
|
+
`remote ${formatNumber(stats.remoteIndexSize)}`,
|
|
79
|
+
`local ${formatNumber(stats.localIndexesSearched)} | ${formatNumber(stats.localIndexSize)}B`,
|
|
80
80
|
];
|
|
81
81
|
|
|
82
82
|
const blockItems = [
|
|
83
|
-
<div title={`Scanned size = ${formatNumber(stats.blocksCheckedCompressedSize)}B, Decompressed size = ${formatNumber(stats.blocksCheckedDecompressedSize)}B, Compression ratio = ${formatNumber(stats.blocksCheckedDecompressedSize / stats.blocksCheckedCompressedSize)}X`}>
|
|
84
|
-
|
|
83
|
+
<div title={`Scanned size = ${formatNumber(stats.blocksCheckedCompressedSize)}B, Decompressed size = ${formatNumber(stats.blocksCheckedDecompressedSize)}B, Compression ratio = ${formatNumber(stats.blocksCheckedDecompressedSize / stats.blocksCheckedCompressedSize)}X`}>
|
|
84
|
+
{formatTime(stats.blockSearchTime)} | {formatNumber(stats.blockCheckedCount)} | {formatNumber(stats.blocksCheckedDecompressedSize)}B uncomp | {formatNumber(stats.blocksCheckedDecompressedSize / stats.blocksCheckedCompressedSize)}X
|
|
85
|
+
</div>,
|
|
86
|
+
`scanned ${formatNumber(stats.blockCheckedCount)} (${formatPercent(stats.blockCheckedCount / stats.totalBlockCount)})`,
|
|
85
87
|
`remote ${formatNumber(stats.remoteBlockCheckedCount)} / ${formatNumber(stats.remoteBlockCount)} (${formatPercent(stats.remoteBlockCheckedCount / stats.remoteBlockCount)}) | local ${formatNumber(stats.localBlockCheckedCount)} / ${formatNumber(stats.localBlockCount)} (${formatPercent(stats.localBlockCheckedCount / stats.localBlockCount)})`,
|
|
86
88
|
];
|
|
87
89
|
let cacheItems = [
|
|
@@ -137,16 +137,16 @@ export class PrimitiveDisplay extends qreact.Component<{
|
|
|
137
137
|
render() {
|
|
138
138
|
let value = this.props.value;
|
|
139
139
|
if (typeof value === "number") {
|
|
140
|
-
return <span className={css.boldStyle.hslcolor(265, 70,
|
|
140
|
+
return <span className={css.boldStyle.hslcolor(265, 70, 70)}>{value}</span>;
|
|
141
141
|
}
|
|
142
142
|
if (typeof value === "boolean") {
|
|
143
|
-
return <span className={css.boldStyle.hslcolor(207, 70,
|
|
143
|
+
return <span className={css.boldStyle.hslcolor(207, 70, 70)}>{JSON.stringify(value)}</span>;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
if (typeof value === "string") {
|
|
147
147
|
let colors = parseAnsiColors(value);
|
|
148
148
|
if (colors.length === 1 && !colors[0].color) {
|
|
149
|
-
return <span className={css.boldStyle.hslcolor(30, 70,
|
|
149
|
+
return <span className={css.boldStyle.hslcolor(30, 70, 70)}>"{value}"</span>;
|
|
150
150
|
}
|
|
151
151
|
return (
|
|
152
152
|
<span className={css.display("inline-flex").rowGap(4).columnGap(1).wrap}>
|
|
@@ -155,7 +155,7 @@ export class PrimitiveDisplay extends qreact.Component<{
|
|
|
155
155
|
return <span
|
|
156
156
|
className={
|
|
157
157
|
(hue !== undefined && css.boldStyle.pad2(4, 1).hsla(hue, 70, 50, 0.5) || "")
|
|
158
|
-
+ (hue === undefined && css.boldStyle.hslcolor(30, 70,
|
|
158
|
+
+ (hue === undefined && css.boldStyle.hslcolor(30, 70, 70))
|
|
159
159
|
}
|
|
160
160
|
>{text}</span>;
|
|
161
161
|
})}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { qreact } from "../../../4-dom/qreact";
|
|
2
|
+
import { Table } from "../../../5-diagnostics/Table";
|
|
3
|
+
import { formatValue } from "../../../5-diagnostics/GenericFormat";
|
|
4
|
+
import { LifeCycle, getVariables } from "./lifeCycles";
|
|
5
|
+
import { LifecycleInstance } from "./lifeCycleSearch";
|
|
6
|
+
|
|
7
|
+
export class LifeCycleInstanceTableView extends qreact.Component<{
|
|
8
|
+
lifeCycle: LifeCycle;
|
|
9
|
+
instance: LifecycleInstance;
|
|
10
|
+
}> {
|
|
11
|
+
render() {
|
|
12
|
+
let { lifeCycle, instance } = this.props;
|
|
13
|
+
|
|
14
|
+
let columnKeyToTitle = new Map<string, string>();
|
|
15
|
+
let allColumnKeys = new Set<string>();
|
|
16
|
+
|
|
17
|
+
for (let entryData of instance.entries) {
|
|
18
|
+
let entry = lifeCycle.entries.find(e => e.matchPattern === entryData.matchPattern);
|
|
19
|
+
if (!entry) continue;
|
|
20
|
+
|
|
21
|
+
let variables = getVariables(entry);
|
|
22
|
+
for (let variable of variables) {
|
|
23
|
+
allColumnKeys.add(variable.key);
|
|
24
|
+
if (variable.title && !columnKeyToTitle.has(variable.key)) {
|
|
25
|
+
columnKeyToTitle.set(variable.key, variable.title);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let rows = instance.entries.map((entryData) => {
|
|
31
|
+
let entry = lifeCycle.entries.find(e => e.matchPattern === entryData.matchPattern);
|
|
32
|
+
let entryTitle = entry && (entry.description || entry.matchPattern) || entryData.matchPattern;
|
|
33
|
+
|
|
34
|
+
let row: { entryTitle: string;[key: string]: unknown } = {
|
|
35
|
+
entryTitle: entryTitle,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
if (entry) {
|
|
39
|
+
let variables = getVariables(entry);
|
|
40
|
+
for (let variable of variables) {
|
|
41
|
+
row[variable.key] = entryData.datum[variable.key];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return row;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
let columns: any = {
|
|
49
|
+
entryTitle: { title: "Entry" },
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
for (let columnKey of allColumnKeys) {
|
|
53
|
+
columns[columnKey] = {
|
|
54
|
+
title: columnKeyToTitle.get(columnKey) || columnKey,
|
|
55
|
+
formatter: (value: unknown) => formatValue(value),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return <Table rows={rows} columns={columns} />;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { qreact } from "../../../4-dom/qreact";
|
|
2
2
|
import { t } from "../../../2-proxy/schema2";
|
|
3
3
|
import { css } from "typesafecss";
|
|
4
|
-
import { deepCloneJSON, nextId } from "socket-function/src/misc";
|
|
4
|
+
import { deepCloneJSON, nextId, sort } from "socket-function/src/misc";
|
|
5
5
|
import { Querysub } from "../../../4-querysub/QuerysubController";
|
|
6
6
|
import { InputLabel } from "../../../library-components/InputLabel";
|
|
7
7
|
import { Button } from "../../../library-components/Button";
|
|
@@ -27,9 +27,12 @@ import { StartEllipsis } from "../../../library-components/StartEllipsis";
|
|
|
27
27
|
import { LifeCycleEntryReadMode } from "./LifeCycleEntryReadMode";
|
|
28
28
|
import { LifeCycleEntryEditor } from "./LifeCycleEntryEditor";
|
|
29
29
|
import { LifeCycleRenderer, LifeCycleInstanceRenderer } from "./LifeCycleRenderer";
|
|
30
|
+
import { readProductionLogsURL } from "../IndexedLogs/LogViewerParams";
|
|
31
|
+
import { isPublic } from "../../../config";
|
|
30
32
|
export let lifecycleIdURL = new URLParam("lifecycleid", "");
|
|
31
|
-
export let limitURL = new URLParam("lifecyclelimit",
|
|
33
|
+
export let limitURL = new URLParam("lifecyclelimit", 16);
|
|
32
34
|
export let additionalSearchURL = new URLParam("lifecyclesearch", "");
|
|
35
|
+
export let lifecycleTableViewURL = new URLParam("lifecycletable", false);
|
|
33
36
|
|
|
34
37
|
export class LifeCyclePage extends qreact.Component {
|
|
35
38
|
controller = LifeCyclesController(SocketFunction.browserNodeId());
|
|
@@ -67,9 +70,10 @@ export class LifeCyclePage extends qreact.Component {
|
|
|
67
70
|
<div>Loading...</div>
|
|
68
71
|
</div>;
|
|
69
72
|
}
|
|
70
|
-
|
|
73
|
+
lifeCycles = deepCloneJSON(lifeCycles);
|
|
74
|
+
sort(lifeCycles, x => -x.id.split("_")[0]);
|
|
71
75
|
|
|
72
|
-
let filteredLifeCycles =
|
|
76
|
+
let filteredLifeCycles = lifeCycles.filter(x =>
|
|
73
77
|
matchFilter({ value: this.state.filterText }, JSON.stringify(x))
|
|
74
78
|
);
|
|
75
79
|
|
|
@@ -81,6 +85,7 @@ export class LifeCyclePage extends qreact.Component {
|
|
|
81
85
|
filteredLifeCycles = filteredLifeCycles.filter(lc => lc.id === this.state.searchingLifeCycleId);
|
|
82
86
|
}
|
|
83
87
|
|
|
88
|
+
|
|
84
89
|
return <div className={css.vbox(16).pad2(16).fillWidth}>
|
|
85
90
|
<style>{`
|
|
86
91
|
.LifeCycleInstanceRenderer:has(.LifeCycleEntryEditor:hover) {
|
|
@@ -94,6 +99,12 @@ export class LifeCyclePage extends qreact.Component {
|
|
|
94
99
|
number
|
|
95
100
|
url={limitURL}
|
|
96
101
|
/>
|
|
102
|
+
{!isPublic() && <InputLabelURL
|
|
103
|
+
checkbox
|
|
104
|
+
label="Read Production Logs"
|
|
105
|
+
url={readProductionLogsURL}
|
|
106
|
+
/>}
|
|
107
|
+
|
|
97
108
|
<TimeRangeSelector />
|
|
98
109
|
</div>
|
|
99
110
|
|
|
@@ -104,6 +115,7 @@ export class LifeCyclePage extends qreact.Component {
|
|
|
104
115
|
fillWidth
|
|
105
116
|
onKeyDown={(e) => {
|
|
106
117
|
if (e.key === "Enter" && lifecycleIdURL.value) {
|
|
118
|
+
additionalSearchURL.value = e.currentTarget.value;
|
|
107
119
|
void this.searchLifeCycle(lifecycleIdURL.value);
|
|
108
120
|
}
|
|
109
121
|
}}
|
|
@@ -141,7 +153,7 @@ export class LifeCyclePage extends qreact.Component {
|
|
|
141
153
|
}}
|
|
142
154
|
className={css.width(500)}
|
|
143
155
|
/>
|
|
144
|
-
<span>({filteredLifeCycles.length} / {
|
|
156
|
+
<span>({filteredLifeCycles.length} / {lifeCycles.length})</span>
|
|
145
157
|
{this.state.searchingLifeCycleId && (
|
|
146
158
|
<Button
|
|
147
159
|
hue={120}
|
|
@@ -221,6 +233,11 @@ export class LifeCyclePage extends qreact.Component {
|
|
|
221
233
|
<span key={key} className={css.colorhsl(0, 70, 40)}>{key}</span>
|
|
222
234
|
))}
|
|
223
235
|
</div>
|
|
236
|
+
<Button hue={200} onClick={() => {
|
|
237
|
+
console.log(invalid.datum);
|
|
238
|
+
}}>
|
|
239
|
+
Log
|
|
240
|
+
</Button>
|
|
224
241
|
</div>
|
|
225
242
|
))}
|
|
226
243
|
</div>
|
|
@@ -251,7 +268,7 @@ export class LifeCyclePage extends qreact.Component {
|
|
|
251
268
|
)}
|
|
252
269
|
|
|
253
270
|
{this.state.lifecycleInstances.length > 0 && (() => {
|
|
254
|
-
let searchedLifeCycle =
|
|
271
|
+
let searchedLifeCycle = lifeCycles.find(lc => lc.id === this.state.searchingLifeCycleId);
|
|
255
272
|
if (!searchedLifeCycle) return <div>Cannot find life cycle instance {this.state.searchingLifeCycleId}</div>;
|
|
256
273
|
|
|
257
274
|
let lc = searchedLifeCycle;
|
|
@@ -13,9 +13,10 @@ import { niceStringify } from "../../../niceStringify";
|
|
|
13
13
|
import { getPathStr } from "../../../path";
|
|
14
14
|
import { MachineThreadInfo } from "../../MachineThreadInfo";
|
|
15
15
|
import { LifeCycleEntryEditor } from "./LifeCycleEntryEditor";
|
|
16
|
-
import { lifecycleIdURL } from "./LifeCyclePage";
|
|
16
|
+
import { lifecycleIdURL, lifecycleTableViewURL } from "./LifeCyclePage";
|
|
17
17
|
import { LifecycleInstance } from "./lifeCycleSearch";
|
|
18
18
|
import { LifeCycle, LifeCyclesController, LifeCycleEntry } from "./lifeCycles";
|
|
19
|
+
import { LifeCycleInstanceTableView } from "./LifeCycleInstanceTableView";
|
|
19
20
|
|
|
20
21
|
export class LifeCycleRenderer extends qreact.Component<{
|
|
21
22
|
lifeCycle: LifeCycle;
|
|
@@ -200,7 +201,7 @@ export class LifeCycleInstanceRenderer extends qreact.Component<{
|
|
|
200
201
|
}
|
|
201
202
|
title={statusTitle}
|
|
202
203
|
onClick={(e) => {
|
|
203
|
-
if ((e.target as HTMLElement).closest(".
|
|
204
|
+
if ((e.target as HTMLElement).closest(".LifeCycleRenderer-contents")) {
|
|
204
205
|
return;
|
|
205
206
|
}
|
|
206
207
|
this.state.expanded = !this.state.expanded;
|
|
@@ -270,19 +271,37 @@ export class LifeCycleInstanceRenderer extends qreact.Component<{
|
|
|
270
271
|
</div>
|
|
271
272
|
|
|
272
273
|
{this.state.expanded && (
|
|
273
|
-
<div className={css.vbox(8)}>
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
274
|
+
<div className={css.vbox(8) + "LifeCycleRenderer-contents"}>
|
|
275
|
+
<Button
|
|
276
|
+
hue={200}
|
|
277
|
+
onClick={() => {
|
|
278
|
+
lifecycleTableViewURL.value = !lifecycleTableViewURL.value;
|
|
279
|
+
}}
|
|
280
|
+
>
|
|
281
|
+
{lifecycleTableViewURL.value && "List View" || "Table View"}
|
|
282
|
+
</Button>
|
|
283
|
+
|
|
284
|
+
{lifecycleTableViewURL.value && (
|
|
285
|
+
<LifeCycleInstanceTableView
|
|
279
286
|
lifeCycle={lifeCycle}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
287
|
+
instance={instance}
|
|
288
|
+
/>
|
|
289
|
+
) || (
|
|
290
|
+
<div className={css.vbox(8)}>
|
|
291
|
+
{instance.entries.map((entryData, idx) => {
|
|
292
|
+
let entryIndex = lifeCycle.entries.findIndex(e => e.matchPattern === entryData.matchPattern);
|
|
293
|
+
let entry = lifeCycle.entries[entryIndex];
|
|
294
|
+
return <LifeCycleEntryEditor
|
|
295
|
+
key={idx}
|
|
296
|
+
lifeCycle={lifeCycle}
|
|
297
|
+
entry={entry}
|
|
298
|
+
entryIndex={entryIndex}
|
|
299
|
+
defaultEditMode={false}
|
|
300
|
+
datum={entryData.datum}
|
|
301
|
+
/>;
|
|
302
|
+
})}
|
|
303
|
+
</div>
|
|
304
|
+
)}
|
|
286
305
|
</div>
|
|
287
306
|
)}
|
|
288
307
|
</div>;
|
|
@@ -5,7 +5,7 @@ import { getLoggers2Async, LogDatum } from "../diskLogger";
|
|
|
5
5
|
import { IndexedLogResults, createEmptyIndexedLogResults, mergeIndexedLogResults } from "../IndexedLogs/BufferIndexHelpers";
|
|
6
6
|
import { errorToUndefined } from "../../../errors";
|
|
7
7
|
import { unique, maybeUndefined } from "../../../misc";
|
|
8
|
-
import { formatSearchString } from "../IndexedLogs/LogViewerParams";
|
|
8
|
+
import { formatSearchString, readProductionLogsURL } from "../IndexedLogs/LogViewerParams";
|
|
9
9
|
import { createMatchesPatternCached } from "../IndexedLogs/bufferSearchFindMatcher";
|
|
10
10
|
import { getPathStr } from "../../../path";
|
|
11
11
|
import { LifeCycle, LifeCycleEntry, LifeCyclesController } from "./lifeCycles";
|
|
@@ -229,6 +229,7 @@ export function createLifeCycleSearch(
|
|
|
229
229
|
limit: limit,
|
|
230
230
|
findBuffer: searchBuffer,
|
|
231
231
|
searchFromStart: range.searchFromStart,
|
|
232
|
+
forceReadProduction: readProductionLogsURL.value,
|
|
232
233
|
},
|
|
233
234
|
onResult: (match: LogDatum) => {
|
|
234
235
|
results.push(match);
|
|
@@ -423,6 +424,7 @@ export function createLifeCycleSearch(
|
|
|
423
424
|
limit: searchLimit,
|
|
424
425
|
findBuffer: searchBuffer,
|
|
425
426
|
searchFromStart: range.searchFromStart,
|
|
427
|
+
forceReadProduction: readProductionLogsURL.value,
|
|
426
428
|
},
|
|
427
429
|
onResult: (match: LogDatum) => {
|
|
428
430
|
results.push(match);
|
|
@@ -562,7 +564,7 @@ export function createLifeCycleSearch(
|
|
|
562
564
|
}
|
|
563
565
|
}
|
|
564
566
|
|
|
565
|
-
sort(allInstances, x => x.startTime);
|
|
567
|
+
sort(allInstances, x => -x.startTime);
|
|
566
568
|
|
|
567
569
|
return allInstances;
|
|
568
570
|
};
|