a2a_rs/observability/
mod.rs1#[cfg(feature = "tracing")]
7use tracing_subscriber::{EnvFilter, fmt, prelude::*};
8
9#[cfg(feature = "tracing")]
28pub fn init_tracing() {
29 init_tracing_with_filter("a2a_rs=info");
30}
31
32#[cfg(feature = "tracing")]
38pub fn init_tracing_with_filter(filter: &str) {
39 let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(filter));
40
41 let fmt_layer = fmt::layer()
42 .with_target(true)
43 .with_level(true)
44 .with_thread_ids(true)
45 .with_thread_names(true);
46
47 tracing_subscriber::registry()
48 .with(env_filter)
49 .with(fmt_layer)
50 .init();
51}
52
53#[cfg(feature = "tracing")]
57pub fn init_tracing_json() {
58 let env_filter =
59 EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("a2a_rs=info,warn"));
60
61 let fmt_layer = fmt::layer()
62 .with_target(true)
63 .with_level(true)
64 .with_thread_ids(true)
65 .with_thread_names(true);
66
67 tracing_subscriber::registry()
68 .with(env_filter)
69 .with(fmt_layer)
70 .init();
71}
72
73pub struct SpanBuilder;
75
76impl SpanBuilder {
77 #[cfg(feature = "tracing")]
79 pub fn message_processing(message_id: &str) -> tracing::Span {
80 tracing::info_span!(
81 "message_processing",
82 message.id = %message_id,
83 processing.duration_ms = tracing::field::Empty,
84 processing.status = tracing::field::Empty,
85 )
86 }
87
88 #[cfg(feature = "tracing")]
90 pub fn task_operation(task_id: &str, operation: &str) -> tracing::Span {
91 tracing::info_span!(
92 "task_operation",
93 task.id = %task_id,
94 task.operation = %operation,
95 operation.duration_ms = tracing::field::Empty,
96 operation.status = tracing::field::Empty,
97 )
98 }
99
100 #[cfg(feature = "tracing")]
102 pub fn http_request(method: &str, path: &str) -> tracing::Span {
103 tracing::info_span!(
104 "http_request",
105 http.method = %method,
106 http.path = %path,
107 http.status_code = tracing::field::Empty,
108 http.duration_ms = tracing::field::Empty,
109 )
110 }
111
112 #[cfg(feature = "tracing")]
114 pub fn websocket_connection(connection_id: &str) -> tracing::Span {
115 tracing::info_span!(
116 "websocket_connection",
117 ws.connection_id = %connection_id,
118 ws.messages_sent = 0u64,
119 ws.messages_received = 0u64,
120 ws.duration_ms = tracing::field::Empty,
121 )
122 }
123
124 #[cfg(feature = "tracing")]
126 pub fn authentication(scheme: &str) -> tracing::Span {
127 tracing::info_span!(
128 "authentication",
129 auth.scheme = %scheme,
130 auth.status = tracing::field::Empty,
131 auth.duration_ms = tracing::field::Empty,
132 )
133 }
134}
135
136#[macro_export]
138macro_rules! log_error {
139 ($err:expr) => {
140 tracing::error!(
141 error = %$err,
142 error_type = std::any::type_name_of_val(&$err),
143 "Operation failed"
144 )
145 };
146 ($err:expr, $msg:expr) => {
147 tracing::error!(
148 error = %$err,
149 error_type = std::any::type_name_of_val(&$err),
150 message = $msg,
151 "Operation failed"
152 )
153 };
154}
155
156#[macro_export]
158macro_rules! measure_duration {
159 ($span:expr, $field:expr, $block:expr) => {{
160 let start = std::time::Instant::now();
161 let result = $block;
162 let duration = start.elapsed();
163 $span.record($field, duration.as_millis() as u64);
164 result
165 }};
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 #[cfg(feature = "tracing")]
174 fn test_span_builders() {
175 let _ = SpanBuilder::message_processing("test-message-id");
177 let _ = SpanBuilder::task_operation("test-task-id", "create");
178 let _ = SpanBuilder::http_request("GET", "/api/v1/agent-card");
179 let _ = SpanBuilder::websocket_connection("ws-conn-123");
180 let _ = SpanBuilder::authentication("bearer");
181 }
182}