76 lines
3.9 KiB
C#
76 lines
3.9 KiB
C#
namespace MoShiro.Backend.Analysis;
|
|
|
|
public sealed record PresetDef(string Id, string Label, IReadOnlyList<string> MetricIds);
|
|
|
|
/// <summary>
|
|
/// 指标注册表:存储、降采样、API、目录都由它驱动。新增指标时,在对应的 Metrics 文件里加一行声明,
|
|
/// 再把它放进这里的 <see cref="All"/>(显式注册,AOT 安全,不使用反射)。
|
|
/// </summary>
|
|
public static class MetricRegistry
|
|
{
|
|
public static readonly IReadOnlyList<MetricDef> All =
|
|
[
|
|
LinkMetrics.Rtt, LinkMetrics.LossUp, LinkMetrics.LossDown, LinkMetrics.LossTotal,
|
|
LinkMetrics.JitterUp, LinkMetrics.JitterDown, LinkMetrics.LossRun, LinkMetrics.LossRunUp, LinkMetrics.LossRunDown, LinkMetrics.Reorder, LinkMetrics.Dup, LinkMetrics.SamplesUdp,
|
|
TcpMetrics.Rtt, TcpMetrics.Slow, TcpMetrics.Stall, TcpMetrics.StallMax, TcpMetrics.Retrans, TcpMetrics.Srtt, TcpMetrics.BulkRate, TcpMetrics.BulkPeak, TcpMetrics.SamplesTcp,
|
|
SessionMetrics.ConnectSuccess, SessionMetrics.ConnectTcp, SessionMetrics.ConnectTls,
|
|
SessionMetrics.FailTimeout, SessionMetrics.FailReset, SessionMetrics.FailTls,
|
|
SessionMetrics.HoldResets, SessionMetrics.HoldLifetime,
|
|
SessionMetrics.TrainLoss, SessionMetrics.TrainJitter, SessionMetrics.TrainRate, SessionMetrics.SamplesConnect,
|
|
];
|
|
|
|
/// <summary>指标分组:前端按分组展示,新增指标只需要把它加进分组。</summary>
|
|
public static readonly IReadOnlyList<PresetDef> Presets =
|
|
[
|
|
new("quic", "QUIC Datagram", ["loss.up", "loss.down", "loss.total", "rtt.p50", "rtt.p95", "rtt.p99", "jitter.up.p95", "jitter.down.p95", "loss.run.up_max", "loss.run.down_max", "reorder", "dup"]),
|
|
new("udp", "QUIC Datagram (legacy alias)", ["loss.up", "loss.down", "loss.total", "rtt.p50", "rtt.p95", "rtt.p99", "jitter.up.p95", "jitter.down.p95", "loss.run_max", "reorder", "dup"]),
|
|
new("tcp", "TCP", ["tcp.rtt.p50", "tcp.rtt.p95", "tcp.rtt.p99", "tcp.rtt.p999", "tcp.rtt.max", "tcp.rtt.tail", "tcp.slow", "tcp.stall.per_min", "tcp.stall_max", "tcp.retrans", "tcp.srtt.p95"]),
|
|
new("connect", "Connect", ["connect.success", "connect.tcp_ms.p95", "connect.tls_ms.p95", "connect.fail.timeout", "connect.fail.rst", "connect.fail.tls"]),
|
|
new("session", "Session", ["hold.resets.per_h", "hold.lifetime_max"]),
|
|
new("scores", "Scores", ["score.base", "score.mc", "score.hy2", "score.reality"]),
|
|
];
|
|
|
|
private static readonly Dictionary<string, MetricDef> s_byId = All.ToDictionary(m => m.Id, StringComparer.Ordinal);
|
|
|
|
private static readonly Dictionary<string, (MetricDef Def, MetricExport Export)> s_byExport = BuildExportIndex();
|
|
|
|
public static MetricDef? Find(string metricId) => s_byId.GetValueOrDefault(metricId);
|
|
|
|
/// <summary>按导出 id(例如 <c>rtt.p95</c>)查找。</summary>
|
|
public static bool TryFindExport(string exportId, out MetricDef def, out MetricExport export)
|
|
{
|
|
if (s_byExport.TryGetValue(exportId, out (MetricDef Def, MetricExport Export) found))
|
|
{
|
|
def = found.Def;
|
|
export = found.Export;
|
|
return true;
|
|
}
|
|
|
|
def = null!;
|
|
export = null!;
|
|
return false;
|
|
}
|
|
|
|
public static PresetDef? FindPreset(string id) => Presets.FirstOrDefault(p => p.Id == id);
|
|
|
|
public static IEnumerable<(MetricDef Def, MetricExport Export)> AllExports() =>
|
|
All.SelectMany(def => def.Exports.Select(export => (def, export)));
|
|
|
|
private static Dictionary<string, (MetricDef, MetricExport)> BuildExportIndex()
|
|
{
|
|
var index = new Dictionary<string, (MetricDef, MetricExport)>(StringComparer.Ordinal);
|
|
foreach (MetricDef def in All)
|
|
{
|
|
foreach (MetricExport export in def.Exports)
|
|
{
|
|
if (!index.TryAdd(export.Id, (def, export)))
|
|
{
|
|
throw new InvalidOperationException($"duplicate export id {export.Id}");
|
|
}
|
|
}
|
|
}
|
|
|
|
return index;
|
|
}
|
|
}
|