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

Propagate tracestate header in the OpenTelemetry tracer #22693

Merged
merged 10 commits into from
Aug 29, 2022

Conversation

AlexanderEllis
Copy link
Contributor

Commit Message: Propagate tracestate header in the OpenTelemetry tracer
Additional Description: OTLP Spans include the tracestate from the tracestate header. We can propagate that information from the headers to the Span. For an initial cut, we can just store whatever incoming tracestate header is in the span. In the future, it may make sense to add more handling to update the tracestate, but I don't believe we currently do any tracestate updates in Envoy.
Risk Level: Low
Testing: Basic tests

Part of #9958

Signed-off-by: Alex Ellis <ellisonjtk@gmail.com>
Signed-off-by: Alex Ellis <ellisonjtk@gmail.com>
Signed-off-by: Alex Ellis <ellisonjtk@gmail.com>
@repokitteh-read-only
Copy link

As a reminder, PRs marked as draft will not be automatically assigned reviewers,
or be handled by maintainer-oncall triage.

Please mark your PR as ready when you want it to be reviewed!

🐱

Caused by: #22693 was opened by AlexanderEllis.

see: more, trace.

Signed-off-by: Alex Ellis <ellisonjtk@gmail.com>
@AlexanderEllis AlexanderEllis marked this pull request as ready for review August 13, 2022 18:39
key: "service.name"
value:
string_value: "unknown_service:envoy"
instrumentation_library_spans:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be resource_spans: ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think as written it's OK, since instrumentation_library_spans is the child field of resource_spans, but now that I think about it, this should really be scope_spans with that other PR. This is the classic dueling PRs though, where with name change, this test would only pass once the other is submitted...

Happy to fix once the other PR is in, or if this one is merged first I can update this code in the other PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Speaking of that PR :) Updated this with scope_spans to match the new behavior. Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Not really related to this PR but interested in how to leverage the config at https://github.com/envoyproxy/envoy/blob/main/test/extensions/tracers/opentelemetry/opentelemetry_tracer_impl_test.cc#L337-L357. Not clear from the test code where this is configured.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I definitely think it could use a little refactoring to be clearer (especially the bytes setting). I was currently using it to verify the final span that the Tracer exports, but so far I've only added the exact proto matching with this config for a few of the tests (you'll see sendMessageRaw_(_, _) in others instead of sendMessageRaw_(Grpc::ProtoBufferEqIgnoreRepeatedFieldOrdering(request_proto), _)). For legibility, I think it would also be helpful to only test the specific fields for each test to help reduce the busyness of each expectation.

@jmarantz
Copy link
Contributor

/assign-from @envoyproxy/first-pass-reviewers

@repokitteh-read-only
Copy link

@envoyproxy/first-pass-reviewers assignee is @tonya11en

🐱

Caused by: a #22693 (comment) was created by @jmarantz.

see: more, trace.

Signed-off-by: Alex Ellis <ellisonjtk@gmail.com>
Signed-off-by: Alex Ellis <ellisonjtk@gmail.com>
Signed-off-by: Alex Ellis <ellisonjtk@gmail.com>
@jmarantz jmarantz assigned phlax and htuch and unassigned tonya11en and phlax Aug 16, 2022
htuch
htuch previously requested changes Aug 17, 2022
Copy link
Member

@htuch htuch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks, just a few minor feedback items.
/wait

// See https://www.w3.org/TR/trace-context/#processing-model-for-working-with-trace-context
absl::string_view tracestate = "";
// TODO: what if tracestate is in multiple headers? Spec says we should combine, but Envoy
// currently only surfaces the first seen.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See get, which returns a GetResult with all header values.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for that pointer! Did a little digging, and it looks like ConnectionManagerImpl::ActiveStream::traceRequest calls startSpan with its RequestHeaderMapPtr request_headers_ (the RequestHeaderMap containing the get). Driver::startSpan is defined with just Tracing::TraceContext though, which only has getByKey and forEach.

With that in mind, I added some handling for this (and a test) with iteration over all the headers. Doesn't seem very efficient to me, but I think this works for the multiple headers case. Happy to improve if you have any suggestions 👍

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe fix TraceContext to deal with the appropriate header-specific aggregation behavior. I think any tracer interacting with tracestate will need this behavior. I think in most places that we see header get-by-key and the value is singular and not aggregated, there is some code smell that we haven't fully thought through the valid wire combinations.

TEST(SpanContextExtractorTest, ExtractSpanContextWithTracestate) {
Http::TestRequestHeaderMapImpl request_headers{
{"traceparent", fmt::format("{}-{}-{}-{}", version, trace_id, parent_id, trace_flags)},
{"tracestate", "sample-tracestate"}};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there one for missing traceparent?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah there isn't, good call! Technically it was already covered via the missing traceparent test above, but I think it's worthwhile to explicitly add one for it for legibility. I also fleshed out the comment in the extractor, mentioning that we are already checking for the existence of the traceparent header.

@@ -174,6 +176,9 @@ Tracing::SpanPtr Tracer::startSpan(const Tracing::Config& config, const std::str
new_span.setId(Hex::uint64ToHex(span_id));
// Respect the previous span's sampled flag.
new_span.setSampled(previous_span_context.sampled());
if (!previous_span_context.tracestate().empty()) {
new_span.setTracestate(std::string{previous_span_context.tracestate()});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this tested?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, via ParseSpanContextFromHeadersTest.

In Driver::startSpan in opentelemetry_tracer_impl.cc, if there is a propagation header present, it'll call this fn, Tracer::startSpan with the previous span context. Since we're manually adding the traceparent propagation header (and tracestate) in the the test and then calling driver_->startSpan, the tracestate in the header is passed through in the previous_span_context here.

Signed-off-by: Alex Ellis <ellisonjtk@gmail.com>
Signed-off-by: Alex Ellis <ellisonjtk@gmail.com>
Signed-off-by: Alex Ellis <ellisonjtk@gmail.com>
@moderation
Copy link
Contributor

This ready to merge @htuch @AlexanderEllis ?

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks will merge as it looks like comments have been addressed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants