20260826 增加BER兼容告警与分类指标

This commit is contained in:
2026-08-26 13:23:29 +08:00
parent 46fb5e5b99
commit ea3f0d5dde
10 changed files with 728 additions and 11 deletions

View File

@@ -424,6 +424,7 @@ struct LatestRunMetrics {
vaps: u64,
publication_points: u64,
warnings: u64,
warning_categories: BTreeMap<String, u64>,
tree_instances_processed: Option<u64>,
tree_instances_failed: Option<u64>,
stage_seconds: BTreeMap<String, f64>,
@@ -1477,13 +1478,9 @@ fn parse_report(path: &Path, snapshot: &mut MetricsSnapshot, latest: &mut Latest
.map(|a| a.len() as u64)
.unwrap_or(0);
}
latest.warnings = latest.warnings.max(
report
.pointer("/tree/warnings")
.and_then(|v| v.as_array())
.map(|a| a.len() as u64)
.unwrap_or(0),
);
let (reported_warning_count, warning_categories) = collect_warning_categories(&report);
latest.warnings = latest.warnings.max(reported_warning_count);
latest.warning_categories = warning_categories;
if let Some(processed) = json_u64(&report, &["tree", "instances_processed"]) {
latest.tree_instances_processed = Some(processed);
}
@@ -1504,6 +1501,47 @@ fn parse_report(path: &Path, snapshot: &mut MetricsSnapshot, latest: &mut Latest
}
}
fn collect_warning_categories(report: &Value) -> (u64, BTreeMap<String, u64>) {
let mut total = 0u64;
let mut categories = BTreeMap::new();
total += count_warning_categories(
report.pointer("/tree/warnings").and_then(Value::as_array),
&mut categories,
);
if let Some(publication_points) = report.get("publication_points").and_then(Value::as_array) {
for publication_point in publication_points {
total += count_warning_categories(
publication_point.get("warnings").and_then(Value::as_array),
&mut categories,
);
}
}
// Export the baseline and BER-compatibility series even when their count is
// zero, so dashboards can distinguish a clean run from a missing metric.
for category in ["unclassified", "ber_compatible_cms_encoding"] {
categories.entry(category.to_string()).or_insert(0);
}
(total, categories)
}
fn count_warning_categories(
warnings: Option<&Vec<Value>>,
categories: &mut BTreeMap<String, u64>,
) -> u64 {
let Some(warnings) = warnings else {
return 0;
};
for warning in warnings {
let category = warning
.get("category")
.and_then(Value::as_str)
.filter(|value| !value.is_empty())
.unwrap_or("unclassified");
*categories.entry(category.to_string()).or_default() += 1;
}
warnings.len() as u64
}
fn extract_publication_point_metrics(
pps: &[Value],
downloads: Option<&Value>,
@@ -2409,6 +2447,17 @@ fn render_latest_metrics(writer: &mut PromWriter<'_>, instance: &str, latest: &L
&[label("instance", instance)],
latest.warnings as f64,
);
for (category, count) in &latest.warning_categories {
writer.gauge(
"ours_rp_warnings_by_category",
"Latest run warning count by category",
&[
label("instance", instance),
label("warning_category", category),
],
*count as f64,
);
}
if let Some(value) = latest.tree_instances_processed {
writer.gauge(
"ours_rp_tree_instances",
@@ -3364,6 +3413,48 @@ mod tests {
assert_eq!(args.out_metrics.as_deref(), Some(Path::new("metrics.prom")));
}
#[test]
fn warning_categories_cover_tree_and_publication_point_warnings() {
let report = json!({
"tree": {
"warnings": [
{"category": "ber_compatible_cms_encoding"},
{"category": "ber_compatible_cms_encoding"}
]
},
"publication_points": [
{"warnings": [{"category": "repository_sync"}]},
{"warnings": [{"message": "legacy warning without category"}]}
]
});
let (total, categories) = collect_warning_categories(&report);
assert_eq!(total, 4);
assert_eq!(categories["ber_compatible_cms_encoding"], 2);
assert_eq!(categories["repository_sync"], 1);
assert_eq!(categories["unclassified"], 1);
}
#[test]
fn renders_warning_category_metrics() {
let mut warning_categories = BTreeMap::new();
warning_categories.insert("ber_compatible_cms_encoding".to_string(), 2);
let snapshot = MetricsSnapshot {
instance: "test".to_string(),
latest_run: Some(LatestRunMetrics {
warnings: 2,
warning_categories,
..LatestRunMetrics::default()
}),
..MetricsSnapshot::default()
};
let metrics = render_metrics(&snapshot);
assert!(metrics.contains(
r#"ours_rp_warnings_by_category{instance="test",warning_category="ber_compatible_cms_encoding"} 2"#
));
}
#[test]
fn scan_fixture_exports_repo_pp_cir_and_ccr_metrics() {
let td = TempDir::new().expect("tempdir");