nydus_utils/
logger.rs

1// Copyright 2020 Ant Group. All rights reserved.
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use std::collections::VecDeque;
6use std::sync::Mutex;
7use std::time::SystemTime;
8
9use serde::Serialize;
10use serde_json::Error as SerdeError;
11
12/// Error codes for `ErrorHolder`.
13#[derive(Debug)]
14pub enum ErrorHolderError {
15    TooLarge(usize),
16    Serde(SerdeError),
17}
18
19/// `Result` specialized for `ErrorHolder`.
20pub type Result<T> = std::result::Result<T, ErrorHolderError>;
21
22/// Struct to record important or critical events or errors in circular buffer mode.
23#[derive(Serialize, Default, Debug)]
24pub struct ErrorHolder {
25    max_errors: usize,
26    total_errors: usize,
27    max_size: usize,
28    total_size: usize,
29    errors: Mutex<VecDeque<String>>,
30}
31
32impl ErrorHolder {
33    /// Create a `ErrorHolder` object.
34    pub fn new(max_errors: usize, max_size: usize) -> Self {
35        Self {
36            max_errors,
37            max_size,
38            total_errors: 0,
39            total_size: 0,
40            errors: Mutex::new(VecDeque::with_capacity(max_errors)),
41        }
42    }
43
44    /// Push an error into the circular buffer.
45    pub fn push(&mut self, error: &str) -> Result<()> {
46        let mut guard = self.errors.lock().unwrap();
47        let formatted_error = format!("{} - {}", httpdate::fmt_http_date(SystemTime::now()), error);
48
49        loop {
50            if formatted_error.len() + self.total_size > self.max_size
51                || self.total_errors >= self.max_errors
52            {
53                let victim = guard.pop_front();
54                match victim {
55                    Some(v) => {
56                        self.total_size -= v.len();
57                        self.total_errors -= 1;
58                    }
59                    None => return Err(ErrorHolderError::TooLarge(error.len())),
60                }
61            } else {
62                break;
63            }
64        }
65
66        self.total_size += formatted_error.len();
67        self.total_errors += 1;
68        guard.push_back(formatted_error);
69        Ok(())
70    }
71
72    /// Export all errors in the circular buffer as an `JSON` string.
73    pub fn export(&self) -> Result<String> {
74        let _guard = self.errors.lock().unwrap();
75        serde_json::to_string(self).map_err(ErrorHolderError::Serde)
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::{ErrorHolder, ErrorHolderError};
82
83    #[test]
84    fn test_overflow() {
85        let mut holder = ErrorHolder::new(10, 80);
86        let error_msg = "123456789";
87        let mut left = 16;
88        while left >= 0 {
89            let r = holder.push(error_msg);
90            assert!(r.is_ok());
91            left -= 1;
92        }
93
94        assert!(holder.total_errors <= 10);
95        assert!(holder.total_size <= 80);
96
97        let mut multi = 10;
98        let mut error_msg_long = "".to_string();
99        while multi >= 0 {
100            multi -= 1;
101            error_msg_long.push_str("123456789");
102        }
103
104        let r = holder.push(&error_msg_long);
105        match r {
106            Err(ErrorHolderError::TooLarge(len)) => assert_eq!(len, error_msg_long.len()),
107            _ => panic!(),
108        }
109    }
110}