Files
git.stella-ops.org/src/StellaOps.Concelier.Connector.Ghsa/Internal/GhsaDiagnostics.cs

165 lines
6.4 KiB
C#

using System.Collections.Generic;
using System.Diagnostics.Metrics;
namespace StellaOps.Concelier.Connector.Ghsa.Internal;
public sealed class GhsaDiagnostics : IDisposable
{
private const string MeterName = "StellaOps.Concelier.Connector.Ghsa";
private const string MeterVersion = "1.0.0";
private readonly Meter _meter;
private readonly Counter<long> _fetchAttempts;
private readonly Counter<long> _fetchDocuments;
private readonly Counter<long> _fetchFailures;
private readonly Counter<long> _fetchUnchanged;
private readonly Counter<long> _parseSuccess;
private readonly Counter<long> _parseFailures;
private readonly Counter<long> _parseQuarantine;
private readonly Counter<long> _mapSuccess;
private readonly Histogram<long> _rateLimitRemaining;
private readonly Histogram<long> _rateLimitLimit;
private readonly Histogram<double> _rateLimitResetSeconds;
private readonly Histogram<double> _rateLimitHeadroomPct;
private readonly ObservableGauge<double> _rateLimitHeadroomGauge;
private readonly Counter<long> _rateLimitExhausted;
private readonly Counter<long> _canonicalMetricFallbacks;
private readonly object _rateLimitLock = new();
private GhsaRateLimitSnapshot? _lastRateLimitSnapshot;
private readonly Dictionary<(string Phase, string? Resource), GhsaRateLimitSnapshot> _rateLimitSnapshots = new();
public GhsaDiagnostics()
{
_meter = new Meter(MeterName, MeterVersion);
_fetchAttempts = _meter.CreateCounter<long>("ghsa.fetch.attempts", unit: "operations");
_fetchDocuments = _meter.CreateCounter<long>("ghsa.fetch.documents", unit: "documents");
_fetchFailures = _meter.CreateCounter<long>("ghsa.fetch.failures", unit: "operations");
_fetchUnchanged = _meter.CreateCounter<long>("ghsa.fetch.unchanged", unit: "operations");
_parseSuccess = _meter.CreateCounter<long>("ghsa.parse.success", unit: "documents");
_parseFailures = _meter.CreateCounter<long>("ghsa.parse.failures", unit: "documents");
_parseQuarantine = _meter.CreateCounter<long>("ghsa.parse.quarantine", unit: "documents");
_mapSuccess = _meter.CreateCounter<long>("ghsa.map.success", unit: "advisories");
_rateLimitRemaining = _meter.CreateHistogram<long>("ghsa.ratelimit.remaining", unit: "requests");
_rateLimitLimit = _meter.CreateHistogram<long>("ghsa.ratelimit.limit", unit: "requests");
_rateLimitResetSeconds = _meter.CreateHistogram<double>("ghsa.ratelimit.reset_seconds", unit: "s");
_rateLimitHeadroomPct = _meter.CreateHistogram<double>("ghsa.ratelimit.headroom_pct", unit: "percent");
_rateLimitHeadroomGauge = _meter.CreateObservableGauge("ghsa.ratelimit.headroom_pct_current", ObserveHeadroom, unit: "percent");
_rateLimitExhausted = _meter.CreateCounter<long>("ghsa.ratelimit.exhausted", unit: "events");
_canonicalMetricFallbacks = _meter.CreateCounter<long>("ghsa.map.canonical_metric_fallbacks", unit: "advisories");
}
public void FetchAttempt() => _fetchAttempts.Add(1);
public void FetchDocument() => _fetchDocuments.Add(1);
public void FetchFailure() => _fetchFailures.Add(1);
public void FetchUnchanged() => _fetchUnchanged.Add(1);
public void ParseSuccess() => _parseSuccess.Add(1);
public void ParseFailure() => _parseFailures.Add(1);
public void ParseQuarantine() => _parseQuarantine.Add(1);
public void MapSuccess(long count) => _mapSuccess.Add(count);
internal void RecordRateLimit(GhsaRateLimitSnapshot snapshot)
{
var tags = new KeyValuePair<string, object?>[]
{
new("phase", snapshot.Phase),
new("resource", snapshot.Resource ?? "unknown")
};
if (snapshot.Limit.HasValue)
{
_rateLimitLimit.Record(snapshot.Limit.Value, tags);
}
if (snapshot.Remaining.HasValue)
{
_rateLimitRemaining.Record(snapshot.Remaining.Value, tags);
}
if (snapshot.ResetAfter.HasValue)
{
_rateLimitResetSeconds.Record(snapshot.ResetAfter.Value.TotalSeconds, tags);
}
if (TryCalculateHeadroom(snapshot, out var headroom))
{
_rateLimitHeadroomPct.Record(headroom, tags);
}
lock (_rateLimitLock)
{
_lastRateLimitSnapshot = snapshot;
_rateLimitSnapshots[(snapshot.Phase, snapshot.Resource)] = snapshot;
}
}
internal void RateLimitExhausted(string phase)
=> _rateLimitExhausted.Add(1, new KeyValuePair<string, object?>("phase", phase));
public void CanonicalMetricFallback(string canonicalMetricId, string severity)
=> _canonicalMetricFallbacks.Add(
1,
new KeyValuePair<string, object?>("canonical_metric_id", canonicalMetricId),
new KeyValuePair<string, object?>("severity", severity),
new KeyValuePair<string, object?>("reason", "no_cvss"));
internal GhsaRateLimitSnapshot? GetLastRateLimitSnapshot()
{
lock (_rateLimitLock)
{
return _lastRateLimitSnapshot;
}
}
private IEnumerable<Measurement<double>> ObserveHeadroom()
{
lock (_rateLimitLock)
{
if (_rateLimitSnapshots.Count == 0)
{
yield break;
}
foreach (var snapshot in _rateLimitSnapshots.Values)
{
if (TryCalculateHeadroom(snapshot, out var headroom))
{
yield return new Measurement<double>(
headroom,
new KeyValuePair<string, object?>("phase", snapshot.Phase),
new KeyValuePair<string, object?>("resource", snapshot.Resource ?? "unknown"));
}
}
}
}
private static bool TryCalculateHeadroom(in GhsaRateLimitSnapshot snapshot, out double headroomPct)
{
headroomPct = 0;
if (!snapshot.Limit.HasValue || !snapshot.Remaining.HasValue)
{
return false;
}
var limit = snapshot.Limit.Value;
if (limit <= 0)
{
return false;
}
headroomPct = (double)snapshot.Remaining.Value / limit * 100d;
return true;
}
public void Dispose()
{
_meter.Dispose();
}
}