querysub 0.374.0 → 0.376.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/package.json +2 -4
  2. package/src/deployManager/components/MachineDetailPage.tsx +2 -5
  3. package/src/deployManager/components/ServiceDetailPage.tsx +2 -5
  4. package/src/deployManager/machineApplyMainCode.ts +7 -0
  5. package/src/diagnostics/NodeViewer.tsx +4 -5
  6. package/src/diagnostics/logs/IndexedLogs/BufferIndex.ts +10 -5
  7. package/src/diagnostics/logs/IndexedLogs/BufferIndexCPP.cpp +20 -0
  8. package/src/diagnostics/logs/IndexedLogs/BufferIndexHelpers.ts +29 -2
  9. package/src/diagnostics/logs/IndexedLogs/BufferUnitIndex.ts +61 -20
  10. package/src/diagnostics/logs/IndexedLogs/BufferUnitSet.ts +2 -2
  11. package/src/diagnostics/logs/IndexedLogs/IndexedLogs.ts +7 -7
  12. package/src/diagnostics/logs/IndexedLogs/LogViewer3.tsx +250 -243
  13. package/src/diagnostics/logs/IndexedLogs/LogViewerParams.ts +21 -0
  14. package/src/diagnostics/logs/IndexedLogs/{bufferMatcher.ts → bufferSearchFindMatcher.ts} +9 -4
  15. package/src/diagnostics/logs/IndexedLogs/moveIndexLogsToPublic.ts +3 -3
  16. package/src/diagnostics/logs/diskLogger.ts +0 -38
  17. package/src/diagnostics/logs/errorNotifications2/errorNotifications2.ts +9 -0
  18. package/src/diagnostics/logs/injectFileLocationToConsole.ts +3 -0
  19. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycles.tsx +24 -22
  20. package/src/diagnostics/managementPages.tsx +0 -18
  21. package/test.ts +0 -5
  22. package/bin/error-email.js +0 -8
  23. package/bin/error-im.js +0 -8
  24. package/src/diagnostics/logs/FastArchiveAppendable.ts +0 -843
  25. package/src/diagnostics/logs/FastArchiveController.ts +0 -573
  26. package/src/diagnostics/logs/FastArchiveViewer.tsx +0 -1090
  27. package/src/diagnostics/logs/LogViewer2.tsx +0 -552
  28. package/src/diagnostics/logs/errorNotifications/ErrorDigestPage.tsx +0 -409
  29. package/src/diagnostics/logs/errorNotifications/ErrorNotificationController.ts +0 -756
  30. package/src/diagnostics/logs/errorNotifications/ErrorSuppressionUI.tsx +0 -280
  31. package/src/diagnostics/logs/errorNotifications/ErrorWarning.tsx +0 -254
  32. package/src/diagnostics/logs/errorNotifications/errorDigestEmail.tsx +0 -233
  33. package/src/diagnostics/logs/errorNotifications/errorDigestEntry.tsx +0 -14
  34. package/src/diagnostics/logs/errorNotifications/errorDigests.tsx +0 -292
  35. package/src/diagnostics/logs/errorNotifications/errorWatchEntry.tsx +0 -209
  36. package/src/diagnostics/logs/importLogsEntry.ts +0 -38
  37. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCyclePages.tsx +0 -150
  38. package/src/diagnostics/logs/logViewerExtractField.ts +0 -36
@@ -1,209 +0,0 @@
1
- import { batchFunction, runInfinitePollCallAtStart } from "socket-function/src/batching";
2
- import { getControllerNodeId } from "../../../-g-core-values/NodeCapabilities";
3
- import { RecentErrors, RecentErrorsController, errorWatcherBase, recentErrorsChannel, suppressionList, watchRecentErrors } from "./ErrorNotificationController";
4
- import { sort, timeInDay, timeInHour, timeInMinute, timeInSecond } from "socket-function/src/misc";
5
- import { formatDate, formatDateTime } from "socket-function/src/formatting/format";
6
- import { getDomain } from "../../../config";
7
- import { Querysub, QuerysubController } from "../../../4-querysub/QuerysubController";
8
- import { formatPercent } from "socket-function/src/formatting/format";
9
- import { LogDatum, getLogFile } from "../diskLogger";
10
- import { sendDiscordMessage } from "../../../email_ims_notifications/discord";
11
- import { user_data } from "../../../user-implementation/userData";
12
- import { createLink } from "../../../library-components/ATag";
13
- import { getErrorLogsLink } from "./ErrorWarning";
14
- import { magenta } from "socket-function/src/formatting/logColors";
15
- import { ellipsisMiddle } from "../../../misc";
16
-
17
- const MAX_IMS_PER_DAY = 3;
18
- const MAX_IMS_PER_HOURS = 1;
19
- const MAX_IMS_PER_FILE_PER_DAY = 1;
20
- const MAX_PER_FILE_PER_IM = 2;
21
- const MAX_PER_IM = 10;
22
-
23
- // Wait a bit, because it's likely if there's one error, there are more errors.
24
- const BATCH_TIME = timeInSecond * 30;
25
-
26
-
27
- // NOTE: Yes, this is stored in memory, so if the server reboots or if this script keeps crashing, we might send a lot of instant messages. However, one, Discord will probably late rate limit us, and two, this means something is really wrong, especially if it happens a lot, and we really should fix it right away.
28
- // NOTE: If we decide not to send IMs, we don't queue them up, because that's just extremely annoying. The point of the limit isn't to send the maximum we can that stays just under the limit! The point of the limit is if a lot happens at once, ignore most of it.
29
- type IMInfo = {
30
- time: number;
31
- perFile: {
32
- [file: string]: LogDatum[];
33
- };
34
- };
35
-
36
- let imHistory: IMInfo[] = [];
37
- function filterIMInfo(info: IMInfo): {
38
- info: IMInfo;
39
- countFiltered: number;
40
- } {
41
- let countFiltered = 0;
42
- // Don't prefer the most warnings, prefer The oldest warnings, which are generally the ones that are first in the object.
43
- // filter based on files per im, and max per im
44
- for (let [key, value] of Object.entries(info.perFile)) {
45
- if (value.length > MAX_PER_FILE_PER_IM) {
46
- countFiltered += value.length - MAX_PER_FILE_PER_IM;
47
- value = value.slice(0, MAX_PER_FILE_PER_IM);
48
- }
49
- info.perFile[key] = value;
50
- }
51
- let entries = Object.entries(info.perFile);
52
- if (entries.length > MAX_PER_IM) {
53
- let removed = entries.slice(MAX_PER_IM);
54
- for (let [key, value] of removed) {
55
- countFiltered += value.length;
56
- }
57
- entries = entries.slice(0, MAX_PER_IM);
58
- }
59
- info.perFile = Object.fromEntries(entries);
60
- // Also ignore files if they've been mentioned too many times today.
61
- let dayThreshold = Date.now() - timeInDay;
62
- let historyInDay = imHistory.filter(x => x.time > dayThreshold);
63
- let countByFile = new Map<string, number>();
64
- for (let obj of historyInDay) {
65
- for (let [key, value] of Object.entries(obj.perFile)) {
66
- countByFile.set(key, (countByFile.get(key) || 0) + value.length);
67
- }
68
- }
69
- for (let key of Object.keys(info.perFile)) {
70
- let count = countByFile.get(key) || 0;
71
- if (count >= MAX_IMS_PER_FILE_PER_DAY) {
72
- countFiltered += count;
73
- delete info.perFile[key];
74
- }
75
- }
76
- return {
77
- info,
78
- countFiltered,
79
- };
80
- }
81
- function canSendNow(info: IMInfo) {
82
- let dayThreshold = Date.now() - timeInDay;
83
- let historyInDay = imHistory.filter(x => x.time > dayThreshold);
84
- let hourThreshold = Date.now() - timeInHour;
85
- let historyInHour = historyInDay.filter(x => x.time > hourThreshold);
86
-
87
- if (historyInDay.length >= MAX_IMS_PER_DAY) {
88
- console.log(magenta(`Not sending IM, because we've sent ${historyInDay.length} > ${MAX_IMS_PER_DAY} IMs in the last day`));
89
- return false;
90
- }
91
- if (historyInHour.length >= MAX_IMS_PER_HOURS) {
92
- console.log(magenta(`Not sending IM, because we've sent ${historyInHour.length} > ${MAX_IMS_PER_HOURS} IMs in the last hour`));
93
- return false;
94
- }
95
- return true;
96
- }
97
-
98
- const sendIMs = batchFunction(({ delay: BATCH_TIME }), async (logsAll: LogDatum[][]) => {
99
- let logs = logsAll.flat();
100
- let infoBase: IMInfo = {
101
- time: Date.now(),
102
- perFile: {},
103
- };
104
- for (let log of logs) {
105
- let file = getLogFile(log);
106
- let array = infoBase.perFile[file];
107
- if (!array) {
108
- array = [];
109
- infoBase.perFile[file] = array;
110
- }
111
- array.push(log);
112
- }
113
- let { info, countFiltered } = filterIMInfo(infoBase);
114
- if (canSendNow(info)) {
115
- imHistory.push(info);
116
- let webhookURL = await Querysub.commitAsync(() => {
117
- return user_data().secure.notifyDiscordWebhookURL;
118
- });
119
- if (!webhookURL) {
120
- console.error(`No Discord webhook URL set, cannot send warning instant messages`);
121
- return;
122
- }
123
- let url = createLink(getErrorLogsLink());
124
- let flat = Object.values(info.perFile).flat();
125
- if (flat.length === 0) {
126
- console.log(`All messages have hit their IM limit, so not sending any message at all.`);
127
- return;
128
- }
129
- let message = flat.map(
130
- x => `[${formatDateTime(x.time)}](${url}) | ${ellipsisMiddle(String(x.param0), 100)} (${x.__NAME__})`
131
- ).join("\n");
132
- message = ellipsisMiddle(message, 900);
133
- if (countFiltered > 0) {
134
- message += `\n+${countFiltered} more errors`;
135
- }
136
- console.log(`Discord message: ${message}`);
137
- void sendDiscordMessage({
138
- webhookURL,
139
- message,
140
- });
141
- }
142
- });
143
-
144
-
145
- async function runIMNotifies() {
146
- await Querysub.hostService("error-notifications");
147
-
148
- // NOTE: This should be fine, as realistically how many errors are we going to see in the last two weeks. At 10 million, we're still probably only going to allocate 160 megs of memory, assuming we allocate 16 bytes per number. If we have more than 16 million for a single thread, it'll fail because the max set size... However, they will likely be somewhat distributed between threads.
149
- let errorByThreadIdByDay = new Map<number, Map<string, Set<number>>>();
150
- function getDay(time: number) {
151
- return Math.floor(time / timeInDay) * timeInDay;
152
- }
153
- function isDuplicate(obj: LogDatum): boolean {
154
- // Checks if it's a duplicate, and if it's not, adds it.
155
- let day = getDay(obj.time);
156
- let threadId = obj.__threadId || "";
157
- let dayMap = errorByThreadIdByDay.get(day);
158
- if (!dayMap) {
159
- dayMap = new Map<string, Set<number>>();
160
- errorByThreadIdByDay.set(day, dayMap);
161
- }
162
- let threadIdMap = dayMap.get(threadId);
163
- if (!threadIdMap) {
164
- threadIdMap = new Set<number>();
165
- dayMap.set(threadId, threadIdMap);
166
- }
167
- if (threadIdMap.has(obj.time)) {
168
- return true;
169
- }
170
- threadIdMap.add(obj.time);
171
- return false;
172
- }
173
- function clearOldDays() {
174
- // Clear all the days that are more than 14 days older than our current day.
175
- let now = Date.now();
176
- let currentDay = getDay(now);
177
- let cutOffDay = currentDay - 14;
178
- for (let day of errorByThreadIdByDay.keys()) {
179
- if (day < cutOffDay) {
180
- errorByThreadIdByDay.delete(day);
181
- console.log(`Cleared old day ${formatDateTime(day)}`);
182
- }
183
- }
184
- }
185
- errorWatcherBase.watch(async (objs) => {
186
- clearOldDays();
187
- console.log(magenta(`Pre-suppression: received ${objs.length} recent errors at ${formatDateTime(Date.now())}`));
188
- objs = await suppressionList.filterObjsToNonSuppressed(objs);
189
- objs = objs.filter(x => !isDuplicate(x));
190
- if (objs.length === 0) return;
191
- // The oldest first as they are most likely the cause.
192
- sort(objs, x => -x.time);
193
- void sendIMs(objs);
194
- console.log();
195
- console.log();
196
- console.log(`Received ${objs.length} recent errors at ${formatDateTime(Date.now())}`);
197
- for (let obj of objs) {
198
- console.log(` ${obj.param0}`);
199
- }
200
- console.log();
201
- console.log();
202
- });
203
-
204
- }
205
-
206
- async function main() {
207
- void runIMNotifies();
208
- }
209
- void main();
@@ -1,38 +0,0 @@
1
- import { Querysub } from "../../4-querysub/Querysub";
2
- import fs from "fs";
3
- import { Zip } from "../../zip";
4
- import { getLoggers } from "./diskLogger";
5
- import { formatNumber } from "socket-function/src/formatting/format";
6
- import { shutdown } from "../periodic";
7
-
8
- async function main() {
9
- let { logLogs } = getLoggers()!;
10
- //let root = "X:/root/machine-services/cyoa-1-dply/git/database-storage/disklogs";
11
- /*
12
- let root = "D:/repos/qs-cyoa/database-storage/disklogs/";
13
- let files = await fs.promises.readdir(root);
14
- for (let fileName of files) {
15
- let path = root + fileName;
16
- if (!path.endsWith(".log")) continue;
17
- let contents = await fs.promises.readFile(path);
18
- let lines = contents.toString("utf8").split("\n");
19
- for (let line of lines) {
20
- if (!line) continue;
21
- try {
22
- let log = JSON.parse(line);
23
- logLogs.append(log);
24
- } catch (e) {
25
- require("debugbreak")(2);
26
- debugger;
27
- console.error(e, line);
28
- }
29
- }
30
- console.log(`Flushing ${path}, line count: ${formatNumber(lines.length)}, ${formatNumber(contents.length)}B`);
31
- let time = Number(fileName.split("-")[0]) || Date.now();
32
- await logLogs.flushNow(time);
33
- }
34
- */
35
- await logLogs.moveLogsToBackblaze();
36
- //await shutdown();
37
- }
38
- main().catch(console.error).finally(() => process.exit());
@@ -1,150 +0,0 @@
1
- module.hotreload = true;
2
- import { qreact } from "../../../4-dom/qreact";
3
- import { css } from "../../../4-dom/css";
4
- import { t } from "../../../2-proxy/schema2";
5
- import { Querysub } from "../../../4-querysub/QuerysubController";
6
- import { sort } from "socket-function/src/misc";
7
- import { FastArchiveViewer } from "../FastArchiveViewer";
8
- import { LogDatum, getLoggers } from "../diskLogger";
9
- import { Table } from "../../../5-diagnostics/Table";
10
- import { formatDateTime } from "socket-function/src/formatting/format";
11
- import { FastArchiveAppendable } from "../FastArchiveAppendable";
12
- import { getTimeRange } from "../TimeRangeSelector";
13
- import { URLParam } from "../../../library-components/URLParam";
14
- import { InputLabelURL } from "../../../library-components/InputLabel";
15
- import { isPublic } from "../../../config";
16
-
17
- const forceGetPublicURL = new URLParam("forceGetPublic", false);
18
-
19
- export class LifeCyclePages extends qreact.Component {
20
- state = t.state({
21
- dataSeqNum: t.atomic<number>(0),
22
- });
23
-
24
- private datums: LogDatum[] = [];
25
- private latestByName = new Map<string, LogDatum>();
26
- private fastArchiveViewer: FastArchiveViewer<LogDatum> | undefined = undefined;
27
-
28
- rerun() {
29
- Querysub.onCommitFinished(async () => {
30
- void this.fastArchiveViewer?.synchronizeDataThrottled();
31
- });
32
- }
33
-
34
- getLoggers = () => {
35
- let logs: FastArchiveAppendable<LogDatum>[] = [];
36
- let loggers = getLoggers();
37
- if (!loggers) {
38
- throw new Error("Loggers not available?");
39
- }
40
- let { logLogs, warnLogs, infoLogs, errorLogs } = loggers;
41
- logs.push(logLogs);
42
- logs.push(infoLogs);
43
- logs.push(warnLogs);
44
- logs.push(errorLogs);
45
- return logs;
46
- };
47
-
48
- renderContent() {
49
- let latestEntries = Array.from(this.latestByName.entries());
50
- sort(latestEntries, x => -(x[1].time || 0));
51
-
52
- let rows = latestEntries.map(([name, datum]) => ({
53
- name,
54
- time: datum.time,
55
- datum,
56
- }));
57
-
58
- return <>
59
- <div className={css.hbox(10)}>
60
- <div>
61
- Total unique __NAME__ values: {this.latestByName.size}
62
- </div>
63
- <div>
64
- Total logs processed: {this.datums.length}
65
- </div>
66
- </div>
67
-
68
- {rows.length === 0 && <div className={css.hsl(40, 50, 50).colorhsl(60, 50, 100).boldStyle.pad2(10).ellipsis}>
69
- No logs matched, either increase the time range or run the search.
70
- </div>}
71
-
72
- <Table
73
- rows={rows}
74
- columns={{
75
- name: {
76
- title: "Name",
77
- formatter: x => <div>{String(x)}</div>
78
- },
79
- time: {
80
- title: "Last Time",
81
- formatter: x => <div>{formatDateTime(Number(x))}</div>
82
- },
83
- }}
84
- lineLimit={4}
85
- initialLimit={20}
86
- characterLimit={400}
87
- />
88
- </>;
89
- }
90
-
91
- render() {
92
- this.state.dataSeqNum;
93
-
94
- const timeRange = getTimeRange();
95
- return (
96
- <div className={css.vbox(20).pad2(20).fillBoth}>
97
- <div className={css.hbox(10)}>
98
- <div className={css.fontSize(14)}>
99
- Life Cycle Analysis
100
- </div>
101
- {!isPublic() && <InputLabelURL
102
- label="Force Public Logs" checkbox url={forceGetPublicURL}
103
- onChangeValue={(newValue) => {
104
- this.rerun();
105
- }}
106
- />}
107
- </div>
108
-
109
- <FastArchiveViewer
110
- ref2={x => this.fastArchiveViewer = x}
111
- fastArchives={this.getLoggers}
112
- runOnLoad={false}
113
- forceGetPublic={forceGetPublicURL.value}
114
- onStart={async () => {
115
- this.datums = [];
116
- this.latestByName = new Map();
117
- }}
118
- onDatums={(source, datums, file) => {
119
- for (let datum of datums) {
120
- let time = datum.time;
121
- if (time && (time < timeRange.startTime || time > timeRange.endTime)) {
122
- continue;
123
- }
124
- this.datums.push(datum);
125
- }
126
- }}
127
- onFinish={() => {
128
- // Sort all datums by time
129
- sort(this.datums, x => x.time || 0);
130
-
131
- // Get the latest value (last time) for every __NAME__
132
- this.latestByName.clear();
133
- for (let datum of this.datums) {
134
- const name = datum.__NAME__;
135
- if (name) {
136
- this.latestByName.set(name, datum);
137
- }
138
- }
139
-
140
- Querysub.commit(() => {
141
- this.state.dataSeqNum++;
142
- });
143
- }}
144
- >
145
- {this.renderContent()}
146
- </FastArchiveViewer>
147
- </div>
148
- );
149
- }
150
- }
@@ -1,36 +0,0 @@
1
- import { ScanFnc } from "./FastArchiveViewer";
2
-
3
- export function createLogViewerExtractField(
4
- fieldName: string,
5
- onFieldValue: (value: string, unpackedSize: number) => void,
6
- ): ScanFnc {
7
- let fieldNameBuffer = Buffer.from(`${JSON.stringify(fieldName)}:"`);
8
- let quoteChar = Buffer.from(`"`)[0];
9
- let escapeChar = Buffer.from(`\\`)[0];
10
- return (posStart: number, posEnd: number, data: Buffer) => {
11
- for (let i = posStart; i < posEnd; i++) {
12
- if (data[i] === fieldNameBuffer[0]) {
13
- for (let j = 1; j < fieldNameBuffer.length; j++) {
14
- if (data[i + j] !== fieldNameBuffer[j]) {
15
- break;
16
- }
17
- if (j === fieldNameBuffer.length - 1) {
18
- // Parse until the string ends, ignoring escaped characters
19
- let start = i + j + 1;
20
- for (let k = start; k < posEnd; k++) {
21
- if (data[k] === quoteChar) {
22
- onFieldValue(data.slice(start, k).toString(), posEnd - start);
23
- break;
24
- }
25
- if (data[k] === escapeChar) {
26
- k++;
27
- }
28
- }
29
- return true;
30
- }
31
- }
32
- }
33
- }
34
- return false;
35
- };
36
- }