1use std::collections::VecDeque;
10use std::sync::RwLock;
11use std::time::{Duration, SystemTime};
12
13use serde::{Deserialize, Serialize};
14
15#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
17pub enum ExecutionType {
18 Publish,
20 HandlerExecution,
22}
23
24#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
26pub enum ExecutionStatus {
27 Success,
29 Failed,
31 Timeout,
33 Lagged,
35}
36
37#[derive(Clone, Debug, Serialize, Deserialize)]
39pub struct ExecutionRecord {
40 pub id: u64,
42 pub event_name: String,
44 pub timestamp: SystemTime,
46 pub execution_type: ExecutionType,
48 pub status: ExecutionStatus,
50 pub duration: Option<Duration>,
52 pub error: Option<String>,
54 pub subscriber_id: Option<usize>,
56 pub receiver_count: Option<usize>,
58 pub lagged_count: Option<usize>,
60}
61
62#[derive(Clone, Debug, Default, Serialize, Deserialize)]
64pub struct ExecutionLogQuery {
65 pub event_name: Option<String>,
67 pub execution_type: Option<ExecutionType>,
69 pub status: Option<ExecutionStatus>,
71 pub since: Option<SystemTime>,
73 pub until: Option<SystemTime>,
75 pub limit: Option<usize>,
77 pub offset: Option<usize>,
79}
80
81pub trait ExecutionLogStorage: Send + Sync + 'static {
86 fn record(&self, record: ExecutionRecord);
88 fn query(&self, filter: &ExecutionLogQuery) -> Vec<ExecutionRecord>;
90 fn count(&self, filter: &ExecutionLogQuery) -> usize;
92 fn clear(&self);
94}
95
96pub struct InMemoryExecutionLog {
100 records: RwLock<VecDeque<ExecutionRecord>>,
101 max_records: usize,
102}
103
104impl InMemoryExecutionLog {
105 pub fn new() -> Self {
107 Self::with_capacity(10000)
108 }
109
110 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 if let Some(ref name) = filter.event_name {
141 if r.event_name != *name {
142 return false;
143 }
144 }
145 if let Some(ref et) = filter.execution_type {
147 if r.execution_type != *et {
148 return false;
149 }
150 }
151 if let Some(ref status) = filter.status {
153 if r.status != *status {
154 return false;
155 }
156 }
157 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 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
222pub struct ExecutionLog {
226 storage: Box<dyn ExecutionLogStorage>,
227}
228
229impl ExecutionLog {
230 pub fn new(storage: Box<dyn ExecutionLogStorage>) -> Self {
232 Self { storage }
233 }
234
235 pub fn in_memory() -> Self {
237 Self::new(Box::new(InMemoryExecutionLog::new()))
238 }
239
240 pub fn in_memory_with_capacity(capacity: usize) -> Self {
242 Self::new(Box::new(InMemoryExecutionLog::with_capacity(capacity)))
243 }
244
245 pub fn record(&self, record: ExecutionRecord) {
247 self.storage.record(record);
248 }
249
250 pub fn query(&self, filter: ExecutionLogQuery) -> Vec<ExecutionRecord> {
252 self.storage.query(&filter)
253 }
254
255 pub fn count(&self, filter: &ExecutionLogQuery) -> usize {
257 self.storage.count(filter)
258 }
259
260 pub fn clear(&self) {
262 self.storage.clear();
263 }
264}
265
266pub struct ExecutionLogTelemetry {
287 log: ExecutionLog,
288 next_id: RwLock<u64>,
289}
290
291impl ExecutionLogTelemetry {
292 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 }
328
329 fn on_subscribe(&self, _event_name: &str, _sub_id: usize) {
330 }
332
333 fn on_handler_start(&self, _event_name: &str, _sub_id: usize) {
334 }
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 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 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 let results = log.query(ExecutionLogQuery::default());
682 assert_eq!(results.len(), 0); }
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 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 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}