foundry_cli/opts/
tracing.rs1use std::str::FromStr;
4
5use alloy_primitives::{Address, map::AddressHashMap};
6use clap::{Parser, ValueHint};
7use foundry_config::TracingConfig;
8use serde::Serialize;
9
10#[derive(Clone, Debug, Default, Serialize, Parser)]
12#[command(about = None, long_about = None)]
13pub struct TracingArgs {
14 #[arg(long, help_heading = "Trace options")]
21 #[serde(skip)]
22 pub decode_internal: bool,
23
24 #[arg(long, value_name = "DEPTH", help_heading = "Trace options")]
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub trace_depth: Option<usize>,
28
29 #[arg(long, help_heading = "Trace options")]
31 #[serde(skip)]
32 pub disable_labels: bool,
33
34 #[arg(long, help_heading = "Trace options")]
36 #[serde(skip)]
37 pub compact_labels: bool,
38
39 #[arg(
43 long = "labels",
44 visible_alias = "label",
45 value_name = "ADDRESS:LABEL",
46 value_hint = ValueHint::Other,
47 help_heading = "Trace options"
48 )]
49 #[serde(skip_serializing_if = "Vec::is_empty")]
50 pub labels: Vec<String>,
51
52 #[arg(long, help_heading = "Trace options")]
54 #[serde(skip)]
55 pub disable_external_identification: bool,
56}
57
58impl TracingArgs {
59 pub fn resolve(&self, config: &TracingConfig, verbosity: u8) -> TracingConfig {
61 let mut tracing = config.clone();
62 tracing.verbosity = tracing.verbosity.max(verbosity);
63 tracing.labels.extend(self.parsed_labels());
64 tracing.disable_labels |= self.disable_labels;
65 tracing.compact_labels |= self.compact_labels;
66 tracing.trace_depth = self.trace_depth.or(tracing.trace_depth);
67 tracing.decode_internal |= self.decode_internal;
68 if self.disable_external_identification {
69 tracing.external_identification_timeout = 0;
70 }
71 tracing
72 }
73
74 pub fn resolve_call_tracer(&self, config: &TracingConfig, verbosity: u8) -> TracingConfig {
76 let mut tracing = self.resolve(config, verbosity);
77 tracing.decode_internal = false;
78 tracing
79 }
80
81 fn parsed_labels(&self) -> AddressHashMap<String> {
82 self.labels
83 .iter()
84 .filter_map(|label| {
85 let (address, label) = label.split_once(':')?;
86 let address = Address::from_str(address).ok()?;
87 Some((address, label.to_string()))
88 })
89 .collect()
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use alloy_primitives::address;
97
98 #[test]
99 fn resolve_merges_cli_overrides() {
100 let address = address!("0x0000000000000000000000000000000000000001");
101 let config = TracingConfig {
102 verbosity: 2,
103 labels: AddressHashMap::from_iter([(address, "config".to_string())]),
104 disable_labels: false,
105 compact_labels: false,
106 trace_depth: Some(1),
107 decode_internal: false,
108 external_identification_timeout: 10,
109 };
110 let args = TracingArgs {
111 decode_internal: true,
112 trace_depth: Some(2),
113 disable_labels: true,
114 compact_labels: true,
115 labels: vec![format!("{address}:cli")],
116 disable_external_identification: true,
117 };
118
119 let tracing = args.resolve(&config, 3);
120 assert_eq!(tracing.verbosity, 3);
121 assert_eq!(tracing.labels.get(&address), Some(&"cli".to_string()));
122 assert!(tracing.disable_labels);
123 assert!(tracing.compact_labels);
124 assert_eq!(tracing.trace_depth, Some(2));
125 assert!(tracing.decode_internal);
126 assert_eq!(tracing.external_identification_timeout, 0);
127 }
128
129 #[test]
130 fn resolve_preserves_external_identification_timeout() {
131 let config = TracingConfig { external_identification_timeout: 17, ..Default::default() };
132
133 let tracing = TracingArgs::default().resolve(&config, 0);
134
135 assert_eq!(tracing.external_identification_timeout, 17);
136 }
137
138 #[test]
139 fn tracing_verbosity_is_independent_from_global_verbosity() {
140 let config = TracingConfig { verbosity: 5, ..Default::default() };
141
142 assert_eq!(TracingArgs::default().resolve(&config, 1).verbosity, 5);
143 assert_eq!(TracingArgs::default().resolve(&config, 0).verbosity, 5);
144 assert_eq!(TracingArgs::default().resolve(&config, 6).verbosity, 6);
145 }
146
147 #[test]
148 fn call_tracer_does_not_decode_internal_functions() {
149 let config = TracingConfig { decode_internal: true, ..Default::default() };
150 let args = TracingArgs { decode_internal: true, ..Default::default() };
151
152 assert!(!args.resolve_call_tracer(&config, 0).decode_internal);
153 }
154}