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 _fetchAttempts; private readonly Counter _fetchDocuments; private readonly Counter _fetchFailures; private readonly Counter _fetchUnchanged; private readonly Counter _parseSuccess; private readonly Counter _parseFailures; private readonly Counter _parseQuarantine; private readonly Counter _mapSuccess; private readonly Histogram _rateLimitRemaining; private readonly Histogram _rateLimitLimit; private readonly Histogram _rateLimitResetSeconds; private readonly Histogram _rateLimitHeadroomPct; private readonly ObservableGauge _rateLimitHeadroomGauge; private readonly Counter _rateLimitExhausted; private readonly Counter _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("ghsa.fetch.attempts", unit: "operations"); _fetchDocuments = _meter.CreateCounter("ghsa.fetch.documents", unit: "documents"); _fetchFailures = _meter.CreateCounter("ghsa.fetch.failures", unit: "operations"); _fetchUnchanged = _meter.CreateCounter("ghsa.fetch.unchanged", unit: "operations"); _parseSuccess = _meter.CreateCounter("ghsa.parse.success", unit: "documents"); _parseFailures = _meter.CreateCounter("ghsa.parse.failures", unit: "documents"); _parseQuarantine = _meter.CreateCounter("ghsa.parse.quarantine", unit: "documents"); _mapSuccess = _meter.CreateCounter("ghsa.map.success", unit: "advisories"); _rateLimitRemaining = _meter.CreateHistogram("ghsa.ratelimit.remaining", unit: "requests"); _rateLimitLimit = _meter.CreateHistogram("ghsa.ratelimit.limit", unit: "requests"); _rateLimitResetSeconds = _meter.CreateHistogram("ghsa.ratelimit.reset_seconds", unit: "s"); _rateLimitHeadroomPct = _meter.CreateHistogram("ghsa.ratelimit.headroom_pct", unit: "percent"); _rateLimitHeadroomGauge = _meter.CreateObservableGauge("ghsa.ratelimit.headroom_pct_current", ObserveHeadroom, unit: "percent"); _rateLimitExhausted = _meter.CreateCounter("ghsa.ratelimit.exhausted", unit: "events"); _canonicalMetricFallbacks = _meter.CreateCounter("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[] { 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("phase", phase)); public void CanonicalMetricFallback(string canonicalMetricId, string severity) => _canonicalMetricFallbacks.Add( 1, new KeyValuePair("canonical_metric_id", canonicalMetricId), new KeyValuePair("severity", severity), new KeyValuePair("reason", "no_cvss")); internal GhsaRateLimitSnapshot? GetLastRateLimitSnapshot() { lock (_rateLimitLock) { return _lastRateLimitSnapshot; } } private IEnumerable> 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( headroom, new KeyValuePair("phase", snapshot.Phase), new KeyValuePair("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(); } }