95 lines
2.3 KiB
TypeScript
95 lines
2.3 KiB
TypeScript
import { readFile } from "node:fs/promises";
|
|
import { resolve } from "node:path";
|
|
import type { RuntimeEvent } from "../telemetry/runtime-events.js";
|
|
|
|
type RuntimeEventFilter = {
|
|
sessionId?: string;
|
|
types?: string[];
|
|
severities?: Array<RuntimeEvent["severity"]>;
|
|
limit?: number;
|
|
};
|
|
|
|
function safeParseLine(line: string): RuntimeEvent | undefined {
|
|
const trimmed = line.trim();
|
|
if (!trimmed) {
|
|
return undefined;
|
|
}
|
|
|
|
try {
|
|
const parsed = JSON.parse(trimmed) as unknown;
|
|
if (!parsed || typeof parsed !== "object") {
|
|
return undefined;
|
|
}
|
|
|
|
const record = parsed as Partial<RuntimeEvent>;
|
|
if (
|
|
typeof record.id !== "string" ||
|
|
typeof record.timestamp !== "string" ||
|
|
typeof record.type !== "string" ||
|
|
typeof record.severity !== "string" ||
|
|
typeof record.message !== "string"
|
|
) {
|
|
return undefined;
|
|
}
|
|
|
|
return record as RuntimeEvent;
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
export async function readRuntimeEvents(logPath: string): Promise<RuntimeEvent[]> {
|
|
const absolutePath = resolve(logPath);
|
|
let content = "";
|
|
|
|
try {
|
|
content = await readFile(absolutePath, "utf8");
|
|
} catch (error) {
|
|
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
return [];
|
|
}
|
|
throw error;
|
|
}
|
|
|
|
const parsed: RuntimeEvent[] = [];
|
|
const lines = content.split(/\r?\n/);
|
|
for (const line of lines) {
|
|
const event = safeParseLine(line);
|
|
if (event) {
|
|
parsed.push(event);
|
|
}
|
|
}
|
|
|
|
parsed.sort((left, right) => left.timestamp.localeCompare(right.timestamp));
|
|
return parsed;
|
|
}
|
|
|
|
export function filterRuntimeEvents(
|
|
events: readonly RuntimeEvent[],
|
|
filter: RuntimeEventFilter,
|
|
): RuntimeEvent[] {
|
|
const filtered: RuntimeEvent[] = [];
|
|
const types = filter.types ? new Set(filter.types) : undefined;
|
|
const severities = filter.severities ? new Set(filter.severities) : undefined;
|
|
|
|
for (const event of events) {
|
|
if (filter.sessionId && event.sessionId !== filter.sessionId) {
|
|
continue;
|
|
}
|
|
if (types && !types.has(event.type)) {
|
|
continue;
|
|
}
|
|
if (severities && !severities.has(event.severity)) {
|
|
continue;
|
|
}
|
|
|
|
filtered.push(event);
|
|
}
|
|
|
|
if (!filter.limit || filter.limit < 1 || filtered.length <= filter.limit) {
|
|
return filtered;
|
|
}
|
|
|
|
return filtered.slice(-filter.limit);
|
|
}
|