forest/rpc/
metrics_layer.rs

1// Copyright 2019-2025 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4use crate::metrics;
5use jsonrpsee::MethodResponse;
6use jsonrpsee::core::middleware::{Batch, Notification};
7use jsonrpsee::server::middleware::rpc::RpcServiceT;
8use tower::Layer;
9
10// State-less jsonrpcsee layer for measuring RPC metrics
11#[derive(Clone, Default)]
12pub(super) struct MetricsLayer {}
13
14impl<S> Layer<S> for MetricsLayer {
15    type Service = RecordMetrics<S>;
16
17    fn layer(&self, service: S) -> Self::Service {
18        RecordMetrics { service }
19    }
20}
21
22#[derive(Clone)]
23pub(super) struct RecordMetrics<S> {
24    service: S,
25}
26
27impl<S> RecordMetrics<S> {
28    async fn log<F>(method: String, future: F) -> MethodResponse
29    where
30        F: Future<Output = MethodResponse>,
31    {
32        let method = metrics::RpcMethodLabel { method };
33        let start_time = std::time::Instant::now();
34        let resp = future.await;
35        metrics::RPC_METHOD_TIME
36            .get_or_create(&method)
37            // Observe the elapsed time in milliseconds
38            .observe(start_time.elapsed().as_secs_f64() * 1000.0);
39        if resp.is_error() {
40            metrics::RPC_METHOD_FAILURE.get_or_create(&method).inc();
41        }
42        resp
43    }
44}
45
46impl<S> RpcServiceT for RecordMetrics<S>
47where
48    S: RpcServiceT<MethodResponse = MethodResponse, NotificationResponse = MethodResponse>
49        + Send
50        + Sync
51        + Clone
52        + 'static,
53{
54    type MethodResponse = S::MethodResponse;
55    type NotificationResponse = S::NotificationResponse;
56    type BatchResponse = S::BatchResponse;
57
58    fn call<'a>(
59        &self,
60        req: jsonrpsee::types::Request<'a>,
61    ) -> impl Future<Output = Self::MethodResponse> + Send + 'a {
62        Self::log(req.method_name().to_owned(), self.service.call(req))
63    }
64
65    fn batch<'a>(&self, batch: Batch<'a>) -> impl Future<Output = Self::BatchResponse> + Send + 'a {
66        self.service.batch(batch)
67    }
68
69    fn notification<'a>(
70        &self,
71        n: Notification<'a>,
72    ) -> impl Future<Output = Self::NotificationResponse> + Send + 'a {
73        Self::log(n.method_name().to_owned(), self.service.notification(n))
74    }
75}