From 319e2cdd6993c2c96f2a3ea0b00de5a7ea6e5ba9 Mon Sep 17 00:00:00 2001 From: DmitriyA Date: Mon, 26 May 2025 08:53:22 -0400 Subject: [PATCH] added a garbage filter --- src/menu/MetricMetadata.ts | 5 ++ src/menu/menu.service.ts | 146 ++++++++++++++++++++++++------------- 2 files changed, 99 insertions(+), 52 deletions(-) create mode 100644 src/menu/MetricMetadata.ts diff --git a/src/menu/MetricMetadata.ts b/src/menu/MetricMetadata.ts new file mode 100644 index 0000000..c16aff0 --- /dev/null +++ b/src/menu/MetricMetadata.ts @@ -0,0 +1,5 @@ +export interface MetricMetadata { + metric: string; + type: string; + help: string; + } \ No newline at end of file diff --git a/src/menu/menu.service.ts b/src/menu/menu.service.ts index 12c9db1..62f1047 100644 --- a/src/menu/menu.service.ts +++ b/src/menu/menu.service.ts @@ -1,15 +1,12 @@ -import { Injectable, Inject } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { PrometheusService } from '../prometheus.service'; import { MenuItem } from './menu.interface'; @Injectable() export class MenuService { - constructor( - private readonly prometheusService: PrometheusService // Просто объявляем зависимость - ) {} + constructor(private readonly prometheusService: PrometheusService) {} async getFullMenu(): Promise { - // Реализация остается прежней const dynamicItems = await this.generateDynamicItems(); return this.injectDynamicItems(this.getStaticStructure(), dynamicItems); } @@ -35,30 +32,65 @@ export class MenuService { } private async generateDynamicItems(): Promise { - const metricNames = await this.prometheusService.fetchAllMetrics(); + const metricNames = await this.prometheusService.fetchAllMetrics(); - const allSeries = await Promise.all( - metricNames.map(async name => { - const series = await this.prometheusService.fetchMetricSeries(name); - return series.map(s => ({ - metric: name, - labels: s - })); - }) - ); + // Получаем все серии для каждой метрики + const allSeries = ( + await Promise.all( + metricNames.map(async name => { + const series = await this.prometheusService.fetchMetricSeries(name); + return series.map(s => ({ + metric: name, + labels: s + })); + }) + ) + ).flat(); - const flatSeries = allSeries.flat(); + // Загружаем мета-информацию по каждой метрике + const metadataMap = new Map(); // metric -> help - const devices = this.extractUniqueEntities(flatSeries, 'device'); + 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}`); + } + }) + ); - return devices.map(device => ({ - id: `device_${device}`, - title: `Graviton S2082I (${device})`, - items: this.generateModuleItems(device, flatSeries), - isDynamic: true - })); -} + 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(allSeries, 'device') + .filter(device => !isGarbageDevice(device)); + + return devices.map(device => ({ + id: `device_${device}`, + title: `Graviton S2082I (${device})`, + items: this.generateModuleItems(device, allSeries, metadataMap), + isDynamic: true + })); + } private extractUniqueEntities(metrics: any[], field: string): string[] { const entities = new Set(); @@ -70,76 +102,86 @@ export class MenuService { return Array.from(entities); } - private generateModuleItems(device: string, seriesData: { metric: string, labels: Record }[]): MenuItem[] { + private generateModuleItems( + device: string, + seriesData: { metric: string; labels: Record }[], + metadataMap: Map + ): MenuItem[] { const modules = new Set(); - + seriesData.forEach(({ labels }) => { if (labels.device === device && labels.source_id) { modules.add(labels.source_id); } }); - + return Array.from(modules).map(module => ({ - id: `module_${module.replace('module$', '')}`, - title: `OS Linux АО (${module})`, - items: this.generateMetricItems(device, module, seriesData), + id: `module_${device}_${module}`, + title: `Module ${module.replace('module$', '')}`, + items: this.generateMetricItems(device, module, seriesData, metadataMap), isDynamic: true })); } - - private generateMetricItems(device: string, module: string, seriesData: { metric: string, labels: Record }[]): MenuItem[] { + private generateMetricItems( + device: string, + module: string, + seriesData: { metric: string; labels: Record }[], + metadataMap: Map + ): MenuItem[] { const filtered = seriesData.filter( ({ labels }) => labels.device === device && labels.source_id === module ); - + const uniqueMetrics = new Set(filtered.map(entry => entry.metric)); - - return Array.from(uniqueMetrics).map(metric => ({ - id: `metric_${device}_${module}_${metric}`, - title: metric, // или запрашивать описание отдельно - metric, - filters: { - device, - source_id: module - }, - isDynamic: true - })); + + return Array.from(uniqueMetrics).map(metric => { + const description = metadataMap.get(metric) || metric; + + return { + id: `metric_${device}_${module}_${metric}`, + title: description, + metric, + filters: { + device, + source_id: module + }, + isDynamic: true + }; + }); } - private injectDynamicItems(menu: MenuItem, dynamicItems: MenuItem[]): MenuItem { if (menu.id === 'media_servers') { return { ...menu, items: dynamicItems }; } - + return { ...menu, items: menu.items?.map(item => this.injectDynamicItems(item, dynamicItems)) || [] }; } - async updateMenuItem(id: string, update: Partial): Promise { const fullMenu = await this.getFullMenu(); const item = this.findMenuItem(fullMenu, id); - + if (!item) throw new Error('Menu item not found'); Object.assign(item, update); - + return item; } private findMenuItem(menu: MenuItem, id: string): MenuItem | null { if (menu.id === id) return menu; - + if (menu.items) { for (const item of menu.items) { const found = this.findMenuItem(item, id); if (found) return found; } } - + return null; } -} \ No newline at end of file +}