1#[cfg(feature = "binary")]
7use crate::repl::runner::ReplClientAdapter;
8#[cfg(feature = "binary")]
9use anyhow::Result;
10#[cfg(feature = "binary")]
11use async_trait::async_trait;
12#[cfg(feature = "binary")]
13use metrics::{counter, histogram};
14#[cfg(feature = "binary")]
15use oxur_repl::protocol::{OperationResult, Request, Response};
16#[cfg(feature = "binary")]
17use std::time::Instant;
18
19#[cfg(feature = "binary")]
37#[derive(Debug)]
38#[allow(dead_code)] pub struct MetricsClientAdapter<C: ReplClientAdapter> {
40 inner: C,
41 request_start: Option<Instant>,
42}
43
44#[cfg(feature = "binary")]
45#[allow(dead_code)] impl<C: ReplClientAdapter> MetricsClientAdapter<C> {
47 pub fn new(inner: C) -> Self {
49 Self { inner, request_start: None }
50 }
51
52 pub fn into_inner(self) -> C {
54 self.inner
55 }
56}
57
58#[cfg(feature = "binary")]
59#[async_trait]
60impl<C: ReplClientAdapter> ReplClientAdapter for MetricsClientAdapter<C> {
61 async fn send_eval(&mut self, request: Request) -> Result<()> {
62 self.request_start = Some(Instant::now());
64
65 let operation = match &request.operation {
67 oxur_repl::protocol::Operation::CreateSession { .. } => "create_session",
68 oxur_repl::protocol::Operation::Clone { .. } => "clone",
69 oxur_repl::protocol::Operation::Eval { .. } => "eval",
70 oxur_repl::protocol::Operation::Close => "close",
71 oxur_repl::protocol::Operation::LsSessions => "ls_sessions",
72 oxur_repl::protocol::Operation::LoadFile { .. } => "load_file",
73 oxur_repl::protocol::Operation::Interrupt => "interrupt",
74 oxur_repl::protocol::Operation::Describe { .. } => "describe",
75 oxur_repl::protocol::Operation::History { .. } => "history",
76 oxur_repl::protocol::Operation::ClearOutput => "clear_output",
77 _ => "unknown",
78 };
79
80 counter!("repl.client.requests_total", "operation" => operation).increment(1);
81
82 self.inner.send_eval(request).await
84 }
85
86 async fn recv_response(&mut self) -> Result<Response> {
87 let response = self.inner.recv_response().await?;
88
89 if let Some(start) = self.request_start.take() {
91 let latency_ms = start.elapsed().as_millis() as f64;
92 histogram!("repl.client.response_latency_ms").record(latency_ms);
93 }
94
95 let status = match &response.result {
97 OperationResult::Success { .. } => "success",
98 OperationResult::Error { .. } => "error",
99 OperationResult::Sessions { .. } => "success",
100 OperationResult::HistoryEntries { .. } => "success",
101 _ => "unknown",
102 };
103 counter!("repl.client.responses_total", "status" => status).increment(1);
104
105 Ok(response)
106 }
107
108 async fn close(&mut self) -> Result<()> {
109 self.inner.close().await
110 }
111
112 fn current_session(&self) -> &oxur_repl::protocol::SessionId {
113 self.inner.current_session()
114 }
115
116 async fn handle_special_command(&mut self, input: &str, color_enabled: bool) -> Option<String> {
117 self.inner.handle_special_command(input, color_enabled).await
118 }
119}
120
121#[cfg(test)]
122#[cfg(feature = "binary")]
123mod tests {
124 }