using Fleet.Core; using MoShiro.Backend.Analysis; using MoShiro.Protocol; namespace MoShiro.Backend.Application; /// 请求的指标:导出 id(rtt.p95)与评分 id(score.mc)。 public sealed record MetricSelection(IReadOnlyList ExportIds, IReadOnlyList ScoreModelIds) { public bool WantsScores => ScoreModelIds.Count > 0; public IEnumerable AllIds => ExportIds.Concat(ScoreModelIds.Select(m => "score." + m)); public static Result Resolve(string? metrics, string? presets, int maxMetrics) { var ids = new List(); if (presets is not null) { foreach (string presetId in presets.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)) { PresetDef? preset = MetricRegistry.FindPreset(presetId); if (preset is null) { return Result.Fail(MoShiroErrors.UnknownPreset, $"unknown preset {presetId}"); } ids.AddRange(preset.MetricIds); } } if (metrics is not null) { ids.AddRange(metrics.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)); } ids = [.. ids.Distinct(StringComparer.Ordinal)]; if (ids.Count > maxMetrics) { return Result.Fail(MoShiroErrors.TooManyMetrics, $"at most {maxMetrics} metrics per request"); } var exports = new List(); var scores = new List(); foreach (string id in ids) { if (id.StartsWith("score.", StringComparison.Ordinal)) { string model = id["score.".Length..]; if (ModelRegistry.Find(model) is null) { return Result.Fail(MoShiroErrors.UnknownMetric, $"unknown metric {id}"); } scores.Add(model); } else if (MetricRegistry.TryFindExport(id, out _, out _)) { exports.Add(id); } else { return Result.Fail(MoShiroErrors.UnknownMetric, $"unknown metric {id}"); } } return Result.Ok(new MetricSelection(exports, scores)); } } public static class ExportEvaluator { /// 导出指定的值;没有样本的指标不出现在结果里。 public static Dictionary Evaluate(MetricSet set, IEnumerable exportIds, double durationSeconds) { var values = new Dictionary(StringComparer.Ordinal); foreach (string id in exportIds) { if (MetricRegistry.TryFindExport(id, out MetricDef def, out MetricExport export) && set.TryGet(def.Id, out Accumulator acc) && MetricDef.Export(acc, export, durationSeconds) is { } value && double.IsFinite(value)) { values[id] = value; } } return values; } public static IEnumerable AllExportIds() => MetricRegistry.AllExports().Select(e => e.Export.Id); }