Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for custom attributes for spans and logs #4102

Merged
merged 109 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
c3919d5
wip
bnjjj Oct 25, 2023
4b4f703
custom dynamic attributes is working for logs and spans
bnjjj Oct 26, 2023
2d7c51c
wip
bnjjj Oct 26, 2023
0903984
add support of custom attributes in json formatter
bnjjj Oct 26, 2023
e69c986
wip
bnjjj Oct 30, 2023
639c3e7
Attributes workaround
Oct 30, 2023
676e4e4
supergraph done, missing subgraph
bnjjj Oct 31, 2023
3e9a77b
add support of custom attributes for subgraph
bnjjj Oct 31, 2023
bbdc0e8
add better implementation of dynamic attributes with sampling
bnjjj Oct 31, 2023
c68a14f
fix lint
bnjjj Oct 31, 2023
d39f85c
Merge branch 'dev' of github.com:apollographql/router into bnjjj/cust…
bnjjj Oct 31, 2023
df02c43
fix tests
bnjjj Nov 2, 2023
448d8b8
comment dyn attribute in text formatter
bnjjj Nov 2, 2023
9def3e3
use otel constants
bnjjj Nov 2, 2023
cb1d523
add support of requirement level
bnjjj Nov 2, 2023
3f94291
lint
bnjjj Nov 2, 2023
3874345
address review comments
bnjjj Nov 2, 2023
d18beda
Merge dev
Nov 3, 2023
b3c2efb
address review comments
bnjjj Nov 3, 2023
f53e188
Merge branch 'bnjjj/custom_attributes_telemetry' of github.com:apollo…
bnjjj Nov 3, 2023
316f48d
remove chrono and fix attribute name for subgraph
bnjjj Nov 3, 2023
5eaa07a
Split attributes.rs
Nov 3, 2023
80e4c95
Move remaining selector only stuff from attributes.rs to selectors.rs
Nov 3, 2023
fc731a9
remove useless print and comment
bnjjj Nov 3, 2023
8c3bd0c
Merge branch 'bnjjj/custom_attributes_telemetry' of github.com:apollo…
bnjjj Nov 3, 2023
6528a26
Add some tests, fix baggage.
Nov 3, 2023
6479545
Add tests for trace id.
Nov 3, 2023
9fd343a
Add env test
Nov 3, 2023
616c821
Add more tests for env
Nov 6, 2023
a12330e
Fix clippy issues
Nov 6, 2023
882bab4
Add selectors test for operations
BrynCooke Nov 6, 2023
ffc5e9a
Fix operation name hash and add test
Nov 6, 2023
c06b887
Fix subgraph supergraph operation name hash and add test
Nov 6, 2023
2a8cf4c
Fix subgraph subgraph operation name hash and add tests
Nov 6, 2023
44c56fa
Add query selector and tests
Nov 6, 2023
1acae79
Add response_status and tests
Nov 6, 2023
ae5bf38
Add tests for query variable.
Nov 6, 2023
681d076
Add subgraph response status
Nov 6, 2023
a1a95bc
Skip new telemetry config that won't be supported in initial drop
Nov 6, 2023
e34dfad
Fix datadog trace IDs
Nov 7, 2023
76452d5
Fix snapshot
Nov 7, 2023
a46b1a7
Factor out baggage logic and add tests.
Nov 7, 2023
f7caed8
Remove AttributeValue::U128.
Nov 7, 2023
a9ef3c3
wip
bnjjj Nov 3, 2023
cc64b6d
delete useless code
bnjjj Nov 3, 2023
238b932
fix apollo telemetry
bnjjj Nov 6, 2023
a41c9c1
fix jaeger test
bnjjj Nov 7, 2023
bffe3e6
fix snapshot
bnjjj Nov 7, 2023
1e81827
Update schema snap
Nov 7, 2023
3df1cdd
Replace use of AttributeValue with opentelemetry::Value
Nov 7, 2023
2699e70
Lint
Nov 7, 2023
12d0098
Update schema snapshot
Nov 7, 2023
947a295
add more snapshot tests for legacy_request_span option
bnjjj Nov 7, 2023
755291d
Merge branch 'bnjjj/custom_attributes_telemetry' of github.com:apollo…
bnjjj Nov 7, 2023
7f55f46
Dry to_otel_value.
Nov 7, 2023
1c4c09a
add http.method attribute on request span to avoid breaking change
bnjjj Nov 7, 2023
86be403
fix rhai tests
bnjjj Nov 7, 2023
a8574a8
Merge branch 'bnjjj/custom_attributes_telemetry' into bnjjj/legacy_re…
Nov 7, 2023
6b89bea
Set legacy request span for integration tests.
Nov 7, 2023
76ca546
Add span factory to allow us to switch between legacy and new mode.
Nov 7, 2023
692ef02
Add span factory to allow us to switch between legacy and new mode. (…
bnjjj Nov 8, 2023
55de44b
fix tests and integrate span mode
bnjjj Nov 8, 2023
7d1867d
rename mode legacy to deprecated
bnjjj Nov 8, 2023
6163566
Add support for `telemetry.spans.mode` (#4139)
bnjjj Nov 8, 2023
46571d1
add usage metrics for spans configuration
bnjjj Nov 8, 2023
e6bb2df
Add config for new logging (#4165)
BrynCooke Nov 8, 2023
46abd1e
filter apollo_private when adding dyn attributes on unsampled spans
bnjjj Nov 8, 2023
670c588
fix text formatter with span attributes
bnjjj Nov 8, 2023
0603f84
fix config file for testdata
bnjjj Nov 9, 2023
f00384d
fix snapshot
bnjjj Nov 9, 2023
0b9c836
few improvements
bnjjj Nov 9, 2023
f911028
Allow log format as a string.
Nov 9, 2023
375fa8c
Fix lints
Nov 10, 2023
31bb81c
review comments
bnjjj Nov 10, 2023
0181124
Merge branch 'bnjjj/custom_attributes_telemetry' of github.com:apollo…
bnjjj Nov 10, 2023
71e3925
fix lint
bnjjj Nov 10, 2023
adbfb36
remove useless logging configuration
bnjjj Nov 10, 2023
6dd98e7
Merge branch 'dev' of github.com:apollographql/router into bnjjj/cust…
bnjjj Nov 10, 2023
f8aec5a
Move `experimental_logging.when_header` to `logging.experimental_when…
Nov 10, 2023
879b25a
Move `experimental_logging.when_header` to `logging.experimental_when…
Nov 10, 2023
f6d2686
Improve metrics a little and add license checks.
Nov 10, 2023
36c30ab
Rename logging_new to logging now the original logging field has gone.
Nov 10, 2023
a24679a
fix experimental notice
bnjjj Nov 10, 2023
a3074dc
Add the ability to set servicename and service namespace from logging
Nov 10, 2023
5b404ef
Make formatters use config directly rather than copying field by field.
Nov 10, 2023
746e1e7
fix test
bnjjj Nov 10, 2023
7d69ab9
Merge branch 'bnjjj/custom_attributes_telemetry' of github.com:apollo…
bnjjj Nov 10, 2023
1bcb533
Rename Trace to TracingCommon for consistency
Nov 10, 2023
32b1dbe
update snapshots
bnjjj Nov 10, 2023
1a4498e
Merge branch 'bnjjj/custom_attributes_telemetry' of github.com:apollo…
bnjjj Nov 10, 2023
5d17679
Rename `new` mode to `spec_compliant`
Nov 10, 2023
5af8456
Add resource support to logging.
Nov 10, 2023
fb00e16
Migrate telemetry.(logging|tracing|metrics) to telemetry.exporters.(l…
Nov 13, 2023
5832540
Change name of GetAttribute and GetAttributes to Selector and Selectors.
Nov 14, 2023
e7f0948
Add missing snapshot
Nov 15, 2023
8c5795b
temp
Nov 15, 2023
af7defd
Lint
Nov 15, 2023
1ad350b
Conditions tests
Nov 15, 2023
961a9d9
Fix tests
Nov 15, 2023
478c8af
Finish tests for conditions.
Nov 15, 2023
266db72
telemetry: get rid of the tracing_fmt layer and use our own optimized…
bnjjj Nov 15, 2023
cee76f4
Remove many of the dead_code statements.
Nov 15, 2023
d92a13f
Add tests for attributes, fix bugs found once tests were added.
Nov 17, 2023
4612e00
telemetry: add tests for the formatting layer (#4209)
bnjjj Nov 20, 2023
9572725
Move some code around for `defaults_for_level`.
Nov 20, 2023
4dab1b0
Add more tests for span attributes.
Nov 20, 2023
103f575
Merge branch 'dev' of github.com:apollographql/router into bnjjj/cust…
bnjjj Nov 20, 2023
9c222e2
Merge branch 'bnjjj/custom_attributes_telemetry' of github.com:apollo…
bnjjj Nov 20, 2023
18b4165
Fix dev mode
Nov 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions apollo-router/src/plugins/telemetry/dynamic_attribute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
use std::collections::HashMap;

use tracing_subscriber::layer::Context;
use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::Layer;
use tracing_subscriber::Registry;

use super::ROUTER_SPAN_NAME;
use super::SUBGRAPH_SPAN_NAME;
use super::SUPERGRAPH_SPAN_NAME;

#[derive(Debug)]
pub(crate) enum LogAttributes {
Router(HashMap<String, String>),
Supergraph(HashMap<String, String>),
Subgraph(HashMap<String, String>),
}
bnjjj marked this conversation as resolved.
Show resolved Hide resolved

impl LogAttributes {
pub(crate) fn get_attributes(&self) -> &HashMap<String, String> {
match self {
LogAttributes::Router(attributes)
| LogAttributes::Subgraph(attributes)
| LogAttributes::Supergraph(attributes) => attributes,
}
}

fn insert(&mut self, span_name: &str, key: String, value: String) {
match span_name {
ROUTER_SPAN_NAME => {
if let Self::Router(attributes) = self {
attributes.insert(key, value);
}
}
SUBGRAPH_SPAN_NAME => {
if let Self::Subgraph(attributes) = self {
attributes.insert(key, value);
}
}
SUPERGRAPH_SPAN_NAME => {
if let Self::Supergraph(attributes) = self {
attributes.insert(key, value);
}
}
_ => {
eprintln!("cannot add custom attributes to this span '{span_name}', it's only available on router/supergraph/subgraph spans");
bnjjj marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
fn extend(&mut self, span_name: &str, val: impl IntoIterator<Item = (String, String)>) {
match span_name {
ROUTER_SPAN_NAME => {
if let Self::Router(attributes) = self {
attributes.extend(val);
}
}
SUBGRAPH_SPAN_NAME => {
if let Self::Subgraph(attributes) = self {
attributes.extend(val);
}
}
SUPERGRAPH_SPAN_NAME => {
if let Self::Supergraph(attributes) = self {
attributes.extend(val);
}
}
_ => {
eprintln!("cannot add custom attributes to this span '{span_name}', it's only available on router/supergraph/subgraph spans");
}
}
}
}

pub(crate) struct DynAttributeLayer;

impl<S> Layer<S> for DynAttributeLayer
where
S: tracing_core::Subscriber + for<'lookup> tracing_subscriber::registry::LookupSpan<'lookup>,
{
fn on_new_span(
&self,
_attrs: &tracing_core::span::Attributes<'_>,
id: &tracing_core::span::Id,
ctx: Context<'_, S>,
) {
let span = ctx.span(id).expect("Span not found, this is a bug");
let custom_attributes = match span.name() {
ROUTER_SPAN_NAME => LogAttributes::Router(HashMap::new()),
SUBGRAPH_SPAN_NAME => LogAttributes::Subgraph(HashMap::new()),
SUPERGRAPH_SPAN_NAME => LogAttributes::Supergraph(HashMap::new()),
_ => {
return;
}
};
let mut extensions = span.extensions_mut();
if extensions.get_mut::<LogAttributes>().is_none() {
extensions.insert(custom_attributes);
}
}
}

impl DynAttributeLayer {
pub(crate) fn new() -> Self {
Self {}
}
}

pub(crate) trait DynAttribute {
fn set_dyn_attribute(&self, key: String, value: String);
bnjjj marked this conversation as resolved.
Show resolved Hide resolved
fn set_dyn_attributes(&self, attributes: HashMap<String, String>);
}

impl DynAttribute for ::tracing::Span {
fn set_dyn_attribute(&self, key: String, value: String) {
self.with_subscriber(move |(id, dispatch)| {
if let Some(reg) = dispatch.downcast_ref::<Registry>() {
match reg.span(id) {
None => eprintln!("no spanref, this is a bug"),
Some(s) => {
let mut extensions = s.extensions_mut();
match extensions.get_mut::<LogAttributes>() {
Some(attributes) => {
attributes.insert(s.name(), key, value);
}
None => {
eprintln!("no SubgraphRequestLogAttributes, this is a bug");
}
}
}
};
} else {
eprintln!("no Registry, this is a bug");
}
});
}

fn set_dyn_attributes(&self, attributes: HashMap<String, String>) {
bnjjj marked this conversation as resolved.
Show resolved Hide resolved
self.with_subscriber(move |(id, dispatch)| {
if let Some(reg) = dispatch.downcast_ref::<Registry>() {
match reg.span(id) {
None => eprintln!("no spanref, this is a bug"),
Some(s) => {
let mut extensions = s.extensions_mut();
match extensions.get_mut::<LogAttributes>() {
Some(registered_attributes) => {
registered_attributes.extend(s.name(), attributes);
}
None => {
eprintln!("no LogAttributes, this is a bug");
}
}
}
};
} else {
eprintln!("no Registry, this is a bug");
}
});
}
}
46 changes: 46 additions & 0 deletions apollo-router/src/plugins/telemetry/formatters/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use tracing_subscriber::fmt::time::SystemTime;
use tracing_subscriber::fmt::FmtContext;
use tracing_subscriber::registry::LookupSpan;

use crate::plugins::telemetry::dynamic_attribute::LogAttributes;
use crate::plugins::telemetry::reload::IsSampled;

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -153,6 +154,50 @@ impl TextFormatter {
writer.write_char(' ')
}

#[inline]
fn format_dyn_attributes<S, N>(
&self,
ctx: &FmtContext<'_, S, N>,
writer: &mut Writer<'_>,
event: &Event<'_>,
) -> fmt::Result
where
S: Subscriber + for<'a> LookupSpan<'a>,
N: for<'a> FormatFields<'a> + 'static,
{
let span = event
.parent()
.and_then(|id| ctx.span(id))
.or_else(|| ctx.lookup_current());
if let Some(span) = span {
let ext = span.extensions();
match &ext.get::<LogAttributes>() {
Some(dyn_attributes) => {
// TODO: put it in a method
let attributes = dyn_attributes.get_attributes();
// if writer.has_ansi_escapes() {
// let style = Style::new().dimmed();
// write!(writer, "{}", style.prefix())?;
// write!(writer, "[trace_id={trace_id}]")?;
// write!(writer, "{}", style.suffix())?;
// } else {
let attrs: Vec<String> = attributes
.iter()
.map(|(key, val)| format!("{key}={val}"))
.collect();
write!(writer, "[attributes=[{}]]", attrs.join(", "))?;
bnjjj marked this conversation as resolved.
Show resolved Hide resolved
// }
writer.write_char(' ')?;
}
None => {
eprintln!("Unable to find LogAttributes in extensions; this is a bug");
}
}
}

Ok(())
}

#[inline]
fn format_request_id<S, N>(
&self,
Expand Down Expand Up @@ -216,6 +261,7 @@ where

self.format_level(meta.level(), &mut writer)?;
self.format_request_id(ctx, &mut writer, event)?;
self.format_dyn_attributes(ctx, &mut writer, event)?;
if self.display_target {
self.format_target(meta.target(), &mut writer)?;
}
Expand Down
30 changes: 29 additions & 1 deletion apollo-router/src/plugins/telemetry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ use tokio::runtime::Handle;
use tower::BoxError;
use tower::ServiceBuilder;
use tower::ServiceExt;
use tracing_core::callsite::DefaultCallsite;
use tracing_core::Callsite;
use tracing_core::Interest;
use tracing_core::Metadata;
use tracing_opentelemetry::OpenTelemetrySpanExt;
use tracing_subscriber::fmt::format::JsonFields;
use tracing_subscriber::Layer;
Expand All @@ -61,6 +65,7 @@ use self::apollo_exporter::Sender;
use self::config::Conf;
use self::config::Sampler;
use self::config::SamplerOption;
use self::dynamic_attribute::DynAttribute;
use self::formatters::text::TextFormatter;
use self::metrics::apollo::studio::SingleTypeStat;
use self::metrics::AttributesForwardConf;
Expand Down Expand Up @@ -125,6 +130,7 @@ pub(crate) mod apollo;
pub(crate) mod apollo_exporter;
pub(crate) mod config;
mod config_new;
pub(crate) mod dynamic_attribute;
mod endpoint;
pub(crate) mod formatters;
pub(crate) mod metrics;
Expand Down Expand Up @@ -310,8 +316,13 @@ impl Plugin for Telemetry {
"apollo_private.http.request_headers" = filter_headers(request.router_request.headers(), &apollo.send_headers).as_str(),
"apollo_private.http.response_headers" = field::Empty
);
// TODO add support of set_attribute for the span
// It doesn't need to be prefixed now.
span.set_dyn_attribute(String::from("custom_attribute_header"), headers.get("host").and_then(|h| h.to_str().ok()).map(|h| h.to_string()).unwrap_or_default());

span
})
// TODO add map_future_with_request_data to log the request
.map_future(move |fut| {
let start = Instant::now();
let config = config_later.clone();
Expand All @@ -321,6 +332,9 @@ impl Plugin for Telemetry {

async move {
let span = Span::current();
::tracing::info!("coucou");


let response: Result<router::Response, BoxError> = fut.await;

span.record(
Expand Down Expand Up @@ -546,6 +560,20 @@ impl Plugin for Telemetry {
}
}

struct LateCallsite {
callsite: std::sync::OnceLock<DefaultCallsite>,
}

impl Callsite for LateCallsite {
fn set_interest(&self, interest: Interest) {
self.callsite.get().unwrap().set_interest(interest);
}

fn metadata(&self) -> &Metadata<'_> {
self.callsite.get().unwrap().metadata()
}
}

bnjjj marked this conversation as resolved.
Show resolved Hide resolved
impl Telemetry {
pub(crate) fn activate(&mut self) {
// Only apply things if we were executing in the context of a vanilla the Apollo executable.
Expand Down Expand Up @@ -954,7 +982,7 @@ impl Telemetry {
subgraph_name: Arc<String>,
sub_request: &mut Request,
) -> Option<CacheAttributes> {
let body = dbg!(sub_request.subgraph_request.body_mut());
let body = sub_request.subgraph_request.body_mut();
let hashed_query = hash_request(body);
let representations = body
.variables
Expand Down
4 changes: 3 additions & 1 deletion apollo-router/src/plugins/telemetry/reload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use tracing_subscriber::EnvFilter;
use tracing_subscriber::Registry;

use super::config::SamplerOption;
use super::dynamic_attribute::DynAttributeLayer;
use super::metrics::span_metrics_exporter::SpanMetricsLayer;
use crate::axum_factory::utils::REQUEST_SPAN_NAME;
use crate::metrics::layer::MetricsLayer;
Expand All @@ -36,7 +37,7 @@ use crate::plugins::telemetry::formatters::text::TextFormatter;
use crate::plugins::telemetry::formatters::FilteringFormatter;
use crate::plugins::telemetry::tracing::reload::ReloadTracer;

pub(crate) type LayeredRegistry = Layered<SpanMetricsLayer, Registry>;
pub(crate) type LayeredRegistry = Layered<SpanMetricsLayer, Layered<DynAttributeLayer, Registry>>;

pub(super) type LayeredTracer = Layered<
Filtered<
Expand Down Expand Up @@ -118,6 +119,7 @@ pub(crate) fn init_telemetry(log_level: &str) -> Result<()> {
// Env filter is separate because of https://github.com/tokio-rs/tracing/issues/1629
// the tracing registry is only created once
tracing_subscriber::registry()
.with(DynAttributeLayer::new())
.with(SpanMetricsLayer::default())
.with(opentelemetry_layer)
.with(fmt_layer)
Expand Down