Skip to content

Commit 5b06820

Browse files
committed
feat(#4242): gather process information from different locations, when available
1 parent 98efd48 commit 5b06820

File tree

11 files changed

+265
-138
lines changed

11 files changed

+265
-138
lines changed

spring-boot-admin-server-ui/src/main/frontend/views/instances/details/details-process.vue

Lines changed: 194 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -29,148 +29,214 @@
2929
</sba-panel>
3030
</template>
3131

32-
<script>
32+
<script setup lang="ts">
3333
import { take } from 'rxjs/operators';
34+
import { computed, onMounted, onUnmounted, ref } from 'vue';
35+
import { useI18n } from 'vue-i18n';
3436
35-
import subscribing from '@/mixins/subscribing';
3637
import sbaConfig from '@/sba-config';
3738
import Instance from '@/services/instance';
3839
import { concatMap, delay, retryWhen, timer } from '@/utils/rxjs';
3940
import processUptime from '@/views/instances/details/process-uptime';
4041
import { toMillis } from '@/views/instances/metrics/metric';
4142
42-
export default {
43-
components: { processUptime },
44-
mixins: [subscribing],
45-
props: {
46-
instance: {
47-
type: Instance,
48-
required: true,
49-
},
50-
},
51-
data: () => ({
52-
hasLoaded: false,
53-
error: null,
54-
pid: null,
55-
uptime: { value: null, baseUnit: null },
56-
systemCpuLoad: null,
57-
processCpuLoad: null,
58-
systemCpuCount: null,
59-
}),
60-
computed: {
61-
tableData() {
62-
return {
63-
pid: {
64-
label: this.$t('instances.details.process.pid'),
65-
value: this.pid,
66-
},
67-
uptime: {
68-
label: this.$t('instances.details.process.uptime'),
69-
value: toMillis(this.uptime.value, this.uptime.baseUnit),
70-
},
71-
processCpuLoad: {
72-
label: this.$t('instances.details.process.process_cpu_usage'),
73-
value: this.processCpuLoad?.toFixed(2),
74-
},
75-
systemCpuLoad: {
76-
label: this.$t('instances.details.process.system_cpu_usage'),
77-
value: this.systemCpuLoad?.toFixed(2),
78-
},
79-
cpus: {
80-
label: this.$t('instances.details.process.cpus'),
81-
value: this.systemCpuCount,
82-
},
83-
};
43+
// Typdefinitionen
44+
interface UptimeData {
45+
value: number | null;
46+
baseUnit: string | null;
47+
}
48+
49+
interface MetricResponse {
50+
measurements: Array<{ value: number }>;
51+
baseUnit: string;
52+
}
53+
54+
interface CpuLoadMetrics {
55+
processCpuLoad: number | null;
56+
systemCpuLoad: number | null;
57+
}
58+
59+
interface TableData {
60+
label: string;
61+
value: number | string | null;
62+
}
63+
64+
interface TableDataMap {
65+
[key: string]: TableData;
66+
}
67+
68+
// Props Definition
69+
const props = defineProps<{
70+
instance: Instance;
71+
}>();
72+
73+
const { t, locale } = useI18n();
74+
75+
// Reaktive Zustandsvariablen
76+
const hasLoaded = ref<boolean>(false);
77+
const error = ref<Error | null>(null);
78+
79+
const pid = ref<number | null>(null);
80+
const parentPid = ref<number | null>(null);
81+
const owner = ref<string | null>(null);
82+
const uptime = ref<UptimeData>({ value: null, baseUnit: null });
83+
const systemCpuLoad = ref<number | null>(null);
84+
const processCpuLoad = ref<number | null>(null);
85+
const systemCpuCount = ref<number | null>(null);
86+
87+
// Berechnete Eigenschaften
88+
const tableData = computed<TableDataMap>(() => {
89+
const formatNumber = Intl.NumberFormat(locale.value, {
90+
maximumFractionDigits: 2,
91+
});
92+
93+
return {
94+
pid: {
95+
label: t('instances.details.process.pid'),
96+
value: pid.value,
8497
},
85-
},
86-
created() {
87-
this.fetchPid();
88-
this.fetchUptime();
89-
this.fetchCpuCount();
90-
},
91-
methods: {
92-
toMillis,
93-
async fetchUptime() {
94-
try {
95-
const response = await this.fetchMetric('process.uptime');
96-
this.uptime = {
97-
value: response.measurements[0].value,
98-
baseUnit: response.baseUnit,
99-
};
100-
} catch (error) {
101-
this.error = error;
102-
console.warn('Fetching Uptime failed:', error);
103-
} finally {
104-
this.hasLoaded = true;
105-
}
98+
parentPid: {
99+
label: t('instances.details.process.parent_pid'),
100+
value: parentPid.value,
106101
},
107-
async fetchPid() {
108-
if (this.instance.hasEndpoint('env')) {
109-
try {
110-
const response = await this.instance.fetchEnv('PID');
111-
this.pid = response.data.property.value;
112-
} catch (error) {
113-
console.warn('Fetching PID failed:', error);
114-
} finally {
115-
this.hasLoaded = true;
116-
}
117-
}
102+
owner: {
103+
label: t('instances.details.process.owner'),
104+
value: owner.value,
118105
},
119-
async fetchCpuCount() {
120-
try {
121-
this.systemCpuCount = (
122-
await this.fetchMetric('system.cpu.count')
123-
).measurements[0].value;
124-
} catch (error) {
125-
console.warn('Fetching Cpu Count failed:', error);
126-
} finally {
127-
this.hasLoaded = true;
128-
}
106+
uptime: {
107+
label: t('instances.details.process.uptime'),
108+
value: toMillis(uptime.value.value, uptime.value.baseUnit),
129109
},
130-
createSubscription() {
131-
return timer(0, sbaConfig.uiSettings.pollTimer.process)
132-
.pipe(
133-
concatMap(this.fetchCpuLoadMetrics),
134-
retryWhen((err) => {
135-
return err.pipe(delay(1000), take(5));
136-
}),
137-
)
138-
.subscribe({
139-
next: (data) => {
140-
this.processCpuLoad = data.processCpuLoad;
141-
this.systemCpuLoad = data.systemCpuLoad;
142-
},
143-
error: (error) => {
144-
this.hasLoaded = true;
145-
console.warn('Fetching CPU Usage metrics failed:', error);
146-
this.error = error;
147-
},
148-
});
110+
processCpuLoad: {
111+
label: t('instances.details.process.process_cpu_usage'),
112+
value: isNaN(processCpuLoad.value)
113+
? '-'
114+
: `${formatNumber.format(processCpuLoad.value * 100)}%`,
149115
},
150-
async fetchCpuLoadMetrics() {
151-
const fetchProcessCpuLoad = this.fetchMetric('process.cpu.usage');
152-
const fetchSystemCpuLoad = this.fetchMetric('system.cpu.usage');
153-
let processCpuLoad;
154-
let systemCpuLoad;
155-
try {
156-
processCpuLoad = (await fetchProcessCpuLoad).measurements[0].value;
157-
} catch (error) {
158-
console.warn('Fetching Process CPU Load failed:', error);
159-
}
160-
try {
161-
systemCpuLoad = (await fetchSystemCpuLoad).measurements[0].value;
162-
} catch (error) {
163-
console.warn('Fetching Sytem CPU Load failed:', error);
164-
}
165-
return {
166-
processCpuLoad,
167-
systemCpuLoad,
168-
};
116+
systemCpuLoad: {
117+
label: t('instances.details.process.system_cpu_usage'),
118+
value: isNaN(systemCpuLoad.value)
119+
? '-'
120+
: `${formatNumber.format(systemCpuLoad.value * 100)}%`,
169121
},
170-
async fetchMetric(name) {
171-
const response = await this.instance.fetchMetric(name);
172-
return response.data;
122+
cpus: {
123+
label: t('instances.details.process.cpus'),
124+
value: systemCpuCount.value,
173125
},
174-
},
126+
};
127+
});
128+
129+
// Hilfsfunktionen
130+
const fetchMetric = async (name: string): Promise<MetricResponse> => {
131+
const response = await props.instance.fetchMetric(name);
132+
return response.data;
175133
};
134+
135+
const fetchUptime = async (): Promise<void> => {
136+
try {
137+
const response = await fetchMetric('process.uptime');
138+
uptime.value = {
139+
value: response.measurements[0].value,
140+
baseUnit: response.baseUnit,
141+
};
142+
} catch (err) {
143+
error.value = err instanceof Error ? err : new Error('Unknown error');
144+
console.warn('Fetching Uptime failed:', err);
145+
}
146+
};
147+
148+
const fetchPid = async (): Promise<void> => {
149+
if (props.instance.hasEndpoint('env')) {
150+
try {
151+
const response = await props.instance.fetchEnv('PID');
152+
pid.value = response.data.property.value;
153+
} catch (err) {
154+
console.warn('Fetching PID failed:', err);
155+
}
156+
}
157+
};
158+
159+
const fetchCpuCount = async (): Promise<void> => {
160+
try {
161+
const response = await fetchMetric('system.cpu.count');
162+
systemCpuCount.value = response.measurements[0].value;
163+
} catch (err) {
164+
console.warn('Fetching Cpu Count failed:', err);
165+
}
166+
};
167+
168+
const fetchProcessInfo = async (): Promise<void> => {
169+
try {
170+
const response = await props.instance.fetchInfo();
171+
const processInfo = response.data.process;
172+
if (processInfo) {
173+
pid.value = processInfo.pid;
174+
parentPid.value = processInfo.parentPid;
175+
owner.value = processInfo.owner;
176+
}
177+
} catch (err) {
178+
console.warn('Fetching Process Info failed:', err);
179+
}
180+
};
181+
182+
const fetchCpuLoadMetrics = async (): Promise<CpuLoadMetrics> => {
183+
const fetchProcessCpuLoad = fetchMetric('process.cpu.usage');
184+
const fetchSystemCpuLoad = fetchMetric('system.cpu.usage');
185+
186+
let processCpuLoadValue: number | null = null;
187+
let systemCpuLoadValue: number | null = null;
188+
189+
try {
190+
const response = await fetchProcessCpuLoad;
191+
processCpuLoadValue = response.measurements[0].value;
192+
} catch (err) {
193+
console.warn('Fetching Process CPU Load failed:', err);
194+
}
195+
196+
try {
197+
const response = await fetchSystemCpuLoad;
198+
systemCpuLoadValue = response.measurements[0].value;
199+
} catch (err) {
200+
console.warn('Fetching System CPU Load failed:', err);
201+
}
202+
203+
return {
204+
processCpuLoad: processCpuLoadValue,
205+
systemCpuLoad: systemCpuLoadValue,
206+
};
207+
};
208+
209+
onMounted(async () => {
210+
try {
211+
await Promise.allSettled([
212+
fetchPid(),
213+
fetchUptime(),
214+
fetchCpuCount(),
215+
fetchProcessInfo(),
216+
]);
217+
} finally {
218+
hasLoaded.value = true;
219+
}
220+
221+
const subscription = timer(0, sbaConfig.uiSettings.pollTimer.process)
222+
.pipe(
223+
concatMap(fetchCpuLoadMetrics),
224+
retryWhen((err) => err.pipe(delay(1000, take(5)))),
225+
)
226+
.subscribe({
227+
next: (data: CpuLoadMetrics) => {
228+
processCpuLoad.value = data.processCpuLoad;
229+
systemCpuLoad.value = data.systemCpuLoad;
230+
},
231+
error: (err: Error) => {
232+
hasLoaded.value = true;
233+
console.warn('Fetching CPU Usage metrics failed:', err);
234+
error.value = rr;
235+
},
236+
});
237+
238+
onUnmounted(() => {
239+
subscription.unsubscribe();
240+
});
241+
});
176242
</script>

spring-boot-admin-server-ui/src/main/frontend/views/instances/details/i18n.de.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
"health": {
2525
"title": "Zustand"
2626
},
27+
"health_group": {
28+
"title": "Zustandsgruppe"
29+
},
2730
"info": {
2831
"no_info_provided": "Keine Informationen verfügbar.",
2932
"title": "Info"
@@ -33,7 +36,8 @@
3336
"metaspace": "Metaspace",
3437
"size": "Größe",
3538
"title": "Speicher",
36-
"used": "In Verwendung"
39+
"used": "In Verwendung",
40+
"committed": "Zugesichert"
3741
},
3842
"metadata": {
3943
"no_data_provided": "Keine Metadaten vorhanden",
@@ -42,6 +46,8 @@
4246
"process": {
4347
"cpus": "CPUs",
4448
"pid": "PID",
49+
"parent_pid": "Eltern-PID",
50+
"owner": "Besitzer",
4551
"process_cpu_usage": "CPU-Auslastung (%)",
4652
"system_cpu_usage": "System-Auslastung (%)",
4753
"title": "Prozess",

spring-boot-admin-server-ui/src/main/frontend/views/instances/details/i18n.en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
"process": {
4747
"cpus": "CPUs",
4848
"pid": "PID",
49+
"parent_pid": "Parent PID",
50+
"owner": "Owner",
4951
"process_cpu_usage": "Process CPU Usage",
5052
"system_cpu_usage": "System CPU Usage",
5153
"title": "Process",

0 commit comments

Comments
 (0)