aether/runtime/
trace.rs

1//! 结构化 TRACE 事件
2//!
3//! 提供带级别、分类、时间戳的结构化 TRACE 事件,支持过滤和查询。
4
5use crate::value::Value;
6use serde::{Deserialize, Serialize};
7use std::time::Instant;
8
9/// TRACE 事件级别
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
11pub enum TraceLevel {
12    Debug = 0,
13    Info = 1,
14    Warn = 2,
15    Error = 3,
16}
17
18impl TraceLevel {
19    /// 转换为字符串
20    pub fn as_str(&self) -> &'static str {
21        match self {
22            Self::Debug => "DEBUG",
23            Self::Info => "INFO",
24            Self::Warn => "WARN",
25            Self::Error => "ERROR",
26        }
27    }
28}
29
30impl std::str::FromStr for TraceLevel {
31    type Err = String;
32
33    fn from_str(s: &str) -> Result<Self, Self::Err> {
34        match s.to_lowercase().as_str() {
35            "debug" => Ok(Self::Debug),
36            "info" => Ok(Self::Info),
37            "warn" | "warning" => Ok(Self::Warn),
38            "error" => Ok(Self::Error),
39            _ => Err(format!("Invalid trace level: {}", s)),
40        }
41    }
42}
43
44impl std::fmt::Display for TraceLevel {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        write!(f, "{}", self.as_str())
47    }
48}
49
50/// 结构化 TRACE 事件条目
51#[derive(Debug, Clone)]
52pub struct TraceEntry {
53    /// 时间戳
54    pub timestamp: Instant,
55    /// 事件级别
56    pub level: TraceLevel,
57    /// 事件类型/类别
58    pub category: String,
59    /// 标签
60    pub label: Option<String>,
61    /// 追踪的值(保留原始值)
62    pub values: Vec<Value>,
63    /// 源代码位置(文件名:行号)
64    pub location: Option<String>,
65}
66
67impl TraceEntry {
68    /// 创建新的 TRACE 条目
69    pub fn new(level: TraceLevel, category: String, values: Vec<Value>) -> Self {
70        Self {
71            timestamp: Instant::now(),
72            level,
73            category,
74            label: None,
75            values,
76            location: None,
77        }
78    }
79
80    /// 设置标签
81    pub fn with_label(mut self, label: String) -> Self {
82        self.label = Some(label);
83        self
84    }
85
86    /// 设置位置
87    pub fn with_location(mut self, location: String) -> Self {
88        self.location = Some(location);
89        self
90    }
91
92    /// 格式化为字符串(用于向后兼容的 take_trace())
93    pub fn format(&self) -> String {
94        let values_str: Vec<String> = self.values.iter().map(|v| v.to_string()).collect();
95        let payload = values_str.join(" ");
96
97        match &self.label {
98            Some(l) => format!("[{}:{}:{}] {}", self.level, self.category, l, payload),
99            None => format!("[{}:{}] {}", self.level, self.category, payload),
100        }
101    }
102}
103
104/// TRACE 过滤器
105#[derive(Debug, Default, Clone)]
106pub struct TraceFilter {
107    /// 最低级别(如 Some(TraceLevel::Warn) 表示只显示 Warn 及以上)
108    pub min_level: Option<TraceLevel>,
109    /// 类别匹配
110    pub category: Option<String>,
111    /// 标签匹配
112    pub label: Option<String>,
113    /// 起始时间
114    pub since: Option<Instant>,
115}
116
117impl TraceFilter {
118    /// 创建新的过滤器
119    pub fn new() -> Self {
120        Self::default()
121    }
122
123    /// 设置最低级别
124    pub fn with_min_level(mut self, level: TraceLevel) -> Self {
125        self.min_level = Some(level);
126        self
127    }
128
129    /// 设置类别
130    pub fn with_category(mut self, category: String) -> Self {
131        self.category = Some(category);
132        self
133    }
134
135    /// 设置标签
136    pub fn with_label(mut self, label: String) -> Self {
137        self.label = Some(label);
138        self
139    }
140
141    /// 设置起始时间
142    pub fn with_since(mut self, since: Instant) -> Self {
143        self.since = Some(since);
144        self
145    }
146
147    /// 检查条目是否匹配过滤器
148    pub fn matches(&self, entry: &TraceEntry) -> bool {
149        if let Some(min_level) = self.min_level
150            && entry.level < min_level
151        {
152            return false;
153        }
154
155        if let Some(ref category) = self.category
156            && &entry.category != category
157        {
158            return false;
159        }
160
161        if let Some(ref label) = self.label
162            && entry.label.as_ref() != Some(label)
163        {
164            return false;
165        }
166
167        if let Some(since) = self.since
168            && entry.timestamp < since
169        {
170            return false;
171        }
172
173        true
174    }
175}
176
177/// TRACE 统计信息
178#[derive(Debug, Clone, Default, Serialize, Deserialize)]
179pub struct TraceStats {
180    /// 总记录数
181    pub total_entries: usize,
182    /// 按级别统计
183    pub by_level: std::collections::HashMap<TraceLevel, usize>,
184    /// 按类别统计
185    pub by_category: std::collections::HashMap<String, usize>,
186    /// 缓冲区大小
187    pub buffer_size: usize,
188    /// 是否已满(最旧的记录被丢弃)
189    pub buffer_full: bool,
190}
191
192#[cfg(test)]
193mod tests {
194    use super::*;
195    use std::str::FromStr;
196
197    #[test]
198    fn test_trace_level_from_str() {
199        assert_eq!(TraceLevel::from_str("debug"), Ok(TraceLevel::Debug));
200        assert_eq!(TraceLevel::from_str("DEBUG"), Ok(TraceLevel::Debug));
201        assert_eq!(TraceLevel::from_str("info"), Ok(TraceLevel::Info));
202        assert_eq!(TraceLevel::from_str("warn"), Ok(TraceLevel::Warn));
203        assert_eq!(TraceLevel::from_str("warning"), Ok(TraceLevel::Warn));
204        assert_eq!(TraceLevel::from_str("error"), Ok(TraceLevel::Error));
205        assert!(TraceLevel::from_str("invalid").is_err());
206    }
207
208    #[test]
209    fn test_trace_level_display() {
210        assert_eq!(TraceLevel::Debug.to_string(), "DEBUG");
211        assert_eq!(TraceLevel::Info.to_string(), "INFO");
212        assert_eq!(TraceLevel::Warn.to_string(), "WARN");
213        assert_eq!(TraceLevel::Error.to_string(), "ERROR");
214    }
215
216    #[test]
217    fn test_trace_level_ordering() {
218        assert!(TraceLevel::Debug < TraceLevel::Info);
219        assert!(TraceLevel::Info < TraceLevel::Warn);
220        assert!(TraceLevel::Warn < TraceLevel::Error);
221    }
222
223    #[test]
224    fn test_trace_entry_creation() {
225        let entry = TraceEntry::new(
226            TraceLevel::Info,
227            "test_category".to_string(),
228            vec![Value::Number(42.0)],
229        );
230
231        assert_eq!(entry.level, TraceLevel::Info);
232        assert_eq!(entry.category, "test_category");
233        assert_eq!(entry.values.len(), 1);
234        assert!(entry.label.is_none());
235        assert!(entry.location.is_none());
236    }
237
238    #[test]
239    fn test_trace_entry_with_label() {
240        let entry = TraceEntry::new(TraceLevel::Info, "test_category".to_string(), vec![])
241            .with_label("test_label".to_string());
242
243        assert_eq!(entry.label, Some("test_label".to_string()));
244    }
245
246    #[test]
247    fn test_trace_entry_format() {
248        // 无标签
249        let entry1 = TraceEntry::new(
250            TraceLevel::Info,
251            "category1".to_string(),
252            vec![Value::Number(42.0)],
253        );
254        let formatted1 = entry1.format();
255        assert!(formatted1.contains("[INFO:category1]"));
256        assert!(formatted1.contains("42"));
257
258        // 有标签
259        let entry2 = TraceEntry::new(
260            TraceLevel::Error,
261            "category2".to_string(),
262            vec![Value::String("error_msg".to_string())],
263        )
264        .with_label("test_label".to_string());
265        let formatted2 = entry2.format();
266        assert!(formatted2.contains("[ERROR:category2:test_label]"));
267        assert!(formatted2.contains("error_msg"));
268    }
269
270    #[test]
271    fn test_trace_filter() {
272        let filter = TraceFilter::new().with_min_level(TraceLevel::Warn);
273
274        let debug_entry = TraceEntry::new(TraceLevel::Debug, "test".to_string(), vec![]);
275        let warn_entry = TraceEntry::new(TraceLevel::Warn, "test".to_string(), vec![]);
276        let error_entry = TraceEntry::new(TraceLevel::Error, "test".to_string(), vec![]);
277
278        assert!(!filter.matches(&debug_entry));
279        assert!(filter.matches(&warn_entry));
280        assert!(filter.matches(&error_entry));
281    }
282
283    #[test]
284    fn test_trace_filter_with_category() {
285        let filter = TraceFilter::new().with_category("api_call".to_string());
286
287        let api_entry = TraceEntry::new(TraceLevel::Info, "api_call".to_string(), vec![]);
288        let db_entry = TraceEntry::new(TraceLevel::Info, "database".to_string(), vec![]);
289
290        assert!(filter.matches(&api_entry));
291        assert!(!filter.matches(&db_entry));
292    }
293
294    #[test]
295    fn test_trace_filter_with_label() {
296        let filter = TraceFilter::new().with_label("slow_request".to_string());
297
298        let entry1 = TraceEntry::new(TraceLevel::Warn, "api".to_string(), vec![])
299            .with_label("slow_request".to_string());
300
301        let entry2 = TraceEntry::new(TraceLevel::Warn, "api".to_string(), vec![])
302            .with_label("fast_request".to_string());
303
304        assert!(filter.matches(&entry1));
305        assert!(!filter.matches(&entry2));
306    }
307
308    #[test]
309    fn test_trace_filter_combined() {
310        let filter = TraceFilter::new()
311            .with_min_level(TraceLevel::Warn)
312            .with_category("api".to_string());
313
314        // 匹配:级别和类别都匹配
315        let entry1 = TraceEntry::new(TraceLevel::Warn, "api".to_string(), vec![]);
316
317        // 不匹配:级别太低
318        let entry2 = TraceEntry::new(TraceLevel::Info, "api".to_string(), vec![]);
319
320        // 不匹配:类别不匹配
321        let entry3 = TraceEntry::new(TraceLevel::Warn, "database".to_string(), vec![]);
322
323        assert!(filter.matches(&entry1));
324        assert!(!filter.matches(&entry2));
325        assert!(!filter.matches(&entry3));
326    }
327}