1use crate::types::Degrade;
7use chrono::{Local, SecondsFormat};
8use parking_lot::Mutex;
9use serde::{Deserialize, Serialize};
10use std::collections::BTreeMap;
11use std::sync::Arc;
12use std::time::Instant;
13
14pub type EventSink = Arc<dyn Fn(&LogEvent) + Send + Sync>;
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct LogEvent {
24 pub ts: String,
26 pub kind: String,
28 pub ms: u64,
30 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
33 pub stages: BTreeMap<String, u64>,
34 #[serde(default, skip_serializing_if = "Option::is_none")]
36 pub candidates: Option<usize>,
37 #[serde(default, skip_serializing_if = "Option::is_none")]
39 pub folded: Option<usize>,
40 #[serde(default, skip_serializing_if = "Option::is_none")]
42 pub rerank_docs: Option<usize>,
43 #[serde(default, skip_serializing_if = "Option::is_none")]
45 pub rerank_tokens: Option<usize>,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
48 pub hits: Option<usize>,
49 #[serde(default, skip_serializing_if = "Vec::is_empty")]
51 pub degraded: Vec<Degrade>,
52}
53
54impl LogEvent {
55 pub fn new(kind: &str) -> Self {
57 Self {
58 ts: Local::now().to_rfc3339_opts(SecondsFormat::Millis, false),
59 kind: kind.to_string(),
60 ms: 0,
61 stages: BTreeMap::new(),
62 candidates: None,
63 folded: None,
64 rerank_docs: None,
65 rerank_tokens: None,
66 hits: None,
67 degraded: Vec::new(),
68 }
69 }
70}
71
72#[derive(Default)]
74pub(crate) struct EventRegistry {
75 sink: Mutex<Option<EventSink>>,
76}
77
78impl EventRegistry {
79 pub fn get(&self) -> Option<EventSink> {
80 self.sink.lock().clone()
81 }
82 pub fn set(&self, sink: EventSink) {
83 *self.sink.lock() = Some(sink);
84 }
85 pub fn clear(&self) -> bool {
86 self.sink.lock().take().is_some()
87 }
88 pub fn is_registered(&self) -> bool {
89 self.sink.lock().is_some()
90 }
91}
92
93pub(crate) struct StageTimer {
95 last: Instant,
96 marks: BTreeMap<String, u64>,
97}
98
99impl StageTimer {
100 pub fn start() -> Self {
101 Self { last: Instant::now(), marks: BTreeMap::new() }
102 }
103 pub fn mark(&mut self, name: &str) {
104 let now = Instant::now();
105 self.marks.insert(name.to_string(), (now - self.last).as_millis() as u64);
106 self.last = now;
107 }
108 pub fn finish(mut self, name: &str) -> BTreeMap<String, u64> {
110 self.mark(name);
111 self.marks
112 }
113}