actix-web-metrics
Metrics.rs integration for actix-web.
This crate tries to adhere to OpenTelemetry Semantic Conventions
The following metrics are supported:
http.server.request.durationhttp.server.active_requestshttp.server.request.body.sizehttp.server.response.body.size
Usage
First add actix-web-metrics to your Cargo.toml:
[]
= "x.x.x"
You then instantiate the metrics middleware and pass it to .wrap():
use HashMap;
use ;
use ;
use PrometheusBuilder;
async
async
In the example above we are using the PrometheusBuilder from the metrics-exporter-prometheus crate which exposes the metrics via an HTTP endpoint.
A call to the localhost:9000/metrics endpoint will expose your metrics:
$ curl http://localhost:9000/metrics
# HELP http_server_active_requests Number of active HTTP server requests.
# TYPE http_server_active_requests gauge
http_server_active_requests{http_request_method="GET",url_scheme="http"} 1
# HELP http_server_request_duration HTTP request duration in seconds for all requests
# TYPE http_server_request_duration summary
http_server_request_duration{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="0"} 0.000227207
http_server_request_duration{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="0.5"} 0.00022719541927422382
http_server_request_duration{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="0.9"} 0.00022719541927422382
http_server_request_duration{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="0.95"} 0.00022719541927422382
http_server_request_duration{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="0.99"} 0.00022719541927422382
http_server_request_duration{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="0.999"} 0.00022719541927422382
http_server_request_duration{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="1"} 0.000227207
http_server_request_duration_sum{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1"} 0.000227207
http_server_request_duration_count{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1"} 1
# HELP http_server_response_body_size HTTP response size in bytes for all requests
# TYPE http_server_response_body_size summary
http_server_response_body_size{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="0"} 0
http_server_response_body_size{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="0.5"} 0
http_server_response_body_size{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="0.9"} 0
http_server_response_body_size{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="0.95"} 0
http_server_response_body_size{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="0.99"} 0
http_server_response_body_size{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="0.999"} 0
http_server_response_body_size{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="1"} 0
http_server_response_body_size_sum{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1"} 0
http_server_response_body_size_count{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1"} 1
# HELP http_server_request_body_size HTTP request size in bytes for all requests
# TYPE http_server_request_body_size summary
http_server_request_body_size{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="0"} 0
http_server_request_body_size{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="0.5"} 0
http_server_request_body_size{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="0.9"} 0
http_server_request_body_size{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="0.95"} 0
http_server_request_body_size{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="0.99"} 0
http_server_request_body_size{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="0.999"} 0
http_server_request_body_size{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1",quantile="1"} 0
http_server_request_body_size_sum{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1"} 0
http_server_request_body_size_count{http_route="/health",http_request_method="GET",http_response_status_code="200",network_protocol_name="http",network_protocol_version="1.1"} 1
NOTE: There are 2 important things to note:
- The
metrics-exporter-prometheuscrate can be swapped for another metrics.rs compatible exporter. - The endpoint exposed by
metrics-exporter-prometheusis not part of the actix web http server.
If you want to expose a prometheus endpoint directly in actix-web see the prometheus_endpoint.rs example.
Features
Custom metrics
The metrics.rs crate provides macros for custom metrics. This crate does not interfere with that functionality.
use ;
use ;
use counter;
async
async
Configurable routes pattern cardinality
Let's say you have on your app a route to fetch posts by language and by slug GET /posts/{language}/{slug}.
By default, actix-web-metrics will provide metrics for the whole route with the label http.route set to the pattern /posts/{language}/{slug}.
This is great but you cannot differentiate metrics across languages (as there is only a limited set of them).
Actix-web-metrics can be configured to allow for more cardinality on some route params.
For that you need to add a middleware to pass some extensions data, specifically the [MetricsConfig] struct that contains the list of params you want to keep cardinality on.
use ;
use ActixWebMetricsExtension;
async
resource
.wrap_fn
.route;
See the full example with_cardinality_on_params.rs.
Configurable metric names
If you want to rename the default metrics, you can use ActixWebMetricsConfig to do so.
use ;
new
.metrics_config
.build;
See full example configuring_default_metrics.rs.
Masking unmatched requests
By default, if a request path is not matched to an Actix Web route, it will be masked as UNKNOWN.
This is useful to avoid producing lots of useless metrics due to bots or malicious actors.
This can be configured in the following ways:
mask_unmatched_patterns()can be used to change thehttp_routelabel to something other thanUNKNOWN.disable_unmatched_pattern_masking()can be used to disable this masking functionality.
use ActixWebMetricsBuilder;
new
.mask_unmatched_patterns
// or .disable_unmatched_pattern_masking()
.build;
The above will convert all /<nonexistent-path> into UNMATCHED:
http_requests_duration_seconds_sum{http_route="/favicon.ico",http_request_method="GET",http_response_status="400"} 0.000424898
becomes
http_requests_duration_seconds_sum{http_route="UNMATCHED",http_request_method="GET",http_response_status="400"} 0.000424898
Motivations
actix-web-metrics is heavily inspired (and forked from) actix-web-prom.
Special thanks to @nlopes for their excellent work on actix-web-prom.
This crate replaces the underlying metrics implementation from the prometheus crate with metrics.rs.
The reasons for doing this are as followed:
- The metrics.rs ecosystem provides more ergonomic ways to instrument applications than the raw prometheus client.
metrics.rsprovides more customizable ways to export metrics.- The future of the
prometheuscrate is uncertain (see https://github.com/tikv/rust-prometheus/issues/530)