diff --git a/internal/handler/agent_handler.go b/internal/handler/agent_handler.go index 333df71..c92ae51 100644 --- a/internal/handler/agent_handler.go +++ b/internal/handler/agent_handler.go @@ -408,7 +408,7 @@ func (h *AgentHandler) GetMetrics(c echo.Context) error { // 验证指标类型 validTypes := map[string]bool{ "cpu": true, "memory": true, "disk": true, "network": true, "network_connection": true, - "disk_io": true, "gpu": true, "temperature": true, + "disk_io": true, "gpu": true, "temperature": true, "monitor": true, } if metricType == "" { return orz.NewError(400, "指标类型不能为空") diff --git a/internal/service/metric_service.go b/internal/service/metric_service.go index 3ec6216..4cb379b 100644 --- a/internal/service/metric_service.go +++ b/internal/service/metric_service.go @@ -23,6 +23,7 @@ type MetricService struct { logger *zap.Logger metricRepo *repo.MetricRepo agentRepo *repo.AgentRepo + monitorRepo *repo.MonitorRepo propertyService *PropertyService trafficService *TrafficService // 流量统计服务 vmClient *vmclient.VMClient @@ -38,6 +39,7 @@ func NewMetricService(logger *zap.Logger, db *gorm.DB, propertyService *Property logger: logger, metricRepo: repo.NewMetricRepo(db), agentRepo: repo.NewAgentRepo(db), + monitorRepo: repo.NewMonitorRepo(db), propertyService: propertyService, trafficService: trafficService, vmClient: vmClient, @@ -244,6 +246,45 @@ func (s *MetricService) GetMetrics(ctx context.Context, agentID, metricType stri series = append(series, convertedSeries...) } + // 如果是监控类型,添加监控任务名称到标签中 + if metricType == "monitor" && len(series) > 0 { + // 收集所有 monitor_id + monitorIdSet := make(map[string]struct{}) + for _, s := range series { + if monitorId, ok := s.Labels["monitor_id"]; ok { + monitorIdSet[monitorId] = struct{}{} + } + } + + // 查询 monitor 信息 + if len(monitorIdSet) > 0 { + monitorIds := make([]string, 0, len(monitorIdSet)) + for monitorId := range monitorIdSet { + monitorIds = append(monitorIds, monitorId) + } + + monitors, err := s.monitorRepo.FindByIdIn(ctx, monitorIds) + if err != nil { + s.logger.Error("查询 monitor 信息失败", zap.Error(err)) + } else { + // 构建 monitorId -> monitorName 映射 + monitorNameMap := make(map[string]string) + for _, monitor := range monitors { + monitorNameMap[monitor.ID] = monitor.Name + } + + // 在每个 series 的 labels 中添加 monitor_name + for i := range series { + if monitorId, ok := series[i].Labels["monitor_id"]; ok { + if monitorName, exists := monitorNameMap[monitorId]; exists { + series[i].Labels["monitor_name"] = monitorName + } + } + } + } + } + } + return &metric.GetMetricsResponse{ AgentID: agentID, Type: metricType, @@ -289,33 +330,7 @@ func (s *MetricService) DeleteAgentMetrics(ctx context.Context, agentID string) // 继续删除 VictoriaMetrics 中的数据 } - // 2. 删除 VictoriaMetrics 中的时间序列数据 - match := []string{fmt.Sprintf(`pika_.*{agent_id="%s"}`, agentID)} - if err := s.vmClient.DeleteSeries(ctx, match); err != nil { - s.logger.Error("删除 VictoriaMetrics 中的探针数据失败", - zap.String("agentID", agentID), - zap.Error(err)) - return err - } - - s.logger.Info("成功删除探针的所有指标数据", - zap.String("agentID", agentID)) - return nil -} - -// DeleteMonitorMetrics 删除指定监控任务的所有指标数据 -func (s *MetricService) DeleteMonitorMetrics(ctx context.Context, monitorID string) error { - // 删除 VictoriaMetrics 中的监控指标数据 - match := []string{fmt.Sprintf(`pika_monitor_.*{monitor_id="%s"}`, monitorID)} - if err := s.vmClient.DeleteSeries(ctx, match); err != nil { - s.logger.Error("删除 VictoriaMetrics 中的监控数据失败", - zap.String("monitorID", monitorID), - zap.Error(err)) - return err - } - - s.logger.Info("成功删除监控任务的所有指标数据", - zap.String("monitorID", monitorID)) + // 2. 不主动删除 VictoriaMetrics 中的时间序列数据,利用过期机制自动删除数据 return nil } @@ -430,6 +445,13 @@ func (s *MetricService) buildPromQLQueries(agentID, metricType string, interface Name: "temperature", Query: fmt.Sprintf(`pika_temperature_celsius{agent_id="%s"}`, agentID), }} + + case "monitor": + // 监控:响应时间(该探针参与的所有监控任务) + queries = []metric.QueryDefinition{{ + Name: "response_time", + Query: fmt.Sprintf(`pika_monitor_response_time_ms{agent_id="%s"}`, agentID), + }} } return queries @@ -492,6 +514,9 @@ func (s *MetricService) convertQueryResultToSeries(result *vmclient.QueryResult, delete(labels, "gpu_index") } + // 移除 target 标签(避免数据泄露) + delete(labels, "target") + allSeries = append(allSeries, metric.Series{ Name: finalName, Labels: labels, diff --git a/internal/service/monitor_service.go b/internal/service/monitor_service.go index f5c1e26..e3a0f89 100644 --- a/internal/service/monitor_service.go +++ b/internal/service/monitor_service.go @@ -187,13 +187,6 @@ func (s *MonitorService) DeleteMonitor(ctx context.Context, id string) error { if err := s.MonitorRepo.DeleteById(ctx, id); err != nil { return err } - - // 删除监控指标数据(从 VictoriaMetrics) - if err := s.metricService.DeleteMonitorMetrics(ctx, id); err != nil { - s.logger.Error("删除监控指标数据失败", zap.String("monitorId", id), zap.Error(err)) - return err - } - return nil }) diff --git a/internal/vmclient/client.go b/internal/vmclient/client.go index 8109848..c3896bc 100644 --- a/internal/vmclient/client.go +++ b/internal/vmclient/client.go @@ -220,37 +220,6 @@ func (c *VMClient) Query(ctx context.Context, query string) (*QueryResult, error return &result, nil } -// DeleteSeries 删除时间序列 -func (c *VMClient) DeleteSeries(ctx context.Context, match []string) error { - reqCtx, cancel := context.WithTimeout(ctx, c.writeTimeout) - defer cancel() - - params := url.Values{} - for _, m := range match { - params.Add("match[]", m) - } - - reqURL := fmt.Sprintf("%s/api/v1/admin/tsdb/delete_series?%s", c.baseURL, params.Encode()) - - req, err := http.NewRequestWithContext(reqCtx, "POST", reqURL, nil) - if err != nil { - return fmt.Errorf("create request failed: %w", err) - } - - resp, err := c.httpClient.Do(req) - if err != nil { - return fmt.Errorf("delete series failed: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return fmt.Errorf("delete series failed with status %d: %s", resp.StatusCode, string(body)) - } - - return nil -} - // ConvertToDataPoints 将查询结果转换为数据点列表 func ConvertToDataPoints(result *QueryResult) []DataPoint { if result == nil || len(result.Data.Result) == 0 { diff --git a/web/src/api/agent.ts b/web/src/api/agent.ts index d7802ed..76fe287 100644 --- a/web/src/api/agent.ts +++ b/web/src/api/agent.ts @@ -8,7 +8,7 @@ export interface ListAgentsResponse { export interface GetAgentMetricsRequest { agentId: string; - type: 'cpu' | 'memory' | 'disk' | 'network' | 'network_connection' | 'disk_io' | 'gpu' | 'temperature'; + type: 'cpu' | 'memory' | 'disk' | 'network' | 'network_connection' | 'disk_io' | 'gpu' | 'temperature' | 'monitor'; range?: string; // 时间范围,如 '15m', '1h', '1d' 等,从后端配置获取 interface?: string; // 网卡过滤参数(仅对 network 类型有效) } diff --git a/web/src/components/CompactResourceBar.tsx b/web/src/components/CompactResourceBar.tsx index 84540c7..c25e057 100644 --- a/web/src/components/CompactResourceBar.tsx +++ b/web/src/components/CompactResourceBar.tsx @@ -25,9 +25,9 @@ const CompactResourceBar = ({ value, label, subtext, icon: Icon, color = "bg-cya } return ( -