Skip to content

Commit 8677a34

Browse files
authored
feat: collects all stats like cpu and mem in background for up to 5 minutes (#2740)
1 parent 2c398cc commit 8677a34

30 files changed

+516
-252
lines changed

assets/auto-imports.d.ts

+3
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ declare global {
290290
const useSeoMeta: typeof import('@vueuse/head')['useSeoMeta']
291291
const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
292292
const useShare: typeof import('@vueuse/core')['useShare']
293+
const useSimpleRefHistory: typeof import('./utils/index')['useSimpleRefHistory']
293294
const useSlots: typeof import('vue')['useSlots']
294295
const useSorted: typeof import('@vueuse/core')['useSorted']
295296
const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
@@ -646,6 +647,7 @@ declare module 'vue' {
646647
readonly useSeoMeta: UnwrapRef<typeof import('@vueuse/head')['useSeoMeta']>
647648
readonly useSessionStorage: UnwrapRef<typeof import('@vueuse/core')['useSessionStorage']>
648649
readonly useShare: UnwrapRef<typeof import('@vueuse/core')['useShare']>
650+
readonly useSimpleRefHistory: UnwrapRef<typeof import('./utils/index')['useSimpleRefHistory']>
649651
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
650652
readonly useSorted: UnwrapRef<typeof import('@vueuse/core')['useSorted']>
651653
readonly useSpeechRecognition: UnwrapRef<typeof import('@vueuse/core')['useSpeechRecognition']>
@@ -995,6 +997,7 @@ declare module '@vue/runtime-core' {
995997
readonly useSeoMeta: UnwrapRef<typeof import('@vueuse/head')['useSeoMeta']>
996998
readonly useSessionStorage: UnwrapRef<typeof import('@vueuse/core')['useSessionStorage']>
997999
readonly useShare: UnwrapRef<typeof import('@vueuse/core')['useShare']>
1000+
readonly useSimpleRefHistory: UnwrapRef<typeof import('./utils/index')['useSimpleRefHistory']>
9981001
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
9991002
readonly useSorted: UnwrapRef<typeof import('@vueuse/core')['useSorted']>
10001003
readonly useSpeechRecognition: UnwrapRef<typeof import('@vueuse/core')['useSpeechRecognition']>

assets/components/FuzzySearchModal.spec.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ function createFuzzySearchModal() {
3131
initialState: {
3232
container: {
3333
containers: [
34-
new Container("123", new Date(), "image", "test", "command", "host", {}, "status", "running"),
35-
new Container("345", new Date(), "image", "foo bar", "command", "host", {}, "status", "running"),
36-
new Container("567", new Date(), "image", "baz", "command", "host", {}, "status", "exited"),
34+
new Container("123", new Date(), "image", "test", "command", "host", {}, "status", "running", []),
35+
new Container("345", new Date(), "image", "foo bar", "command", "host", {}, "status", "running", []),
36+
new Container("567", new Date(), "image", "baz", "command", "host", {}, "status", "exited", []),
3737
],
3838
},
3939
},

assets/components/LogViewer/ContainerStat.vue

+6-6
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ const { container } = useContainerContext();
1111
const cpuData = computedWithControl(
1212
() => container.value.stat,
1313
() => {
14-
const history = container.value.statHistory;
14+
const history = container.value.statsHistory;
1515
const points: Point<unknown>[] = history.map((stat, i) => ({
1616
x: i,
17-
y: Math.max(0, stat.snapshot.cpu),
18-
value: Math.max(0, stat.snapshot.cpu).toFixed(2) + "%",
17+
y: Math.max(0, stat.cpu),
18+
value: Math.max(0, stat.cpu).toFixed(2) + "%",
1919
}));
2020
return points;
2121
},
@@ -24,11 +24,11 @@ const cpuData = computedWithControl(
2424
const memoryData = computedWithControl(
2525
() => container.value.stat,
2626
() => {
27-
const history = container.value.statHistory;
27+
const history = container.value.statsHistory;
2828
const points: Point<string>[] = history.map((stat, i) => ({
2929
x: i,
30-
y: stat.snapshot.memory,
31-
value: formatBytes(stat.snapshot.memoryUsage),
30+
y: stat.memory,
31+
value: formatBytes(stat.memoryUsage),
3232
}));
3333
return points;
3434
},

assets/components/LogViewer/StatSparkline.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { area, curveStep } from "d3-shape";
1212
1313
const d3 = { extent, scaleLinear, area, curveStep };
1414
const { data, width = 150, height = 30 } = defineProps<{ data: Point<unknown>[]; width?: number; height?: number }>();
15-
const x = d3.scaleLinear().range([width, 0]);
15+
const x = d3.scaleLinear().range([0, width]);
1616
const y = d3.scaleLinear().range([height, 0]);
1717
1818
const selectedPoint = defineEmit<[value: Point<unknown>]>();

assets/models/Container.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ describe("Container", () => {
1818
];
1919

2020
test.each(names)("name %s should be %s and %s", (name, expectedName, expectedSwarmId) => {
21-
const c = new Container("id", new Date(), "image", name!, "command", "host", {}, "status", "created");
21+
const c = new Container("id", new Date(), "image", name!, "command", "host", {}, "status", "created", []);
2222
expect(c.name).toBe(expectedName);
2323
expect(c.swarmId).toBe(expectedSwarmId);
2424
});

assets/models/Container.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { ContainerHealth, ContainerStat, ContainerState } from "@/types/Container";
2-
import type { UseThrottledRefHistoryReturn } from "@vueuse/core";
3-
import { useExponentialMovingAverage } from "@/utils";
2+
import { useExponentialMovingAverage, useSimpleRefHistory } from "@/utils";
43
import { Ref } from "vue";
54

65
type Stat = Omit<ContainerStat, "id">;
@@ -19,7 +18,7 @@ const hosts = computed(() =>
1918

2019
export class Container {
2120
private _stat: Ref<Stat>;
22-
private readonly throttledStatHistory: UseThrottledRefHistoryReturn<Stat, Stat>;
21+
private readonly _statsHistory: Ref<Stat[]>;
2322
public readonly swarmId: string | null = null;
2423
public readonly isSwarm: boolean = false;
2524
private readonly movingAverageStat: Ref<Stat>;
@@ -34,10 +33,11 @@ export class Container {
3433
public readonly labels = {} as Record<string, string>,
3534
public status: string,
3635
public state: ContainerState,
36+
stats: Stat[],
3737
public health?: ContainerHealth,
3838
) {
3939
this._stat = ref({ cpu: 0, memory: 0, memoryUsage: 0 });
40-
this.throttledStatHistory = useThrottledRefHistory(this._stat, { capacity: 300, deep: true, throttle: 1000 });
40+
this._statsHistory = useSimpleRefHistory(this._stat, { capacity: 300, deep: true, initial: stats });
4141
this.movingAverageStat = useExponentialMovingAverage(this._stat, 0.2);
4242

4343
const match = name.match(SWARM_ID_REGEX);
@@ -48,8 +48,8 @@ export class Container {
4848
}
4949
}
5050

51-
get statHistory() {
52-
return unref(this.throttledStatHistory.history);
51+
get statsHistory() {
52+
return unref(this._statsHistory);
5353
}
5454

5555
get movingAverage() {

assets/stores/container.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export const useContainerStore = defineStore("container", () => {
6464
const event = JSON.parse((e as MessageEvent).data) as { actorId: string };
6565
const container = allContainersById.value[event.actorId];
6666
if (container) {
67-
container.state = "dead";
67+
container.state = "exited";
6868
}
6969
});
7070

@@ -127,6 +127,7 @@ export const useContainerStore = defineStore("container", () => {
127127
c.labels,
128128
c.status,
129129
c.state,
130+
c.stats,
130131
c.health,
131132
);
132133
}),

assets/types/Container.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export type ContainerJson = {
1515
readonly state: ContainerState;
1616
readonly host: string;
1717
readonly labels: Record<string, string>;
18+
readonly stats: ContainerStat[];
1819
readonly health?: ContainerHealth;
1920
};
2021

assets/utils/index.ts

+24
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,27 @@ export function useExponentialMovingAverage<T extends Record<string, number>>(so
5151

5252
return ema;
5353
}
54+
55+
interface UseSimpleRefHistoryOptions<T> {
56+
capacity: number;
57+
deep?: boolean;
58+
initial?: T[];
59+
}
60+
61+
export function useSimpleRefHistory<T>(source: Ref<T>, options: UseSimpleRefHistoryOptions<T>) {
62+
const { capacity, deep = true, initial = [] as T[] } = options;
63+
const history = ref<T[]>(initial) as Ref<T[]>;
64+
65+
watch(
66+
source,
67+
(value) => {
68+
history.value.push(value);
69+
if (history.value.length > capacity) {
70+
history.value.shift();
71+
}
72+
},
73+
{ deep },
74+
);
75+
76+
return history;
77+
}

0 commit comments

Comments
 (0)