@@ -226,8 +226,9 @@ http_requests_duration_seconds_sum{http_route="UNMATCHED",http_request_method="G
226226*/
227227#![ deny( missing_docs) ]
228228
229+ use actix_web:: http:: Uri ;
229230use log:: warn;
230- use metrics:: { describe_histogram, gauge, histogram, Unit } ;
231+ use metrics:: { describe_gauge , describe_histogram, gauge, histogram, Unit } ;
231232use std:: collections:: { HashMap , HashSet } ;
232233use std:: future:: { ready, Future , Ready } ;
233234use std:: marker:: PhantomData ;
@@ -378,12 +379,14 @@ impl ActixWebMetricsBuilder {
378379 "HTTP response size in bytes for all requests"
379380 ) ;
380381
381- let version: Option < & ' static str > = if let Some ( ref v) = self . metrics_config . labels . version
382- {
383- Some ( Box :: leak ( Box :: new ( v. clone ( ) ) ) )
384- } else {
385- None
386- } ;
382+ let http_server_active_requests_name = format ! (
383+ "{namespace_prefix}{}" ,
384+ self . metrics_config. http_server_active_requests_name
385+ ) ;
386+ describe_gauge ! (
387+ http_server_active_requests_name. clone( ) ,
388+ "Number of active HTTP server requests."
389+ ) ;
387390
388391 let mut const_labels: Vec < ( & ' static str , String ) > = self
389392 . const_labels
@@ -396,23 +399,32 @@ impl ActixWebMetricsBuilder {
396399 const_labels. sort_by_key ( |v| v. 0 ) ;
397400
398401 ActixWebMetrics {
399- exclude : self . exclude ,
400- exclude_regex : self . exclude_regex ,
401- exclude_status : self . exclude_status ,
402- enable_http_version_label : self . metrics_config . labels . version . is_some ( ) ,
403- unmatched_patterns_mask : self . unmatched_patterns_mask ,
404- names : MetricsMetadata {
405- http_requests_duration_seconds : Box :: leak ( Box :: new (
406- http_server_request_duration_name,
407- ) ) ,
408- http_request_size_bytes : Box :: leak ( Box :: new ( http_server_request_body_size_name) ) ,
409- http_response_size_bytes : Box :: leak ( Box :: new ( http_server_response_body_size_name) ) ,
410- endpoint : Box :: leak ( Box :: new ( self . metrics_config . labels . route ) ) ,
411- method : Box :: leak ( Box :: new ( self . metrics_config . labels . method ) ) ,
412- status : Box :: leak ( Box :: new ( self . metrics_config . labels . status ) ) ,
413- version,
414- const_labels,
415- } ,
402+ inner : Arc :: new ( ActixWebMetricsInner {
403+ exclude : self . exclude ,
404+ exclude_regex : self . exclude_regex ,
405+ exclude_status : self . exclude_status ,
406+ unmatched_patterns_mask : self . unmatched_patterns_mask ,
407+ names : MetricsMetadata {
408+ http_requests_duration_seconds : Box :: leak ( Box :: new (
409+ http_server_request_duration_name,
410+ ) ) ,
411+ http_request_size_bytes : Box :: leak ( Box :: new (
412+ http_server_request_body_size_name,
413+ ) ) ,
414+ http_response_size_bytes : Box :: leak ( Box :: new (
415+ http_server_response_body_size_name,
416+ ) ) ,
417+ http_server_active_requests : Box :: leak ( Box :: new (
418+ http_server_active_requests_name,
419+ ) ) ,
420+ http_route : Box :: leak ( Box :: new ( self . metrics_config . labels . route ) ) ,
421+ http_request_method : Box :: leak ( Box :: new ( self . metrics_config . labels . method ) ) ,
422+ http_response_status : Box :: leak ( Box :: new ( self . metrics_config . labels . status ) ) ,
423+ http_version : Box :: leak ( Box :: new ( self . metrics_config . labels . version ) ) ,
424+ url_scheme : Box :: leak ( Box :: new ( self . metrics_config . labels . url_scheme ) ) ,
425+ const_labels,
426+ } ,
427+ } ) ,
416428 }
417429 }
418430}
@@ -429,7 +441,8 @@ pub struct LabelsConfig {
429441 route : String ,
430442 method : String ,
431443 status : String ,
432- version : Option < String > ,
444+ version : String ,
445+ url_scheme : String ,
433446}
434447
435448impl Default for LabelsConfig {
@@ -438,7 +451,8 @@ impl Default for LabelsConfig {
438451 route : String :: from ( "http.route" ) ,
439452 method : String :: from ( "http.request.method" ) ,
440453 status : String :: from ( "http.response.status_code" ) ,
441- version : None ,
454+ version : String :: from ( "network.protocol.version" ) ,
455+ url_scheme : String :: from ( "url.scheme" ) ,
442456 }
443457 }
444458}
@@ -464,7 +478,13 @@ impl LabelsConfig {
464478
465479 /// set http version label
466480 pub fn version < T : Into < String > > ( mut self , name : T ) -> Self {
467- self . version = Some ( name. into ( ) ) ;
481+ self . version = name. into ( ) ;
482+ self
483+ }
484+
485+ /// set url.scheme label
486+ pub fn url_scheme < T : Into < String > > ( mut self , name : T ) -> Self {
487+ self . url_scheme = name. into ( ) ;
468488 self
469489 }
470490}
@@ -477,6 +497,7 @@ pub struct ActixWebMetricsConfig {
477497 http_server_request_duration_name : String ,
478498 http_server_request_body_size_name : String ,
479499 http_server_response_body_size_name : String ,
500+ http_server_active_requests_name : String ,
480501 labels : LabelsConfig ,
481502}
482503
@@ -486,6 +507,7 @@ impl Default for ActixWebMetricsConfig {
486507 http_server_request_duration_name : String :: from ( "http.server.request.duration" ) ,
487508 http_server_request_body_size_name : String :: from ( "http.server.request.body.size" ) ,
488509 http_server_response_body_size_name : String :: from ( "http.server.response.body.size" ) ,
510+ http_server_active_requests_name : String :: from ( "http.server.active_requests" ) ,
489511 labels : LabelsConfig :: default ( ) ,
490512 }
491513 }
@@ -515,6 +537,12 @@ impl ActixWebMetricsConfig {
515537 self . http_server_response_body_size_name = name. into ( ) ;
516538 self
517539 }
540+
541+ /// Set name for `http.server.active_requests` metric
542+ pub fn http_server_active_requests_name < T : Into < String > > ( mut self , name : T ) -> Self {
543+ self . http_server_active_requests_name = name. into ( ) ;
544+ self
545+ }
518546}
519547
520548/// Static references to variable metrics/label names.
@@ -524,10 +552,12 @@ struct MetricsMetadata {
524552 http_requests_duration_seconds : & ' static str ,
525553 http_request_size_bytes : & ' static str ,
526554 http_response_size_bytes : & ' static str ,
527- endpoint : & ' static str ,
528- method : & ' static str ,
529- status : & ' static str ,
530- version : Option < & ' static str > ,
555+ http_server_active_requests : & ' static str ,
556+ http_route : & ' static str ,
557+ http_request_method : & ' static str ,
558+ http_response_status : & ' static str ,
559+ http_version : & ' static str ,
560+ url_scheme : & ' static str ,
531561 const_labels : Vec < ( & ' static str , String ) > ,
532562}
533563
@@ -544,32 +574,63 @@ struct MetricsMetadata {
544574#[ derive( Clone ) ]
545575#[ must_use = "must be set up as middleware for actix-web" ]
546576pub struct ActixWebMetrics {
577+ inner : Arc < ActixWebMetricsInner > ,
578+ }
579+
580+ struct ActixWebMetricsInner {
547581 pub ( crate ) names : MetricsMetadata ,
548582
549583 pub ( crate ) exclude : HashSet < String > ,
550584 pub ( crate ) exclude_regex : RegexSet ,
551585 pub ( crate ) exclude_status : HashSet < StatusCode > ,
552- pub ( crate ) enable_http_version_label : bool ,
553586 pub ( crate ) unmatched_patterns_mask : Option < String > ,
554587}
555588
556589impl ActixWebMetrics {
590+ fn pre_request_update_metrics ( & self , req : & ServiceRequest ) {
591+ let this = & * self . inner ;
592+
593+ let mut labels = Vec :: with_capacity ( 2 ) ;
594+ labels. push ( (
595+ this. names . http_request_method ,
596+ req. method ( ) . as_str ( ) . to_string ( ) ,
597+ ) ) ;
598+ labels. push ( ( this. names . url_scheme , url_scheme ( & req. uri ( ) ) . to_string ( ) ) ) ;
599+
600+ gauge ! ( this. names. http_server_active_requests, & labels) . increment ( 1 ) ;
601+ }
602+
557603 #[ allow( clippy:: too_many_arguments) ]
558- fn update_metrics (
604+ fn post_request_update_metrics (
559605 & self ,
560606 http_version : Version ,
561607 mixed_pattern : & str ,
562608 fallback_pattern : & str ,
563609 method : & Method ,
564610 status : StatusCode ,
611+ scheme : & str ,
565612 clock : Instant ,
566613 was_path_matched : bool ,
567614 request_size : usize ,
568615 response_size : usize ,
569616 ) {
570- if self . exclude . contains ( mixed_pattern)
571- || self . exclude_regex . is_match ( mixed_pattern)
572- || self . exclude_status . contains ( & status)
617+ let this = & * self . inner ;
618+
619+ // NOTE: active_requests cannot be skips as we need to decrement the increment we did that
620+ // the beginning of the request.
621+ let active_request_labels = vec ! [
622+ ( this. names. http_request_method, method. as_str( ) . to_string( ) ) ,
623+ ( this. names. url_scheme, scheme. to_string( ) ) ,
624+ ] ;
625+ gauge ! (
626+ this. names. http_server_active_requests,
627+ & active_request_labels
628+ )
629+ . decrement ( 1 ) ;
630+
631+ if this. exclude . contains ( mixed_pattern)
632+ || this. exclude_regex . is_match ( mixed_pattern)
633+ || this. exclude_status . contains ( & status)
573634 {
574635 return ;
575636 }
@@ -584,34 +645,32 @@ impl ActixWebMetrics {
584645
585646 let final_pattern = if was_path_matched {
586647 final_pattern
587- } else if let Some ( mask) = & self . unmatched_patterns_mask {
648+ } else if let Some ( mask) = & this . unmatched_patterns_mask {
588649 mask
589650 } else {
590651 final_pattern
591652 } ;
592653
593- let mut labels = Vec :: with_capacity ( 4 + self . names . const_labels . len ( ) ) ;
594- labels. push ( ( self . names . endpoint , final_pattern. to_string ( ) ) ) ;
595- labels. push ( ( self . names . method , method. as_str ( ) . to_string ( ) ) ) ;
596- labels. push ( ( self . names . status , status. as_str ( ) . to_string ( ) ) ) ;
654+ let mut labels = Vec :: with_capacity ( 4 + this . names . const_labels . len ( ) ) ;
655+ labels. push ( ( this . names . http_route , final_pattern. to_string ( ) ) ) ;
656+ labels. push ( ( this . names . http_request_method , method. as_str ( ) . to_string ( ) ) ) ;
657+ labels. push ( ( this . names . http_response_status , status. as_str ( ) . to_string ( ) ) ) ;
597658
598- if self . enable_http_version_label {
599- labels. push ( (
600- self . names . version . unwrap ( ) ,
601- Self :: http_version_label ( http_version) . to_string ( ) ,
602- ) ) ;
603- }
659+ labels. push ( (
660+ this. names . http_version ,
661+ Self :: http_version_label ( http_version) . to_string ( ) ,
662+ ) ) ;
604663
605- for ( k, v) in & self . names . const_labels {
664+ for ( k, v) in & this . names . const_labels {
606665 labels. push ( ( k, v. clone ( ) ) ) ;
607666 }
608667
609668 let elapsed = clock. elapsed ( ) ;
610669 let duration =
611670 ( elapsed. as_secs ( ) as f64 ) + f64:: from ( elapsed. subsec_nanos ( ) ) / 1_000_000_000_f64 ;
612- histogram ! ( self . names. http_requests_duration_seconds, & labels) . record ( duration) ;
613- histogram ! ( self . names. http_request_size_bytes, & labels) . record ( request_size as f64 ) ;
614- histogram ! ( self . names. http_response_size_bytes, & labels) . record ( response_size as f64 ) ;
671+ histogram ! ( this . names. http_requests_duration_seconds, & labels) . record ( duration) ;
672+ histogram ! ( this . names. http_request_size_bytes, & labels) . record ( request_size as f64 ) ;
673+ histogram ! ( this . names. http_response_size_bytes, & labels) . record ( response_size as f64 ) ;
615674 }
616675
617676 fn http_version_label ( version : Version ) -> & ' static str {
@@ -639,7 +698,7 @@ where
639698 fn new_transform ( & self , service : S ) -> Self :: Future {
640699 ready ( Ok ( MetricsMiddleware {
641700 service,
642- inner : Arc :: new ( self . clone ( ) ) ,
701+ inner : self . clone ( ) ,
643702 } ) )
644703 }
645704}
@@ -653,7 +712,7 @@ pin_project! {
653712 #[ pin]
654713 fut: S :: Future ,
655714 time: Instant ,
656- inner: Arc < ActixWebMetrics > ,
715+ inner: ActixWebMetrics ,
657716 _t: PhantomData <( ) >,
658717 }
659718}
@@ -721,6 +780,7 @@ where
721780 . and_then ( |v| v. parse :: < usize > ( ) . ok ( ) )
722781 . unwrap_or ( 0 ) ;
723782
783+ let scheme = url_scheme ( & req. uri ( ) ) . to_string ( ) ;
724784 let inner = this. inner . clone ( ) ;
725785 Poll :: Ready ( Ok ( res. map_body ( move |head, body| StreamLog {
726786 body,
@@ -729,6 +789,7 @@ where
729789 clock : time,
730790 inner,
731791 status : head. status ,
792+ scheme,
732793 mixed_pattern,
733794 fallback_pattern,
734795 method,
@@ -742,7 +803,7 @@ where
742803#[ doc( hidden) ]
743804pub struct MetricsMiddleware < S > {
744805 service : S ,
745- inner : Arc < ActixWebMetrics > ,
806+ inner : ActixWebMetrics ,
746807}
747808
748809impl < S , B > Service < ServiceRequest > for MetricsMiddleware < S >
@@ -756,6 +817,8 @@ where
756817 dev:: forward_ready!( service) ;
757818
758819 fn call ( & self , req : ServiceRequest ) -> Self :: Future {
820+ self . inner . pre_request_update_metrics ( & req) ;
821+
759822 LoggerResponse {
760823 fut : self . service . call ( req) ,
761824 time : Instant :: now ( ) ,
@@ -773,8 +836,9 @@ pin_project! {
773836 response_size: usize ,
774837 request_size: usize ,
775838 clock: Instant ,
776- inner: Arc < ActixWebMetrics > ,
839+ inner: ActixWebMetrics ,
777840 status: StatusCode ,
841+ scheme: String ,
778842 // a route pattern with some params not-filled and some params filled in by user-defined
779843 mixed_pattern: String ,
780844 fallback_pattern: String ,
@@ -788,7 +852,7 @@ pin_project! {
788852 fn drop( this: Pin <& mut Self >) {
789853 // update the metrics for this request at the very end of responding
790854 this. inner
791- . update_metrics ( this. version, & this. mixed_pattern, & this. fallback_pattern, & this. method, this. status, this. clock, this. was_path_matched, this. request_size, this. response_size) ;
855+ . post_request_update_metrics ( this. version, & this. mixed_pattern, & this. fallback_pattern, & this. method, this. status, & this . scheme , this. clock, this. was_path_matched, this. request_size, this. response_size) ;
792856 }
793857 }
794858}
@@ -815,3 +879,7 @@ impl<B: MessageBody> MessageBody for StreamLog<B> {
815879 }
816880 }
817881}
882+
883+ fn url_scheme ( uri : & Uri ) -> & str {
884+ uri. scheme ( ) . map ( |s| s. as_str ( ) ) . unwrap_or ( "http" )
885+ }
0 commit comments