Axum으로 Prometheus 메트릭 내보내기
원문은 Ellie Huxtable님이 에 게재했습니다. 이 블로그 구독하기
관측 가능성(옵저버빌리티)은 중요하다! 나는 보통 Rust에서 HTTP 프레임워크로 Axum을 사용하는데, 쓰기 편하고 빠르기 때문이다.
tower-http는 많은 프로젝트에서 쓰이는 유용한 HTTP 미들웨어를 잔뜩 제공한다. 현재는 메트릭 미들웨어를 제공하지 않는다. 언젠가는 제공하게 될지도 모른다!
추적용 이슈
이 과정을 자동으로 처리해 주는 크레이트도 꽤 많지만, Axum 예제에서는 metrics를 사용하라고 권장한다. 솔직히 나는 더 복잡한 건 필요 없고, 대부분 그냥 카운터 등이 있는 /metrics 엔드포인트만 있으면 된다 — 그래서 metrics로 가기로 했다!
어쨌든 먼저 prometheus exporter를 설정해야 한다. 이게 바로 /metrics의 내용을 생성하는 부분이다. metrics-exporter-prometheus 크레이트를 사용한다. 글로벌 recorder는 실행 파일에서만 설정하는 것이 좋다 — 라이브러리라면 사용자에게 맡기면 된다.
위에서 링크한 Axum 예제에서 거의 그대로 가져왔다 😇
fn setup_metrics_recorder() -> PrometheusHandle {
const EXPONENTIAL_SECONDS: &[f64] = &[
0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0,
];
PrometheusBuilder::new()
.set_buckets_for_metric(
Matcher::Full("http_requests_duration_seconds".to_string()),
EXPONENTIAL_SECONDS,
)
.unwrap()
.install_recorder()
.unwrap()
}사실 오늘은 두 가지 메트릭을 설정하지만, history_requests_duration_seconds만 별도 설정이 필요하다. 히스토그램이기 때문에 exporter에게 데이터를 어떻게 버킷으로 나눌지 알려줘야 하기 때문이다.
준비가 끝나면 axum 미들웨어를 작성할 수 있다! (예제에서 가져와 제대로 컴파일되도록 수정했다. PR을 올릴 예정이다)
/// Middleware to record some common HTTP metrics
/// Generic over B to allow for arbitrary body types (eg Vec<u8>, Streams, a deserialized thing, etc)
/// Someday tower-http might provide a metrics middleware: https://github.com/tower-rs/tower-http/issues/57
pub async fn track_metrics<B>(req: Request<B>, next: Next<B>) -> impl IntoResponse {
let start = Instant::now();
let path = if let Some(matched_path) = req.extensions().get::<MatchedPath>() {
matched_path.as_str().to_owned()
} else {
req.uri().path().to_owned()
};
let method = req.method().clone();
// Run the rest of the request handling first, so we can measure it and get response
// codes.
let response = next.run(req).await;
let latency = start.elapsed().as_secs_f64();
let status = response.status().as_u16().to_string();
let labels = [
("method", method.to_string()),
("path", path),
("status", status),
];
metrics::increment_counter!("http_requests_total", &labels);
metrics::histogram!("http_requests_duration_seconds", latency, &labels);
response
}그런 다음 Axum 라우터를 설정하는 곳에서 /metrics 라우트를 연결하면 된다! 단, 외부에 공개되지 않도록 해야 한다.
여기서
std::future::ready에 대해 알게 됐다! 기본적으로 값을 가지고 즉시 사용 가능한 future를 만드는 함수다. 예를 들면:let f = std::future::ready(1); assert_eq!(a.await, 1);
let recorder_handle = setup_metrics_recorder();
let router = Router::new()
.route("/metrics", get(move || ready(recorder_handle.render())))
.layer(axum::middleware::from_fn(track_metrics));사실 이게 전부다!
글을 무작위로 읽기
댓글
로그인하고 댓글 남기기