querysub 0.343.0 → 0.344.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 +3 -2
- package/src/diagnostics/logs/diskLogger.ts +11 -0
- package/src/diagnostics/logs/errorNotifications/errorDigestEmail.tsx +65 -6
- package/src/diagnostics/logs/errorNotifications/errorDigestEntry.tsx +9 -2
- package/src/diagnostics/logs/errorNotifications/errorDigests.tsx +1 -1
- package/src/misc/cloneHelpers.ts +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "querysub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.344.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",
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"test": "yarn typenode ./test.ts",
|
|
16
16
|
"test3": "yarn typenode ./src/test/test.tsx --local",
|
|
17
17
|
"test2": "yarn typenode ./src/4-dom/qreactTest.tsx --local",
|
|
18
|
-
"error-watch": "yarn typenode ./src/diagnostics/logs/errorNotifications/errorWatchEntry.tsx"
|
|
18
|
+
"error-watch": "yarn typenode ./src/diagnostics/logs/errorNotifications/errorWatchEntry.tsx",
|
|
19
|
+
"error-email": "yarn typenode ./src/diagnostics/logs/errorNotifications/errorDigestEntry.tsx"
|
|
19
20
|
},
|
|
20
21
|
"bin": {
|
|
21
22
|
"deploy": "./bin/deploy.js",
|
|
@@ -40,10 +40,21 @@ export type LogDatum = Record<string, unknown> & {
|
|
|
40
40
|
export function getLogHash(obj: LogDatum) {
|
|
41
41
|
return getPathStr2(obj.__threadId || "", obj.time.toString());
|
|
42
42
|
}
|
|
43
|
+
const errorMessageFileRegex = /\n at .+?\(([^:]+):\d+:\d+\)/;
|
|
44
|
+
|
|
43
45
|
export function getLogFile(obj: LogDatum) {
|
|
44
46
|
let logType = obj.param0 || "";
|
|
45
47
|
if (obj.__FILE__) {
|
|
46
48
|
logType = String(obj.__FILE__);
|
|
49
|
+
// /root/machine-services/*/git/
|
|
50
|
+
if (logType.startsWith("/root/machine-services/")) {
|
|
51
|
+
logType = logType.split("/").slice(4).join("/");
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
let match = logType.match(errorMessageFileRegex);
|
|
55
|
+
if (match) {
|
|
56
|
+
logType = match[1];
|
|
57
|
+
}
|
|
47
58
|
}
|
|
48
59
|
if (obj[LOG_LINE_LIMIT_ID]) {
|
|
49
60
|
logType += "::" + String(obj[LOG_LINE_LIMIT_ID]);
|
|
@@ -23,6 +23,14 @@ export async function sendErrorDigestEmail(digestInfo: ErrorDigestInfo) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
let errors: {
|
|
26
|
+
file: string;
|
|
27
|
+
errorsInFile: number;
|
|
28
|
+
warningsInFile: number;
|
|
29
|
+
message: string;
|
|
30
|
+
messageTime: string;
|
|
31
|
+
}[] = [];
|
|
32
|
+
let warnings: {
|
|
33
|
+
file: string;
|
|
26
34
|
errorsInFile: number;
|
|
27
35
|
warningsInFile: number;
|
|
28
36
|
message: string;
|
|
@@ -38,6 +46,8 @@ export async function sendErrorDigestEmail(digestInfo: ErrorDigestInfo) {
|
|
|
38
46
|
let corruptWarning = "";
|
|
39
47
|
|
|
40
48
|
let failingFiles = 0;
|
|
49
|
+
let errorFiles = 0;
|
|
50
|
+
let warningFiles = 0;
|
|
41
51
|
|
|
42
52
|
for (let value of digestInfo.histogram.values()) {
|
|
43
53
|
errorCount += value.unsuppressedErrors;
|
|
@@ -52,25 +62,44 @@ export async function sendErrorDigestEmail(digestInfo: ErrorDigestInfo) {
|
|
|
52
62
|
if (value.firstCorruptWarning && !corruptWarning) {
|
|
53
63
|
corruptWarning = value.firstCorruptWarning;
|
|
54
64
|
}
|
|
55
|
-
if (value.unsuppressedErrors > 0) {
|
|
56
|
-
failingFiles++;
|
|
57
|
-
}
|
|
58
65
|
}
|
|
59
66
|
|
|
60
|
-
for (let value of digestInfo.byFile
|
|
67
|
+
for (let [file, value] of digestInfo.byFile) {
|
|
61
68
|
for (let error of value.latestErrors.slice(-MAX_COUNT_PER_FILE)) {
|
|
62
69
|
errors.push({
|
|
70
|
+
file,
|
|
63
71
|
errorsInFile: value.errors,
|
|
64
72
|
warningsInFile: value.warnings,
|
|
65
73
|
message: `${error.param0} (${error.__NAME__})`,
|
|
66
74
|
messageTime: formatDateTime(error.time),
|
|
67
75
|
});
|
|
68
76
|
}
|
|
77
|
+
|
|
78
|
+
for (let warning of value.latestWarnings.slice(-MAX_COUNT_PER_FILE)) {
|
|
79
|
+
warnings.push({
|
|
80
|
+
file,
|
|
81
|
+
errorsInFile: value.errors,
|
|
82
|
+
warningsInFile: value.warnings,
|
|
83
|
+
message: `${warning.param0} (${warning.__NAME__})`,
|
|
84
|
+
messageTime: formatDateTime(warning.time),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (value.errors > 0) {
|
|
89
|
+
failingFiles++;
|
|
90
|
+
errorFiles++;
|
|
91
|
+
}
|
|
92
|
+
if (value.warnings > 0) {
|
|
93
|
+
warningFiles++;
|
|
94
|
+
}
|
|
69
95
|
}
|
|
70
96
|
|
|
71
97
|
sort(errors, x => -x.errorsInFile);
|
|
72
98
|
errors = errors.slice(0, MAX_COUNT);
|
|
73
99
|
|
|
100
|
+
sort(warnings, x => -x.warningsInFile);
|
|
101
|
+
warnings = warnings.slice(0, MAX_COUNT);
|
|
102
|
+
|
|
74
103
|
let link = createLink([
|
|
75
104
|
showingManagementURL.getOverride(true),
|
|
76
105
|
managementPageURL.getOverride("ErrorDigestPage"),
|
|
@@ -81,7 +110,7 @@ export async function sendErrorDigestEmail(digestInfo: ErrorDigestInfo) {
|
|
|
81
110
|
await sendEmail({
|
|
82
111
|
to: notifyEmails,
|
|
83
112
|
fromPrefix: "error-digest",
|
|
84
|
-
subject: `${errorCount} err |
|
|
113
|
+
subject: `${errorCount} err | ~${formatNumber(failingFiles)} lines | ${warningCount} warn${corruptErrors + corruptWarnings > 0 ? ` | ${corruptErrors + corruptWarnings} corrupt` : ""} | ${suppressedErrors + suppressedWarnings} hid | ${formatTime(digestInfo.scanDuration)} | ${formatNumber(digestInfo.totalUncompressedBytes)} | ${formatNumber(digestInfo.totalFiles)} log files`,
|
|
85
114
|
contents: <div>
|
|
86
115
|
<h2>Error Summary</h2>
|
|
87
116
|
<ul style="list-style-type: none; padding-left: 0;">
|
|
@@ -89,12 +118,14 @@ export async function sendErrorDigestEmail(digestInfo: ErrorDigestInfo) {
|
|
|
89
118
|
<strong style="color: #dc3545;">Errors:</strong>
|
|
90
119
|
<span style="background-color: #dc3545; color: white; padding: 2px 6px; border-radius: 3px; font-weight: bold; margin-left: 8px;">{errorCount}</span>
|
|
91
120
|
<span style="color: #dc3545;"> unsuppressed</span>
|
|
121
|
+
<span style="color: #6c757d; margin-left: 8px;">({formatNumber(errorFiles)} files)</span>
|
|
92
122
|
{suppressedErrors > 0 && <span>, <span style="color: #6c757d;">{formatNumber(suppressedErrors)} suppressed</span></span>}
|
|
93
123
|
</li>
|
|
94
124
|
<li>
|
|
95
125
|
<strong style="color: #fd7e14;">Warnings:</strong>
|
|
96
126
|
<span style="background-color: #fd7e14; color: white; padding: 2px 6px; border-radius: 3px; font-weight: bold; margin-left: 8px;">{warningCount}</span>
|
|
97
127
|
<span style="color: #fd7e14;"> unsuppressed</span>
|
|
128
|
+
<span style="color: #6c757d; margin-left: 8px;">({formatNumber(warningFiles)} files)</span>
|
|
98
129
|
{suppressedWarnings > 0 && <span>, <span style="color: #6c757d;">{formatNumber(suppressedWarnings)} suppressed</span></span>}
|
|
99
130
|
</li>
|
|
100
131
|
</ul>
|
|
@@ -147,11 +178,12 @@ export async function sendErrorDigestEmail(digestInfo: ErrorDigestInfo) {
|
|
|
147
178
|
<a href={link} style="display: block; margin-bottom: 20px; padding: 10px; background-color: #007bff; color: white; text-decoration: none; border-radius: 4px; text-align: center;">View live logs</a>
|
|
148
179
|
|
|
149
180
|
{errors.length > 0 && <div>
|
|
150
|
-
<h2 style="color: #495057;">Recent Errors (<span style="color: #dc3545; font-weight: bold;">{errors.length}</span> shown)</h2>
|
|
181
|
+
<h2 style="color: #495057;">Recent Errors (<span style="color: #dc3545; font-weight: bold;">{errors.length}</span> files shown, <span style="color: #dc3545; font-weight: bold;">{errors.reduce((acc, error) => acc + error.errorsInFile, 0)}</span> errors)</h2>
|
|
151
182
|
<table style="border-collapse: collapse; width: 100%; margin-top: 10px;">
|
|
152
183
|
<thead>
|
|
153
184
|
<tr style="background-color: #495057; color: white;">
|
|
154
185
|
<th style="border: 1px solid #6c757d; padding: 8px; text-align: left;">Time</th>
|
|
186
|
+
<th style="border: 1px solid #6c757d; padding: 8px; text-align: left;">File</th>
|
|
155
187
|
<th style="border: 1px solid #6c757d; padding: 8px; text-align: left;">Message</th>
|
|
156
188
|
<th style="border: 1px solid #6c757d; padding: 8px; text-align: right; background-color: #dc3545;">Errors in File</th>
|
|
157
189
|
<th style="border: 1px solid #6c757d; padding: 8px; text-align: right; background-color: #fd7e14;">Warnings in File</th>
|
|
@@ -161,6 +193,7 @@ export async function sendErrorDigestEmail(digestInfo: ErrorDigestInfo) {
|
|
|
161
193
|
{errors.map((error, index) => (
|
|
162
194
|
<tr key={index} style={`background-color: ${index % 2 === 0 ? "#f8f9fa" : "white"}`}>
|
|
163
195
|
<td style="border: 1px solid #ddd; padding: 8px;">{error.messageTime}</td>
|
|
196
|
+
<td style="border: 1px solid #ddd; padding: 8px; font-family: monospace; font-size: 12px;">{error.file}</td>
|
|
164
197
|
<td style="border: 1px solid #ddd; padding: 8px;">{error.message}</td>
|
|
165
198
|
<td style="border: 1px solid #ddd; padding: 8px; text-align: right; background-color: #f8d7da; color: #721c24; font-weight: bold;">{error.errorsInFile}</td>
|
|
166
199
|
<td style="border: 1px solid #ddd; padding: 8px; text-align: right; background-color: #fff3cd; color: #856404; font-weight: bold;">{error.warningsInFile}</td>
|
|
@@ -169,6 +202,32 @@ export async function sendErrorDigestEmail(digestInfo: ErrorDigestInfo) {
|
|
|
169
202
|
</tbody>
|
|
170
203
|
</table>
|
|
171
204
|
</div>}
|
|
205
|
+
|
|
206
|
+
{warnings.length > 0 && <div style="margin-top: 20px;">
|
|
207
|
+
<h2 style="color: #495057;">Recent Warnings (<span style="color: #fd7e14; font-weight: bold;">{warnings.length}</span> files shown, <span style="color: #fd7e14; font-weight: bold;">{warnings.reduce((acc, warning) => acc + warning.warningsInFile, 0)}</span> warnings)</h2>
|
|
208
|
+
<table style="border-collapse: collapse; width: 100%; margin-top: 10px;">
|
|
209
|
+
<thead>
|
|
210
|
+
<tr style="background-color: #495057; color: white;">
|
|
211
|
+
<th style="border: 1px solid #6c757d; padding: 8px; text-align: left;">Time</th>
|
|
212
|
+
<th style="border: 1px solid #6c757d; padding: 8px; text-align: left;">File</th>
|
|
213
|
+
<th style="border: 1px solid #6c757d; padding: 8px; text-align: left;">Message</th>
|
|
214
|
+
<th style="border: 1px solid #6c757d; padding: 8px; text-align: right; background-color: #dc3545;">Errors in File</th>
|
|
215
|
+
<th style="border: 1px solid #6c757d; padding: 8px; text-align: right; background-color: #fd7e14;">Warnings in File</th>
|
|
216
|
+
</tr>
|
|
217
|
+
</thead>
|
|
218
|
+
<tbody>
|
|
219
|
+
{warnings.map((warning, index) => (
|
|
220
|
+
<tr key={index} style={`background-color: ${index % 2 === 0 ? "#f8f9fa" : "white"}`}>
|
|
221
|
+
<td style="border: 1px solid #ddd; padding: 8px;">{warning.messageTime}</td>
|
|
222
|
+
<td style="border: 1px solid #ddd; padding: 8px; font-family: monospace; font-size: 12px;">{warning.file}</td>
|
|
223
|
+
<td style="border: 1px solid #ddd; padding: 8px;">{warning.message}</td>
|
|
224
|
+
<td style="border: 1px solid #ddd; padding: 8px; text-align: right; background-color: #f8d7da; color: #721c24; font-weight: bold;">{warning.errorsInFile}</td>
|
|
225
|
+
<td style="border: 1px solid #ddd; padding: 8px; text-align: right; background-color: #fff3cd; color: #856404; font-weight: bold;">{warning.warningsInFile}</td>
|
|
226
|
+
</tr>
|
|
227
|
+
))}
|
|
228
|
+
</tbody>
|
|
229
|
+
</table>
|
|
230
|
+
</div>}
|
|
172
231
|
</div>,
|
|
173
232
|
});
|
|
174
233
|
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Querysub } from "../../../4-querysub/QuerysubController";
|
|
2
|
+
import { runDigest, runDigestLoop } from "./errorDigests";
|
|
2
3
|
|
|
3
4
|
async function main() {
|
|
4
|
-
|
|
5
|
+
if (process.argv.includes("--now")) {
|
|
6
|
+
await Querysub.hostService("error-digests-now");
|
|
7
|
+
|
|
8
|
+
await runDigest();
|
|
9
|
+
} else {
|
|
10
|
+
await runDigestLoop();
|
|
11
|
+
}
|
|
5
12
|
}
|
|
6
13
|
// The digest loop should never exit, and if it does, we probably want to terminate ourselves so that the service manager will restart us, hopefully putting us back in a good state.
|
|
7
14
|
main().catch(console.error).finally(() => process.exit());
|
|
@@ -92,7 +92,7 @@ function getClosest(value: number, choices: number[]) {
|
|
|
92
92
|
return closest;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
async function runDigest() {
|
|
95
|
+
export async function runDigest() {
|
|
96
96
|
console.log("Running error digest gathering");
|
|
97
97
|
// Find the previous day
|
|
98
98
|
let endTime = getClosest(
|
package/src/misc/cloneHelpers.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import cborx from "cbor-x";
|
|
2
2
|
import { lazy } from "socket-function/src/caching";
|
|
3
|
-
const cborxInstance = lazy(() => new cborx.Encoder({
|
|
3
|
+
const cborxInstance = lazy(() => new cborx.Encoder({
|
|
4
|
+
structuredClone: true,
|
|
5
|
+
|
|
6
|
+
}));
|
|
4
7
|
export function deepCloneCborx<T>(value: T): T {
|
|
5
8
|
return decodeCborx(encodeCborx(value));
|
|
6
9
|
}
|