aether/runtime/
trace.rs

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