rrag_graph/
observability.rs1use crate::core::{ExecutionContext, NodeId};
6use async_trait::async_trait;
8use std::time::Duration;
9
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12
13#[async_trait]
15pub trait GraphObserver: Send + Sync {
16 async fn on_execution_start(&self, graph_id: &str, context: &ExecutionContext);
18
19 async fn on_execution_end(&self, graph_id: &str, success: bool, duration: Duration);
21
22 async fn on_node_start(&self, node_id: &NodeId, context: &ExecutionContext);
24
25 async fn on_node_end(&self, node_id: &NodeId, success: bool, duration: Duration);
27
28 async fn on_state_change(&self, key: &str, old_value: Option<&str>, new_value: &str);
30}
31
32pub struct LoggingObserver;
34
35#[async_trait]
36impl GraphObserver for LoggingObserver {
37 async fn on_execution_start(&self, graph_id: &str, _context: &ExecutionContext) {
38 #[cfg(feature = "observability")]
39 tracing::info!("Graph execution started: {}", graph_id);
40 #[cfg(not(feature = "observability"))]
41 {
42 let _ = (graph_id, _context);
43 eprintln!("Graph execution started: {}", graph_id);
44 }
45 }
46
47 async fn on_execution_end(&self, graph_id: &str, success: bool, duration: Duration) {
48 #[cfg(feature = "observability")]
49 tracing::info!(
50 "Graph execution ended: {} (success: {}, duration: {:?})",
51 graph_id,
52 success,
53 duration
54 );
55 #[cfg(not(feature = "observability"))]
56 eprintln!(
57 "Graph execution ended: {} (success: {}, duration: {:?})",
58 graph_id, success, duration
59 );
60 }
61
62 async fn on_node_start(&self, node_id: &NodeId, _context: &ExecutionContext) {
63 #[cfg(feature = "observability")]
64 tracing::debug!("Node execution started: {}", node_id.as_str());
65 #[cfg(not(feature = "observability"))]
66 {
67 let _ = _context;
68 eprintln!("Node execution started: {}", node_id.as_str());
69 }
70 }
71
72 async fn on_node_end(&self, node_id: &NodeId, success: bool, duration: Duration) {
73 #[cfg(feature = "observability")]
74 tracing::debug!(
75 "Node execution ended: {} (success: {}, duration: {:?})",
76 node_id.as_str(),
77 success,
78 duration
79 );
80 #[cfg(not(feature = "observability"))]
81 eprintln!(
82 "Node execution ended: {} (success: {}, duration: {:?})",
83 node_id.as_str(),
84 success,
85 duration
86 );
87 }
88
89 async fn on_state_change(&self, key: &str, old_value: Option<&str>, new_value: &str) {
90 #[cfg(feature = "observability")]
91 tracing::trace!(
92 "State change: {} = {} (was: {:?})",
93 key,
94 new_value,
95 old_value
96 );
97 #[cfg(not(feature = "observability"))]
98 eprintln!(
99 "State change: {} = {} (was: {:?})",
100 key, new_value, old_value
101 );
102 }
103}
104
105#[derive(Debug, Clone, Default)]
107#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
108pub struct ExecutionMetrics {
109 pub nodes_executed: usize,
110 pub total_duration: Duration,
111 pub node_metrics: Vec<NodeMetrics>,
112}
113
114#[derive(Debug, Clone)]
116#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
117pub struct NodeMetrics {
118 pub node_id: String,
119 pub execution_count: usize,
120 pub total_duration: Duration,
121 pub average_duration: Duration,
122 pub success_rate: f32,
123}
124
125#[derive(Debug, Clone)]
127#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
128pub struct ObservabilityConfig {
129 pub enable_logging: bool,
130 pub enable_metrics: bool,
131 pub enable_tracing: bool,
132 pub log_level: String,
133}