Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -264,6 +264,184 @@ internal static class LedgerMetrics
|
||||
StalenessValidationFailures.Add(1, tags);
|
||||
}
|
||||
|
||||
private static readonly Counter<long> ScoredFindingsExports = Meter.CreateCounter<long>(
|
||||
"ledger_scored_findings_exports_total",
|
||||
description: "Count of scored findings export operations.");
|
||||
|
||||
private static readonly Histogram<double> ScoredFindingsExportDuration = Meter.CreateHistogram<double>(
|
||||
"ledger_scored_findings_export_duration_seconds",
|
||||
unit: "s",
|
||||
description: "Duration of scored findings export operations.");
|
||||
|
||||
public static void RecordScoredFindingsExport(string? tenantId, int recordCount, double durationSeconds)
|
||||
{
|
||||
var tags = new KeyValuePair<string, object?>[]
|
||||
{
|
||||
new("tenant", tenantId ?? "unknown"),
|
||||
new("record_count", recordCount)
|
||||
};
|
||||
ScoredFindingsExports.Add(1, tags);
|
||||
ScoredFindingsExportDuration.Record(durationSeconds, tags);
|
||||
}
|
||||
|
||||
// LEDGER-RISK-69-001: Scoring metrics/dashboards
|
||||
|
||||
private static readonly Histogram<double> ScoringLatencySeconds = Meter.CreateHistogram<double>(
|
||||
"ledger_scoring_latency_seconds",
|
||||
unit: "s",
|
||||
description: "Latency of risk scoring operations per finding.");
|
||||
|
||||
private static readonly Counter<long> ScoringOperationsTotal = Meter.CreateCounter<long>(
|
||||
"ledger_scoring_operations_total",
|
||||
description: "Total number of scoring operations by result.");
|
||||
|
||||
private static readonly Counter<long> ScoringProviderGaps = Meter.CreateCounter<long>(
|
||||
"ledger_scoring_provider_gaps_total",
|
||||
description: "Count of findings where scoring provider was unavailable or returned no data.");
|
||||
|
||||
private static readonly ConcurrentDictionary<string, SeveritySnapshot> SeverityByTenantPolicy = new(StringComparer.Ordinal);
|
||||
private static readonly ConcurrentDictionary<string, double> ScoreFreshnessByTenant = new(StringComparer.Ordinal);
|
||||
|
||||
private static readonly ObservableGauge<long> SeverityCriticalGauge =
|
||||
Meter.CreateObservableGauge("ledger_severity_distribution_critical", ObserveSeverityCritical,
|
||||
description: "Current count of critical severity findings by tenant and policy.");
|
||||
|
||||
private static readonly ObservableGauge<long> SeverityHighGauge =
|
||||
Meter.CreateObservableGauge("ledger_severity_distribution_high", ObserveSeverityHigh,
|
||||
description: "Current count of high severity findings by tenant and policy.");
|
||||
|
||||
private static readonly ObservableGauge<long> SeverityMediumGauge =
|
||||
Meter.CreateObservableGauge("ledger_severity_distribution_medium", ObserveSeverityMedium,
|
||||
description: "Current count of medium severity findings by tenant and policy.");
|
||||
|
||||
private static readonly ObservableGauge<long> SeverityLowGauge =
|
||||
Meter.CreateObservableGauge("ledger_severity_distribution_low", ObserveSeverityLow,
|
||||
description: "Current count of low severity findings by tenant and policy.");
|
||||
|
||||
private static readonly ObservableGauge<long> SeverityUnknownGauge =
|
||||
Meter.CreateObservableGauge("ledger_severity_distribution_unknown", ObserveSeverityUnknown,
|
||||
description: "Current count of unknown/unscored findings by tenant and policy.");
|
||||
|
||||
private static readonly ObservableGauge<double> ScoreFreshnessGauge =
|
||||
Meter.CreateObservableGauge("ledger_score_freshness_seconds", ObserveScoreFreshness, unit: "s",
|
||||
description: "Time since last scoring operation completed by tenant.");
|
||||
|
||||
public static void RecordScoringLatency(TimeSpan duration, string? tenantId, string? policyVersion, string result)
|
||||
{
|
||||
var tags = new KeyValuePair<string, object?>[]
|
||||
{
|
||||
new("tenant", tenantId ?? string.Empty),
|
||||
new("policy_version", policyVersion ?? string.Empty),
|
||||
new("result", result)
|
||||
};
|
||||
ScoringLatencySeconds.Record(duration.TotalSeconds, tags);
|
||||
ScoringOperationsTotal.Add(1, tags);
|
||||
}
|
||||
|
||||
public static void RecordScoringProviderGap(string? tenantId, string? provider, string reason)
|
||||
{
|
||||
var tags = new KeyValuePair<string, object?>[]
|
||||
{
|
||||
new("tenant", tenantId ?? string.Empty),
|
||||
new("provider", provider ?? "unknown"),
|
||||
new("reason", reason)
|
||||
};
|
||||
ScoringProviderGaps.Add(1, tags);
|
||||
}
|
||||
|
||||
public static void UpdateSeverityDistribution(
|
||||
string tenantId,
|
||||
string? policyVersion,
|
||||
int critical,
|
||||
int high,
|
||||
int medium,
|
||||
int low,
|
||||
int unknown)
|
||||
{
|
||||
var key = BuildTenantPolicyKey(tenantId, policyVersion);
|
||||
SeverityByTenantPolicy[key] = new SeveritySnapshot(tenantId, policyVersion ?? "default", critical, high, medium, low, unknown);
|
||||
}
|
||||
|
||||
public static void UpdateScoreFreshness(string tenantId, double secondsSinceLastScoring)
|
||||
{
|
||||
var key = NormalizeTenant(tenantId);
|
||||
ScoreFreshnessByTenant[key] = secondsSinceLastScoring < 0 ? 0 : secondsSinceLastScoring;
|
||||
}
|
||||
|
||||
private static string BuildTenantPolicyKey(string? tenantId, string? policyVersion)
|
||||
{
|
||||
var t = string.IsNullOrWhiteSpace(tenantId) ? string.Empty : tenantId;
|
||||
var p = string.IsNullOrWhiteSpace(policyVersion) ? "default" : policyVersion;
|
||||
return $"{t}|{p}";
|
||||
}
|
||||
|
||||
private sealed record SeveritySnapshot(
|
||||
string TenantId,
|
||||
string PolicyVersion,
|
||||
int Critical,
|
||||
int High,
|
||||
int Medium,
|
||||
int Low,
|
||||
int Unknown);
|
||||
|
||||
private static IEnumerable<Measurement<long>> ObserveSeverityCritical()
|
||||
{
|
||||
foreach (var kvp in SeverityByTenantPolicy)
|
||||
{
|
||||
yield return new Measurement<long>(kvp.Value.Critical,
|
||||
new KeyValuePair<string, object?>("tenant", kvp.Value.TenantId),
|
||||
new KeyValuePair<string, object?>("policy_version", kvp.Value.PolicyVersion));
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<Measurement<long>> ObserveSeverityHigh()
|
||||
{
|
||||
foreach (var kvp in SeverityByTenantPolicy)
|
||||
{
|
||||
yield return new Measurement<long>(kvp.Value.High,
|
||||
new KeyValuePair<string, object?>("tenant", kvp.Value.TenantId),
|
||||
new KeyValuePair<string, object?>("policy_version", kvp.Value.PolicyVersion));
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<Measurement<long>> ObserveSeverityMedium()
|
||||
{
|
||||
foreach (var kvp in SeverityByTenantPolicy)
|
||||
{
|
||||
yield return new Measurement<long>(kvp.Value.Medium,
|
||||
new KeyValuePair<string, object?>("tenant", kvp.Value.TenantId),
|
||||
new KeyValuePair<string, object?>("policy_version", kvp.Value.PolicyVersion));
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<Measurement<long>> ObserveSeverityLow()
|
||||
{
|
||||
foreach (var kvp in SeverityByTenantPolicy)
|
||||
{
|
||||
yield return new Measurement<long>(kvp.Value.Low,
|
||||
new KeyValuePair<string, object?>("tenant", kvp.Value.TenantId),
|
||||
new KeyValuePair<string, object?>("policy_version", kvp.Value.PolicyVersion));
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<Measurement<long>> ObserveSeverityUnknown()
|
||||
{
|
||||
foreach (var kvp in SeverityByTenantPolicy)
|
||||
{
|
||||
yield return new Measurement<long>(kvp.Value.Unknown,
|
||||
new KeyValuePair<string, object?>("tenant", kvp.Value.TenantId),
|
||||
new KeyValuePair<string, object?>("policy_version", kvp.Value.PolicyVersion));
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<Measurement<double>> ObserveScoreFreshness()
|
||||
{
|
||||
foreach (var kvp in ScoreFreshnessByTenant)
|
||||
{
|
||||
yield return new Measurement<double>(kvp.Value, new KeyValuePair<string, object?>("tenant", kvp.Key));
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<Measurement<double>> ObserveProjectionLag()
|
||||
{
|
||||
foreach (var kvp in ProjectionLagByTenant)
|
||||
|
||||
Reference in New Issue
Block a user