querysub 0.374.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/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 +0 -38
- 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/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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "querysub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.375.0",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"note1": "note on node-forge fork, see https://github.com/digitalbazaar/forge/issues/744 for details",
|
|
@@ -34,9 +34,7 @@
|
|
|
34
34
|
"join": "./bin/join.js",
|
|
35
35
|
"join-public": "./bin/join-public.js",
|
|
36
36
|
"movelogs": "./bin/movelogs.js",
|
|
37
|
-
"addsuperuser": "./bin/addsuperuser.js"
|
|
38
|
-
"error-email": "./bin/error-email.js",
|
|
39
|
-
"error-im": "./bin/error-im.js"
|
|
37
|
+
"addsuperuser": "./bin/addsuperuser.js"
|
|
40
38
|
},
|
|
41
39
|
"dependencies": {
|
|
42
40
|
"@types/fs-ext": "^2.0.3",
|
|
@@ -7,10 +7,10 @@ import { formatNumber, formatVeryNiceDateTime } from "socket-function/src/format
|
|
|
7
7
|
import { sort } from "socket-function/src/misc";
|
|
8
8
|
import { ATag, Anchor } from "../../library-components/ATag";
|
|
9
9
|
import { ShowMore } from "../../library-components/ShowMore";
|
|
10
|
-
import { filterParam } from "../../diagnostics/logs/FastArchiveViewer";
|
|
11
10
|
import { managementPageURL } from "../../diagnostics/managementPages";
|
|
12
11
|
import { t } from "../../2-proxy/schema2";
|
|
13
12
|
import { Querysub } from "../../4-querysub/QuerysubController";
|
|
13
|
+
import { getLogViewerParams } from "../../diagnostics/logs/IndexedLogs/LogViewerParams";
|
|
14
14
|
|
|
15
15
|
export class MachineDetailPage extends qreact.Component {
|
|
16
16
|
render() {
|
|
@@ -105,10 +105,7 @@ export class MachineDetailPage extends qreact.Component {
|
|
|
105
105
|
<div>
|
|
106
106
|
Apply Node ID: {machine.applyNodeId}
|
|
107
107
|
</div>
|
|
108
|
-
<ATag values={
|
|
109
|
-
managementPageURL.getOverride("LogViewer2"),
|
|
110
|
-
filterParam.getOverride(`__machineId = ${machineInfo.machineId}`),
|
|
111
|
-
]}>
|
|
108
|
+
<ATag values={getLogViewerParams({ __machineId: machineInfo.machineId })}>
|
|
112
109
|
Machine Logs
|
|
113
110
|
</ATag>
|
|
114
111
|
</div>
|
|
@@ -20,8 +20,8 @@ import { PrimitiveDisplay } from "../../diagnostics/logs/ObjectDisplay";
|
|
|
20
20
|
import { parseAnsiColors, rgbToHsl } from "../../diagnostics/logs/ansiFormat";
|
|
21
21
|
import { RenderGitRefInfo, UpdateServiceButtons, bigEmoji, buttonStyle } from "./deployButtons";
|
|
22
22
|
import { TypedConfigEditor } from "../../library-components/TypedConfigEditor";
|
|
23
|
-
import { filterParam } from "../../diagnostics/logs/FastArchiveViewer";
|
|
24
23
|
import { managementPageURL } from "../../diagnostics/managementPages";
|
|
24
|
+
import { getLogViewerParams } from "../../diagnostics/logs/IndexedLogs/LogViewerParams";
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
|
|
@@ -401,10 +401,7 @@ export class ServiceDetailPage extends qreact.Component {
|
|
|
401
401
|
{isWatching ? "Stop Watching Output" : "Watch Screen Output"}
|
|
402
402
|
</div>
|
|
403
403
|
|
|
404
|
-
<ATag values={
|
|
405
|
-
managementPageURL.getOverride("LogViewer2"),
|
|
406
|
-
filterParam.getOverride(`__machineId = ${machineId}`),
|
|
407
|
-
]}>
|
|
404
|
+
<ATag values={getLogViewerParams({ __machineId: machineId })}>
|
|
408
405
|
Machine Logs
|
|
409
406
|
</ATag>
|
|
410
407
|
</div>
|
|
@@ -866,6 +866,13 @@ export async function machineApplyMain() {
|
|
|
866
866
|
if (await quickIsOutdated()) {
|
|
867
867
|
console.log(magenta("Likely outdated, resyncing now"));
|
|
868
868
|
await resyncServices();
|
|
869
|
+
} else {
|
|
870
|
+
let config = await machineInfos.get(getOwnMachineId());
|
|
871
|
+
if (config) {
|
|
872
|
+
config.heartbeat = Date.now();
|
|
873
|
+
console.log(magenta(`Updating heartbeat for ${getOwnMachineId()} to ${config.heartbeat}`));
|
|
874
|
+
await machineInfos.set(getOwnMachineId(), config);
|
|
875
|
+
}
|
|
869
876
|
}
|
|
870
877
|
});
|
|
871
878
|
|
|
@@ -36,8 +36,8 @@ import { SocketRegistered } from "socket-function/SocketFunctionTypes";
|
|
|
36
36
|
import { ATag } from "../library-components/ATag";
|
|
37
37
|
import { getSyncedController } from "../library-components/SyncedController";
|
|
38
38
|
import child_process from "child_process";
|
|
39
|
-
import { filterParam } from "./logs/FastArchiveViewer";
|
|
40
39
|
import { getHTTPSKeyCert } from "../-e-certs/certAuthority";
|
|
40
|
+
import { getLogViewerParams } from "./logs/IndexedLogs/LogViewerParams";
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
type NodeData = {
|
|
@@ -296,10 +296,9 @@ export class NodeViewer extends qreact.Component {
|
|
|
296
296
|
if (str.startsWith("http")) return formatValue(obj);
|
|
297
297
|
|
|
298
298
|
return (
|
|
299
|
-
<ATag values={
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
]}>
|
|
299
|
+
<ATag values={getLogViewerParams({
|
|
300
|
+
__nodeId: str,
|
|
301
|
+
})}>
|
|
303
302
|
Logs
|
|
304
303
|
</ATag>
|
|
305
304
|
);
|
|
@@ -272,10 +272,10 @@ export class IndexedLogs<T> {
|
|
|
272
272
|
startTime: number;
|
|
273
273
|
endTime: number;
|
|
274
274
|
only?: "local" | "public";
|
|
275
|
-
|
|
275
|
+
forceReadProduction?: boolean;
|
|
276
276
|
}): Promise<TimeFilePathWithSize[]> {
|
|
277
277
|
let finalPaths: TimeFilePathWithSize[] = [];
|
|
278
|
-
if (config.
|
|
278
|
+
if (config.forceReadProduction && !isPublic()) {
|
|
279
279
|
let machineNodes = await this.getMachineNodes();
|
|
280
280
|
if (machineNodes.length === 0) throw new Error(`Cannot find any public nodes to read from`);
|
|
281
281
|
return await IndexedLogShimController.nodes[machineNodes[0]].getPaths({
|
|
@@ -352,7 +352,7 @@ export class IndexedLogs<T> {
|
|
|
352
352
|
onResults?: (results: IndexedLogResults) => Promise<boolean>;
|
|
353
353
|
}): Promise<IndexedLogResults> {
|
|
354
354
|
|
|
355
|
-
if (config.params.
|
|
355
|
+
if (config.params.forceReadProduction && !isPublic()) {
|
|
356
356
|
let machineNodes = await this.getMachineNodes();
|
|
357
357
|
if (machineNodes.length === 0) throw new Error(`Cannot find any public nodes to read from`);
|
|
358
358
|
return await this.clientFind({
|
|
@@ -649,7 +649,7 @@ export class IndexedLogs<T> {
|
|
|
649
649
|
startTime: number;
|
|
650
650
|
endTime: number;
|
|
651
651
|
only?: "local" | "public";
|
|
652
|
-
|
|
652
|
+
forceReadProduction?: boolean;
|
|
653
653
|
|
|
654
654
|
}): Promise<TimeFilePathWithSize[]> {
|
|
655
655
|
let controller = IndexedLogShimController.nodes[SocketFunction.getBrowserNodeId()];
|
|
@@ -658,7 +658,7 @@ export class IndexedLogs<T> {
|
|
|
658
658
|
startTime: config.startTime,
|
|
659
659
|
endTime: config.endTime,
|
|
660
660
|
only: config.only,
|
|
661
|
-
|
|
661
|
+
forceReadProduction: config.forceReadProduction,
|
|
662
662
|
});
|
|
663
663
|
}
|
|
664
664
|
public onFindResult(config: {
|
|
@@ -778,7 +778,7 @@ class IndexedLogShim {
|
|
|
778
778
|
startTime: number;
|
|
779
779
|
endTime: number;
|
|
780
780
|
only?: "local" | "public";
|
|
781
|
-
|
|
781
|
+
forceReadProduction?: boolean;
|
|
782
782
|
}): Promise<TimeFilePathWithSize[]> {
|
|
783
783
|
let indexedLogs = loggerByName.get(config.indexedLogsName);
|
|
784
784
|
if (!indexedLogs) throw new Error(`Indexed logs ${config.indexedLogsName} not found`);
|
|
@@ -786,7 +786,7 @@ class IndexedLogShim {
|
|
|
786
786
|
startTime: config.startTime,
|
|
787
787
|
endTime: config.endTime,
|
|
788
788
|
only: config.only,
|
|
789
|
-
|
|
789
|
+
forceReadProduction: config.forceReadProduction,
|
|
790
790
|
});
|
|
791
791
|
}
|
|
792
792
|
|
|
@@ -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>
|