envoy icon indicating copy to clipboard operation
envoy copied to clipboard

Propagate Tracing Headers in ext_proc

Open scrocquesel opened this issue 2 years ago • 12 comments

Title: B3 headers should be propagated to the gRPC context call

Description:

Unlike ext_authz, ext_proc do not send tracing headers. Or I don't find how to do it.

[optional Relevant Links:]

We should be able to fetch the header like in ext_authz with injectContext : https://github.com/envoyproxy/envoy/issues/6520

scrocquesel avatar May 02 '22 20:05 scrocquesel

I mean like https://github.com/envoyproxy/envoy/blob/main/source/extensions/filters/common/ext_authz/ext_authz_grpc_impl.cc

scrocquesel avatar May 02 '22 20:05 scrocquesel

From what I understand, tracing span for streaming grpc call was not implemented during https://github.com/envoyproxy/envoy/pull/1885.

scrocquesel avatar May 02 '22 20:05 scrocquesel

cc @snowp

wbpcode avatar May 03 '22 14:05 wbpcode

ext_proc will sends all request headers include B3 headers to the server as part of grpc requests.

So do you mean that you want to add tracing support of grpc rqeuests self?

wbpcode avatar May 03 '22 14:05 wbpcode

Yes, span should begin with the start of the stream and should end at the end of it. And a child span can be created in the filter to align with how ext_authz is doing it. Use the header from the request as parent span seems not right as envoy may have create child span itself.

Maybe a new span be created for each message in the stream and pass as metadata also but it seems that gRPC tracing interceptor are not working this way. At least, https://github.com/opentracing-contrib/java-grpc/blob/master/src/main/java/io/opentracing/contrib/grpc/TracingServerInterceptor.java, is just creating a span for the whole stream and adding logs.

scrocquesel avatar May 03 '22 17:05 scrocquesel

I tried to set the initial_metadata with %REQ(x-b3-traceid)% but if the downstream request do not have those headers, envoy generate a new trace and I don't get those values at all (stream request nor message request)

scrocquesel avatar May 07 '22 21:05 scrocquesel

if the downstream request do not have those headers, envoy generate a new trace and I don't get those values at all (stream request nor message request)

This is because Envoy inject the tracing headers at the last of request header processing. So you get nothing in the ext_proc.

wbpcode avatar May 10 '22 05:05 wbpcode

Looks like this is a general issue for the external grpc requests. But sorry I have no enough bandwidth for this recently. 😞 I will mark this help wanted.

wbpcode avatar May 10 '22 05:05 wbpcode

@wbpcode would love to get my hands dirty on this, but don't really know where to start - wanna give me a hand?

xvzf avatar Jan 19 '23 15:01 xvzf

@xvzf It seems like the reason lies in the fact that ext_proc filter is using stream API, while ext_authz is using simple request/response. For request/response (aka send), there is a dedicated parent_span parameter in the gRPC client, which will eventually add tracing information to the outgoing request. For the gRPC stream API, there is no mentioning of trace headers anywhere near.

I ain't no gRPC expert, but it appears to me that with streaming, you only send the headers once and not per message, so perhaps what needs to happen is AsyncClientImpl::startRaw should be updated to take in parent_span (similar to basic send), then we spawn new child span like this in the AsyncStreamImpl constructor, and finally populate the trace headers in AsyncStreamImpl::initialize before sending them like this. I don't know if gRPC streaming API headers (Http::RequestMessagePtr) are handled anyhow different compared to basic request/response (Http::RequestHeaderMap), so there might be some additional work we need to do to adopt/shim one to another.

In the ext_proc code you can then get the span information from decoder_callbacks_ field, like this and pass it to the start (that you will modify to accept span information) here.

We were thinking to work-around this by adding a Lua filter at the beginning of the filter chain, that will inject tracing header(s) with our manually generated IDs, so that we can reference it through initial_metadata with %REQ(..) and therefore share between main request processing pipeline and calls to ext_proc, but it seems like that is not going to work either, because connection manager will generate new Span structure at the beginning before calling filters (which I assume makes sense, as filters can log/trace), and that structure is used throughout the code, instead of reading headers all the time. It doesn't seem to care about any headers' changes afterwards. So you will still get different IDs in the main processing pipeline and everything that tries to read the trace id from the headers, unless I missed something.

nahk-ivanov avatar Mar 28 '23 20:03 nahk-ivanov

cc @xvzf sorry for the delayed respnose. I almost forget this PR. But I think @nahk-ivanov has given a great explanation about this work. Sorry again for the delay.

wbpcode avatar Mar 29 '23 01:03 wbpcode

It's been some time since the last comment. Is there an update on this issue? We're transitioning from Lua filters to exproc, but it appears that traces are still not being propagated to the grpc server.

cainelli avatar Feb 23 '24 18:02 cainelli

I'm really interested on this and decided to take a stab https://github.com/envoyproxy/envoy/pull/33665. Thank you for the very detailed pointers @nahk-ivanov, it was sufficient for one with no prior C++ knowledge to follow. I would appreciate any review.

cainelli avatar Apr 16 '24 14:04 cainelli

@cainelli LEGEND!!!!

nahk-ivanov avatar May 22 '24 23:05 nahk-ivanov