1212
1313% % Metrics API
1414-export ([start_request /1 ,
15- finish_request /2 ,
16- get_metrics_engine /0 ]).
15+ finish_request /2 ]).
1716
1817% % Backward compatibility API
1918-export ([get_state /1 , async_response_pid /1 ]).
2322-export ([init /1 , handle_call /3 , handle_cast /2 , handle_info /2 ,
2423 terminate /2 , code_change /3 ]).
2524
26- -record (state , {
27- metrics_engine
28- }).
25+ -record (state , {}).
2926
3027% %====================================================================
3128% % API
@@ -41,16 +38,11 @@ start_request(Host) ->
4138finish_request (Host , StartTime ) ->
4239 gen_server :cast (? MODULE , {finish_request , Host , StartTime }).
4340
44- % % @doc Get the current metrics engine.
45- -spec get_metrics_engine () -> module ().
46- get_metrics_engine () ->
47- hackney_metrics :get_engine ().
48-
4941% % @doc Check the state of a connection (backward compatibility).
5042% % In the old architecture, this tracked request state.
5143% % In the new architecture, we simply check if the connection process is alive.
52- % % Returns `req_not_found' if the process is dead, or the connection state.
53- -spec get_state (pid () | term ()) -> req_not_found | term ().
44+ % % Returns `req_not_found' if the process is dead, or the connection state name .
45+ -spec get_state (pid () | term ()) -> req_not_found | atom ().
5446get_state (ConnPid ) when is_pid (ConnPid ) ->
5547 case is_process_alive (ConnPid ) of
5648 false -> req_not_found ;
@@ -63,21 +55,15 @@ get_state(ConnPid) when is_pid(ConnPid) ->
6355get_state (_ ) ->
6456 req_not_found .
6557
66- % % @doc Check if a connection is in async mode (backward compatibility).
67- % % In the old architecture, this returned the async response process PID.
68- % % In the new architecture, we check if the connection process is in async mode.
69- % % Returns `{error, req_not_async}' if not in async mode.
70- -spec async_response_pid (pid () | term ()) -> {ok , pid ()} | {error , req_not_async }.
71- async_response_pid (ConnPid ) when is_pid (ConnPid ) ->
72- case is_process_alive (ConnPid ) of
73- false -> {error , req_not_async };
74- true ->
75- case hackney_conn :get_state (ConnPid ) of
76- {ok , State } when State =:= receiving ; State =:= streaming ->
77- {ok , ConnPid };
78- _ ->
79- {error , req_not_async }
80- end
58+ % % @doc Get the async response pid (backward compatibility).
59+ % % In the new architecture, all streaming connections are considered "async".
60+ -spec async_response_pid (pid ()) -> {ok , pid ()} | {error , req_not_found | req_not_async }.
61+ async_response_pid (Ref ) when is_pid (Ref ) ->
62+ case get_state (Ref ) of
63+ req_not_found -> {error , req_not_found };
64+ streaming -> {ok , Ref };
65+ streaming_once -> {ok , Ref };
66+ _ -> {error , req_not_async }
8167 end ;
8268async_response_pid (_ ) ->
8369 {error , req_not_async }.
@@ -90,28 +76,27 @@ start_link() ->
9076 gen_server :start_link ({local , ? MODULE }, ? MODULE , [], []).
9177
9278init ([]) ->
93- % % Initialize metrics
94- Engine = hackney_metrics :get_engine (),
95- _ = metrics :new (Engine , counter , [hackney , nb_requests ]),
96- _ = metrics :new (Engine , counter , [hackney , total_requests ]),
97- _ = metrics :new (Engine , counter , [hackney , finished_requests ]),
98- {ok , # state {metrics_engine = Engine }}.
79+ {ok , # state {}}.
9980
10081handle_call (_Request , _From , State ) ->
10182 {reply , ok , State }.
10283
103- handle_cast ({start_request , Host }, # state {metrics_engine = Engine } = State ) ->
104- _ = metrics :increment_counter (Engine , [hackney , Host , nb_requests ]),
105- _ = metrics :increment_counter (Engine , [hackney , nb_requests ]),
106- _ = metrics :increment_counter (Engine , [hackney , total_requests ]),
84+ handle_cast ({start_request , Host }, State ) ->
85+ HostBin = to_binary (Host ),
86+ Labels = #{host => HostBin },
87+ _ = hackney_metrics :counter_inc (hackney_requests_total , Labels ),
88+ _ = hackney_metrics :gauge_inc (hackney_requests_active , Labels ),
10789 {noreply , State };
10890
109- handle_cast ({finish_request , Host , StartTime }, # state {metrics_engine = Engine } = State ) ->
110- RequestTime = timer :now_diff (os :timestamp (), StartTime ) / 1000 ,
111- _ = metrics :update_histogram (Engine , [hackney , Host , request_time ], RequestTime ),
112- _ = metrics :decrement_counter (Engine , [hackney , Host , nb_requests ]),
113- _ = metrics :decrement_counter (Engine , [hackney , nb_requests ]),
114- _ = metrics :increment_counter (Engine , [hackney , finished_requests ]),
91+ handle_cast ({finish_request , Host , StartTime }, State ) ->
92+ HostBin = to_binary (Host ),
93+ Labels = #{host => HostBin },
94+ % % Calculate duration in seconds (Prometheus convention)
95+ DurationMicros = timer :now_diff (os :timestamp (), StartTime ),
96+ DurationSeconds = DurationMicros / 1000000 ,
97+ _ = hackney_metrics :histogram_observe (hackney_request_duration_seconds , Labels , DurationSeconds ),
98+ _ = hackney_metrics :gauge_dec (hackney_requests_active , Labels ),
99+ _ = hackney_metrics :counter_inc (hackney_requests_finished_total , Labels ),
115100 {noreply , State };
116101
117102handle_cast (_Msg , State ) ->
@@ -125,3 +110,11 @@ terminate(_Reason, _State) ->
125110
126111code_change (_OldVsn , State , _Extra ) ->
127112 {ok , State }.
113+
114+ % %====================================================================
115+ % % Internal functions
116+ % %====================================================================
117+
118+ to_binary (Host ) when is_binary (Host ) -> Host ;
119+ to_binary (Host ) when is_list (Host ) -> list_to_binary (Host );
120+ to_binary (Host ) when is_atom (Host ) -> atom_to_binary (Host , utf8 ).
0 commit comments