You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-29 22:01:14 +03:00
Replace the OTEL-based tracing layer with tracing
based layers
This commit is contained in:
@ -18,8 +18,6 @@ hyper = "0.14.25"
|
||||
hyper-rustls = { version = "0.23.2", features = ["http1", "http2"], default-features = false, optional = true }
|
||||
once_cell = "1.17.1"
|
||||
opentelemetry = "0.18.0"
|
||||
opentelemetry-http = "0.7.0"
|
||||
opentelemetry-semantic-conventions = "0.10.0"
|
||||
rustls = { version = "0.20.8", optional = true }
|
||||
rustls-native-certs = { version = "0.6.2", optional = true }
|
||||
serde = "1.0.158"
|
||||
@ -34,6 +32,8 @@ tracing-opentelemetry = "0.18.0"
|
||||
webpki = { version = "0.22.0", optional = true }
|
||||
webpki-roots = { version = "0.22.6", optional = true }
|
||||
|
||||
mas-tower = { path = "../tower" }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.69"
|
||||
serde = { version = "1.0.158", features = ["derive"] }
|
||||
|
@ -15,14 +15,20 @@
|
||||
use std::convert::Infallible;
|
||||
|
||||
use hyper::{
|
||||
client::{connect::dns::GaiResolver, HttpConnector},
|
||||
client::{
|
||||
connect::dns::{GaiResolver, Name},
|
||||
HttpConnector,
|
||||
},
|
||||
Client,
|
||||
};
|
||||
use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder};
|
||||
use mas_tower::{
|
||||
DurationRecorderLayer, DurationRecorderService, FnWrapper, InFlightCounterLayer,
|
||||
InFlightCounterService, TraceLayer, TraceService,
|
||||
};
|
||||
use thiserror::Error;
|
||||
use tower::Layer;
|
||||
|
||||
use crate::layers::otel::{TraceDns, TraceLayer};
|
||||
use tracing::Span;
|
||||
|
||||
#[cfg(all(not(feature = "webpki-roots"), not(feature = "native-roots")))]
|
||||
compile_error!("enabling the 'client' feature requires also enabling the 'webpki-roots' or the 'native-roots' features");
|
||||
@ -165,8 +171,10 @@ where
|
||||
Ok(Client::builder().build(https))
|
||||
}
|
||||
|
||||
pub type TraceResolver<S> =
|
||||
InFlightCounterService<DurationRecorderService<TraceService<S, FnWrapper<fn(&Name) -> Span>>>>;
|
||||
pub type UntracedConnector = HttpsConnector<HttpConnector<GaiResolver>>;
|
||||
pub type TracedConnector = HttpsConnector<HttpConnector<TraceDns<GaiResolver>>>;
|
||||
pub type TracedConnector = HttpsConnector<HttpConnector<TraceResolver<GaiResolver>>>;
|
||||
|
||||
/// Create a traced HTTP and HTTPS connector
|
||||
///
|
||||
@ -176,8 +184,20 @@ pub type TracedConnector = HttpsConnector<HttpConnector<TraceDns<GaiResolver>>>;
|
||||
pub async fn make_traced_connector() -> Result<TracedConnector, ClientInitError>
|
||||
where
|
||||
{
|
||||
// Trace DNS requests
|
||||
let resolver = TraceLayer::dns().layer(GaiResolver::new());
|
||||
let in_flight_counter = InFlightCounterLayer::new("dns.resolve.active_requests");
|
||||
let duration_recorder = DurationRecorderLayer::new("dns.resolve.duration");
|
||||
let trace_layer = TraceLayer::from_fn(
|
||||
(|request: &Name| {
|
||||
tracing::info_span!(
|
||||
"dns.resolve",
|
||||
"otel.kind" = "client",
|
||||
"net.host.name" = %request,
|
||||
)
|
||||
}) as fn(&Name) -> Span,
|
||||
);
|
||||
|
||||
let resolver = (in_flight_counter, duration_recorder, trace_layer).layer(GaiResolver::new());
|
||||
|
||||
let tls_config = make_tls_config().await?;
|
||||
Ok(make_connector(resolver, tls_config))
|
||||
}
|
||||
|
@ -14,7 +14,8 @@
|
||||
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use http::{header::USER_AGENT, HeaderValue};
|
||||
use http::{header::USER_AGENT, HeaderValue, Request};
|
||||
use mas_tower::{MakeSpan, TraceContextLayer, TraceContextService, TraceLayer, TraceService};
|
||||
use tokio::sync::Semaphore;
|
||||
use tower::{
|
||||
limit::{ConcurrencyLimit, GlobalConcurrencyLimitLayer},
|
||||
@ -26,42 +27,61 @@ use tower_http::{
|
||||
timeout::{Timeout, TimeoutLayer},
|
||||
};
|
||||
|
||||
use super::otel::TraceLayer;
|
||||
use crate::otel::{TraceHttpClient, TraceHttpClientLayer};
|
||||
|
||||
pub type ClientService<S> = SetRequestHeader<
|
||||
TraceHttpClient<ConcurrencyLimit<FollowRedirect<TraceHttpClient<Timeout<S>>>>>,
|
||||
ConcurrencyLimit<
|
||||
FollowRedirect<TraceService<TraceContextService<Timeout<S>>, MakeSpanForRequest>>,
|
||||
>,
|
||||
HeaderValue,
|
||||
>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MakeSpanForRequest;
|
||||
|
||||
impl<B> MakeSpan<Request<B>> for MakeSpanForRequest {
|
||||
fn make_span(&self, request: &Request<B>) -> tracing::Span {
|
||||
// TODO: better attributes
|
||||
tracing::info_span!(
|
||||
"http.client.request",
|
||||
"http.method" = %request.method(),
|
||||
"http.uri" = %request.uri(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ClientLayer {
|
||||
user_agent_layer: SetRequestHeaderLayer<HeaderValue>,
|
||||
outer_trace_layer: TraceHttpClientLayer,
|
||||
concurrency_limit_layer: GlobalConcurrencyLimitLayer,
|
||||
follow_redirect_layer: FollowRedirectLayer,
|
||||
inner_trace_layer: TraceHttpClientLayer,
|
||||
trace_layer: TraceLayer<MakeSpanForRequest>,
|
||||
trace_context_layer: TraceContextLayer,
|
||||
timeout_layer: TimeoutLayer,
|
||||
}
|
||||
|
||||
impl Default for ClientLayer {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientLayer {
|
||||
#[must_use]
|
||||
pub fn new(operation: &'static str) -> Self {
|
||||
pub fn new() -> Self {
|
||||
let semaphore = Arc::new(Semaphore::new(10));
|
||||
Self::with_semaphore(operation, semaphore)
|
||||
Self::with_semaphore(semaphore)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_semaphore(operation: &'static str, semaphore: Arc<Semaphore>) -> Self {
|
||||
pub fn with_semaphore(semaphore: Arc<Semaphore>) -> Self {
|
||||
Self {
|
||||
user_agent_layer: SetRequestHeaderLayer::overriding(
|
||||
USER_AGENT,
|
||||
HeaderValue::from_static("matrix-authentication-service/0.0.1"),
|
||||
),
|
||||
outer_trace_layer: TraceLayer::http_client(operation),
|
||||
concurrency_limit_layer: GlobalConcurrencyLimitLayer::with_semaphore(semaphore),
|
||||
follow_redirect_layer: FollowRedirectLayer::new(),
|
||||
inner_trace_layer: TraceLayer::inner_http_client(),
|
||||
trace_layer: TraceLayer::new(MakeSpanForRequest),
|
||||
trace_context_layer: TraceContextLayer::new(),
|
||||
timeout_layer: TimeoutLayer::new(Duration::from_secs(10)),
|
||||
}
|
||||
}
|
||||
@ -76,10 +96,10 @@ where
|
||||
fn layer(&self, inner: S) -> Self::Service {
|
||||
(
|
||||
&self.user_agent_layer,
|
||||
&self.outer_trace_layer,
|
||||
&self.concurrency_limit_layer,
|
||||
&self.follow_redirect_layer,
|
||||
&self.inner_trace_layer,
|
||||
&self.trace_layer,
|
||||
&self.trace_context_layer,
|
||||
&self.timeout_layer,
|
||||
)
|
||||
.layer(inner)
|
||||
|
@ -18,7 +18,6 @@ pub mod catch_http_codes;
|
||||
pub mod form_urlencoded_request;
|
||||
pub mod json_request;
|
||||
pub mod json_response;
|
||||
pub mod otel;
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
pub(crate) mod client;
|
||||
|
@ -1,51 +0,0 @@
|
||||
// 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 http::Request;
|
||||
use opentelemetry::{trace::TraceContextExt, Context};
|
||||
use opentelemetry_http::HeaderExtractor;
|
||||
|
||||
pub trait ExtractContext<R> {
|
||||
fn extract_context(&self, request: &R) -> Context;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct DefaultExtractContext;
|
||||
|
||||
impl<T> ExtractContext<T> for DefaultExtractContext {
|
||||
fn extract_context(&self, _request: &T) -> Context {
|
||||
Context::current()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct ExtractFromHttpRequest;
|
||||
|
||||
impl<T> ExtractContext<Request<T>> for ExtractFromHttpRequest {
|
||||
fn extract_context(&self, request: &Request<T>) -> Context {
|
||||
let headers = request.headers();
|
||||
let extractor = HeaderExtractor(headers);
|
||||
let parent_cx = Context::current();
|
||||
|
||||
let cx = opentelemetry::global::get_text_map_propagator(|propagator| {
|
||||
propagator.extract_with_context(&parent_cx, &extractor)
|
||||
});
|
||||
|
||||
if cx.span().span_context().is_remote() {
|
||||
cx
|
||||
} else {
|
||||
parent_cx
|
||||
}
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
// 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 http::Request;
|
||||
use opentelemetry::Context;
|
||||
use opentelemetry_http::HeaderInjector;
|
||||
|
||||
pub trait InjectContext<R> {
|
||||
type Output;
|
||||
|
||||
fn inject_context(&self, cx: &Context, request: R) -> Self::Output;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct DefaultInjectContext;
|
||||
|
||||
impl<R> InjectContext<R> for DefaultInjectContext {
|
||||
type Output = R;
|
||||
|
||||
fn inject_context(&self, _cx: &Context, request: R) -> Self::Output {
|
||||
request
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct InjectInHttpRequest;
|
||||
|
||||
impl<T> InjectContext<Request<T>> for InjectInHttpRequest {
|
||||
type Output = Request<T>;
|
||||
|
||||
fn inject_context(&self, cx: &Context, mut request: Request<T>) -> Self::Output {
|
||||
let headers = request.headers_mut();
|
||||
let mut injector = HeaderInjector(headers);
|
||||
|
||||
opentelemetry::global::get_text_map_propagator(|propagator| {
|
||||
propagator.inject_context(cx, &mut injector);
|
||||
});
|
||||
|
||||
request
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "aws-sdk")]
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct InjectInAwsRequest;
|
||||
|
||||
#[cfg(feature = "aws-sdk")]
|
||||
impl InjectContext<aws_smithy_http::operation::Request> for InjectInAwsRequest {
|
||||
type Output = aws_smithy_http::operation::Request;
|
||||
|
||||
fn inject_context(
|
||||
&self,
|
||||
cx: &Context,
|
||||
mut request: aws_smithy_http::operation::Request,
|
||||
) -> Self::Output {
|
||||
let headers = request.http_mut().headers_mut();
|
||||
let mut injector = HeaderInjector(headers);
|
||||
|
||||
opentelemetry::global::get_text_map_propagator(|propagator| {
|
||||
propagator.inject_context(cx, &mut injector);
|
||||
});
|
||||
|
||||
request
|
||||
}
|
||||
}
|
@ -1,334 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use opentelemetry::{
|
||||
metrics::{Counter, Histogram, UpDownCounter},
|
||||
KeyValue,
|
||||
};
|
||||
use tower::Layer;
|
||||
|
||||
use super::{
|
||||
extract_context::DefaultExtractContext, inject_context::DefaultInjectContext,
|
||||
make_metrics_labels::DefaultMakeMetricsLabels, make_span_builder::DefaultMakeSpanBuilder,
|
||||
on_error::DefaultOnError, on_response::DefaultOnResponse, service::Trace,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TraceLayer<
|
||||
ExtractContext = DefaultExtractContext,
|
||||
InjectContext = DefaultInjectContext,
|
||||
MakeSpanBuilder = DefaultMakeSpanBuilder,
|
||||
MakeMetricsLabels = DefaultMakeMetricsLabels,
|
||||
OnResponse = DefaultOnResponse,
|
||||
OnError = DefaultOnError,
|
||||
> {
|
||||
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 Default for TraceLayer {
|
||||
fn default() -> Self {
|
||||
Self::with_namespace("http")
|
||||
}
|
||||
}
|
||||
|
||||
impl TraceLayer {
|
||||
#[must_use]
|
||||
pub fn with_namespace(namespace: &'static str) -> Self {
|
||||
let tracer = Arc::new(opentelemetry::global::tracer("mas-http"));
|
||||
let meter = opentelemetry::global::meter("mas-http");
|
||||
|
||||
let inflight_requests = meter
|
||||
.i64_up_down_counter(format!("{namespace}.inflight_requests"))
|
||||
.with_description("Number of in-flight requests")
|
||||
.init();
|
||||
let request_counter = meter
|
||||
.u64_counter(format!("{namespace}.requests_total"))
|
||||
.with_description("Total number of requests made.")
|
||||
.init();
|
||||
let request_histogram = meter
|
||||
.f64_histogram(format!("{namespace}.request_duration_seconds"))
|
||||
.with_description("The request latencies in seconds.")
|
||||
.init();
|
||||
|
||||
Self::new(
|
||||
tracer,
|
||||
inflight_requests,
|
||||
request_counter,
|
||||
request_histogram,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<ExtractContext, InjectContext, MakeSpanBuilder, MakeMetricsLabels, OnResponse, OnError>
|
||||
TraceLayer<
|
||||
ExtractContext,
|
||||
InjectContext,
|
||||
MakeSpanBuilder,
|
||||
MakeMetricsLabels,
|
||||
OnResponse,
|
||||
OnError,
|
||||
>
|
||||
{
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
tracer: Arc<opentelemetry::global::BoxedTracer>,
|
||||
inflight_requests: UpDownCounter<i64>,
|
||||
request_counter: Counter<u64>,
|
||||
request_histogram: Histogram<f64>,
|
||||
) -> Self
|
||||
where
|
||||
ExtractContext: Default,
|
||||
InjectContext: Default,
|
||||
MakeSpanBuilder: Default,
|
||||
MakeMetricsLabels: Default,
|
||||
OnResponse: Default,
|
||||
OnError: Default,
|
||||
{
|
||||
Self {
|
||||
tracer,
|
||||
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,
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn inject_context<NewInjectContext>(
|
||||
self,
|
||||
inject_context: NewInjectContext,
|
||||
) -> 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,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn make_span_builder<NewMakeSpanBuilder>(
|
||||
self,
|
||||
make_span_builder: NewMakeSpanBuilder,
|
||||
) -> 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,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn on_response<NewOnResponse>(
|
||||
self,
|
||||
on_response: NewOnResponse,
|
||||
) -> 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,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn on_error<NewOnError>(
|
||||
self,
|
||||
on_error: 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, 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,
|
||||
MakeMetricsLabels,
|
||||
OnResponse,
|
||||
OnError,
|
||||
S,
|
||||
>;
|
||||
|
||||
fn layer(&self, inner: S) -> Self::Service {
|
||||
Trace::new(
|
||||
inner,
|
||||
self.tracer.clone(),
|
||||
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(),
|
||||
)
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
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),
|
||||
]
|
||||
}
|
||||
}
|
@ -1,235 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#[cfg(any(feature = "axum", feature = "aws-sdk"))]
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
use axum::extract::{ConnectInfo, MatchedPath};
|
||||
use headers::{ContentLength, HeaderMapExt, Host, UserAgent};
|
||||
use http::Request;
|
||||
#[cfg(feature = "client")]
|
||||
use hyper::client::connect::dns::Name;
|
||||
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;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct DefaultMakeSpanBuilder {
|
||||
operation: &'static str,
|
||||
}
|
||||
|
||||
impl DefaultMakeSpanBuilder {
|
||||
#[must_use]
|
||||
pub fn new(operation: &'static str) -> Self {
|
||||
Self { operation }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DefaultMakeSpanBuilder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
operation: "service",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> MakeSpanBuilder<R> for DefaultMakeSpanBuilder {
|
||||
fn make_span_builder(&self, _request: &R) -> SpanBuilder {
|
||||
SpanBuilder::from_name(self.operation)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SpanFromHttpRequest {
|
||||
operation: &'static str,
|
||||
span_kind: SpanKind,
|
||||
}
|
||||
|
||||
impl SpanFromHttpRequest {
|
||||
#[must_use]
|
||||
pub fn server() -> Self {
|
||||
Self {
|
||||
operation: "http-server",
|
||||
span_kind: SpanKind::Server,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn inner_client() -> Self {
|
||||
Self {
|
||||
operation: "http-client",
|
||||
span_kind: SpanKind::Client,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn client(operation: &'static str) -> Self {
|
||||
Self {
|
||||
operation,
|
||||
span_kind: SpanKind::Client,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> MakeSpanBuilder<Request<B>> for SpanFromHttpRequest {
|
||||
fn make_span_builder(&self, request: &Request<B>) -> SpanBuilder {
|
||||
let mut attributes = vec![
|
||||
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()),
|
||||
];
|
||||
|
||||
let headers = request.headers();
|
||||
|
||||
if let Some(host) = headers.typed_get::<Host>() {
|
||||
attributes.push(SC::HTTP_HOST.string(host.to_string()));
|
||||
}
|
||||
|
||||
if let Some(user_agent) = headers.typed_get::<UserAgent>() {
|
||||
attributes.push(SC::HTTP_USER_AGENT.string(user_agent.to_string()));
|
||||
}
|
||||
|
||||
if let Some(ContentLength(content_length)) = headers.typed_get() {
|
||||
if let Ok(content_length) = content_length.try_into() {
|
||||
attributes.push(SC::HTTP_REQUEST_CONTENT_LENGTH.i64(content_length));
|
||||
}
|
||||
}
|
||||
|
||||
SpanBuilder::from_name(self.operation)
|
||||
.with_kind(self.span_kind.clone())
|
||||
.with_attributes(attributes)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SpanFromAxumRequest;
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
impl<B> MakeSpanBuilder<Request<B>> for SpanFromAxumRequest {
|
||||
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![
|
||||
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();
|
||||
|
||||
if let Some(host) = headers.typed_get::<Host>() {
|
||||
attributes.push(SC::HTTP_HOST.string(host.to_string()));
|
||||
}
|
||||
|
||||
if let Some(user_agent) = headers.typed_get::<UserAgent>() {
|
||||
attributes.push(SC::HTTP_USER_AGENT.string(user_agent.to_string()));
|
||||
}
|
||||
|
||||
if let Some(ContentLength(content_length)) = headers.typed_get() {
|
||||
if let Ok(content_length) = content_length.try_into() {
|
||||
attributes.push(SC::HTTP_REQUEST_CONTENT_LENGTH.i64(content_length));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ConnectInfo(addr)) = request
|
||||
.extensions()
|
||||
.get::<ConnectInfo<std::net::SocketAddr>>()
|
||||
{
|
||||
attributes.push(SC::NET_TRANSPORT.string("ip_tcp"));
|
||||
attributes.push(SC::NET_PEER_IP.string(addr.ip().to_string()));
|
||||
attributes.push(SC::NET_PEER_PORT.i64(addr.port().into()));
|
||||
}
|
||||
|
||||
SpanBuilder::from_name(name)
|
||||
.with_kind(SpanKind::Server)
|
||||
.with_attributes(attributes)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct SpanFromDnsRequest;
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
impl MakeSpanBuilder<Name> for SpanFromDnsRequest {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "aws-sdk")]
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct SpanFromAwsRequest;
|
||||
|
||||
#[cfg(feature = "aws-sdk")]
|
||||
impl MakeSpanBuilder<aws_smithy_http::operation::Request> for SpanFromAwsRequest {
|
||||
fn make_span_builder(&self, request: &aws_smithy_http::operation::Request) -> SpanBuilder {
|
||||
let properties = request.properties();
|
||||
let request = request.http();
|
||||
let mut attributes = vec![
|
||||
SC::RPC_SYSTEM.string("aws-api"),
|
||||
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()),
|
||||
];
|
||||
|
||||
let mut name = Cow::Borrowed("aws_sdk");
|
||||
if let Some(metadata) = properties.get::<aws_smithy_http::operation::Metadata>() {
|
||||
attributes.push(SC::RPC_SERVICE.string(metadata.service().to_owned()));
|
||||
attributes.push(SC::RPC_METHOD.string(metadata.name().to_owned()));
|
||||
name = Cow::Owned(metadata.name().to_owned());
|
||||
} else if let Some(service) = properties.get::<aws_types::SigningService>() {
|
||||
attributes.push(SC::RPC_SERVICE.string(service.as_ref().to_owned()));
|
||||
}
|
||||
|
||||
let headers = request.headers();
|
||||
|
||||
if let Some(host) = headers.typed_get::<Host>() {
|
||||
attributes.push(SC::HTTP_HOST.string(host.to_string()));
|
||||
}
|
||||
|
||||
if let Some(user_agent) = headers.typed_get::<UserAgent>() {
|
||||
attributes.push(SC::HTTP_USER_AGENT.string(user_agent.to_string()));
|
||||
}
|
||||
|
||||
if let Some(ContentLength(content_length)) = headers.typed_get() {
|
||||
if let Ok(content_length) = content_length.try_into() {
|
||||
attributes.push(SC::HTTP_REQUEST_CONTENT_LENGTH.i64(content_length));
|
||||
}
|
||||
}
|
||||
|
||||
SpanBuilder::from_name(name)
|
||||
.with_kind(SpanKind::Client)
|
||||
.with_attributes(attributes)
|
||||
}
|
||||
}
|
@ -1,197 +0,0 @@
|
||||
// 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.
|
||||
|
||||
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,
|
||||
>;
|
||||
|
||||
pub type TraceHttpServer<S> = Trace<
|
||||
ExtractFromHttpRequest,
|
||||
DefaultInjectContext,
|
||||
SpanFromHttpRequest,
|
||||
MetricsLabelsFromHttpRequest,
|
||||
OnHttpResponse,
|
||||
DefaultOnError,
|
||||
S,
|
||||
>;
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
pub type TraceAxumServerLayer = TraceLayer<
|
||||
ExtractFromHttpRequest,
|
||||
DefaultInjectContext,
|
||||
SpanFromAxumRequest,
|
||||
MetricsLabelsFromAxumRequest,
|
||||
OnHttpResponse,
|
||||
DefaultOnError,
|
||||
>;
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
pub type TraceAxumServer<S> = Trace<
|
||||
ExtractFromHttpRequest,
|
||||
DefaultInjectContext,
|
||||
SpanFromAxumRequest,
|
||||
MetricsLabelsFromAxumRequest,
|
||||
OnHttpResponse,
|
||||
DefaultOnError,
|
||||
S,
|
||||
>;
|
||||
|
||||
pub type TraceHttpClientLayer = TraceLayer<
|
||||
DefaultExtractContext,
|
||||
InjectInHttpRequest,
|
||||
SpanFromHttpRequest,
|
||||
MetricsLabelsFromHttpRequest,
|
||||
OnHttpResponse,
|
||||
DefaultOnError,
|
||||
>;
|
||||
|
||||
pub type TraceHttpClient<S> = Trace<
|
||||
DefaultExtractContext,
|
||||
InjectInHttpRequest,
|
||||
SpanFromHttpRequest,
|
||||
MetricsLabelsFromHttpRequest,
|
||||
OnHttpResponse,
|
||||
DefaultOnError,
|
||||
S,
|
||||
>;
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
pub type TraceDnsLayer = TraceLayer<
|
||||
DefaultExtractContext,
|
||||
DefaultInjectContext,
|
||||
SpanFromDnsRequest,
|
||||
DefaultMakeMetricsLabels,
|
||||
DefaultOnResponse,
|
||||
DefaultOnError,
|
||||
>;
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
pub type TraceDns<S> = Trace<
|
||||
DefaultExtractContext,
|
||||
DefaultInjectContext,
|
||||
SpanFromDnsRequest,
|
||||
DefaultMakeMetricsLabels,
|
||||
DefaultOnResponse,
|
||||
DefaultOnError,
|
||||
S,
|
||||
>;
|
||||
|
||||
#[cfg(feature = "aws-sdk")]
|
||||
pub type TraceAwsSdkClientLayer = TraceLayer<
|
||||
DefaultExtractContext,
|
||||
InjectInAwsRequest,
|
||||
SpanFromAwsRequest,
|
||||
DefaultMakeMetricsLabels,
|
||||
OnAwsResponse,
|
||||
DebugOnError,
|
||||
>;
|
||||
|
||||
#[cfg(feature = "aws-sdk")]
|
||||
pub type TraceAwsSdkClient<S> = Trace<
|
||||
DefaultExtractContext,
|
||||
InjectInAwsRequest,
|
||||
SpanFromAwsRequest,
|
||||
DefaultMakeMetricsLabels,
|
||||
OnAwsResponse,
|
||||
DebugOnError,
|
||||
S,
|
||||
>;
|
||||
|
||||
impl TraceHttpServerLayer {
|
||||
#[must_use]
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
impl TraceAxumServerLayer {
|
||||
#[must_use]
|
||||
pub fn axum() -> Self {
|
||||
TraceLayer::with_namespace("http_server")
|
||||
.make_span_builder(SpanFromAxumRequest)
|
||||
.make_metrics_labels(MetricsLabelsFromAxumRequest::default())
|
||||
.on_response(OnHttpResponse)
|
||||
.extract_context(ExtractFromHttpRequest)
|
||||
}
|
||||
}
|
||||
|
||||
impl TraceHttpClientLayer {
|
||||
#[must_use]
|
||||
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)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
impl TraceDnsLayer {
|
||||
#[must_use]
|
||||
pub fn dns() -> Self {
|
||||
TraceLayer::with_namespace("dns").make_span_builder(SpanFromDnsRequest)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "aws-sdk")]
|
||||
impl TraceAwsSdkClientLayer {
|
||||
#[must_use]
|
||||
pub fn aws_sdk() -> Self {
|
||||
TraceLayer::with_namespace("aws_sdk")
|
||||
.make_span_builder(SpanFromAwsRequest)
|
||||
.on_response(OnAwsResponse)
|
||||
.on_error(DebugOnError)
|
||||
.inject_context(InjectInAwsRequest)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
use self::make_metrics_labels::DefaultMakeMetricsLabels;
|
||||
#[cfg(feature = "axum")]
|
||||
use self::make_metrics_labels::MetricsLabelsFromAxumRequest;
|
||||
use self::make_metrics_labels::MetricsLabelsFromHttpRequest;
|
||||
pub use self::{
|
||||
extract_context::*, inject_context::*, layer::*, make_span_builder::*, on_error::*,
|
||||
on_response::*, service::*,
|
||||
};
|
@ -1,46 +0,0 @@
|
||||
// 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 opentelemetry::{trace::SpanRef, KeyValue};
|
||||
use opentelemetry_semantic_conventions::trace::EXCEPTION_MESSAGE;
|
||||
|
||||
pub trait OnError<E> {
|
||||
fn on_error(&self, span: &SpanRef<'_>, metrics_labels: &mut Vec<KeyValue>, err: &E);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct DefaultOnError;
|
||||
|
||||
impl<E> OnError<E> for DefaultOnError
|
||||
where
|
||||
E: std::fmt::Display,
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct DebugOnError;
|
||||
|
||||
impl<E> OnError<E> for DebugOnError
|
||||
where
|
||||
E: std::fmt::Debug,
|
||||
{
|
||||
fn on_error(&self, span: &SpanRef<'_>, _metrics_labels: &mut Vec<KeyValue>, err: &E) {
|
||||
let attributes = vec![EXCEPTION_MESSAGE.string(format!("{err:?}"))];
|
||||
span.add_event("exception".to_owned(), attributes);
|
||||
}
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
// 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 headers::{ContentLength, HeaderMapExt};
|
||||
use http::Response;
|
||||
#[cfg(feature = "client")]
|
||||
use hyper::client::connect::HttpInfo;
|
||||
use opentelemetry::{trace::SpanRef, KeyValue};
|
||||
use opentelemetry_semantic_conventions::trace as SC;
|
||||
|
||||
pub trait OnResponse<R> {
|
||||
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<'_>, _metrics_labels: &mut Vec<KeyValue>, _response: &R) {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct OnHttpResponse;
|
||||
|
||||
impl<B> OnResponse<Response<B>> for OnHttpResponse {
|
||||
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() {
|
||||
span.set_attribute(SC::HTTP_RESPONSE_CONTENT_LENGTH.i64(content_length));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
// Get local and remote address from hyper's HttpInfo injected by the
|
||||
// HttpConnector
|
||||
if let Some(info) = response.extensions().get::<HttpInfo>() {
|
||||
span.set_attribute(SC::NET_PEER_IP.string(info.remote_addr().ip().to_string()));
|
||||
span.set_attribute(SC::NET_PEER_PORT.i64(info.remote_addr().port().into()));
|
||||
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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "aws-sdk")]
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct OnAwsResponse;
|
||||
|
||||
#[cfg(feature = "aws-sdk")]
|
||||
impl OnResponse<aws_smithy_http::operation::Response> for OnAwsResponse {
|
||||
fn on_response(
|
||||
&self,
|
||||
span: &SpanRef<'_>,
|
||||
metrics_labels: &mut Vec<KeyValue>,
|
||||
response: &aws_smithy_http::operation::Response,
|
||||
) {
|
||||
let response = response.http();
|
||||
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() {
|
||||
span.set_attribute(SC::HTTP_RESPONSE_CONTENT_LENGTH.i64(content_length));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
// Get local and remote address from hyper's HttpInfo injected by the
|
||||
// HttpConnector
|
||||
if let Some(info) = response.extensions().get::<HttpInfo>() {
|
||||
span.set_attribute(SC::NET_PEER_IP.string(info.remote_addr().ip().to_string()));
|
||||
span.set_attribute(SC::NET_PEER_PORT.i64(info.remote_addr().port().into()));
|
||||
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()));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,208 +0,0 @@
|
||||
// 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::{sync::Arc, task::Poll, time::SystemTime};
|
||||
|
||||
use futures_util::{future::BoxFuture, FutureExt as _};
|
||||
use opentelemetry::{
|
||||
metrics::{Counter, Histogram, UpDownCounter},
|
||||
trace::{FutureExt as _, TraceContextExt},
|
||||
Context, KeyValue,
|
||||
};
|
||||
use tower::Service;
|
||||
|
||||
use super::{
|
||||
extract_context::ExtractContext, inject_context::InjectContext,
|
||||
make_metrics_labels::MakeMetricsLabels, make_span_builder::MakeSpanBuilder, on_error::OnError,
|
||||
on_response::OnResponse,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
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, MakeMetricsLabels, OnResponse, OnError, S>
|
||||
Trace<ExtractContext, InjectContext, MakeSpanBuilder, MakeMetricsLabels, OnResponse, OnError, S>
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
service: 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>,
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: service,
|
||||
tracer,
|
||||
|
||||
extract_context,
|
||||
inject_context,
|
||||
make_span_builder,
|
||||
make_metrics_labels,
|
||||
on_response,
|
||||
on_error,
|
||||
|
||||
inflight_requests,
|
||||
request_counter,
|
||||
request_histogram,
|
||||
static_attributes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct InFlightGuard {
|
||||
context: Context,
|
||||
meter: UpDownCounter<i64>,
|
||||
attributes: Vec<KeyValue>,
|
||||
}
|
||||
|
||||
impl InFlightGuard {
|
||||
fn increment(context: &Context, meter: &UpDownCounter<i64>, attributes: &[KeyValue]) -> Self {
|
||||
meter.add(context, 1, attributes);
|
||||
Self {
|
||||
context: context.clone(),
|
||||
meter: meter.clone(),
|
||||
attributes: attributes.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for InFlightGuard {
|
||||
fn drop(&mut self) {
|
||||
self.meter.add(&self.context, -1, &self.attributes);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
S: Service<InjectContextT::Output> + Send,
|
||||
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;
|
||||
type Error = S::Error;
|
||||
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.inner.poll_ready(cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, request: Req) -> Self::Future {
|
||||
let request_counter = self.request_counter.clone();
|
||||
let request_histogram = self.request_histogram.clone();
|
||||
let start_time = SystemTime::now();
|
||||
|
||||
let cx = self.extract_context.extract_context(&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);
|
||||
let request = self.inject_context.inject_context(&cx, request);
|
||||
|
||||
let guard = InFlightGuard::increment(&cx, &self.inflight_requests, &metrics_labels);
|
||||
|
||||
let on_response = self.on_response.clone();
|
||||
let on_error = self.on_error.clone();
|
||||
let attachment = cx.clone().attach();
|
||||
let ret = self
|
||||
.inner
|
||||
.call(request)
|
||||
.with_context(cx.clone())
|
||||
.inspect(move |r| {
|
||||
// This ensures the guard gets moved to the future. In case the future panics,
|
||||
// it will be dropped anyway, ensuring the in-flight counter stays accurate
|
||||
let _guard = guard;
|
||||
|
||||
let span = cx.span();
|
||||
match r {
|
||||
Ok(response) => on_response.on_response(&span, &mut metrics_labels, response),
|
||||
Err(err) => on_error.on_error(&span, &mut metrics_labels, err),
|
||||
};
|
||||
|
||||
request_counter.add(&cx, 1, &metrics_labels);
|
||||
request_histogram.record(
|
||||
&cx,
|
||||
start_time.elapsed().map_or(0.0, |d| d.as_secs_f64()),
|
||||
&metrics_labels,
|
||||
);
|
||||
|
||||
span.end();
|
||||
})
|
||||
.boxed();
|
||||
|
||||
drop(attachment);
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
// 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(),
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! [`tower`] layers and services to help building HTTP client and servers
|
||||
//! [`tower`] layers and services to help building HTTP client and servers
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(
|
||||
@ -47,7 +47,6 @@ pub use self::{
|
||||
form_urlencoded_request::{self, FormUrlencodedRequest, FormUrlencodedRequestLayer},
|
||||
json_request::{self, JsonRequest, JsonRequestLayer},
|
||||
json_response::{self, JsonResponse, JsonResponseLayer},
|
||||
otel,
|
||||
},
|
||||
service::{BoxCloneSyncService, HttpService},
|
||||
};
|
||||
|
Reference in New Issue
Block a user