added a garbage filter
parent
1a63f20bb0
commit
319e2cdd69
|
|
@ -0,0 +1,5 @@
|
||||||
|
export interface MetricMetadata {
|
||||||
|
metric: string;
|
||||||
|
type: string;
|
||||||
|
help: string;
|
||||||
|
}
|
||||||
|
|
@ -1,15 +1,12 @@
|
||||||
import { Injectable, Inject } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { PrometheusService } from '../prometheus.service';
|
import { PrometheusService } from '../prometheus.service';
|
||||||
import { MenuItem } from './menu.interface';
|
import { MenuItem } from './menu.interface';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MenuService {
|
export class MenuService {
|
||||||
constructor(
|
constructor(private readonly prometheusService: PrometheusService) {}
|
||||||
private readonly prometheusService: PrometheusService // Просто объявляем зависимость
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async getFullMenu(): Promise<MenuItem> {
|
async getFullMenu(): Promise<MenuItem> {
|
||||||
// Реализация остается прежней
|
|
||||||
const dynamicItems = await this.generateDynamicItems();
|
const dynamicItems = await this.generateDynamicItems();
|
||||||
return this.injectDynamicItems(this.getStaticStructure(), dynamicItems);
|
return this.injectDynamicItems(this.getStaticStructure(), dynamicItems);
|
||||||
}
|
}
|
||||||
|
|
@ -37,7 +34,9 @@ export class MenuService {
|
||||||
private async generateDynamicItems(): Promise<MenuItem[]> {
|
private async generateDynamicItems(): Promise<MenuItem[]> {
|
||||||
const metricNames = await this.prometheusService.fetchAllMetrics();
|
const metricNames = await this.prometheusService.fetchAllMetrics();
|
||||||
|
|
||||||
const allSeries = await Promise.all(
|
// Получаем все серии для каждой метрики
|
||||||
|
const allSeries = (
|
||||||
|
await Promise.all(
|
||||||
metricNames.map(async name => {
|
metricNames.map(async name => {
|
||||||
const series = await this.prometheusService.fetchMetricSeries(name);
|
const series = await this.prometheusService.fetchMetricSeries(name);
|
||||||
return series.map(s => ({
|
return series.map(s => ({
|
||||||
|
|
@ -45,20 +44,53 @@ export class MenuService {
|
||||||
labels: s
|
labels: s
|
||||||
}));
|
}));
|
||||||
})
|
})
|
||||||
|
)
|
||||||
|
).flat();
|
||||||
|
|
||||||
|
// Загружаем мета-информацию по каждой метрике
|
||||||
|
const metadataMap = new Map<string, string>(); // metric -> help
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
metricNames.map(async metric => {
|
||||||
|
try {
|
||||||
|
const meta = await this.prometheusService.fetchMetricMetadata(metric);
|
||||||
|
if (meta?.help) {
|
||||||
|
metadataMap.set(metric, meta.help);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(`No metadata for metric ${metric}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const flatSeries = allSeries.flat();
|
const isGarbageDevice = (device: string) =>
|
||||||
|
device.startsWith('/dev') ||
|
||||||
|
device.startsWith('/proc') ||
|
||||||
|
device.startsWith('/sys') ||
|
||||||
|
device.startsWith('/rootfs') ||
|
||||||
|
device.startsWith('/var') ||
|
||||||
|
device.startsWith('overlay') ||
|
||||||
|
device.startsWith('br') ||
|
||||||
|
device.startsWith('docker0') ||
|
||||||
|
device.startsWith('ens18') ||
|
||||||
|
device.startsWith('sda') ||
|
||||||
|
device.startsWith('sr0') ||
|
||||||
|
device.startsWith('tmpfs') ||
|
||||||
|
device.startsWith('veth') ||
|
||||||
|
device.startsWith('gvfsd') ||
|
||||||
|
device.startsWith('lo') ||
|
||||||
|
device.startsWith('/run');
|
||||||
|
|
||||||
const devices = this.extractUniqueEntities(flatSeries, 'device');
|
const devices = this.extractUniqueEntities(allSeries, 'device')
|
||||||
|
.filter(device => !isGarbageDevice(device));
|
||||||
|
|
||||||
return devices.map(device => ({
|
return devices.map(device => ({
|
||||||
id: `device_${device}`,
|
id: `device_${device}`,
|
||||||
title: `Graviton S2082I (${device})`,
|
title: `Graviton S2082I (${device})`,
|
||||||
items: this.generateModuleItems(device, flatSeries),
|
items: this.generateModuleItems(device, allSeries, metadataMap),
|
||||||
isDynamic: true
|
isDynamic: true
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private extractUniqueEntities(metrics: any[], field: string): string[] {
|
private extractUniqueEntities(metrics: any[], field: string): string[] {
|
||||||
const entities = new Set<string>();
|
const entities = new Set<string>();
|
||||||
|
|
@ -70,7 +102,11 @@ export class MenuService {
|
||||||
return Array.from(entities);
|
return Array.from(entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
private generateModuleItems(device: string, seriesData: { metric: string, labels: Record<string, string> }[]): MenuItem[] {
|
private generateModuleItems(
|
||||||
|
device: string,
|
||||||
|
seriesData: { metric: string; labels: Record<string, string> }[],
|
||||||
|
metadataMap: Map<string, string>
|
||||||
|
): MenuItem[] {
|
||||||
const modules = new Set<string>();
|
const modules = new Set<string>();
|
||||||
|
|
||||||
seriesData.forEach(({ labels }) => {
|
seriesData.forEach(({ labels }) => {
|
||||||
|
|
@ -80,34 +116,41 @@ export class MenuService {
|
||||||
});
|
});
|
||||||
|
|
||||||
return Array.from(modules).map(module => ({
|
return Array.from(modules).map(module => ({
|
||||||
id: `module_${module.replace('module$', '')}`,
|
id: `module_${device}_${module}`,
|
||||||
title: `OS Linux АО (${module})`,
|
title: `Module ${module.replace('module$', '')}`,
|
||||||
items: this.generateMetricItems(device, module, seriesData),
|
items: this.generateMetricItems(device, module, seriesData, metadataMap),
|
||||||
isDynamic: true
|
isDynamic: true
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private generateMetricItems(
|
||||||
private generateMetricItems(device: string, module: string, seriesData: { metric: string, labels: Record<string, string> }[]): MenuItem[] {
|
device: string,
|
||||||
|
module: string,
|
||||||
|
seriesData: { metric: string; labels: Record<string, string> }[],
|
||||||
|
metadataMap: Map<string, string>
|
||||||
|
): MenuItem[] {
|
||||||
const filtered = seriesData.filter(
|
const filtered = seriesData.filter(
|
||||||
({ labels }) => labels.device === device && labels.source_id === module
|
({ labels }) => labels.device === device && labels.source_id === module
|
||||||
);
|
);
|
||||||
|
|
||||||
const uniqueMetrics = new Set(filtered.map(entry => entry.metric));
|
const uniqueMetrics = new Set(filtered.map(entry => entry.metric));
|
||||||
|
|
||||||
return Array.from(uniqueMetrics).map(metric => ({
|
return Array.from(uniqueMetrics).map(metric => {
|
||||||
|
const description = metadataMap.get(metric) || metric;
|
||||||
|
|
||||||
|
return {
|
||||||
id: `metric_${device}_${module}_${metric}`,
|
id: `metric_${device}_${module}_${metric}`,
|
||||||
title: metric, // или запрашивать описание отдельно
|
title: description,
|
||||||
metric,
|
metric,
|
||||||
filters: {
|
filters: {
|
||||||
device,
|
device,
|
||||||
source_id: module
|
source_id: module
|
||||||
},
|
},
|
||||||
isDynamic: true
|
isDynamic: true
|
||||||
}));
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private injectDynamicItems(menu: MenuItem, dynamicItems: MenuItem[]): MenuItem {
|
private injectDynamicItems(menu: MenuItem, dynamicItems: MenuItem[]): MenuItem {
|
||||||
if (menu.id === 'media_servers') {
|
if (menu.id === 'media_servers') {
|
||||||
return { ...menu, items: dynamicItems };
|
return { ...menu, items: dynamicItems };
|
||||||
|
|
@ -119,7 +162,6 @@ export class MenuService {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async updateMenuItem(id: string, update: Partial<MenuItem>): Promise<MenuItem> {
|
async updateMenuItem(id: string, update: Partial<MenuItem>): Promise<MenuItem> {
|
||||||
const fullMenu = await this.getFullMenu();
|
const fullMenu = await this.getFullMenu();
|
||||||
const item = this.findMenuItem(fullMenu, id);
|
const item = this.findMenuItem(fullMenu, id);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue