Skip to main content

anycms_event/
execution_log.rs

1//! 事件执行日志模块,提供事件发布和 Handler 执行的记录与查询能力。
2//!
3//! 通过执行日志,系统管理功能可以:
4//! - 追踪每个事件的发布和执行历史
5//! - 查看 Handler 的执行状态(成功/失败/超时)
6//! - 按条件查询和过滤执行记录
7//! - 排查事件处理问题
8
9use std::collections::VecDeque;
10use std::sync::RwLock;
11use std::time::{Duration, SystemTime};
12
13use serde::{Deserialize, Serialize};
14
15/// 执行记录的类型。
16#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
17pub enum ExecutionType {
18    /// 事件发布。
19    Publish,
20    /// Handler 执行。
21    HandlerExecution,
22}
23
24/// 执行记录的状态。
25#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
26pub enum ExecutionStatus {
27    /// 执行成功。
28    Success,
29    /// 执行失败。
30    Failed,
31    /// 执行超时。
32    Timeout,
33    /// Handler 滞后(broadcast channel lagged)。
34    Lagged,
35}
36
37/// 单条执行记录。
38#[derive(Clone, Debug, Serialize, Deserialize)]
39pub struct ExecutionRecord {
40    /// 记录唯一 ID。
41    pub id: u64,
42    /// 事件名称。
43    pub event_name: String,
44    /// 记录时间。
45    pub timestamp: SystemTime,
46    /// 执行类型。
47    pub execution_type: ExecutionType,
48    /// 执行状态。
49    pub status: ExecutionStatus,
50    /// 执行耗时。
51    pub duration: Option<Duration>,
52    /// 错误信息(如果有)。
53    pub error: Option<String>,
54    /// 订阅者 ID(仅 Handler 执行时有)。
55    pub subscriber_id: Option<usize>,
56    /// 接收者数量(仅发布时有)。
57    pub receiver_count: Option<usize>,
58    /// 滞后消息数(仅 Lagged 状态时有)。
59    pub lagged_count: Option<usize>,
60}
61
62/// 执行日志查询过滤器。
63#[derive(Clone, Debug, Default, Serialize, Deserialize)]
64pub struct ExecutionLogQuery {
65    /// 按事件名称过滤。
66    pub event_name: Option<String>,
67    /// 按执行类型过滤。
68    pub execution_type: Option<ExecutionType>,
69    /// 按执行状态过滤。
70    pub status: Option<ExecutionStatus>,
71    /// 查询起始时间。
72    pub since: Option<SystemTime>,
73    /// 查询截止时间。
74    pub until: Option<SystemTime>,
75    /// 最大返回数量。
76    pub limit: Option<usize>,
77    /// 分页偏移。
78    pub offset: Option<usize>,
79}
80
81/// 执行日志存储 trait。
82///
83/// 实现此 trait 以自定义执行日志的存储方式。
84/// 默认提供 [`InMemoryExecutionLog`](内存存储)。
85pub trait ExecutionLogStorage: Send + Sync + 'static {
86    /// 记录一条执行记录。
87    fn record(&self, record: ExecutionRecord);
88    /// 按条件查询执行记录。
89    fn query(&self, filter: &ExecutionLogQuery) -> Vec<ExecutionRecord>;
90    /// 统计符合条件的记录数。
91    fn count(&self, filter: &ExecutionLogQuery) -> usize;
92    /// 清空所有执行记录。
93    fn clear(&self);
94}
95
96/// 内存执行日志存储。
97///
98/// 使用环形缓冲区存储执行记录,超过最大容量时自动丢弃最旧的记录。
99pub struct InMemoryExecutionLog {
100    records: RwLock<VecDeque<ExecutionRecord>>,
101    max_records: usize,
102}
103
104impl InMemoryExecutionLog {
105    /// 创建一个新的内存执行日志,默认最大记录数为 10000。
106    pub fn new() -> Self {
107        Self::with_capacity(10000)
108    }
109
110    /// 创建指定容量的内存执行日志。
111    pub fn with_capacity(max_records: usize) -> Self {
112        Self {
113            records: RwLock::new(VecDeque::with_capacity(max_records)),
114            max_records,
115        }
116    }
117}
118
119impl Default for InMemoryExecutionLog {
120    fn default() -> Self {
121        Self::new()
122    }
123}
124
125impl ExecutionLogStorage for InMemoryExecutionLog {
126    fn record(&self, record: ExecutionRecord) {
127        let mut records = self.records.write().unwrap();
128        if records.len() >= self.max_records {
129            records.pop_front();
130        }
131        records.push_back(record);
132    }
133
134    fn query(&self, filter: &ExecutionLogQuery) -> Vec<ExecutionRecord> {
135        let records = self.records.read().unwrap();
136        let mut results: Vec<ExecutionRecord> = records
137            .iter()
138            .filter(|r| {
139                // 事件名称过滤
140                if let Some(ref name) = filter.event_name {
141                    if r.event_name != *name {
142                        return false;
143                    }
144                }
145                // 执行类型过滤
146                if let Some(ref et) = filter.execution_type {
147                    if r.execution_type != *et {
148                        return false;
149                    }
150                }
151                // 状态过滤
152                if let Some(ref status) = filter.status {
153                    if r.status != *status {
154                        return false;
155                    }
156                }
157                // 时间范围过滤
158                if let Some(since) = filter.since {
159                    if r.timestamp < since {
160                        return false;
161                    }
162                }
163                if let Some(until) = filter.until {
164                    if r.timestamp > until {
165                        return false;
166                    }
167                }
168                true
169            })
170            .cloned()
171            .collect();
172
173        // 最新的记录排在前面
174        results.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
175
176        let offset = filter.offset.unwrap_or(0);
177        let limit = filter.limit.unwrap_or(usize::MAX);
178        results.into_iter().skip(offset).take(limit).collect()
179    }
180
181    fn count(&self, filter: &ExecutionLogQuery) -> usize {
182        let records = self.records.read().unwrap();
183        records
184            .iter()
185            .filter(|r| {
186                if let Some(ref name) = filter.event_name {
187                    if r.event_name != *name {
188                        return false;
189                    }
190                }
191                if let Some(ref et) = filter.execution_type {
192                    if r.execution_type != *et {
193                        return false;
194                    }
195                }
196                if let Some(ref status) = filter.status {
197                    if r.status != *status {
198                        return false;
199                    }
200                }
201                if let Some(since) = filter.since {
202                    if r.timestamp < since {
203                        return false;
204                    }
205                }
206                if let Some(until) = filter.until {
207                    if r.timestamp > until {
208                        return false;
209                    }
210                }
211                true
212            })
213            .count()
214    }
215
216    fn clear(&self) {
217        let mut records = self.records.write().unwrap();
218        records.clear();
219    }
220}
221
222/// 执行日志查询器,提供便捷的查询方法。
223///
224/// 包装 [`ExecutionLogStorage`],提供更友好的查询 API。
225pub struct ExecutionLog {
226    storage: Box<dyn ExecutionLogStorage>,
227}
228
229impl ExecutionLog {
230    /// 使用指定的存储后端创建执行日志。
231    pub fn new(storage: Box<dyn ExecutionLogStorage>) -> Self {
232        Self { storage }
233    }
234
235    /// 创建默认的内存执行日志。
236    pub fn in_memory() -> Self {
237        Self::new(Box::new(InMemoryExecutionLog::new()))
238    }
239
240    /// 创建指定容量的内存执行日志。
241    pub fn in_memory_with_capacity(capacity: usize) -> Self {
242        Self::new(Box::new(InMemoryExecutionLog::with_capacity(capacity)))
243    }
244
245    /// 记录一条执行记录。
246    pub fn record(&self, record: ExecutionRecord) {
247        self.storage.record(record);
248    }
249
250    /// 查询执行记录。
251    pub fn query(&self, filter: ExecutionLogQuery) -> Vec<ExecutionRecord> {
252        self.storage.query(&filter)
253    }
254
255    /// 统计记录数。
256    pub fn count(&self, filter: &ExecutionLogQuery) -> usize {
257        self.storage.count(filter)
258    }
259
260    /// 清空所有记录。
261    pub fn clear(&self) {
262        self.storage.clear();
263    }
264}
265
266// ── ExecutionLogTelemetry ────────────────────────────────────────
267
268/// 将执行日志与 Telemetry 桥接的实现。
269///
270/// 实现 [`Telemetry`](crate::telemetry::Telemetry) trait,
271/// 将事件生命周期事件记录到 [`ExecutionLog`] 中。
272///
273/// # Example
274///
275/// ```ignore
276/// use anycms_event::prelude::*;
277/// use anycms_event::execution_log::{ExecutionLog, ExecutionLogTelemetry};
278///
279/// let log = ExecutionLog::in_memory();
280/// let telemetry = ExecutionLogTelemetry::new(log);
281///
282/// let bus = EventBus::builder()
283///     .telemetry(telemetry)
284///     .build();
285/// ```
286pub struct ExecutionLogTelemetry {
287    log: ExecutionLog,
288    next_id: RwLock<u64>,
289}
290
291impl ExecutionLogTelemetry {
292    /// 创建新的执行日志遥测。
293    pub fn new(log: ExecutionLog) -> Self {
294        Self {
295            log,
296            next_id: RwLock::new(0),
297        }
298    }
299
300    fn next_id(&self) -> u64 {
301        let mut id = self.next_id.write().unwrap();
302        let current = *id;
303        *id += 1;
304        current
305    }
306}
307
308impl crate::telemetry::Telemetry for ExecutionLogTelemetry {
309    fn on_publish(&self, event_name: &str, receivers: usize) {
310        let record = ExecutionRecord {
311            id: self.next_id(),
312            event_name: event_name.to_string(),
313            timestamp: SystemTime::now(),
314            execution_type: ExecutionType::Publish,
315            status: ExecutionStatus::Success,
316            duration: None,
317            error: None,
318            subscriber_id: None,
319            receiver_count: Some(receivers),
320            lagged_count: None,
321        };
322        self.log.record(record);
323    }
324
325    fn on_publish_complete(&self, _event_name: &str, _elapsed: Duration) {
326        // Publish complete is recorded in on_publish for simplicity
327    }
328
329    fn on_subscribe(&self, _event_name: &str, _sub_id: usize) {
330        // Subscription registration is not an execution event
331    }
332
333    fn on_handler_start(&self, _event_name: &str, _sub_id: usize) {
334        // Handler start is tracked via on_handler_complete
335    }
336
337    fn on_handler_complete(
338        &self,
339        event_name: &str,
340        sub_id: usize,
341        elapsed: Duration,
342        error: Option<&str>,
343    ) {
344        let status = if error.is_some() {
345            ExecutionStatus::Failed
346        } else {
347            ExecutionStatus::Success
348        };
349        let record = ExecutionRecord {
350            id: self.next_id(),
351            event_name: event_name.to_string(),
352            timestamp: SystemTime::now(),
353            execution_type: ExecutionType::HandlerExecution,
354            status,
355            duration: Some(elapsed),
356            error: error.map(|s| s.to_string()),
357            subscriber_id: Some(sub_id),
358            receiver_count: None,
359            lagged_count: None,
360        };
361        self.log.record(record);
362    }
363
364    fn on_handler_lagged(&self, event_name: &str, sub_id: usize, lagged_count: usize) {
365        let record = ExecutionRecord {
366            id: self.next_id(),
367            event_name: event_name.to_string(),
368            timestamp: SystemTime::now(),
369            execution_type: ExecutionType::HandlerExecution,
370            status: ExecutionStatus::Lagged,
371            duration: None,
372            error: None,
373            subscriber_id: Some(sub_id),
374            receiver_count: None,
375            lagged_count: Some(lagged_count),
376        };
377        self.log.record(record);
378    }
379}
380
381#[cfg(test)]
382mod tests {
383    use super::*;
384
385    #[test]
386    fn test_in_memory_log_record_and_query() {
387        let log = InMemoryExecutionLog::new();
388
389        log.record(ExecutionRecord {
390            id: 1,
391            event_name: "user.created".to_string(),
392            timestamp: SystemTime::now(),
393            execution_type: ExecutionType::Publish,
394            status: ExecutionStatus::Success,
395            duration: None,
396            error: None,
397            subscriber_id: None,
398            receiver_count: Some(2),
399            lagged_count: None,
400        });
401
402        log.record(ExecutionRecord {
403            id: 2,
404            event_name: "user.created".to_string(),
405            timestamp: SystemTime::now(),
406            execution_type: ExecutionType::HandlerExecution,
407            status: ExecutionStatus::Success,
408            duration: Some(Duration::from_millis(5)),
409            error: None,
410            subscriber_id: Some(1),
411            receiver_count: None,
412            lagged_count: None,
413        });
414
415        let all = log.query(&ExecutionLogQuery::default());
416        assert_eq!(all.len(), 2);
417    }
418
419    #[test]
420    fn test_query_by_event_name() {
421        let log = InMemoryExecutionLog::new();
422        log.record(ExecutionRecord {
423            id: 1,
424            event_name: "user.created".to_string(),
425            timestamp: SystemTime::now(),
426            execution_type: ExecutionType::Publish,
427            status: ExecutionStatus::Success,
428            duration: None,
429            error: None,
430            subscriber_id: None,
431            receiver_count: Some(1),
432            lagged_count: None,
433        });
434        log.record(ExecutionRecord {
435            id: 2,
436            event_name: "order.placed".to_string(),
437            timestamp: SystemTime::now(),
438            execution_type: ExecutionType::Publish,
439            status: ExecutionStatus::Success,
440            duration: None,
441            error: None,
442            subscriber_id: None,
443            receiver_count: Some(1),
444            lagged_count: None,
445        });
446
447        let results = log.query(&ExecutionLogQuery {
448            event_name: Some("user.created".to_string()),
449            ..Default::default()
450        });
451        assert_eq!(results.len(), 1);
452        assert_eq!(results[0].event_name, "user.created");
453    }
454
455    #[test]
456    fn test_query_by_status() {
457        let log = InMemoryExecutionLog::new();
458        log.record(ExecutionRecord {
459            id: 1,
460            event_name: "user.created".to_string(),
461            timestamp: SystemTime::now(),
462            execution_type: ExecutionType::HandlerExecution,
463            status: ExecutionStatus::Success,
464            duration: Some(Duration::from_millis(5)),
465            error: None,
466            subscriber_id: Some(1),
467            receiver_count: None,
468            lagged_count: None,
469        });
470        log.record(ExecutionRecord {
471            id: 2,
472            event_name: "user.created".to_string(),
473            timestamp: SystemTime::now(),
474            execution_type: ExecutionType::HandlerExecution,
475            status: ExecutionStatus::Failed,
476            duration: Some(Duration::from_millis(10)),
477            error: Some("something went wrong".to_string()),
478            subscriber_id: Some(2),
479            receiver_count: None,
480            lagged_count: None,
481        });
482
483        let failed = log.query(&ExecutionLogQuery {
484            status: Some(ExecutionStatus::Failed),
485            ..Default::default()
486        });
487        assert_eq!(failed.len(), 1);
488        assert_eq!(failed[0].error.as_ref().unwrap(), "something went wrong");
489    }
490
491    #[test]
492    fn test_query_by_execution_type() {
493        let log = InMemoryExecutionLog::new();
494        log.record(ExecutionRecord {
495            id: 1,
496            event_name: "user.created".to_string(),
497            timestamp: SystemTime::now(),
498            execution_type: ExecutionType::Publish,
499            status: ExecutionStatus::Success,
500            duration: None,
501            error: None,
502            subscriber_id: None,
503            receiver_count: Some(1),
504            lagged_count: None,
505        });
506        log.record(ExecutionRecord {
507            id: 2,
508            event_name: "user.created".to_string(),
509            timestamp: SystemTime::now(),
510            execution_type: ExecutionType::HandlerExecution,
511            status: ExecutionStatus::Success,
512            duration: Some(Duration::from_millis(3)),
513            error: None,
514            subscriber_id: Some(1),
515            receiver_count: None,
516            lagged_count: None,
517        });
518
519        let handlers = log.query(&ExecutionLogQuery {
520            execution_type: Some(ExecutionType::HandlerExecution),
521            ..Default::default()
522        });
523        assert_eq!(handlers.len(), 1);
524        assert_eq!(handlers[0].subscriber_id, Some(1));
525    }
526
527    #[test]
528    fn test_query_pagination() {
529        let log = InMemoryExecutionLog::new();
530        for i in 0..20 {
531            log.record(ExecutionRecord {
532                id: i,
533                event_name: "test.event".to_string(),
534                timestamp: SystemTime::now(),
535                execution_type: ExecutionType::Publish,
536                status: ExecutionStatus::Success,
537                duration: None,
538                error: None,
539                subscriber_id: None,
540                receiver_count: Some(1),
541                lagged_count: None,
542            });
543        }
544
545        let page1 = log.query(&ExecutionLogQuery {
546            limit: Some(5),
547            offset: Some(0),
548            ..Default::default()
549        });
550        assert_eq!(page1.len(), 5);
551
552        let page2 = log.query(&ExecutionLogQuery {
553            limit: Some(5),
554            offset: Some(5),
555            ..Default::default()
556        });
557        assert_eq!(page2.len(), 5);
558    }
559
560    #[test]
561    fn test_count() {
562        let log = InMemoryExecutionLog::new();
563        log.record(ExecutionRecord {
564            id: 1,
565            event_name: "user.created".to_string(),
566            timestamp: SystemTime::now(),
567            execution_type: ExecutionType::Publish,
568            status: ExecutionStatus::Success,
569            duration: None,
570            error: None,
571            subscriber_id: None,
572            receiver_count: Some(1),
573            lagged_count: None,
574        });
575        log.record(ExecutionRecord {
576            id: 2,
577            event_name: "user.created".to_string(),
578            timestamp: SystemTime::now(),
579            execution_type: ExecutionType::HandlerExecution,
580            status: ExecutionStatus::Failed,
581            duration: Some(Duration::from_millis(5)),
582            error: Some("err".to_string()),
583            subscriber_id: Some(1),
584            receiver_count: None,
585            lagged_count: None,
586        });
587
588        let total = log.count(&ExecutionLogQuery::default());
589        assert_eq!(total, 2);
590
591        let failed = log.count(&ExecutionLogQuery {
592            status: Some(ExecutionStatus::Failed),
593            ..Default::default()
594        });
595        assert_eq!(failed, 1);
596    }
597
598    #[test]
599    fn test_max_capacity_eviction() {
600        let log = InMemoryExecutionLog::with_capacity(3);
601        for i in 0..5 {
602            log.record(ExecutionRecord {
603                id: i,
604                event_name: format!("event.{}", i),
605                timestamp: SystemTime::now(),
606                execution_type: ExecutionType::Publish,
607                status: ExecutionStatus::Success,
608                duration: None,
609                error: None,
610                subscriber_id: None,
611                receiver_count: None,
612                lagged_count: None,
613            });
614        }
615
616        let all = log.query(&ExecutionLogQuery::default());
617        assert_eq!(all.len(), 3);
618        // The oldest records should have been evicted
619        let names: Vec<&str> = all.iter().map(|r| r.event_name.as_str()).collect();
620        assert!(!names.contains(&"event.0"));
621        assert!(!names.contains(&"event.1"));
622    }
623
624    #[test]
625    fn test_clear() {
626        let log = InMemoryExecutionLog::new();
627        log.record(ExecutionRecord {
628            id: 1,
629            event_name: "test".to_string(),
630            timestamp: SystemTime::now(),
631            execution_type: ExecutionType::Publish,
632            status: ExecutionStatus::Success,
633            duration: None,
634            error: None,
635            subscriber_id: None,
636            receiver_count: None,
637            lagged_count: None,
638        });
639        assert_eq!(log.count(&ExecutionLogQuery::default()), 1);
640        log.clear();
641        assert_eq!(log.count(&ExecutionLogQuery::default()), 0);
642    }
643
644    #[test]
645    fn test_execution_log_wrapper() {
646        let log = ExecutionLog::in_memory();
647        log.record(ExecutionRecord {
648            id: 1,
649            event_name: "test".to_string(),
650            timestamp: SystemTime::now(),
651            execution_type: ExecutionType::Publish,
652            status: ExecutionStatus::Success,
653            duration: None,
654            error: None,
655            subscriber_id: None,
656            receiver_count: None,
657            lagged_count: None,
658        });
659
660        let results = log.query(ExecutionLogQuery::default());
661        assert_eq!(results.len(), 1);
662    }
663
664    #[test]
665    fn test_execution_log_telemetry() {
666        use crate::telemetry::Telemetry;
667
668        let storage = Box::new(InMemoryExecutionLog::new());
669        let log = ExecutionLog::new(storage);
670        let _telemetry = ExecutionLogTelemetry::new(ExecutionLog::in_memory());
671
672        // 验证 telemetry 可以被创建并调用(不 panic)
673        // 实际集成测试在 integration_test 中进行
674        let tel: &dyn Telemetry = &_telemetry;
675        tel.on_publish("test.event", 3);
676        tel.on_handler_complete("test.event", 1, Duration::from_millis(5), None);
677        tel.on_handler_complete("test.event", 2, Duration::from_millis(10), Some("error msg"));
678        tel.on_handler_lagged("test.event", 1, 42);
679
680        // 验证 log 查询功能正常
681        let results = log.query(ExecutionLogQuery::default());
682        assert_eq!(results.len(), 0); // log 和 telemetry 是不同的实例
683    }
684
685    #[test]
686    fn test_time_range_query() {
687        let log = InMemoryExecutionLog::new();
688        let now = SystemTime::now();
689        let one_hour_ago = now - Duration::from_secs(3600);
690        let two_hours_ago = now - Duration::from_secs(7200);
691
692        log.record(ExecutionRecord {
693            id: 1,
694            event_name: "old.event".to_string(),
695            timestamp: two_hours_ago,
696            execution_type: ExecutionType::Publish,
697            status: ExecutionStatus::Success,
698            duration: None,
699            error: None,
700            subscriber_id: None,
701            receiver_count: None,
702            lagged_count: None,
703        });
704        log.record(ExecutionRecord {
705            id: 2,
706            event_name: "new.event".to_string(),
707            timestamp: now,
708            execution_type: ExecutionType::Publish,
709            status: ExecutionStatus::Success,
710            duration: None,
711            error: None,
712            subscriber_id: None,
713            receiver_count: None,
714            lagged_count: None,
715        });
716
717        // Query for events in the last hour
718        let recent = log.query(&ExecutionLogQuery {
719            since: Some(one_hour_ago),
720            ..Default::default()
721        });
722        assert_eq!(recent.len(), 1);
723        assert_eq!(recent[0].event_name, "new.event");
724
725        // Query for events before one hour ago
726        let old = log.query(&ExecutionLogQuery {
727            until: Some(one_hour_ago),
728            ..Default::default()
729        });
730        assert_eq!(old.len(), 1);
731        assert_eq!(old[0].event_name, "old.event");
732    }
733}