1 // Copyright 2021 Ian Jackson, yaahc and contributors to Hippotat and Eyre
2 // SPDX-License-Identifier: GPL-3.0-or-later
3 // There is NO WARRANTY.
7 #[derive(StructOpt,Debug)]
9 /// Increase debug level
10 #[structopt(long, short="D", parse(from_occurrences))]
16 pub fn log_init(&self) {
17 let env = env_logger::Env::new()
18 .filter("HIPPOTAT_LOG")
19 .write_style("HIPPOTAT_LOG_STYLE");
21 let mut logb = env_logger::Builder::new();
22 logb.filter(Some("hippotat"),
23 *[ log::LevelFilter::Info,
24 log::LevelFilter::Debug ]
27 &log::LevelFilter::Trace
34 pub struct OptionPrefixColon<T>(pub Option<T>);
35 impl<T:Display> Display for OptionPrefixColon<T> {
37 fn fmt(&self, f: &mut fmt::Formatter) {
38 if let Some(x) = &self.0 { write!(f, "{}: ", x)? }
42 // For clients only, really.
43 pub struct Reporter<'r> {
44 ic: &'r InstanceConfig,
46 last_report: Option<Report>,
56 // - report all errors
57 // - report first success after a period of lack of messages
58 // - if error, report last success
60 impl<'r> Reporter<'r> {
61 pub fn new(ic: &'r InstanceConfig) -> Self { Reporter {
67 pub fn success(&mut self) {
69 let now = Instant::now();
70 if let Some(rep) = &self.last_report {
71 if now - rep.when < match rep.ok {
72 Ok(()) => match self.ic.success_report_interval {
73 z if z == Duration::default() => return,
76 Err(()) => self.ic.effective_http_timeout,
82 info!(target:"hippotat", "{} ({}ok): running", self.ic, self.successes);
83 self.last_report = Some(Report { when: now, ok: Ok(()) });
86 pub fn filter<T>(&mut self, req_num: Option<ReqNum>, r: Result<T,AE>)
88 let now = Instant::now();
95 let mut m = self.ic.to_string();
96 if let Some(req_num) = req_num {
97 write!(m, " #{}", req_num)?;
99 if self.successes > 0 {
100 write!(m, " ({}ok)", self.successes)?;
103 write!(m, ": {}", e)?;
104 Ok::<_,fmt::Error>(m)
106 warn!(target:"hippotat", "{}", m);
107 self.last_report = Some(Report { when: now, ok: Err(()) });
114 use backtrace::Backtrace;
116 use indenter::indented;
119 struct EyreDedupHandler {
120 backtrace: Option<Arc<parking_lot::Mutex<Backtrace>>>,
123 type EyreDynError<'r> = &'r (dyn std::error::Error + 'static);
125 impl eyre::EyreHandler for EyreDedupHandler {
126 #[throws(fmt::Error)]
127 fn display(&self, error: EyreDynError, f: &mut fmt::Formatter) {
128 let mut last: Option<String> = None;
129 let mut error = Some(error);
130 while let Some(e) = error {
131 let m = e.to_string();
133 None => write!(f, "{}", m)?,
134 Some(l) if l.contains(&m) => { },
135 Some(_) => write!(f, ": {}", m)?,
142 #[throws(fmt::Error)]
143 fn debug(&self, error: EyreDynError, f: &mut fmt::Formatter) {
145 return core::fmt::Debug::fmt(error, f)?;
148 write!(f, "{}", error)?;
150 if let Some(cause) = error.source() {
151 write!(f, "\n\nCaused by:")?;
152 let multiple = cause.source().is_some();
154 for (n, error) in Chain::new(cause).enumerate() {
157 write!(indented(f).ind(n), "{}", error)?;
159 write!(indented(f), "{}", error)?;
164 if let Some(bt) = &self.backtrace {
165 let mut bt = bt.lock();
167 write!(f, "\n\nStack backtrace:\n{:?}", bt)?;
173 pub fn dedup_eyre_setup() {
174 eyre::set_hook(Box::new(|_error| {
176 static ref BACKTRACE: bool = {
177 match env::var("RUST_BACKTRACE") {
178 Ok(s) if s.starts_with("1") => true,
179 Ok(s) if s == "0" => false,
180 Err(env::VarError::NotPresent) => false,
182 eprintln!("warning: RUST_BACKTRACE not understood: {:?}", x);
188 let backtrace = if *BACKTRACE {
189 let bt = Backtrace::new_unresolved();
190 let bt = Arc::new(bt.into());
195 Box::new(EyreDedupHandler { backtrace })
197 .context("set error handler")?;
200 const MAX_WARNINGS: usize = 15;
202 #[derive(Debug,Default)]
203 pub struct Warnings {
204 pub warnings: Vec<String>,
207 #[derive(Debug,Error)]
208 #[error("too many warnings")]
209 pub struct TooManyWarnings;
212 #[throws(TooManyWarnings)]
213 pub fn add(&mut self, e: &dyn Display) {
214 if self.warnings.len() >= MAX_WARNINGS { throw!(TooManyWarnings) }
215 self.warnings.push(e.to_string());