Skip to content

Commit 8dc7596

Browse files
authored
Stats info (#10)
* add first and last occurrence info to stats * fix search case-sensitivity
1 parent 48d1495 commit 8dc7596

File tree

7 files changed

+184
-97
lines changed

7 files changed

+184
-97
lines changed

Taskfile.yml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@ tasks:
1919
- cmd:install
2020

2121
test:
22-
desc: run tests
23-
deps:
24-
- ui:test
22+
desc: run tests
23+
deps:
24+
- ui:test
25+
26+
run:
27+
desc: run the UI
28+
deps:
29+
- ui:run
2530

2631
build:
2732
desc: transpile, bundle and create the release zip

cmd/src/commands/summary/index.ts

Lines changed: 68 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,36 @@ import { Table } from "console-table-printer";
33
import { parseArgs } from "util";
44
import { cpus } from "node:os";
55
import type { ICmd } from "@al/cmd/utils/cmd-runner";
6-
import { type ITask, type IResult } from "./worker";
6+
import type {
7+
ITask,
8+
IResult,
9+
IGroupedMsg,
10+
ISummaryMap,
11+
ISummaryMapGeneric,
12+
} from "./worker";
713
import WorkerPool from "@al/cmd/utils/worker-pool";
814
import fileHelper from "@al/cmd/utils/file-helper";
9-
import type {
10-
GroupedMsg,
11-
Summary as SummaryData,
12-
SummaryMap,
13-
} from "@al/ui/models/logData";
14-
import LogData from "@al/ui/models/logData";
1515

1616
let workerURL = new URL("worker.ts", import.meta.url);
1717

18-
interface IStats extends Omit<IResult, "filePath"> {
18+
interface IStats extends Omit<IResult, "filePath" | "dataMap"> {
1919
minTimeFile: string;
2020
maxTimeFile: string;
21+
dataMap: IStatsSummaryMap;
22+
}
23+
24+
interface IStatsGroupedMsg extends IGroupedMsg {
25+
firstFile: string;
26+
lastFile: string;
27+
}
28+
29+
type IStatsSummaryMap = ISummaryMapGeneric<IStatsGroupedMsg>;
30+
31+
interface ISummary {
32+
msgs: IStatsGroupedMsg[];
33+
httpCodes: IStatsGroupedMsg[];
34+
jobs: IStatsGroupedMsg[];
35+
plugins: IStatsGroupedMsg[];
2136
}
2237

2338
const stats: IStats = {
@@ -27,10 +42,10 @@ const stats: IStats = {
2742
minTimeFile: "",
2843
size: 0,
2944
dataMap: {
30-
httpCodes: new Map<string, GroupedMsg>(),
31-
jobs: new Map<string, GroupedMsg>(),
32-
msgs: new Map<string, GroupedMsg>(),
33-
plugins: new Map<string, GroupedMsg>(),
45+
httpCodes: new Map<string, IStatsGroupedMsg>(),
46+
jobs: new Map<string, IStatsGroupedMsg>(),
47+
msgs: new Map<string, IStatsGroupedMsg>(),
48+
plugins: new Map<string, IStatsGroupedMsg>(),
3449
},
3550
};
3651

@@ -109,7 +124,7 @@ async function processLogs() {
109124
await readFiles(filePaths);
110125
console.log("=========End Read Files=========");
111126

112-
const summary = LogData.initSummary(stats.dataMap);
127+
const summary = initSummary(stats.dataMap);
113128

114129
writeContent(summary);
115130
}
@@ -155,37 +170,64 @@ function processFileResponse(fileStats: IResult) {
155170

156171
stats.size += fileStats.size;
157172

158-
initSummaryMap(fileStats.dataMap);
173+
initSummaryMap(fileStats.dataMap, fileStats.filePath);
159174
}
160175

161-
function initSummaryMap(dataMap: SummaryMap) {
162-
mergeIntoOverallMap(dataMap.httpCodes, stats.dataMap.httpCodes);
163-
mergeIntoOverallMap(dataMap.jobs, stats.dataMap.jobs);
164-
mergeIntoOverallMap(dataMap.msgs, stats.dataMap.msgs);
165-
mergeIntoOverallMap(dataMap.plugins, stats.dataMap.plugins);
176+
function initSummaryMap(dataMap: ISummaryMap, filePath: string) {
177+
mergeIntoOverallMap(dataMap.httpCodes, stats.dataMap.httpCodes, filePath);
178+
mergeIntoOverallMap(dataMap.jobs, stats.dataMap.jobs, filePath);
179+
mergeIntoOverallMap(dataMap.msgs, stats.dataMap.msgs, filePath);
180+
mergeIntoOverallMap(dataMap.plugins, stats.dataMap.plugins, filePath);
166181
}
167182

168183
function mergeIntoOverallMap(
169-
fileMap: Map<string, GroupedMsg>,
170-
overallMap: Map<string, GroupedMsg>
184+
fileMap: Map<string, IGroupedMsg>,
185+
overallMap: Map<string, IStatsGroupedMsg>,
186+
filePath: string
171187
) {
172188
for (const [k, v] of fileMap) {
173189
if (!overallMap.has(k)) {
174190
overallMap.set(k, {
175191
msg: v.msg,
176192
hasErrors: false,
177-
logs: [],
178193
logsCount: 0,
194+
firstTime: v.firstTime,
195+
lastTime: v.lastTime,
196+
firstFile: filePath,
197+
lastFile: filePath,
179198
});
180199
}
181200

182201
const grpOverall = overallMap.get(k)!;
183-
grpOverall.hasErrors = grpOverall.hasErrors || v.hasErrors;
202+
grpOverall.hasErrors ||= v.hasErrors;
184203
grpOverall.logsCount += v.logsCount!;
204+
205+
if (v.firstTime < grpOverall.firstTime) {
206+
grpOverall.firstTime = v.firstTime;
207+
grpOverall.firstFile = filePath;
208+
}
209+
210+
if (v.lastTime > grpOverall.lastTime) {
211+
grpOverall.lastTime = v.lastTime;
212+
grpOverall.lastFile = filePath;
213+
}
185214
}
186215
}
187216

188-
function writeContent(summary: SummaryData) {
217+
function summarySorterFn(a: IStatsGroupedMsg, b: IStatsGroupedMsg) {
218+
return b.logsCount - a.logsCount;
219+
}
220+
221+
function initSummary(summaryMap: IStatsSummaryMap): ISummary {
222+
return {
223+
msgs: [...summaryMap.msgs.values()].sort(summarySorterFn),
224+
httpCodes: [...summaryMap.httpCodes.values()].sort(summarySorterFn),
225+
jobs: [...summaryMap.jobs.values()].sort(summarySorterFn),
226+
plugins: [...summaryMap.plugins.values()].sort(summarySorterFn),
227+
};
228+
}
229+
230+
function writeContent(summary: ISummary) {
189231
console.log();
190232

191233
stats.size = prettyBytes(stats.size) as any;
@@ -217,15 +259,15 @@ function writeContent(summary: SummaryData) {
217259
writeGroupedMsgs(summary.plugins, "Plugins");
218260
}
219261

220-
function writeGroupedMsgs(grpMsgs: GroupedMsg[], title: string) {
262+
function writeGroupedMsgs(grpMsgs: IStatsGroupedMsg[], title: string) {
221263
console.log();
222264

223265
const table = new Table({
224266
columns: [
225267
{ name: "msg", title: title, alignment: "left" },
226268
{ name: "logsCount", title: "Count" },
227269
],
228-
disabledColumns: ["hasErrors", "logs"],
270+
disabledColumns: ["hasErrors"],
229271
});
230272

231273
for (const grp of grpMsgs.slice(0, flags.topLogsCount)) {

cmd/src/commands/summary/worker.ts

Lines changed: 75 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
import { parentPort } from "node:worker_threads";
2-
import LogData, {
3-
type GroupedMsg,
4-
type SummaryMap,
5-
} from "@al/ui/models/logData";
2+
import LogData, { type JSONLog } from "@al/ui/models/logData";
63
import normalizer from "@al/ui/services/normalizer";
74

5+
interface IGroupedMsg {
6+
msg: string;
7+
logsCount: number;
8+
hasErrors: boolean;
9+
firstTime: string;
10+
lastTime: string;
11+
}
12+
13+
interface ISummaryMapGeneric<TGroupedMsg> {
14+
msgs: Map<string, TGroupedMsg>;
15+
httpCodes: Map<string, TGroupedMsg>;
16+
jobs: Map<string, TGroupedMsg>;
17+
plugins: Map<string, TGroupedMsg>;
18+
}
19+
20+
type ISummaryMap = ISummaryMapGeneric<IGroupedMsg>;
21+
822
interface ITask {
923
filePath: string;
1024
}
@@ -14,34 +28,36 @@ interface IResult {
1428
minTime: string;
1529
maxTime: string;
1630
size: number;
17-
dataMap: SummaryMap;
31+
dataMap: ISummaryMap;
1832
}
1933

2034
// Thread Code
2135
if (parentPort) {
2236
parentPort.on("message", async (task: ITask) => {
2337
const stats = await processFile(task);
2438
parentPort!.postMessage(stats);
25-
console.log(`Processed File: ${task.filePath}`);
39+
console.log(`Processed File: ${stats.filePath}`);
2640
});
2741
}
2842

2943
async function processFile(task: ITask): Promise<IResult> {
3044
const stats: IResult = {
31-
filePath: task.filePath,
45+
filePath: "",
3246
maxTime: "0",
3347
minTime: "z",
3448
size: 0,
3549
dataMap: {
36-
httpCodes: new Map<string, GroupedMsg>(),
37-
jobs: new Map<string, GroupedMsg>(),
38-
msgs: new Map<string, GroupedMsg>(),
39-
plugins: new Map<string, GroupedMsg>(),
50+
httpCodes: new Map<string, IGroupedMsg>(),
51+
jobs: new Map<string, IGroupedMsg>(),
52+
msgs: new Map<string, IGroupedMsg>(),
53+
plugins: new Map<string, IGroupedMsg>(),
4054
},
4155
};
4256

4357
const logFile = Bun.file(task.filePath);
4458
stats.size = logFile.size;
59+
// Keep only the last folder from the filePath
60+
stats.filePath = task.filePath.split("/").slice(-2).join("/");
4561

4662
const text = await logFile.text();
4763

@@ -54,18 +70,60 @@ async function processFile(task: ITask): Promise<IResult> {
5470
for (const jsonLog of logsGeneratorFn()) {
5571
if (!jsonLog) continue;
5672

57-
if (jsonLog[LogData.logKeys.timestamp] > stats.maxTime) {
58-
stats.maxTime = jsonLog[LogData.logKeys.timestamp];
73+
const time = jsonLog[LogData.logKeys.timestamp];
74+
75+
if (time < stats.minTime) {
76+
stats.minTime = time;
5977
}
6078

61-
if (jsonLog[LogData.logKeys.timestamp] < stats.minTime) {
62-
stats.minTime = jsonLog[LogData.logKeys.timestamp];
79+
if (time > stats.maxTime) {
80+
stats.maxTime = time;
6381
}
6482

65-
LogData.initSummaryMap(jsonLog, stats.dataMap, false);
83+
initSummaryMap(jsonLog, stats.dataMap);
6684
}
6785

6886
return stats;
6987
}
7088

71-
export type { ITask, IResult };
89+
function initSummaryMap(log: JSONLog, summaryMap: ISummaryMap) {
90+
populateSummaryMap(log, summaryMap.msgs, LogData.msgKeySelector);
91+
populateSummaryMap(log, summaryMap.jobs, LogData.jobKeySelector);
92+
populateSummaryMap(log, summaryMap.httpCodes, LogData.httpCodeKeySelector);
93+
populateSummaryMap(log, summaryMap.plugins, LogData.pluginKeySelector);
94+
}
95+
96+
function populateSummaryMap(
97+
log: JSONLog,
98+
grpLogsMap: Map<string, IGroupedMsg>,
99+
keySelectorFn: (log: JSONLog) => string | undefined
100+
) {
101+
const key = keySelectorFn(log);
102+
if (!key) return;
103+
104+
const time = log[LogData.logKeys.timestamp];
105+
106+
if (!grpLogsMap.has(key)) {
107+
grpLogsMap.set(key, {
108+
msg: key,
109+
hasErrors: false,
110+
logsCount: 0,
111+
firstTime: time,
112+
lastTime: time,
113+
});
114+
}
115+
116+
const grpLog = grpLogsMap.get(key)!;
117+
grpLog.logsCount++;
118+
grpLog.hasErrors ||= LogData.isErrorLog(log);
119+
120+
if (time < grpLog.firstTime) {
121+
grpLog.firstTime = time;
122+
}
123+
124+
if (time > grpLog.lastTime) {
125+
grpLog.lastTime = time;
126+
}
127+
}
128+
129+
export type { ITask, IResult, IGroupedMsg, ISummaryMap, ISummaryMapGeneric };

ui/Taskfile.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,8 @@ tasks:
1616
cmds:
1717
- pnpm build
1818
- cp analog.sh analog.ps1 analog
19+
20+
run:
21+
desc: run
22+
cmds:
23+
- pnpm start

ui/src/components/filters/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,9 @@ function Filters(props: FiltersProps) {
140140
<TextField
141141
label="Search"
142142
value={term.value}
143-
onChange={(_, val) => setFilters("terms", i(), "value", val)}
143+
onChange={(_, val) =>
144+
setFilters("terms", i(), "value", val.toLowerCase())
145+
}
144146
onKeyDown={handleEnterKey}
145147
/>
146148
</>

0 commit comments

Comments
 (0)