1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-08-07 17:03:01 +03:00

Use OTEL semantic conventions constants for most attributes

This commit is contained in:
Quentin Gliech
2024-03-19 16:38:06 +01:00
parent 7e30daf83e
commit dde907758e
11 changed files with 72 additions and 57 deletions

2
Cargo.lock generated
View File

@@ -3239,6 +3239,7 @@ dependencies = [
"hyper-rustls", "hyper-rustls",
"mas-tower", "mas-tower",
"opentelemetry", "opentelemetry",
"opentelemetry-semantic-conventions",
"rustls 0.22.2", "rustls 0.22.2",
"rustls-platform-verifier", "rustls-platform-verifier",
"serde", "serde",
@@ -3538,6 +3539,7 @@ dependencies = [
"mas-jose", "mas-jose",
"mas-storage", "mas-storage",
"oauth2-types", "oauth2-types",
"opentelemetry-semantic-conventions",
"rand 0.8.5", "rand 0.8.5",
"rand_chacha 0.3.1", "rand_chacha 0.3.1",
"sea-query", "sea-query",

View File

@@ -42,7 +42,7 @@ use opentelemetry::{Key, KeyValue};
use opentelemetry_http::HeaderExtractor; use opentelemetry_http::HeaderExtractor;
use opentelemetry_semantic_conventions::trace::{ use opentelemetry_semantic_conventions::trace::{
HTTP_REQUEST_METHOD, HTTP_RESPONSE_STATUS_CODE, HTTP_ROUTE, NETWORK_PROTOCOL_NAME, HTTP_REQUEST_METHOD, HTTP_RESPONSE_STATUS_CODE, HTTP_ROUTE, NETWORK_PROTOCOL_NAME,
NETWORK_PROTOCOL_VERSION, URL_SCHEME, NETWORK_PROTOCOL_VERSION, URL_PATH, URL_QUERY, URL_SCHEME, USER_AGENT_ORIGINAL,
}; };
use rustls::ServerConfig; use rustls::ServerConfig;
use sentry_tower::{NewSentryLayer, SentryHttpLayer}; use sentry_tower::{NewSentryLayer, SentryHttpLayer};
@@ -120,30 +120,31 @@ fn make_http_span<B>(req: &Request<B>) -> Span {
"otel.kind" = "server", "otel.kind" = "server",
"otel.name" = span_name, "otel.name" = span_name,
"otel.status_code" = tracing::field::Empty, "otel.status_code" = tracing::field::Empty,
"network.protocol.name" = "http", { NETWORK_PROTOCOL_NAME } = "http",
"network.protocol.version" = otel_net_protocol_version(req), { NETWORK_PROTOCOL_VERSION } = otel_net_protocol_version(req),
"http.method" = method, { HTTP_REQUEST_METHOD } = method,
"http.route" = tracing::field::Empty, { HTTP_ROUTE } = tracing::field::Empty,
"http.response.status_code" = tracing::field::Empty, { HTTP_RESPONSE_STATUS_CODE } = tracing::field::Empty,
"url.path" = req.uri().path(), { URL_PATH } = req.uri().path(),
"url.query" = tracing::field::Empty, { URL_QUERY } = tracing::field::Empty,
"url.scheme" = otel_url_scheme(req), { URL_SCHEME } = otel_url_scheme(req),
"user_agent.original" = tracing::field::Empty, { USER_AGENT_ORIGINAL } = tracing::field::Empty,
); );
if let Some(route) = route.as_ref() { if let Some(route) = route.as_ref() {
span.record("http.route", route); span.record(HTTP_ROUTE, route);
} }
if let Some(query) = req.uri().query() { if let Some(query) = req.uri().query() {
span.record("url.query", query); span.record(URL_QUERY, query);
} }
if let Some(user_agent) = req.headers().get(USER_AGENT) { if let Some(user_agent) = req
span.record( .headers()
"user_agent.original", .get(USER_AGENT)
user_agent.to_str().unwrap_or("INVALID"), .and_then(|ua| ua.to_str().ok())
); {
span.record(USER_AGENT_ORIGINAL, user_agent);
} }
// Extract the parent span context from the request headers // Extract the parent span context from the request headers

View File

@@ -39,6 +39,7 @@ use mas_storage::{
BoxClock, BoxRepository, BoxRng, Clock, Repository, RepositoryError, SystemClock, BoxClock, BoxRepository, BoxRng, Clock, Repository, RepositoryError, SystemClock,
}; };
use mas_storage_pg::PgRepository; use mas_storage_pg::PgRepository;
use opentelemetry_semantic_conventions::trace::{GRAPHQL_DOCUMENT, GRAPHQL_OPERATION_NAME};
use rand::{thread_rng, SeedableRng}; use rand::{thread_rng, SeedableRng};
use rand_chacha::ChaChaRng; use rand_chacha::ChaChaRng;
use sqlx::PgPool; use sqlx::PgPool;
@@ -112,13 +113,13 @@ fn span_for_graphql_request(request: &async_graphql::Request) -> tracing::Span {
"GraphQL operation", "GraphQL operation",
"otel.name" = tracing::field::Empty, "otel.name" = tracing::field::Empty,
"otel.kind" = "server", "otel.kind" = "server",
"graphql.document" = request.query, { GRAPHQL_DOCUMENT } = request.query,
"graphql.operation.name" = tracing::field::Empty, { GRAPHQL_OPERATION_NAME } = tracing::field::Empty,
); );
if let Some(name) = &request.operation_name { if let Some(name) = &request.operation_name {
span.record("otel.name", name); span.record("otel.name", name);
span.record("graphql.operation.name", name); span.record(GRAPHQL_OPERATION_NAME, name);
} }
span span

View File

@@ -20,6 +20,7 @@ http-body.workspace = true
hyper.workspace = true hyper.workspace = true
hyper-rustls = { workspace = true, optional = true } hyper-rustls = { workspace = true, optional = true }
opentelemetry.workspace = true opentelemetry.workspace = true
opentelemetry-semantic-conventions.workspace = true
rustls = { workspace = true, optional = true } rustls = { workspace = true, optional = true }
rustls-platform-verifier = { workspace = true, optional = true } rustls-platform-verifier = { workspace = true, optional = true }
serde.workspace = true serde.workspace = true

View File

@@ -22,6 +22,7 @@ use mas_tower::{
DurationRecorderLayer, DurationRecorderService, FnWrapper, InFlightCounterLayer, DurationRecorderLayer, DurationRecorderService, FnWrapper, InFlightCounterLayer,
InFlightCounterService, TraceLayer, TraceService, InFlightCounterService, TraceLayer, TraceService,
}; };
use opentelemetry_semantic_conventions::trace::SERVER_ADDRESS;
use tower::Layer; use tower::Layer;
use tracing::Span; use tracing::Span;
@@ -54,9 +55,10 @@ where
let trace_layer = TraceLayer::from_fn( let trace_layer = TraceLayer::from_fn(
(|request: &Name| { (|request: &Name| {
tracing::info_span!( tracing::info_span!(
"dns.resolve", "dns.lookup",
"otel.kind" = "client", "otel.kind" = "client",
"net.host.name" = %request, { SERVER_ADDRESS } = %request,
) )
}) as fn(&Name) -> Span, }) as fn(&Name) -> Span,
); );

View File

@@ -23,6 +23,11 @@ use mas_tower::{
TraceLayer, TraceService, TraceLayer, TraceService,
}; };
use opentelemetry::KeyValue; use opentelemetry::KeyValue;
use opentelemetry_semantic_conventions::trace::{
CLIENT_ADDRESS, CLIENT_PORT, HTTP_REQUEST_BODY_SIZE, HTTP_REQUEST_METHOD,
HTTP_RESPONSE_BODY_SIZE, HTTP_RESPONSE_STATUS_CODE, NETWORK_PROTOCOL_NAME, NETWORK_TRANSPORT,
NETWORK_TYPE, SERVER_ADDRESS, SERVER_PORT, URL_FULL, USER_AGENT_ORIGINAL,
};
use tower::{ use tower::{
limit::{ConcurrencyLimit, GlobalConcurrencyLimitLayer}, limit::{ConcurrencyLimit, GlobalConcurrencyLimitLayer},
Layer, Layer,
@@ -69,27 +74,25 @@ impl<B> MakeSpan<Request<B>> for MakeSpanForRequest {
.typed_get::<UserAgent>() .typed_get::<UserAgent>()
.map(tracing::field::display); .map(tracing::field::display);
let content_length = headers.typed_get().map(|ContentLength(len)| len); let content_length = headers.typed_get().map(|ContentLength(len)| len);
let net_sock_peer_name = request.uri().host();
let category = self.category.unwrap_or("UNSET"); let category = self.category.unwrap_or("UNSET");
tracing::info_span!( tracing::info_span!(
"http.client.request", "http.client.request",
"otel.kind" = "client", "otel.kind" = "client",
"otel.status_code" = tracing::field::Empty, "otel.status_code" = tracing::field::Empty,
"http.method" = %request.method(), { HTTP_REQUEST_METHOD } = %request.method(),
"http.url" = %request.uri(), { URL_FULL } = %request.uri(),
"http.status_code" = tracing::field::Empty, { HTTP_RESPONSE_STATUS_CODE } = tracing::field::Empty,
"http.host" = host, { SERVER_ADDRESS } = host,
"http.request_content_length" = content_length, { HTTP_REQUEST_BODY_SIZE } = content_length,
"http.response_content_length" = tracing::field::Empty, { HTTP_RESPONSE_BODY_SIZE } = tracing::field::Empty,
"net.transport" = "ip_tcp", { NETWORK_TRANSPORT } = "tcp",
"net.sock.family" = tracing::field::Empty, { NETWORK_TYPE } = tracing::field::Empty,
"net.sock.peer.name" = net_sock_peer_name, { SERVER_ADDRESS } = tracing::field::Empty,
"net.sock.peer.addr" = tracing::field::Empty, { SERVER_PORT } = tracing::field::Empty,
"net.sock.peer.port" = tracing::field::Empty, { CLIENT_ADDRESS } = tracing::field::Empty,
"net.sock.host.addr" = tracing::field::Empty, { CLIENT_PORT } = tracing::field::Empty,
"net.sock.host.port" = tracing::field::Empty, { USER_AGENT_ORIGINAL } = user_agent,
"user_agent.original" = user_agent,
"rust.error" = tracing::field::Empty, "rust.error" = tracing::field::Empty,
"mas.category" = category, "mas.category" = category,
) )
@@ -102,22 +105,22 @@ pub struct EnrichSpanOnResponse;
impl<B> EnrichSpan<Response<B>> for EnrichSpanOnResponse { impl<B> EnrichSpan<Response<B>> for EnrichSpanOnResponse {
fn enrich_span(&self, span: &Span, response: &Response<B>) { fn enrich_span(&self, span: &Span, response: &Response<B>) {
span.record("otel.status_code", "OK"); span.record("otel.status_code", "OK");
span.record("http.status_code", response.status().as_u16()); span.record(HTTP_RESPONSE_STATUS_CODE, response.status().as_u16());
if let Some(ContentLength(content_length)) = response.headers().typed_get() { if let Some(ContentLength(content_length)) = response.headers().typed_get() {
span.record("http.response_content_length", content_length); span.record(HTTP_RESPONSE_BODY_SIZE, content_length);
} }
if let Some(http_info) = response.extensions().get::<HttpInfo>() { if let Some(http_info) = response.extensions().get::<HttpInfo>() {
let local = http_info.local_addr(); let local = http_info.local_addr();
let remote = http_info.remote_addr(); let remote = http_info.remote_addr();
let family = if local.is_ipv4() { "inet" } else { "inet6" }; let family = if local.is_ipv4() { "ipv4" } else { "ipv6" };
span.record("net.sock.family", family); span.record(NETWORK_TYPE, family);
span.record("net.sock.peer.addr", remote.ip().to_string()); span.record(CLIENT_ADDRESS, remote.ip().to_string());
span.record("net.sock.peer.port", remote.port()); span.record(CLIENT_PORT, remote.port());
span.record("net.sock.host.addr", local.ip().to_string()); span.record(SERVER_ADDRESS, local.ip().to_string());
span.record("net.sock.host.port", local.port()); span.record(SERVER_PORT, local.port());
} else { } else {
tracing::warn!("No HttpInfo injected in response extensions"); tracing::warn!("No HttpInfo injected in response extensions");
} }
@@ -149,8 +152,8 @@ where
type Iter<'a> = std::array::IntoIter<KeyValue, 3>; type Iter<'a> = std::array::IntoIter<KeyValue, 3>;
fn attributes<'a>(&'a self, t: &'a Request<B>) -> Self::Iter<'a> { fn attributes<'a>(&'a self, t: &'a Request<B>) -> Self::Iter<'a> {
[ [
KeyValue::new("http.request.method", t.method().as_str().to_owned()), KeyValue::new(HTTP_REQUEST_METHOD, t.method().as_str().to_owned()),
KeyValue::new("network.protocol.name", "http"), KeyValue::new(NETWORK_PROTOCOL_NAME, "http"),
KeyValue::new("mas.category", self.category.unwrap_or("UNSET")), KeyValue::new("mas.category", self.category.unwrap_or("UNSET")),
] ]
.into_iter() .into_iter()
@@ -167,7 +170,7 @@ where
type Iter<'a> = std::iter::Once<KeyValue>; type Iter<'a> = std::iter::Once<KeyValue>;
fn attributes<'a>(&'a self, t: &'a Response<B>) -> Self::Iter<'a> { fn attributes<'a>(&'a self, t: &'a Response<B>) -> Self::Iter<'a> {
std::iter::once(KeyValue::new( std::iter::once(KeyValue::new(
"http.response.status_code", HTTP_RESPONSE_STATUS_CODE,
i64::from(t.status().as_u16()), i64::from(t.status().as_u16()),
)) ))
} }

View File

@@ -22,6 +22,7 @@ serde_json.workspace = true
thiserror.workspace = true thiserror.workspace = true
tracing.workspace = true tracing.workspace = true
futures-util = "0.3.30" futures-util = "0.3.30"
opentelemetry-semantic-conventions.workspace = true
rand.workspace = true rand.workspace = true
rand_chacha = "0.3.1" rand_chacha = "0.3.1"

View File

@@ -31,6 +31,7 @@ use oauth2_types::{
requests::GrantType, requests::GrantType,
scope::{Scope, ScopeToken}, scope::{Scope, ScopeToken},
}; };
use opentelemetry_semantic_conventions::trace::DB_STATEMENT;
use rand::RngCore; use rand::RngCore;
use sqlx::PgConnection; use sqlx::PgConnection;
use tracing::{info_span, Instrument}; use tracing::{info_span, Instrument};
@@ -786,7 +787,7 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> {
{ {
let span = info_span!( let span = info_span!(
"db.oauth2_client.delete_by_id.authorization_grants", "db.oauth2_client.delete_by_id.authorization_grants",
db.statement = tracing::field::Empty, { DB_STATEMENT } = tracing::field::Empty,
); );
sqlx::query!( sqlx::query!(
@@ -806,7 +807,7 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> {
{ {
let span = info_span!( let span = info_span!(
"db.oauth2_client.delete_by_id.consents", "db.oauth2_client.delete_by_id.consents",
db.statement = tracing::field::Empty, { DB_STATEMENT } = tracing::field::Empty,
); );
sqlx::query!( sqlx::query!(
@@ -826,7 +827,7 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> {
{ {
let span = info_span!( let span = info_span!(
"db.oauth2_client.delete_by_id.access_tokens", "db.oauth2_client.delete_by_id.access_tokens",
db.statement = tracing::field::Empty, { DB_STATEMENT } = tracing::field::Empty,
); );
sqlx::query!( sqlx::query!(
@@ -849,7 +850,7 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> {
{ {
let span = info_span!( let span = info_span!(
"db.oauth2_client.delete_by_id.refresh_tokens", "db.oauth2_client.delete_by_id.refresh_tokens",
db.statement = tracing::field::Empty, { DB_STATEMENT } = tracing::field::Empty,
); );
sqlx::query!( sqlx::query!(
@@ -872,7 +873,7 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> {
{ {
let span = info_span!( let span = info_span!(
"db.oauth2_client.delete_by_id.sessions", "db.oauth2_client.delete_by_id.sessions",
db.statement = tracing::field::Empty, { DB_STATEMENT } = tracing::field::Empty,
); );
sqlx::query!( sqlx::query!(

View File

@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use opentelemetry_semantic_conventions::trace::DB_STATEMENT;
use tracing::Span; use tracing::Span;
/// An extension trait for [`sqlx::Execute`] that records the SQL statement as /// An extension trait for [`sqlx::Execute`] that records the SQL statement as
@@ -34,7 +35,7 @@ where
DB: sqlx::Database, DB: sqlx::Database,
{ {
fn record(self, span: &Span) -> Self { fn record(self, span: &Span) -> Self {
span.record("db.statement", self.sql()); span.record(DB_STATEMENT, self.sql());
self self
} }
} }

View File

@@ -21,6 +21,7 @@ use mas_storage::{
}, },
Clock, Page, Pagination, Clock, Page, Pagination,
}; };
use opentelemetry_semantic_conventions::trace::DB_STATEMENT;
use rand::RngCore; use rand::RngCore;
use sea_query::{enum_def, Expr, PostgresQueryBuilder, Query}; use sea_query::{enum_def, Expr, PostgresQueryBuilder, Query};
use sea_query_binder::SqlxBinder; use sea_query_binder::SqlxBinder;
@@ -333,7 +334,7 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<'
let span = info_span!( let span = info_span!(
"db.oauth2_client.delete_by_id.authorization_sessions", "db.oauth2_client.delete_by_id.authorization_sessions",
upstream_oauth_provider.id = %id, upstream_oauth_provider.id = %id,
db.statement = tracing::field::Empty, { DB_STATEMENT } = tracing::field::Empty,
); );
sqlx::query!( sqlx::query!(
r#" r#"
@@ -354,7 +355,7 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<'
let span = info_span!( let span = info_span!(
"db.oauth2_client.delete_by_id.links", "db.oauth2_client.delete_by_id.links",
upstream_oauth_provider.id = %id, upstream_oauth_provider.id = %id,
db.statement = tracing::field::Empty, { DB_STATEMENT } = tracing::field::Empty,
); );
sqlx::query!( sqlx::query!(
r#" r#"

View File

@@ -19,6 +19,7 @@ use mas_storage::{
user::{UserEmailFilter, UserEmailRepository}, user::{UserEmailFilter, UserEmailRepository},
Clock, Page, Pagination, Clock, Page, Pagination,
}; };
use opentelemetry_semantic_conventions::trace::DB_STATEMENT;
use rand::RngCore; use rand::RngCore;
use sea_query::{enum_def, Expr, PostgresQueryBuilder, Query}; use sea_query::{enum_def, Expr, PostgresQueryBuilder, Query};
use sea_query_binder::SqlxBinder; use sea_query_binder::SqlxBinder;
@@ -382,7 +383,7 @@ impl<'c> UserEmailRepository for PgUserEmailRepository<'c> {
async fn remove(&mut self, user_email: UserEmail) -> Result<(), Self::Error> { async fn remove(&mut self, user_email: UserEmail) -> Result<(), Self::Error> {
let span = info_span!( let span = info_span!(
"db.user_email.remove.codes", "db.user_email.remove.codes",
db.statement = tracing::field::Empty { DB_STATEMENT } = tracing::field::Empty
); );
sqlx::query!( sqlx::query!(
r#" r#"