You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-31 09:24:31 +03:00
Have the listeners names in the metrics and traces
Also refactors the OTEL layer to have a separate metrics logic
This commit is contained in:
@ -1,12 +1,15 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use opentelemetry::metrics::{Counter, Histogram, UpDownCounter};
|
||||
use opentelemetry::{
|
||||
metrics::{Counter, Histogram, UpDownCounter},
|
||||
KeyValue,
|
||||
};
|
||||
use tower::Layer;
|
||||
|
||||
use super::{
|
||||
extract_context::DefaultExtractContext, inject_context::DefaultInjectContext,
|
||||
make_span_builder::DefaultMakeSpanBuilder, on_error::DefaultOnError,
|
||||
on_response::DefaultOnResponse, service::Trace,
|
||||
make_metrics_labels::DefaultMakeMetricsLabels, make_span_builder::DefaultMakeSpanBuilder,
|
||||
on_error::DefaultOnError, on_response::DefaultOnResponse, service::Trace,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -14,6 +17,7 @@ pub struct TraceLayer<
|
||||
ExtractContext = DefaultExtractContext,
|
||||
InjectContext = DefaultInjectContext,
|
||||
MakeSpanBuilder = DefaultMakeSpanBuilder,
|
||||
MakeMetricsLabels = DefaultMakeMetricsLabels,
|
||||
OnResponse = DefaultOnResponse,
|
||||
OnError = DefaultOnError,
|
||||
> {
|
||||
@ -21,12 +25,14 @@ pub struct TraceLayer<
|
||||
extract_context: ExtractContext,
|
||||
inject_context: InjectContext,
|
||||
make_span_builder: MakeSpanBuilder,
|
||||
make_metrics_labels: MakeMetricsLabels,
|
||||
on_response: OnResponse,
|
||||
on_error: OnError,
|
||||
|
||||
inflight_requests: UpDownCounter<i64>,
|
||||
request_counter: Counter<u64>,
|
||||
request_histogram: Histogram<f64>,
|
||||
static_attributes: Vec<KeyValue>,
|
||||
}
|
||||
|
||||
impl Default for TraceLayer {
|
||||
@ -63,8 +69,15 @@ impl TraceLayer {
|
||||
}
|
||||
}
|
||||
|
||||
impl<ExtractContext, InjectContext, MakeSpanBuilder, OnResponse, OnError>
|
||||
TraceLayer<ExtractContext, InjectContext, MakeSpanBuilder, OnResponse, OnError>
|
||||
impl<ExtractContext, InjectContext, MakeSpanBuilder, MakeMetricsLabels, OnResponse, OnError>
|
||||
TraceLayer<
|
||||
ExtractContext,
|
||||
InjectContext,
|
||||
MakeSpanBuilder,
|
||||
MakeMetricsLabels,
|
||||
OnResponse,
|
||||
OnError,
|
||||
>
|
||||
{
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
@ -77,6 +90,7 @@ impl<ExtractContext, InjectContext, MakeSpanBuilder, OnResponse, OnError>
|
||||
ExtractContext: Default,
|
||||
InjectContext: Default,
|
||||
MakeSpanBuilder: Default,
|
||||
MakeMetricsLabels: Default,
|
||||
OnResponse: Default,
|
||||
OnError: Default,
|
||||
{
|
||||
@ -85,29 +99,55 @@ impl<ExtractContext, InjectContext, MakeSpanBuilder, OnResponse, OnError>
|
||||
extract_context: ExtractContext::default(),
|
||||
inject_context: InjectContext::default(),
|
||||
make_span_builder: MakeSpanBuilder::default(),
|
||||
make_metrics_labels: MakeMetricsLabels::default(),
|
||||
on_response: OnResponse::default(),
|
||||
on_error: OnError::default(),
|
||||
inflight_requests,
|
||||
request_counter,
|
||||
request_histogram,
|
||||
static_attributes: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_static_attribute(mut self, attribute: KeyValue) -> Self {
|
||||
self.static_attributes.push(attribute);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_static_attributes(
|
||||
mut self,
|
||||
attributes: impl IntoIterator<Item = KeyValue>,
|
||||
) -> Self {
|
||||
self.static_attributes.extend(attributes);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn extract_context<NewExtractContext>(
|
||||
self,
|
||||
extract_context: NewExtractContext,
|
||||
) -> TraceLayer<NewExtractContext, InjectContext, MakeSpanBuilder, OnResponse, OnError> {
|
||||
) -> TraceLayer<
|
||||
NewExtractContext,
|
||||
InjectContext,
|
||||
MakeSpanBuilder,
|
||||
MakeMetricsLabels,
|
||||
OnResponse,
|
||||
OnError,
|
||||
> {
|
||||
TraceLayer {
|
||||
tracer: self.tracer,
|
||||
extract_context,
|
||||
inject_context: self.inject_context,
|
||||
make_span_builder: self.make_span_builder,
|
||||
make_metrics_labels: self.make_metrics_labels,
|
||||
on_response: self.on_response,
|
||||
on_error: self.on_error,
|
||||
inflight_requests: self.inflight_requests,
|
||||
request_counter: self.request_counter,
|
||||
request_histogram: self.request_histogram,
|
||||
static_attributes: self.static_attributes,
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,17 +155,26 @@ impl<ExtractContext, InjectContext, MakeSpanBuilder, OnResponse, OnError>
|
||||
pub fn inject_context<NewInjectContext>(
|
||||
self,
|
||||
inject_context: NewInjectContext,
|
||||
) -> TraceLayer<ExtractContext, NewInjectContext, MakeSpanBuilder, OnResponse, OnError> {
|
||||
) -> TraceLayer<
|
||||
ExtractContext,
|
||||
NewInjectContext,
|
||||
MakeSpanBuilder,
|
||||
MakeMetricsLabels,
|
||||
OnResponse,
|
||||
OnError,
|
||||
> {
|
||||
TraceLayer {
|
||||
tracer: self.tracer,
|
||||
extract_context: self.extract_context,
|
||||
inject_context,
|
||||
make_span_builder: self.make_span_builder,
|
||||
make_metrics_labels: self.make_metrics_labels,
|
||||
on_response: self.on_response,
|
||||
on_error: self.on_error,
|
||||
inflight_requests: self.inflight_requests,
|
||||
request_counter: self.request_counter,
|
||||
request_histogram: self.request_histogram,
|
||||
static_attributes: self.static_attributes,
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,17 +182,53 @@ impl<ExtractContext, InjectContext, MakeSpanBuilder, OnResponse, OnError>
|
||||
pub fn make_span_builder<NewMakeSpanBuilder>(
|
||||
self,
|
||||
make_span_builder: NewMakeSpanBuilder,
|
||||
) -> TraceLayer<ExtractContext, InjectContext, NewMakeSpanBuilder, OnResponse, OnError> {
|
||||
) -> TraceLayer<
|
||||
ExtractContext,
|
||||
InjectContext,
|
||||
NewMakeSpanBuilder,
|
||||
MakeMetricsLabels,
|
||||
OnResponse,
|
||||
OnError,
|
||||
> {
|
||||
TraceLayer {
|
||||
tracer: self.tracer,
|
||||
extract_context: self.extract_context,
|
||||
inject_context: self.inject_context,
|
||||
make_span_builder,
|
||||
make_metrics_labels: self.make_metrics_labels,
|
||||
on_response: self.on_response,
|
||||
on_error: self.on_error,
|
||||
inflight_requests: self.inflight_requests,
|
||||
request_counter: self.request_counter,
|
||||
request_histogram: self.request_histogram,
|
||||
static_attributes: self.static_attributes,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn make_metrics_labels<NewMakeMetricsLabels>(
|
||||
self,
|
||||
make_metrics_labels: NewMakeMetricsLabels,
|
||||
) -> TraceLayer<
|
||||
ExtractContext,
|
||||
InjectContext,
|
||||
MakeSpanBuilder,
|
||||
NewMakeMetricsLabels,
|
||||
OnResponse,
|
||||
OnError,
|
||||
> {
|
||||
TraceLayer {
|
||||
tracer: self.tracer,
|
||||
extract_context: self.extract_context,
|
||||
inject_context: self.inject_context,
|
||||
make_span_builder: self.make_span_builder,
|
||||
make_metrics_labels,
|
||||
on_response: self.on_response,
|
||||
on_error: self.on_error,
|
||||
inflight_requests: self.inflight_requests,
|
||||
request_counter: self.request_counter,
|
||||
request_histogram: self.request_histogram,
|
||||
static_attributes: self.static_attributes,
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,17 +236,26 @@ impl<ExtractContext, InjectContext, MakeSpanBuilder, OnResponse, OnError>
|
||||
pub fn on_response<NewOnResponse>(
|
||||
self,
|
||||
on_response: NewOnResponse,
|
||||
) -> TraceLayer<ExtractContext, InjectContext, MakeSpanBuilder, NewOnResponse, OnError> {
|
||||
) -> TraceLayer<
|
||||
ExtractContext,
|
||||
InjectContext,
|
||||
MakeSpanBuilder,
|
||||
MakeMetricsLabels,
|
||||
NewOnResponse,
|
||||
OnError,
|
||||
> {
|
||||
TraceLayer {
|
||||
tracer: self.tracer,
|
||||
extract_context: self.extract_context,
|
||||
inject_context: self.inject_context,
|
||||
make_span_builder: self.make_span_builder,
|
||||
make_metrics_labels: self.make_metrics_labels,
|
||||
on_response,
|
||||
on_error: self.on_error,
|
||||
inflight_requests: self.inflight_requests,
|
||||
request_counter: self.request_counter,
|
||||
request_histogram: self.request_histogram,
|
||||
static_attributes: self.static_attributes,
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,31 +263,57 @@ impl<ExtractContext, InjectContext, MakeSpanBuilder, OnResponse, OnError>
|
||||
pub fn on_error<NewOnError>(
|
||||
self,
|
||||
on_error: NewOnError,
|
||||
) -> TraceLayer<ExtractContext, InjectContext, MakeSpanBuilder, OnResponse, NewOnError> {
|
||||
) -> TraceLayer<
|
||||
ExtractContext,
|
||||
InjectContext,
|
||||
MakeSpanBuilder,
|
||||
MakeMetricsLabels,
|
||||
OnResponse,
|
||||
NewOnError,
|
||||
> {
|
||||
TraceLayer {
|
||||
tracer: self.tracer,
|
||||
extract_context: self.extract_context,
|
||||
inject_context: self.inject_context,
|
||||
make_span_builder: self.make_span_builder,
|
||||
make_metrics_labels: self.make_metrics_labels,
|
||||
on_response: self.on_response,
|
||||
on_error,
|
||||
inflight_requests: self.inflight_requests,
|
||||
request_counter: self.request_counter,
|
||||
request_histogram: self.request_histogram,
|
||||
static_attributes: self.static_attributes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<ExtractContext, InjectContext, MakeSpanBuilder, OnResponse, OnError, S> Layer<S>
|
||||
for TraceLayer<ExtractContext, InjectContext, MakeSpanBuilder, OnResponse, OnError>
|
||||
impl<ExtractContext, InjectContext, MakeSpanBuilder, MakeMetricsLabels, OnResponse, OnError, S>
|
||||
Layer<S>
|
||||
for TraceLayer<
|
||||
ExtractContext,
|
||||
InjectContext,
|
||||
MakeSpanBuilder,
|
||||
MakeMetricsLabels,
|
||||
OnResponse,
|
||||
OnError,
|
||||
>
|
||||
where
|
||||
ExtractContext: Clone,
|
||||
InjectContext: Clone,
|
||||
MakeSpanBuilder: Clone,
|
||||
MakeMetricsLabels: Clone,
|
||||
OnResponse: Clone,
|
||||
OnError: Clone,
|
||||
{
|
||||
type Service = Trace<ExtractContext, InjectContext, MakeSpanBuilder, OnResponse, OnError, S>;
|
||||
type Service = Trace<
|
||||
ExtractContext,
|
||||
InjectContext,
|
||||
MakeSpanBuilder,
|
||||
MakeMetricsLabels,
|
||||
OnResponse,
|
||||
OnError,
|
||||
S,
|
||||
>;
|
||||
|
||||
fn layer(&self, inner: S) -> Self::Service {
|
||||
Trace::new(
|
||||
@ -202,11 +322,13 @@ where
|
||||
self.extract_context.clone(),
|
||||
self.inject_context.clone(),
|
||||
self.make_span_builder.clone(),
|
||||
self.make_metrics_labels.clone(),
|
||||
self.on_response.clone(),
|
||||
self.on_error.clone(),
|
||||
self.inflight_requests.clone(),
|
||||
self.request_counter.clone(),
|
||||
self.request_histogram.clone(),
|
||||
self.static_attributes.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
61
crates/http/src/layers/otel/make_metrics_labels.rs
Normal file
61
crates/http/src/layers/otel/make_metrics_labels.rs
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use http::Request;
|
||||
use opentelemetry::KeyValue;
|
||||
|
||||
use super::utils::http_method_str;
|
||||
|
||||
pub trait MakeMetricsLabels<R> {
|
||||
fn make_metrics_labels(&self, request: &R) -> Vec<KeyValue>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct DefaultMakeMetricsLabels;
|
||||
|
||||
impl<R> MakeMetricsLabels<R> for DefaultMakeMetricsLabels {
|
||||
fn make_metrics_labels(&self, _request: &R) -> Vec<KeyValue> {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct MetricsLabelsFromHttpRequest;
|
||||
|
||||
impl<B> MakeMetricsLabels<Request<B>> for MetricsLabelsFromHttpRequest {
|
||||
fn make_metrics_labels(&self, request: &Request<B>) -> Vec<KeyValue> {
|
||||
vec![KeyValue::new("method", http_method_str(request.method()))]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct MetricsLabelsFromAxumRequest;
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
impl<B> MakeMetricsLabels<Request<B>> for MetricsLabelsFromAxumRequest {
|
||||
fn make_metrics_labels(&self, request: &Request<B>) -> Vec<KeyValue> {
|
||||
let path: Cow<'static, str> = request
|
||||
.extensions()
|
||||
.get::<axum::extract::MatchedPath>()
|
||||
.map_or("FALLBACK".into(), |path| path.as_str().to_owned().into());
|
||||
|
||||
vec![
|
||||
KeyValue::new("method", http_method_str(request.method())),
|
||||
KeyValue::new("route", path),
|
||||
]
|
||||
}
|
||||
}
|
@ -12,22 +12,21 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::{borrow::Cow, vec::Vec};
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
use axum::extract::{ConnectInfo, MatchedPath};
|
||||
use headers::{ContentLength, HeaderMapExt, Host, UserAgent};
|
||||
use http::{Method, Request, Version};
|
||||
use http::Request;
|
||||
#[cfg(feature = "client")]
|
||||
use hyper::client::connect::dns::Name;
|
||||
use opentelemetry::{
|
||||
trace::{SpanBuilder, SpanKind},
|
||||
KeyValue,
|
||||
};
|
||||
use opentelemetry::trace::{SpanBuilder, SpanKind};
|
||||
use opentelemetry_semantic_conventions::trace as SC;
|
||||
|
||||
use super::utils::{http_flavor, http_method_str};
|
||||
|
||||
pub trait MakeSpanBuilder<R> {
|
||||
fn make_span_builder(&self, request: &R) -> (SpanBuilder, Vec<KeyValue>);
|
||||
fn make_span_builder(&self, request: &R) -> SpanBuilder;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@ -51,36 +50,8 @@ impl Default for DefaultMakeSpanBuilder {
|
||||
}
|
||||
|
||||
impl<R> MakeSpanBuilder<R> for DefaultMakeSpanBuilder {
|
||||
fn make_span_builder(&self, _request: &R) -> (SpanBuilder, Vec<KeyValue>) {
|
||||
(SpanBuilder::from_name(self.operation), Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn http_method_str(method: &Method) -> Cow<'static, str> {
|
||||
match method {
|
||||
&Method::OPTIONS => "OPTIONS".into(),
|
||||
&Method::GET => "GET".into(),
|
||||
&Method::POST => "POST".into(),
|
||||
&Method::PUT => "PUT".into(),
|
||||
&Method::DELETE => "DELETE".into(),
|
||||
&Method::HEAD => "HEAD".into(),
|
||||
&Method::TRACE => "TRACE".into(),
|
||||
&Method::CONNECT => "CONNECT".into(),
|
||||
&Method::PATCH => "PATCH".into(),
|
||||
other => other.to_string().into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn http_flavor(version: Version) -> Cow<'static, str> {
|
||||
match version {
|
||||
Version::HTTP_09 => "0.9".into(),
|
||||
Version::HTTP_10 => "1.0".into(),
|
||||
Version::HTTP_11 => "1.1".into(),
|
||||
Version::HTTP_2 => "2.0".into(),
|
||||
Version::HTTP_3 => "3.0".into(),
|
||||
other => format!("{:?}", other).into(),
|
||||
fn make_span_builder(&self, _request: &R) -> SpanBuilder {
|
||||
SpanBuilder::from_name(self.operation)
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,10 +88,9 @@ impl SpanFromHttpRequest {
|
||||
}
|
||||
|
||||
impl<B> MakeSpanBuilder<Request<B>> for SpanFromHttpRequest {
|
||||
fn make_span_builder(&self, request: &Request<B>) -> (SpanBuilder, Vec<KeyValue>) {
|
||||
let method = SC::HTTP_METHOD.string(http_method_str(request.method()));
|
||||
fn make_span_builder(&self, request: &Request<B>) -> SpanBuilder {
|
||||
let mut attributes = vec![
|
||||
method.clone(),
|
||||
SC::HTTP_METHOD.string(http_method_str(request.method())),
|
||||
SC::HTTP_FLAVOR.string(http_flavor(request.version())),
|
||||
SC::HTTP_TARGET.string(request.uri().to_string()),
|
||||
];
|
||||
@ -141,12 +111,9 @@ impl<B> MakeSpanBuilder<Request<B>> for SpanFromHttpRequest {
|
||||
}
|
||||
}
|
||||
|
||||
let span_builder = SpanBuilder::from_name(self.operation)
|
||||
SpanBuilder::from_name(self.operation)
|
||||
.with_kind(self.span_kind.clone())
|
||||
.with_attributes(attributes);
|
||||
|
||||
let metrics_labels = vec![method];
|
||||
(span_builder, metrics_labels)
|
||||
.with_attributes(attributes)
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,15 +123,21 @@ pub struct SpanFromAxumRequest;
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
impl<B> MakeSpanBuilder<Request<B>> for SpanFromAxumRequest {
|
||||
fn make_span_builder(&self, request: &Request<B>) -> (SpanBuilder, Vec<KeyValue>) {
|
||||
let method = SC::HTTP_METHOD.string(http_method_str(request.method()));
|
||||
|
||||
let mut metrics_labels = vec![method.clone()];
|
||||
fn make_span_builder(&self, request: &Request<B>) -> SpanBuilder {
|
||||
let (name, route): (String, Cow<'static, str>) =
|
||||
if let Some(path) = request.extensions().get::<MatchedPath>() {
|
||||
let path = path.as_str().to_owned();
|
||||
let name = path.clone();
|
||||
(name, path.into())
|
||||
} else {
|
||||
(request.uri().path().to_owned(), Cow::Borrowed("FALLBACK"))
|
||||
};
|
||||
|
||||
let mut attributes = vec![
|
||||
method,
|
||||
SC::HTTP_METHOD.string(http_method_str(request.method())),
|
||||
SC::HTTP_FLAVOR.string(http_flavor(request.version())),
|
||||
SC::HTTP_TARGET.string(request.uri().to_string()),
|
||||
SC::HTTP_ROUTE.string(route),
|
||||
];
|
||||
|
||||
let headers = request.headers();
|
||||
@ -192,23 +165,9 @@ impl<B> MakeSpanBuilder<Request<B>> for SpanFromAxumRequest {
|
||||
attributes.push(SC::NET_PEER_PORT.i64(addr.port().into()));
|
||||
}
|
||||
|
||||
let (name, route) = if let Some(path) = request.extensions().get::<MatchedPath>() {
|
||||
let path = path.as_str();
|
||||
(path, path)
|
||||
} else {
|
||||
(request.uri().path(), "FALLBACK")
|
||||
};
|
||||
|
||||
let route = SC::HTTP_ROUTE.string(route.to_owned());
|
||||
attributes.push(route.clone());
|
||||
metrics_labels.push(route);
|
||||
|
||||
(
|
||||
SpanBuilder::from_name(name.to_owned())
|
||||
.with_kind(SpanKind::Server)
|
||||
.with_attributes(attributes),
|
||||
metrics_labels,
|
||||
)
|
||||
SpanBuilder::from_name(name)
|
||||
.with_kind(SpanKind::Server)
|
||||
.with_attributes(attributes)
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,14 +177,11 @@ pub struct SpanFromDnsRequest;
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
impl MakeSpanBuilder<Name> for SpanFromDnsRequest {
|
||||
fn make_span_builder(&self, request: &Name) -> (SpanBuilder, Vec<KeyValue>) {
|
||||
fn make_span_builder(&self, request: &Name) -> SpanBuilder {
|
||||
let attributes = vec![SC::NET_HOST_NAME.string(request.as_str().to_owned())];
|
||||
|
||||
(
|
||||
SpanBuilder::from_name("resolve")
|
||||
.with_kind(SpanKind::Client)
|
||||
.with_attributes(attributes.clone()),
|
||||
attributes,
|
||||
)
|
||||
SpanBuilder::from_name("resolve")
|
||||
.with_kind(SpanKind::Client)
|
||||
.with_attributes(attributes)
|
||||
}
|
||||
}
|
||||
|
@ -15,15 +15,18 @@
|
||||
mod extract_context;
|
||||
mod inject_context;
|
||||
mod layer;
|
||||
mod make_metrics_labels;
|
||||
mod make_span_builder;
|
||||
mod on_error;
|
||||
mod on_response;
|
||||
mod service;
|
||||
mod utils;
|
||||
|
||||
pub type TraceHttpServerLayer = TraceLayer<
|
||||
ExtractFromHttpRequest,
|
||||
DefaultInjectContext,
|
||||
SpanFromHttpRequest,
|
||||
MetricsLabelsFromHttpRequest,
|
||||
OnHttpResponse,
|
||||
DefaultOnError,
|
||||
>;
|
||||
@ -32,6 +35,7 @@ pub type TraceHttpServer<S> = Trace<
|
||||
ExtractFromHttpRequest,
|
||||
DefaultInjectContext,
|
||||
SpanFromHttpRequest,
|
||||
MetricsLabelsFromHttpRequest,
|
||||
OnHttpResponse,
|
||||
DefaultOnError,
|
||||
S,
|
||||
@ -42,6 +46,7 @@ pub type TraceAxumServerLayer = TraceLayer<
|
||||
ExtractFromHttpRequest,
|
||||
DefaultInjectContext,
|
||||
SpanFromAxumRequest,
|
||||
MetricsLabelsFromAxumRequest,
|
||||
OnHttpResponse,
|
||||
DefaultOnError,
|
||||
>;
|
||||
@ -51,6 +56,7 @@ pub type TraceAxumServer<S> = Trace<
|
||||
ExtractFromHttpRequest,
|
||||
DefaultInjectContext,
|
||||
SpanFromAxumRequest,
|
||||
MetricsLabelsFromAxumRequest,
|
||||
OnHttpResponse,
|
||||
DefaultOnError,
|
||||
S,
|
||||
@ -60,6 +66,7 @@ pub type TraceHttpClientLayer = TraceLayer<
|
||||
DefaultExtractContext,
|
||||
InjectInHttpRequest,
|
||||
SpanFromHttpRequest,
|
||||
MetricsLabelsFromHttpRequest,
|
||||
OnHttpResponse,
|
||||
DefaultOnError,
|
||||
>;
|
||||
@ -68,6 +75,7 @@ pub type TraceHttpClient<S> = Trace<
|
||||
DefaultExtractContext,
|
||||
InjectInHttpRequest,
|
||||
SpanFromHttpRequest,
|
||||
MetricsLabelsFromHttpRequest,
|
||||
OnHttpResponse,
|
||||
DefaultOnError,
|
||||
S,
|
||||
@ -78,6 +86,7 @@ pub type TraceDnsLayer = TraceLayer<
|
||||
DefaultExtractContext,
|
||||
DefaultInjectContext,
|
||||
SpanFromDnsRequest,
|
||||
DefaultMakeMetricsLabels,
|
||||
DefaultOnResponse,
|
||||
DefaultOnError,
|
||||
>;
|
||||
@ -87,6 +96,7 @@ pub type TraceDns<S> = Trace<
|
||||
DefaultExtractContext,
|
||||
DefaultInjectContext,
|
||||
SpanFromDnsRequest,
|
||||
DefaultMakeMetricsLabels,
|
||||
DefaultOnResponse,
|
||||
DefaultOnError,
|
||||
S,
|
||||
@ -97,6 +107,7 @@ impl TraceHttpServerLayer {
|
||||
pub fn http_server() -> Self {
|
||||
TraceLayer::with_namespace("http_server")
|
||||
.make_span_builder(SpanFromHttpRequest::server())
|
||||
.make_metrics_labels(MetricsLabelsFromHttpRequest::default())
|
||||
.on_response(OnHttpResponse)
|
||||
.extract_context(ExtractFromHttpRequest)
|
||||
}
|
||||
@ -108,6 +119,7 @@ impl TraceAxumServerLayer {
|
||||
pub fn axum() -> Self {
|
||||
TraceLayer::with_namespace("http_server")
|
||||
.make_span_builder(SpanFromAxumRequest)
|
||||
.make_metrics_labels(MetricsLabelsFromAxumRequest::default())
|
||||
.on_response(OnHttpResponse)
|
||||
.extract_context(ExtractFromHttpRequest)
|
||||
}
|
||||
@ -118,6 +130,7 @@ impl TraceHttpClientLayer {
|
||||
pub fn http_client(operation: &'static str) -> Self {
|
||||
TraceLayer::with_namespace("http_client")
|
||||
.make_span_builder(SpanFromHttpRequest::client(operation))
|
||||
.make_metrics_labels(MetricsLabelsFromHttpRequest::default())
|
||||
.on_response(OnHttpResponse)
|
||||
.inject_context(InjectInHttpRequest)
|
||||
}
|
||||
@ -126,6 +139,7 @@ impl TraceHttpClientLayer {
|
||||
pub fn inner_http_client() -> Self {
|
||||
TraceLayer::with_namespace("inner_http_client")
|
||||
.make_span_builder(SpanFromHttpRequest::inner_client())
|
||||
.make_metrics_labels(MetricsLabelsFromHttpRequest::default())
|
||||
.on_response(OnHttpResponse)
|
||||
.inject_context(InjectInHttpRequest)
|
||||
}
|
||||
@ -139,6 +153,9 @@ impl TraceDnsLayer {
|
||||
}
|
||||
}
|
||||
|
||||
use self::make_metrics_labels::{
|
||||
DefaultMakeMetricsLabels, MetricsLabelsFromAxumRequest, MetricsLabelsFromHttpRequest,
|
||||
};
|
||||
pub use self::{
|
||||
extract_context::*, inject_context::*, layer::*, make_span_builder::*, on_error::*,
|
||||
on_response::*, service::*,
|
||||
|
@ -16,7 +16,7 @@ use opentelemetry::{trace::SpanRef, KeyValue};
|
||||
use opentelemetry_semantic_conventions::trace::EXCEPTION_MESSAGE;
|
||||
|
||||
pub trait OnError<E> {
|
||||
fn on_error(&self, span: &SpanRef<'_>, err: &E) -> Vec<KeyValue>;
|
||||
fn on_error(&self, span: &SpanRef<'_>, metrics_labels: &mut Vec<KeyValue>, err: &E);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
@ -26,10 +26,8 @@ impl<E> OnError<E> for DefaultOnError
|
||||
where
|
||||
E: std::fmt::Display,
|
||||
{
|
||||
fn on_error(&self, span: &SpanRef<'_>, err: &E) -> Vec<KeyValue> {
|
||||
fn on_error(&self, span: &SpanRef<'_>, _metrics_labels: &mut Vec<KeyValue>, err: &E) {
|
||||
let attributes = vec![EXCEPTION_MESSAGE.string(err.to_string())];
|
||||
span.add_event("exception".to_owned(), attributes);
|
||||
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
@ -20,15 +20,14 @@ use opentelemetry::{trace::SpanRef, KeyValue};
|
||||
use opentelemetry_semantic_conventions::trace as SC;
|
||||
|
||||
pub trait OnResponse<R> {
|
||||
fn on_response(&self, span: &SpanRef<'_>, response: &R) -> Vec<KeyValue>;
|
||||
fn on_response(&self, span: &SpanRef<'_>, metrics_labels: &mut Vec<KeyValue>, response: &R);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct DefaultOnResponse;
|
||||
|
||||
impl<R> OnResponse<R> for DefaultOnResponse {
|
||||
fn on_response(&self, _span: &SpanRef<'_>, _response: &R) -> Vec<KeyValue> {
|
||||
Vec::new()
|
||||
fn on_response(&self, _span: &SpanRef<'_>, _metrics_labels: &mut Vec<KeyValue>, _response: &R) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,9 +35,15 @@ impl<R> OnResponse<R> for DefaultOnResponse {
|
||||
pub struct OnHttpResponse;
|
||||
|
||||
impl<B> OnResponse<Response<B>> for OnHttpResponse {
|
||||
fn on_response(&self, span: &SpanRef<'_>, response: &Response<B>) -> Vec<KeyValue> {
|
||||
fn on_response(
|
||||
&self,
|
||||
span: &SpanRef<'_>,
|
||||
metrics_labels: &mut Vec<KeyValue>,
|
||||
response: &Response<B>,
|
||||
) {
|
||||
let status_code = i64::from(response.status().as_u16());
|
||||
span.set_attribute(SC::HTTP_STATUS_CODE.i64(status_code));
|
||||
metrics_labels.push(KeyValue::new("status_code", status_code));
|
||||
|
||||
if let Some(ContentLength(content_length)) = response.headers().typed_get() {
|
||||
if let Ok(content_length) = content_length.try_into() {
|
||||
@ -55,7 +60,5 @@ impl<B> OnResponse<Response<B>> for OnHttpResponse {
|
||||
span.set_attribute(SC::NET_HOST_IP.string(info.local_addr().ip().to_string()));
|
||||
span.set_attribute(SC::NET_HOST_PORT.i64(info.local_addr().port().into()));
|
||||
}
|
||||
|
||||
vec![KeyValue::new("status_code", status_code)]
|
||||
}
|
||||
}
|
||||
|
@ -24,26 +24,37 @@ use tower::Service;
|
||||
|
||||
use super::{
|
||||
extract_context::ExtractContext, inject_context::InjectContext,
|
||||
make_span_builder::MakeSpanBuilder, on_error::OnError, on_response::OnResponse,
|
||||
make_metrics_labels::MakeMetricsLabels, make_span_builder::MakeSpanBuilder, on_error::OnError,
|
||||
on_response::OnResponse,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Trace<ExtractContext, InjectContext, MakeSpanBuilder, OnResponse, OnError, S> {
|
||||
pub struct Trace<
|
||||
ExtractContext,
|
||||
InjectContext,
|
||||
MakeSpanBuilder,
|
||||
MakeMetricsLabels,
|
||||
OnResponse,
|
||||
OnError,
|
||||
S,
|
||||
> {
|
||||
inner: S,
|
||||
tracer: Arc<opentelemetry::global::BoxedTracer>,
|
||||
extract_context: ExtractContext,
|
||||
inject_context: InjectContext,
|
||||
make_span_builder: MakeSpanBuilder,
|
||||
make_metrics_labels: MakeMetricsLabels,
|
||||
on_response: OnResponse,
|
||||
on_error: OnError,
|
||||
|
||||
inflight_requests: UpDownCounter<i64>,
|
||||
request_counter: Counter<u64>,
|
||||
request_histogram: Histogram<f64>,
|
||||
static_attributes: Vec<KeyValue>,
|
||||
}
|
||||
|
||||
impl<ExtractContext, InjectContext, MakeSpanBuilder, OnResponse, OnError, S>
|
||||
Trace<ExtractContext, InjectContext, MakeSpanBuilder, OnResponse, OnError, S>
|
||||
impl<ExtractContext, InjectContext, MakeSpanBuilder, MakeMetricsLabels, OnResponse, OnError, S>
|
||||
Trace<ExtractContext, InjectContext, MakeSpanBuilder, MakeMetricsLabels, OnResponse, OnError, S>
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
@ -52,11 +63,13 @@ impl<ExtractContext, InjectContext, MakeSpanBuilder, OnResponse, OnError, S>
|
||||
extract_context: ExtractContext,
|
||||
inject_context: InjectContext,
|
||||
make_span_builder: MakeSpanBuilder,
|
||||
make_metrics_labels: MakeMetricsLabels,
|
||||
on_response: OnResponse,
|
||||
on_error: OnError,
|
||||
inflight_requests: UpDownCounter<i64>,
|
||||
request_counter: Counter<u64>,
|
||||
request_histogram: Histogram<f64>,
|
||||
static_attributes: Vec<KeyValue>,
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: service,
|
||||
@ -65,12 +78,14 @@ impl<ExtractContext, InjectContext, MakeSpanBuilder, OnResponse, OnError, S>
|
||||
extract_context,
|
||||
inject_context,
|
||||
make_span_builder,
|
||||
make_metrics_labels,
|
||||
on_response,
|
||||
on_error,
|
||||
|
||||
inflight_requests,
|
||||
request_counter,
|
||||
request_histogram,
|
||||
static_attributes,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -98,8 +113,25 @@ impl Drop for InFlightGuard {
|
||||
}
|
||||
}
|
||||
|
||||
impl<Req, S, ExtractContextT, InjectContextT, MakeSpanBuilderT, OnResponseT, OnErrorT> Service<Req>
|
||||
for Trace<ExtractContextT, InjectContextT, MakeSpanBuilderT, OnResponseT, OnErrorT, S>
|
||||
impl<
|
||||
Req,
|
||||
S,
|
||||
ExtractContextT,
|
||||
InjectContextT,
|
||||
MakeSpanBuilderT,
|
||||
MakeMetricsLabelsT,
|
||||
OnResponseT,
|
||||
OnErrorT,
|
||||
> Service<Req>
|
||||
for Trace<
|
||||
ExtractContextT,
|
||||
InjectContextT,
|
||||
MakeSpanBuilderT,
|
||||
MakeMetricsLabelsT,
|
||||
OnResponseT,
|
||||
OnErrorT,
|
||||
S,
|
||||
>
|
||||
where
|
||||
ExtractContextT: ExtractContext<Req> + Send,
|
||||
InjectContextT: InjectContext<Req> + Send,
|
||||
@ -107,6 +139,7 @@ where
|
||||
OnResponseT: OnResponse<S::Response> + Send + Clone + 'static,
|
||||
OnErrorT: OnError<S::Error> + Send + Clone + 'static,
|
||||
MakeSpanBuilderT: MakeSpanBuilder<Req> + Send,
|
||||
MakeMetricsLabelsT: MakeMetricsLabels<Req> + Send,
|
||||
S::Future: Send + 'static,
|
||||
{
|
||||
type Response = S::Response;
|
||||
@ -123,7 +156,15 @@ where
|
||||
let start_time = SystemTime::now();
|
||||
|
||||
let cx = self.extract_context.extract_context(&request);
|
||||
let (span_builder, mut metrics_labels) = self.make_span_builder.make_span_builder(&request);
|
||||
let mut span_builder = self.make_span_builder.make_span_builder(&request);
|
||||
let mut metrics_labels = self.make_metrics_labels.make_metrics_labels(&request);
|
||||
|
||||
// Add the static attributes to the metrics and the span
|
||||
metrics_labels.extend_from_slice(&self.static_attributes[..]);
|
||||
let mut span_attributes = span_builder.attributes.unwrap_or_default();
|
||||
span_attributes.extend(self.static_attributes.iter().cloned());
|
||||
span_builder.attributes = Some(span_attributes);
|
||||
|
||||
let span = span_builder.start_with_context(self.tracer.as_ref(), &cx);
|
||||
|
||||
let cx = cx.with_span(span);
|
||||
@ -144,11 +185,10 @@ where
|
||||
let _guard = guard;
|
||||
|
||||
let span = cx.span();
|
||||
let extra_labels = match r {
|
||||
Ok(response) => on_response.on_response(&span, response),
|
||||
Err(err) => on_error.on_error(&span, err),
|
||||
match r {
|
||||
Ok(response) => on_response.on_response(&span, &mut metrics_labels, response),
|
||||
Err(err) => on_error.on_error(&span, &mut metrics_labels, err),
|
||||
};
|
||||
metrics_labels.extend_from_slice(&extra_labels);
|
||||
|
||||
request_counter.add(&cx, 1, &metrics_labels);
|
||||
request_histogram.record(
|
||||
|
45
crates/http/src/layers/otel/utils.rs
Normal file
45
crates/http/src/layers/otel/utils.rs
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use http::{Method, Version};
|
||||
|
||||
#[inline]
|
||||
pub(super) fn http_method_str(method: &Method) -> Cow<'static, str> {
|
||||
match method {
|
||||
&Method::OPTIONS => "OPTIONS".into(),
|
||||
&Method::GET => "GET".into(),
|
||||
&Method::POST => "POST".into(),
|
||||
&Method::PUT => "PUT".into(),
|
||||
&Method::DELETE => "DELETE".into(),
|
||||
&Method::HEAD => "HEAD".into(),
|
||||
&Method::TRACE => "TRACE".into(),
|
||||
&Method::CONNECT => "CONNECT".into(),
|
||||
&Method::PATCH => "PATCH".into(),
|
||||
other => other.to_string().into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn http_flavor(version: Version) -> Cow<'static, str> {
|
||||
match version {
|
||||
Version::HTTP_09 => "0.9".into(),
|
||||
Version::HTTP_10 => "1.0".into(),
|
||||
Version::HTTP_11 => "1.1".into(),
|
||||
Version::HTTP_2 => "2.0".into(),
|
||||
Version::HTTP_3 => "3.0".into(),
|
||||
other => format!("{:?}", other).into(),
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use http::{Request, Response};
|
||||
use opentelemetry::KeyValue;
|
||||
use tower::{util::BoxCloneService, Layer, Service, ServiceBuilder, ServiceExt};
|
||||
use tower_http::{compression::CompressionBody, ServiceBuilderExt};
|
||||
|
||||
@ -51,10 +52,17 @@ where
|
||||
let builder = ServiceBuilder::new().compression();
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
let builder = builder.layer(TraceLayer::axum());
|
||||
let mut trace_layer = TraceLayer::axum();
|
||||
|
||||
#[cfg(not(feature = "axum"))]
|
||||
let builder = builder.layer(TraceLayer::http_server());
|
||||
let mut trace_layer = TraceLayer::http_server();
|
||||
|
||||
if let Some(name) = &self.listener_name {
|
||||
trace_layer =
|
||||
trace_layer.with_static_attribute(KeyValue::new("listener", name.clone()));
|
||||
}
|
||||
|
||||
let builder = builder.layer(trace_layer);
|
||||
|
||||
builder.service(inner).boxed_clone()
|
||||
}
|
||||
|
Reference in New Issue
Block a user