Files
MoShiro/backend/src/MoShiro.Backend.Application/Query/MetricSelection.cs
T
justme 2e146d4da7
ci/woodpecker/push/main-amd64-build Pipeline was successful
refactor!: rename the project from Monitor to MoShiro [CI]
Namespaces, assemblies, solution, binaries, images, systemd units, config paths, environment variables, protocol methods and the Linux user are all renamed. Breaking: existing databases, node identities and installed units must be recreated.
2026-10-07 02:03:25 +08:00

92 lines
3.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Fleet.Core;
using MoShiro.Backend.Analysis;
using MoShiro.Protocol;
namespace MoShiro.Backend.Application;
/// <summary>请求的指标:导出 id(<c>rtt.p95</c>)与评分 id(<c>score.mc</c>)。</summary>
public sealed record MetricSelection(IReadOnlyList<string> ExportIds, IReadOnlyList<string> ScoreModelIds)
{
public bool WantsScores => ScoreModelIds.Count > 0;
public IEnumerable<string> AllIds => ExportIds.Concat(ScoreModelIds.Select(m => "score." + m));
public static Result<MetricSelection> Resolve(string? metrics, string? presets, int maxMetrics)
{
var ids = new List<string>();
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<MetricSelection>.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<MetricSelection>.Fail(MoShiroErrors.TooManyMetrics, $"at most {maxMetrics} metrics per request");
}
var exports = new List<string>();
var scores = new List<string>();
foreach (string id in ids)
{
if (id.StartsWith("score.", StringComparison.Ordinal))
{
string model = id["score.".Length..];
if (ModelRegistry.Find(model) is null)
{
return Result<MetricSelection>.Fail(MoShiroErrors.UnknownMetric, $"unknown metric {id}");
}
scores.Add(model);
}
else if (MetricRegistry.TryFindExport(id, out _, out _))
{
exports.Add(id);
}
else
{
return Result<MetricSelection>.Fail(MoShiroErrors.UnknownMetric, $"unknown metric {id}");
}
}
return Result<MetricSelection>.Ok(new MetricSelection(exports, scores));
}
}
public static class ExportEvaluator
{
/// <summary>导出指定的值;没有样本的指标不出现在结果里。</summary>
public static Dictionary<string, double> Evaluate(MetricSet set, IEnumerable<string> exportIds, double durationSeconds)
{
var values = new Dictionary<string, double>(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<string> AllExportIds() => MetricRegistry.AllExports().Select(e => e.Export.Id);
}