Skip to content
This repository has been archived by the owner on Jul 26, 2024. It is now read-only.

Commit

Permalink
feat: add an optional trace header to metrics/logging (#146)
Browse files Browse the repository at this point in the history
* feat: add an optional trace header to metrics/logging

This adds a `trace_header` setting. This will add the value (if found in
the Headers) to the tags as `header.trace`.

(e.g. if a request has `X-Cloud-Trace-Context: abc123` this will add a
tag with `header.trace: abc123` to the outboud tags for logging and
metrics.)

Closes #145
  • Loading branch information
jrconlin authored Jun 10, 2021
1 parent 109ec11 commit 762bc39
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 10 deletions.
9 changes: 6 additions & 3 deletions src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ impl Drop for Metrics {

impl From<&HttpRequest> for Metrics {
fn from(req: &HttpRequest) -> Self {
let state = req.app_data::<Data<ServerState>>().expect("No State!");
let exts = req.extensions();
let def_tags = Tags::from(req.head());
let def_tags = Tags::from_head(req.head(), &state.settings);
let tags = exts.get::<Tags>().unwrap_or(&def_tags);
Metrics {
client: match req.app_data::<Data<ServerState>>() {
Expand Down Expand Up @@ -236,6 +237,7 @@ mod tests {
use std::collections::HashMap;

let mut rh = RequestHead::default();
let settings = Settings::default();
let path = "/1.5/42/storage/meta/global";
rh.uri = Uri::from_static(path);
rh.headers.insert(
Expand All @@ -245,7 +247,7 @@ mod tests {
),
);

let tags = Tags::from(&rh);
let tags = Tags::from_head(&rh, &settings);

let mut result = HashMap::<String, String>::new();
result.insert("ua.os.ver".to_owned(), "NT 10.0".to_owned());
Expand All @@ -262,14 +264,15 @@ mod tests {
use actix_web::http::{header, uri::Uri};

let mut rh = RequestHead::default();
let settings = Settings::default();
let path = "/1.5/42/storage/meta/global";
rh.uri = Uri::from_static(path);
rh.headers.insert(
header::USER_AGENT,
header::HeaderValue::from_static("Mozilla/5.0 (curl) Gecko/20100101 curl"),
);

let tags = Tags::from(&rh);
let tags = Tags::from_head(&rh, &settings);
assert!(!tags.tags.contains_key("ua.os.ver"));
println!("{:?}", tags);
}
Expand Down
20 changes: 19 additions & 1 deletion src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
use std::collections::HashMap;

use actix_web::{dev::ServiceRequest, web::Data, HttpRequest};
use config::{Config, ConfigError, Environment, File};
use serde::Deserialize;

use crate::adm::AdmSettings;
use crate::server::{img_storage::StorageSettings, location::Location};
use crate::server::{img_storage::StorageSettings, location::Location, ServerState};

static PREFIX: &str = "contile";

Expand Down Expand Up @@ -66,6 +67,8 @@ pub struct Settings {
pub fallback_location: String,
/// URL to the official documentation
pub documentation_url: String,
/// Operational trace header
pub trace_header: Option<String>,

// TODO: break these out into a PartnerSettings?
/// Adm partner ID (default: "demofeed")
Expand Down Expand Up @@ -116,6 +119,7 @@ impl Default for Settings {
location_test_header: None,
fallback_location: "USOK".to_owned(),
documentation_url: "https://developer.mozilla.org/".to_owned(),
trace_header: Some("X-Cloud-Trace-Context".to_owned()),
// ADM specific settings
adm_endpoint_url: "".to_owned(),
adm_partner_id: None,
Expand Down Expand Up @@ -217,6 +221,20 @@ impl Settings {
}
}

impl<'a> From<&'a HttpRequest> for &'a Settings {
fn from(req: &'a HttpRequest) -> Self {
let state = req.app_data::<Data<ServerState>>().expect("No State!");
&state.settings
}
}

impl<'a> From<&'a ServiceRequest> for &'a Settings {
fn from(req: &'a ServiceRequest) -> Self {
let state = req.app_data::<Data<ServerState>>().expect("No State!");
&state.settings
}
}

#[cfg(test)]
pub fn test_settings() -> Settings {
Settings::with_env_and_config_file(&None, true)
Expand Down
21 changes: 17 additions & 4 deletions src/tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ use serde_json::value::Value;
use slog::{Key, Record, KV};
use woothee::parser::{Parser, WootheeResult};

use crate::settings::Settings;

/*
/// List of valid user-agent attributes to keep, anything not in this
/// list is considered 'Other'. We log the user-agent on connect always
Expand Down Expand Up @@ -111,8 +113,8 @@ fn insert_if_not_empty(label: &str, val: &str, tags: &mut HashMap<String, String
}
}

impl From<&RequestHead> for Tags {
fn from(req_head: &RequestHead) -> Self {
impl Tags {
pub fn from_head(req_head: &RequestHead, settings: &Settings) -> Self {
// Return an Option<> type because the later consumers (HandlerErrors) presume that
// tags are optional and wrapped by an Option<> type.
let mut tags = HashMap::new();
Expand All @@ -127,6 +129,15 @@ impl From<&RequestHead> for Tags {
extra.insert("ua".to_owned(), uas.to_string());
}
}
if let Some(tracer) = settings.trace_header.clone() {
if let Some(header) = req_head.headers().get(tracer) {
if let Ok(val) = header.to_str() {
if !val.is_empty() {
extra.insert("header.trace".to_owned(), val.to_owned());
}
}
}
}
tags.insert("uri.method".to_owned(), req_head.method.to_string());
// `uri.path` causes too much cardinality for influx but keep it in
// extra for sentry
Expand All @@ -137,9 +148,10 @@ impl From<&RequestHead> for Tags {

impl From<HttpRequest> for Tags {
fn from(request: HttpRequest) -> Self {
let settings = (&request).into();
match request.extensions().get::<Self>() {
Some(v) => v.clone(),
None => Tags::from(request.head()),
None => Tags::from_head(request.head(), settings),
}
}
}
Expand Down Expand Up @@ -254,11 +266,12 @@ impl FromRequest for Tags {
type Future = Ready<Result<Self, Self::Error>>;

fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
let settings = req.into();
let tags = {
let exts = req.extensions();
match exts.get::<Tags>() {
Some(t) => t.clone(),
None => Tags::from(req.head()),
None => Tags::from_head(req.head(), settings),
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/web/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ pub async fn get_tiles(
metrics.incr_with_tags("tiles.invalid", Some(&tags));
// Report directly to sentry
// (This is starting to become a pattern. 🤔)
let mut tags = Tags::from(request.head());
let mut tags = Tags::from_head(request.head(), &settings);
tags.add_extra("err", &es);
tags.add_tag("level", "warning");
l_sentry::report(&tags, sentry::event_from_error(&e));
Expand Down
4 changes: 3 additions & 1 deletion src/web/middleware/sentry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use sentry::protocol::Event;
use std::task::Poll;

use crate::error::HandlerError;
use crate::settings::Settings;
use crate::tags::Tags;

pub struct SentryWrapper;
Expand Down Expand Up @@ -106,7 +107,8 @@ where
}

fn call(&mut self, sreq: ServiceRequest) -> Self::Future {
let mut tags = Tags::from(sreq.head());
let settings: &Settings = (&sreq).into();
let mut tags = Tags::from_head(sreq.head(), settings);
sreq.extensions_mut().insert(tags.clone());

Box::pin(self.service.call(sreq).and_then(move |mut sresp| {
Expand Down

0 comments on commit 762bc39

Please sign in to comment.