Repository navigation
Expand file tree
/
Copy pathProcessResourceMonitor.ts
More file actions
88 lines (82 loc) · 3.21 KB
/
Copy pathProcessResourceMonitor.ts
File metadata and controls
88 lines (82 loc) · 3.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import type {
ResourceTelemetryProcessCategory,
ServerProcessResourceHistoryInput,
ServerProcessResourceHistoryResult,
} from "@t3tools/contracts";
import * as Context from "effect/Context";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Option from "effect/Option";
import * as ResourceTelemetry from "../resourceTelemetry/ResourceTelemetry.ts";
export class ProcessResourceMonitor extends Context.Service<
ProcessResourceMonitor,
{
readonly readHistory: (
input: ServerProcessResourceHistoryInput,
) => Effect.Effect<ServerProcessResourceHistoryResult>;
}
>()("t3/diagnostics/ProcessResourceMonitor") {}
function isLegacyBackendCategory(category: ResourceTelemetryProcessCategory): boolean {
return (
category === "server" ||
category === "server-child" ||
category === "provider-root" ||
category === "terminal-root"
);
}
/** @public */
export const make = Effect.fn("makeProcessResourceMonitor")(function* () {
const telemetry = yield* ResourceTelemetry.ResourceTelemetry;
const readHistory: ProcessResourceMonitor["Service"]["readHistory"] = (input) =>
telemetry.readHistory(input).pipe(
Effect.map((history) => {
const topProcesses = history.topProcesses
.filter((entry) => isLegacyBackendCategory(entry.category))
.map((entry) => ({
processKey: `${entry.identity.pid}:${entry.identity.startTimeMs}`,
pid: entry.identity.pid,
ppid: entry.ppid,
command: entry.command || entry.name || "unknown",
depth: entry.depth,
isServerRoot: entry.category === "server",
firstSeenAt: entry.firstSeenAt,
lastSeenAt: entry.lastSeenAt,
currentCpuPercent: entry.currentCpuPercent,
avgCpuPercent: entry.avgCpuPercent,
maxCpuPercent: entry.maxCpuPercent,
cpuSecondsApprox: entry.cpuTimeMs / 1_000,
currentRssBytes: entry.currentRssBytes,
maxRssBytes: entry.peakRssBytes,
sampleCount: entry.sampleCount,
}));
return {
readAt: history.readAt,
windowMs: history.windowMs,
bucketMs: history.bucketMs,
sampleIntervalMs: history.sampleIntervalMs,
retainedSampleCount: history.retainedSampleCount,
totalCpuSecondsApprox: topProcesses.reduce(
(total, entry) => total + entry.cpuSecondsApprox,
0,
),
buckets: (history.legacyBackendBuckets ?? history.buckets).map((bucket) => ({
startedAt: bucket.startedAt,
endedAt: bucket.endedAt,
avgCpuPercent: bucket.avgCpuPercent,
maxCpuPercent: bucket.maxCpuPercent,
maxRssBytes: bucket.maxRssBytes,
maxProcessCount: bucket.maxProcessCount,
})),
topProcesses,
error: history.health.native.lastError.pipe(
Option.map((message) => ({
failureTag: "ProcessDiagnosticsQueryFailedError" as const,
message,
})),
),
};
}),
);
return ProcessResourceMonitor.of({ readHistory });
});
export const layer = Layer.effect(ProcessResourceMonitor, make());