Files
git.stella-ops.org/src/StellaOps.Concelier.Models/OsvGhsaParityDiagnostics.cs
2025-10-18 20:46:16 +03:00

73 lines
2.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
namespace StellaOps.Concelier.Models;
/// <summary>
/// Emits telemetry for OSV vs GHSA parity reports so QA dashboards can track regression trends.
/// </summary>
public static class OsvGhsaParityDiagnostics
{
private static readonly Meter Meter = new("StellaOps.Concelier.Models.OsvGhsaParity");
private static readonly Counter<long> TotalCounter = Meter.CreateCounter<long>(
"concelier.osv_ghsa.total",
unit: "count",
description: "Total GHSA identifiers evaluated for OSV parity.");
private static readonly Counter<long> IssueCounter = Meter.CreateCounter<long>(
"concelier.osv_ghsa.issues",
unit: "count",
description: "Parity issues grouped by dataset, issue kind, and field mask.");
public static void RecordReport(OsvGhsaParityReport report, string dataset)
{
ArgumentNullException.ThrowIfNull(report);
dataset = NormalizeDataset(dataset);
if (report.TotalGhsaIds > 0)
{
TotalCounter.Add(report.TotalGhsaIds, CreateTotalTags(dataset));
}
if (!report.HasIssues)
{
return;
}
foreach (var issue in report.Issues)
{
IssueCounter.Add(1, CreateIssueTags(dataset, issue));
}
}
private static KeyValuePair<string, object?>[] CreateTotalTags(string dataset)
=> new[]
{
new KeyValuePair<string, object?>("dataset", dataset),
};
private static KeyValuePair<string, object?>[] CreateIssueTags(string dataset, OsvGhsaParityIssue issue)
{
var mask = issue.FieldMask.IsDefaultOrEmpty
? "none"
: string.Join('|', issue.FieldMask);
return new[]
{
new KeyValuePair<string, object?>("dataset", dataset),
new KeyValuePair<string, object?>("issueKind", issue.IssueKind),
new KeyValuePair<string, object?>("fieldMask", mask),
};
}
private static string NormalizeDataset(string dataset)
{
if (string.IsNullOrWhiteSpace(dataset))
{
return "default";
}
return dataset.Trim().ToLowerInvariant();
}
}