diff --git a/src/parsers.rs b/src/parsers.rs index 1e16315..31d16ca 100644 --- a/src/parsers.rs +++ b/src/parsers.rs @@ -550,6 +550,36 @@ impl StructuredLogParser for CompilationMetricsParser<'_> { readable_url: o.readable_url.as_ref().map(|u| remove_prefix(u)), }) .collect(); + let extra_metrics: Vec = m + .extra + .iter() + .map(|(key, value)| { + let value_html = match value { + serde_json::Value::String(s) => { + if s.len() > 200 { + format!("
(click to expand)
{}
", + html_escape::encode_text(s)) + } else { + html_escape::encode_text(s).to_string() + } + } + serde_json::Value::Null => "null".to_string(), + other => { + let s = other.to_string(); + if s.len() > 200 { + format!("
(click to expand)
{}
", + html_escape::encode_text(&s)) + } else { + html_escape::encode_text(&s).to_string() + } + } + }; + ExtraMetricContext { + key: key.clone(), + value_html, + } + }) + .collect(); let context = CompilationMetricsContext { css: crate::CSS, m: &m, @@ -562,6 +592,7 @@ impl StructuredLogParser for CompilationMetricsParser<'_> { unbacked_symbols: unbacked_symbols, output_files: &output_files, compile_id_dir: &self.compile_id_dir, + extra_metrics: extra_metrics, qps: TEMPLATE_QUERY_PARAM_SCRIPT, }; let output = self.tt.render(&filename, &context)?; diff --git a/src/templates.rs b/src/templates.rs index 5977a47..a59e2a8 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -406,6 +406,20 @@ pub static TEMPLATE_COMPILATION_METRICS: &str = r#" {{ endfor }} + {{ if extra_metrics }} +

Other Metrics

+ + + + + {{ for em in extra_metrics }} + + + + + {{ endfor }} +
Key Value
{em.key}{em.value_html | format_unescaped}
+ {{ endif }} {qps | format_unescaped} diff --git a/src/types.rs b/src/types.rs index bc4b1bf..e20735e 100644 --- a/src/types.rs +++ b/src/types.rs @@ -481,6 +481,9 @@ pub struct CompilationMetricsMetadata { pub compliant_custom_ops: Option>, pub restart_reasons: Option>, pub dynamo_time_before_restart_s: Option, + // Capture any additional fields not explicitly listed above + #[serde(flatten)] + pub extra: std::collections::BTreeMap, } #[derive(Debug, Deserialize, Serialize, Clone)] @@ -627,9 +630,16 @@ pub struct CompilationMetricsContext<'e> { pub output_files: &'e Vec, pub compile_id_dir: &'e PathBuf, pub mini_stack_html: String, + pub extra_metrics: Vec, pub qps: &'static str, } +#[derive(Debug, Serialize)] +pub struct ExtraMetricContext { + pub key: String, + pub value_html: String, +} + #[derive(Debug, Serialize)] pub struct SymbolicGuardContext { pub css: &'static str, diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 27d9137..03f58cb 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -390,6 +390,67 @@ fn test_cache_hit_miss() { } } +#[test] +fn test_compilation_metrics_extra_fields() { + // cache_hit_miss.log has compilation_metrics entries with extra fields + // not explicitly in CompilationMetricsMetadata (e.g. dynamo_config, is_forward, etc.) + // Verify those extra fields appear in the "Other Metrics" section of the output HTML. + let path = Path::new("tests/inputs/cache_hit_miss.log").to_path_buf(); + let config = tlparse::ParseConfig { + strict: true, + ..Default::default() + }; + let output = tlparse::parse_path(&path, &config); + assert!(output.is_ok()); + let map: HashMap = output.unwrap().into_iter().collect(); + + // Find the compilation_metrics HTML file + let metrics_html = map + .iter() + .find(|(k, _)| { + k.to_str() + .map_or(false, |s| s.contains("compilation_metrics")) + }) + .unwrap_or_else(|| { + let keys: Vec<_> = map.keys().map(|k| k.display().to_string()).collect(); + panic!( + "compilation_metrics not found in output. Available keys: {:?}", + keys + ); + }); + + let html = metrics_html.1; + + // These keys exist in the log's compilation_metrics JSON but are NOT explicit + // fields in CompilationMetricsMetadata, so they should be captured via #[serde(flatten)] + // and rendered in the "Other Metrics" section. + let expected_extra_keys = [ + "aot_autograd_cumulative_compile_time_us", + "config_inline_inbuilt_nn_modules", + "config_suppress_errors", + "dynamo_compile_time_before_restart_us", + "dynamo_config", + "dynamo_cumulative_compile_time_us", + "frame_key", + "has_guarded_code", + "is_forward", + "specialize_float", + ]; + + assert!( + html.contains("Other Metrics"), + "Expected 'Other Metrics' section in compilation_metrics.html" + ); + + for key in expected_extra_keys { + assert!( + html.contains(key), + "Expected extra key '{}' in compilation_metrics.html", + key + ); + } +} + #[test] fn test_export_report() { let expected_files = [