1use crate::llm::actions::protocol_trait::ActionResult;
11use crate::protocol::log_template::{LogLevel, LogTemplate};
12use crate::protocol::Event;
13use crate::server::connection::ConnectionId;
14use crate::state::ServerId;
15use crate::utils::clock::Instant;
16use serde_json::Value;
17use std::net::SocketAddr;
18use tokio::sync::mpsc::UnboundedSender;
19use tracing::{debug, info, trace};
20
21pub struct EventLogContext<'a> {
43 pub event: &'a Event,
45 pub start_time: Instant,
47 pub server_id: ServerId,
49 pub connection_id: Option<ConnectionId>,
51 pub client_addr: Option<SocketAddr>,
53 pub protocol_name: String,
55}
56
57impl<'a> EventLogContext<'a> {
58 pub fn new(
62 event: &'a Event,
63 server_id: ServerId,
64 connection_id: Option<ConnectionId>,
65 client_addr: Option<SocketAddr>,
66 protocol_name: impl Into<String>,
67 ) -> Self {
68 Self {
69 event,
70 start_time: Instant::now(),
71 server_id,
72 connection_id,
73 client_addr,
74 protocol_name: protocol_name.into(),
75 }
76 }
77
78 pub fn build_enriched_data(&self) -> Value {
86 let mut data = self.event.data.clone();
87 if let Some(obj) = data.as_object_mut() {
88 if let Some(addr) = self.client_addr {
89 obj.insert(
90 "client_ip".to_string(),
91 serde_json::json!(addr.ip().to_string()),
92 );
93 obj.insert("client_port".to_string(), serde_json::json!(addr.port()));
94 }
95 obj.insert(
96 "server_id".to_string(),
97 serde_json::json!(self.server_id.as_u32()),
98 );
99 if let Some(conn_id) = self.connection_id {
100 obj.insert(
101 "connection_id".to_string(),
102 serde_json::json!(conn_id.as_u32()),
103 );
104 }
105 obj.insert(
106 "protocol".to_string(),
107 serde_json::json!(self.protocol_name.clone()),
108 );
109 obj.insert("event_id".to_string(), serde_json::json!(self.event.id()));
110 }
111 data
112 }
113
114 pub fn log_start(&self, status_tx: Option<&UnboundedSender<String>>) {
120 let data = self.build_enriched_data();
121
122 if let Some(ref template) = self.event.event_type.log_template {
123 if let Some(msg) = template.render(LogLevel::Debug, &data) {
124 debug!("{}", msg);
125 if let Some(tx) = status_tx {
126 let _ = tx.send(format!("[DEBUG] {}", msg));
127 }
128 return;
129 }
130 }
131
132 let fallback_msg = format!(
134 "{} event '{}' from {}",
135 self.protocol_name,
136 self.event.id(),
137 self.client_addr
138 .map(|a| a.to_string())
139 .unwrap_or_else(|| "unknown".to_string())
140 );
141 debug!("{}", fallback_msg);
142 if let Some(tx) = status_tx {
143 let _ = tx.send(format!("[DEBUG] {}", fallback_msg));
144 }
145 }
146
147 pub fn log_complete(
157 &self,
158 status_tx: Option<&UnboundedSender<String>>,
159 action_results: &[ActionResult],
160 ) {
161 let duration = self.start_time.elapsed();
162 let mut data = self.build_enriched_data();
163
164 if let Some(obj) = data.as_object_mut() {
166 obj.insert(
167 "duration_ms".to_string(),
168 serde_json::json!(duration.as_millis() as u64),
169 );
170 obj.insert(
171 "action_count".to_string(),
172 serde_json::json!(action_results.len()),
173 );
174
175 let response_bytes: usize = action_results
177 .iter()
178 .flat_map(|r| r.get_all_output())
179 .map(|o| o.len())
180 .sum();
181 obj.insert(
182 "response_bytes".to_string(),
183 serde_json::json!(response_bytes),
184 );
185
186 for result in action_results {
188 if let ActionResult::Custom {
189 name,
190 data: custom_data,
191 } = result
192 {
193 if let Some(status) = custom_data.get("status") {
194 obj.insert("status".to_string(), status.clone());
195 }
196 obj.insert("action_name".to_string(), serde_json::json!(name));
197 break;
198 }
199 }
200 }
201
202 if let Some(ref template) = self.event.event_type.log_template {
203 if let Some(msg) = template.render(LogLevel::Trace, &data) {
205 trace!("{}", msg);
206 if let Some(tx) = status_tx {
207 let _ = tx.send(format!("[TRACE] {}", msg));
208 }
209 }
210
211 if let Some(msg) = template.render(LogLevel::Info, &data) {
213 info!("{}", msg);
214 if let Some(tx) = status_tx {
215 let _ = tx.send(format!("[INFO] {}", msg));
216 }
217 }
218
219 if let Some(msg) = template.render(LogLevel::Debug, &data) {
221 let msg_with_timing = format!("{} ({}ms)", msg, duration.as_millis());
222 debug!("{}", msg_with_timing);
223 if let Some(tx) = status_tx {
224 let _ = tx.send(format!("[DEBUG] {}", msg_with_timing));
225 }
226 } else {
227 let fallback_msg = format!(
229 "{} '{}' completed in {}ms, {} action(s), {} bytes",
230 self.protocol_name,
231 self.event.id(),
232 duration.as_millis(),
233 action_results.len(),
234 data.get("response_bytes")
235 .and_then(|v| v.as_u64())
236 .unwrap_or(0)
237 );
238 debug!("{}", fallback_msg);
239 if let Some(tx) = status_tx {
240 let _ = tx.send(format!("[DEBUG] {}", fallback_msg));
241 }
242 }
243 } else {
244 let fallback_msg = format!(
246 "{} '{}' completed in {}ms, {} action(s)",
247 self.protocol_name,
248 self.event.id(),
249 duration.as_millis(),
250 action_results.len()
251 );
252 debug!("{}", fallback_msg);
253 if let Some(tx) = status_tx {
254 let _ = tx.send(format!("[DEBUG] {}", fallback_msg));
255 }
256 }
257 }
258}
259
260pub fn log_action_result(
272 action_name: &str,
273 action_data: &Value,
274 result: &ActionResult,
275 template: Option<&LogTemplate>,
276 status_tx: Option<&UnboundedSender<String>>,
277) {
278 let mut enriched_data = action_data.clone();
280 if let Some(obj) = enriched_data.as_object_mut() {
281 let output_bytes: usize = result.get_all_output().iter().map(|o| o.len()).sum();
283 obj.insert("output_bytes".to_string(), serde_json::json!(output_bytes));
284 obj.insert(
285 "closes_connection".to_string(),
286 serde_json::json!(result.closes_connection()),
287 );
288 obj.insert(
289 "waits_for_more".to_string(),
290 serde_json::json!(result.waits_for_more()),
291 );
292 obj.insert("action_name".to_string(), serde_json::json!(action_name));
293 }
294
295 if let Some(template) = template {
296 if let Some(msg) = template.render(LogLevel::Trace, &enriched_data) {
298 trace!("Action {}: {}", action_name, msg);
299 if let Some(tx) = status_tx {
300 let _ = tx.send(format!("[TRACE] Action {}: {}", action_name, msg));
301 }
302 }
303
304 if let Some(msg) = template.render(LogLevel::Debug, &enriched_data) {
305 debug!("Action {}: {}", action_name, msg);
306 if let Some(tx) = status_tx {
307 let _ = tx.send(format!("[DEBUG] Action {}: {}", action_name, msg));
308 }
309 }
310
311 if let Some(msg) = template.render(LogLevel::Info, &enriched_data) {
312 info!("Action {}: {}", action_name, msg);
313 if let Some(tx) = status_tx {
314 let _ = tx.send(format!("[INFO] Action {}: {}", action_name, msg));
315 }
316 }
317 } else {
318 let summary = crate::llm::actions::summarize_action(action_data);
320 debug!("Action executed: {}", summary);
321 if let Some(tx) = status_tx {
322 let _ = tx.send(format!("[DEBUG] Action: {}", summary));
323 }
324 }
325}