1use std::{
7 borrow::Cow,
8 fmt::Display,
9 hash::{DefaultHasher, Hash as _, Hasher},
10};
11
12use futures::future::Either;
13use jsonrpsee::MethodResponse;
14use jsonrpsee::core::middleware::{Batch, Notification};
15use jsonrpsee::{server::middleware::rpc::RpcServiceT, types::Id};
16use tower::Layer;
17
18#[derive(Clone, Default)]
20pub(super) struct LogLayer {}
21
22impl<S> Layer<S> for LogLayer {
23 type Service = Logging<S>;
24
25 fn layer(&self, service: S) -> Self::Service {
26 Logging { service }
27 }
28}
29
30#[derive(Clone)]
31pub(super) struct Logging<S> {
32 service: S,
33}
34
35impl<S> Logging<S> {
36 fn log_enabled() -> bool {
37 tracing::enabled!(tracing::Level::DEBUG)
38 }
39
40 async fn log<F>(
41 id: Id<'_>,
42 method_name: impl Display,
43 params: impl Display,
44 future: F,
45 ) -> MethodResponse
46 where
47 F: Future<Output = MethodResponse>,
48 {
49 if !tracing::enabled!(tracing::Level::DEBUG) {
51 return future.await;
52 }
53
54 let start_time = std::time::Instant::now();
55 let id = create_unique_id(id, start_time);
56 tracing::trace!("RPC#{id}: {method_name}. Params: {params}");
57 let resp = future.await;
58 let elapsed = start_time.elapsed();
59 let result = resp.as_error_code().map_or(Cow::Borrowed("OK"), |code| {
60 Cow::Owned(format!("ERR({code})"))
61 });
62 tracing::debug!(
63 "RPC#{id} {result}: {method_name}. Took {}",
64 humantime::format_duration(elapsed)
65 );
66 resp
67 }
68}
69
70impl<S> RpcServiceT for Logging<S>
71where
72 S: RpcServiceT<MethodResponse = MethodResponse, NotificationResponse = MethodResponse>
73 + Send
74 + Sync
75 + Clone
76 + 'static,
77{
78 type MethodResponse = S::MethodResponse;
79 type NotificationResponse = S::NotificationResponse;
80 type BatchResponse = S::BatchResponse;
81
82 fn call<'a>(
83 &self,
84 req: jsonrpsee::types::Request<'a>,
85 ) -> impl Future<Output = Self::MethodResponse> + Send + 'a {
86 if !Self::log_enabled() {
88 Either::Left(self.service.call(req))
89 } else {
90 Either::Right(Self::log(
91 req.id(),
92 req.method_name().to_owned(),
93 req.params().as_str().unwrap_or("[]").to_owned(),
94 self.service.call(req),
95 ))
96 }
97 }
98
99 fn batch<'a>(&self, batch: Batch<'a>) -> impl Future<Output = Self::BatchResponse> + Send + 'a {
100 self.service.batch(batch)
101 }
102
103 fn notification<'a>(
104 &self,
105 n: Notification<'a>,
106 ) -> impl Future<Output = Self::NotificationResponse> + Send + 'a {
107 if !Self::log_enabled() {
109 Either::Left(self.service.notification(n))
110 } else {
111 Either::Right(Self::log(
112 Id::Null,
113 n.method_name().to_owned(),
114 "",
115 self.service.notification(n),
116 ))
117 }
118 }
119}
120
121fn create_unique_id(id: Id, start_time: std::time::Instant) -> String {
123 const ID_LEN: usize = 6;
124 let mut hasher = DefaultHasher::new();
125 id.hash(&mut hasher);
126 start_time.hash(&mut hasher);
127 let mut id = format!("{:x}", hasher.finish());
128 id.truncate(ID_LEN);
129 id
130}
131
132#[cfg(test)]
133mod tests {
134 use std::time::Duration;
135
136 use super::*;
137
138 #[test]
139 fn test_create_unique_id_same() {
140 let id = Id::Number(1);
141 let start_time = std::time::Instant::now();
142 let id1 = create_unique_id(id.clone(), start_time);
143 let id2 = create_unique_id(id, start_time);
144 assert_eq!(id1, id2);
145 }
146
147 #[test]
148 fn test_create_unique_id_different_ids() {
149 let id1 = Id::Number(1);
150 let id2 = Id::Number(2);
151 let start_time = std::time::Instant::now();
152 let id1 = create_unique_id(id1, start_time);
153 let id2 = create_unique_id(id2, start_time);
154 assert_ne!(id1, id2);
155 }
156
157 #[test]
158 fn test_create_unique_id_different_times() {
159 let id = Id::Number(1);
160 let start_time1 = std::time::Instant::now();
161 let start_time2 = std::time::Instant::now() + Duration::from_nanos(1);
162 let id1 = create_unique_id(id.clone(), start_time1);
163 let id2 = create_unique_id(id, start_time2);
164 assert_ne!(id1, id2);
165 }
166}