diff --git a/src/bin_node/node_run_command.ml b/src/bin_node/node_run_command.ml index 3a2aecd57ac37d866838b5ab3bf19ed7b9fce73e..1f67c659f6d80d1378b43e687845c8958c51a92b 100644 --- a/src/bin_node/node_run_command.ml +++ b/src/bin_node/node_run_command.ml @@ -366,7 +366,7 @@ let sanitize_cors_headers ~default headers = |> String.Set.(union (of_list default)) |> String.Set.elements -let metric = +let rpc_metrics = Prometheus.Summary.v_labels ~label_names:["endpoint"; "method"] ~help:"RPC endpoint call counts and sum of execution times." @@ -374,6 +374,8 @@ let metric = ~subsystem:"rpc" "calls" +module Metrics_server = Prometheus_app.Cohttp (Cohttp_lwt_unix.Server) + let launch_rpc_server ~acl_policy ~media_types (config : Node_config_file.t) node (addr, port) = let open Lwt_result_syntax in @@ -404,31 +406,6 @@ let launch_rpc_server ~acl_policy ~media_types (config : Node_config_file.t) let cors_headers = sanitize_cors_headers ~default:["Content-Type"] rpc_config.cors_headers in - let transform_callback callback connection request body = - let open Lwt_result_syntax in - let do_call () = callback connection request body in - let cohttp_meth = Cohttp.Request.meth request in - let uri = Cohttp.Request.uri request in - let path = Uri.path uri in - let decoded = Resto.Utils.decode_split_path path in - let*! description = - let* resto_meth = - match cohttp_meth with - | #Resto.meth as meth -> Lwt.return_ok meth - | _ -> Lwt.return_error @@ `Method_not_allowed [] - in - let* uri_desc = RPC_directory.lookup_uri_desc dir () resto_meth decoded in - Lwt.return_ok (uri_desc, Resto.string_of_meth resto_meth) - in - match description with - | Ok (uri, meth) -> - (* We update the metric only if the URI can succesfully - be matched in the directory tree. *) - Prometheus.Summary.(time (labels metric [uri; meth]) Sys.time do_call) - | Error _ -> - (* Otherwise, the call must be done anyway. *) - do_call () - in let cors = RPC_server. { @@ -443,7 +420,19 @@ let launch_rpc_server ~acl_policy ~media_types (config : Node_config_file.t) ~media_types:(Media_type.Command_line.of_command_line media_types) dir in - let callback = transform_callback (RPC_server.resto_callback server) in + let callback (conn : Cohttp_lwt_unix.Server.conn) req body = + let path = Cohttp_lwt.Request.uri req |> Uri.path in + if path = "/metrics" then + let*! response = Metrics_server.callback conn req body in + Lwt.return (`Response response) + else Tezos_rpc_http_server.RPC_server.resto_callback server conn req body + in + let update_metrics uri meth = + Prometheus.Summary.(time (labels rpc_metrics [uri; meth]) Sys.time) + in + let callback = + RPC_middleware.rpc_metrics_transform_callback ~update_metrics dir callback + in Lwt.catch (fun () -> let*! () = RPC_server.launch ~host server ~callback mode in @@ -474,8 +463,6 @@ let init_rpc (config : Node_config_file.t) node = addrs) config.rpc.listen_addrs -module Metrics_server = Prometheus_app.Cohttp (Cohttp_lwt_unix.Server) - let metrics_serve metrics_addrs = let open Lwt_result_syntax in let* addrs = diff --git a/src/lib_rpc_http/RPC_middleware.ml b/src/lib_rpc_http/RPC_middleware.ml index 90b5b197b0eb08a0ecce7d83c458f21ed281f899..c5b83ca54d85bdb4087843a3652940fadcb9c49e 100644 --- a/src/lib_rpc_http/RPC_middleware.ml +++ b/src/lib_rpc_http/RPC_middleware.ml @@ -46,5 +46,30 @@ let make_transform_callback forwarding_endpoint callback conn req body = overriding) )) else Lwt.return answer +let rpc_metrics_transform_callback ~update_metrics dir callback conn req body = + let open Lwt_result_syntax in + let do_call () = callback conn req body in + let cohttp_meth = Cohttp.Request.meth req in + let uri = Cohttp.Request.uri req in + let path = Uri.path uri in + let decoded = Resto.Utils.decode_split_path path in + let*! description = + let* resto_meth = + match cohttp_meth with + | #Resto.meth as meth -> Lwt.return_ok meth + | _ -> Lwt.return_error @@ `Method_not_allowed [] + in + let* uri_desc = RPC_directory.lookup_uri_desc dir () resto_meth decoded in + Lwt.return_ok (uri_desc, Resto.string_of_meth resto_meth) + in + match description with + | Ok (uri, meth) -> + (* We update the metric only if the URI can succesfully + be matched in the directory tree. *) + update_metrics uri meth do_call + | Error _ -> + (* Otherwise, the call must be done anyway. *) + do_call () + let proxy_server_query_forwarder forwarding_endpoint = make_transform_callback forwarding_endpoint diff --git a/src/lib_rpc_http/RPC_middleware.mli b/src/lib_rpc_http/RPC_middleware.mli index 405721b22cf90737dd8d34a74dc2f907a9650934..1906081cb46945908713522e7f2a20e50aaf3998 100644 --- a/src/lib_rpc_http/RPC_middleware.mli +++ b/src/lib_rpc_http/RPC_middleware.mli @@ -26,8 +26,20 @@ (** This module provides middlewares that is used by the RPC servers to forward unsupported RPCs to a full node. *) -(** A Resto middleware that transform any callback to an other +(** A Resto middleware that transforms any callback to an other that rewrites queries that the proxy server cannot handle and forwards them to the full node at the given [Uri.t]. *) val proxy_server_query_forwarder : Uri.t -> RPC_server.callback -> RPC_server.callback + +(** A Resto middleware that transforms any server callback to an other + that handles RPC metrics *) +val rpc_metrics_transform_callback : + update_metrics: + (string -> + string -> + (unit -> Cohttp_lwt_unix.Server.response_action Lwt.t) -> + Cohttp_lwt_unix.Server.response_action Lwt.t) -> + unit RPC_directory.t -> + RPC_server.callback -> + RPC_server.callback