conjure_cp_cli/utils/
rule_trace.rs1use std::collections::BTreeMap;
3use std::sync::{Arc, Mutex};
4
5use tracing::field::{Field, Visit};
6use tracing::{Event, Subscriber};
7use tracing_subscriber::{Layer, layer::Context};
8
9#[derive(Clone, Default)]
11pub struct RuleApplicationCounter {
12 counts: Arc<Mutex<BTreeMap<String, u64>>>,
13}
14
15impl RuleApplicationCounter {
16 pub fn snapshot(&self) -> BTreeMap<String, u64> {
18 self.counts
19 .lock()
20 .expect("rule trace aggregate state lock poisoned")
21 .clone()
22 }
23}
24
25impl<S: Subscriber> Layer<S> for RuleApplicationCounter {
26 fn on_event(&self, event: &Event<'_>, _: Context<'_, S>) {
27 let mut visitor = RuleNameVisitor::default();
28 event.record(&mut visitor);
29 if let Some(rule) = visitor.rule_name {
30 *self
31 .counts
32 .lock()
33 .expect("rule trace aggregate state lock poisoned")
34 .entry(rule)
35 .or_default() += 1;
36 }
37 }
38}
39
40#[derive(Default)]
41struct RuleNameVisitor {
42 rule_name: Option<String>,
43}
44
45impl Visit for RuleNameVisitor {
46 fn record_str(&mut self, field: &Field, value: &str) {
47 if field.name() == "rule_name" {
48 self.rule_name = Some(value.to_owned());
49 }
50 }
51
52 fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
53 if field.name() == "rule_name" && self.rule_name.is_none() {
54 self.rule_name = Some(format!("{value:?}").trim_matches('"').to_owned());
55 }
56 }
57}