-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
tracing: add grpc-status and grpc-message to spans #7996
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
#include "common/common/fmt.h" | ||
#include "common/common/macros.h" | ||
#include "common/common/utility.h" | ||
#include "common/grpc/common.h" | ||
#include "common/http/codes.h" | ||
#include "common/http/header_map_impl.h" | ||
#include "common/http/headers.h" | ||
|
@@ -121,6 +122,8 @@ static void annotateVerbose(Span& span, const StreamInfo::StreamInfo& stream_inf | |
} | ||
|
||
void HttpTracerUtility::finalizeSpan(Span& span, const Http::HeaderMap* request_headers, | ||
const Http::HeaderMap* response_headers, | ||
const Http::HeaderMap* response_trailers, | ||
const StreamInfo::StreamInfo& stream_info, | ||
const Config& tracing_config) { | ||
// Pre response data. | ||
|
@@ -163,6 +166,25 @@ void HttpTracerUtility::finalizeSpan(Span& span, const Http::HeaderMap* request_ | |
span.setTag(Tracing::Tags::get().ResponseFlags, | ||
StreamInfo::ResponseFlagUtils::toShortString(stream_info)); | ||
|
||
// GRPC data. | ||
if (response_headers && response_trailers && stream_info.protocol() == Http::Protocol::Http2 && | ||
Grpc::Common::hasGrpcContentType(*response_headers)) { | ||
const Http::HeaderEntry* grpc_status_header = response_trailers->GrpcStatus(); | ||
if (grpc_status_header) { | ||
span.setTag(Tracing::Tags::get().GrpcStatusCode, grpc_status_header->value().getStringView()); | ||
} | ||
const Http::HeaderEntry* grpc_message_header = response_trailers->GrpcMessage(); | ||
if (grpc_message_header) { | ||
span.setTag(Tracing::Tags::get().GrpcMessage, grpc_message_header->value().getStringView()); | ||
} | ||
absl::optional<Grpc::Status::GrpcStatus> grpc_status_code = | ||
Grpc::Common::getGrpcStatus(*response_trailers); | ||
// Set error tag when status is not OK. | ||
if (grpc_status_code && grpc_status_code.value() != Grpc::Status::GrpcStatus::Ok) { | ||
span.setTag(Tracing::Tags::get().Error, Tracing::Tags::get().True); | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be good to also set the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks @objectiser - I've added this. |
||
|
||
if (tracing_config.verbose()) { | ||
annotateVerbose(span, stream_info); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -122,12 +122,14 @@ TEST(HttpConnManFinalizerImpl, OriginalAndLongPath) { | |
{"x-envoy-original-path", path}, | ||
{":method", "GET"}, | ||
{"x-forwarded-proto", "http"}}; | ||
Http::TestHeaderMapImpl response_headers; | ||
Http::TestHeaderMapImpl response_trailers; | ||
NiceMock<StreamInfo::MockStreamInfo> stream_info; | ||
|
||
absl::optional<Http::Protocol> protocol = Http::Protocol::Http2; | ||
EXPECT_CALL(stream_info, bytesReceived()).WillOnce(Return(10)); | ||
EXPECT_CALL(stream_info, bytesSent()).WillOnce(Return(11)); | ||
EXPECT_CALL(stream_info, protocol()).WillOnce(ReturnPointee(&protocol)); | ||
EXPECT_CALL(stream_info, protocol()).WillRepeatedly(ReturnPointee(&protocol)); | ||
absl::optional<uint32_t> response_code; | ||
EXPECT_CALL(stream_info, responseCode()).WillRepeatedly(ReturnPointee(&response_code)); | ||
|
||
|
@@ -137,7 +139,8 @@ TEST(HttpConnManFinalizerImpl, OriginalAndLongPath) { | |
EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().HttpProtocol), Eq("HTTP/2"))); | ||
|
||
NiceMock<MockConfig> config; | ||
HttpTracerUtility::finalizeSpan(span, &request_headers, stream_info, config); | ||
HttpTracerUtility::finalizeSpan(span, &request_headers, &response_headers, &response_trailers, | ||
stream_info, config); | ||
} | ||
|
||
TEST(HttpConnManFinalizerImpl, NoGeneratedId) { | ||
|
@@ -148,12 +151,14 @@ TEST(HttpConnManFinalizerImpl, NoGeneratedId) { | |
|
||
Http::TestHeaderMapImpl request_headers{ | ||
{"x-envoy-original-path", path}, {":method", "GET"}, {"x-forwarded-proto", "http"}}; | ||
Http::TestHeaderMapImpl response_headers; | ||
Http::TestHeaderMapImpl response_trailers; | ||
NiceMock<StreamInfo::MockStreamInfo> stream_info; | ||
|
||
absl::optional<Http::Protocol> protocol = Http::Protocol::Http2; | ||
EXPECT_CALL(stream_info, bytesReceived()).WillOnce(Return(10)); | ||
EXPECT_CALL(stream_info, bytesSent()).WillOnce(Return(11)); | ||
EXPECT_CALL(stream_info, protocol()).WillOnce(ReturnPointee(&protocol)); | ||
EXPECT_CALL(stream_info, protocol()).WillRepeatedly(ReturnPointee(&protocol)); | ||
absl::optional<uint32_t> response_code; | ||
EXPECT_CALL(stream_info, responseCode()).WillRepeatedly(ReturnPointee(&response_code)); | ||
|
||
|
@@ -163,7 +168,8 @@ TEST(HttpConnManFinalizerImpl, NoGeneratedId) { | |
EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().HttpProtocol), Eq("HTTP/2"))); | ||
|
||
NiceMock<MockConfig> config; | ||
HttpTracerUtility::finalizeSpan(span, &request_headers, stream_info, config); | ||
HttpTracerUtility::finalizeSpan(span, &request_headers, &response_headers, &response_trailers, | ||
stream_info, config); | ||
} | ||
|
||
TEST(HttpConnManFinalizerImpl, NullRequestHeaders) { | ||
|
@@ -184,7 +190,7 @@ TEST(HttpConnManFinalizerImpl, NullRequestHeaders) { | |
EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().UpstreamCluster), _)).Times(0); | ||
|
||
NiceMock<MockConfig> config; | ||
HttpTracerUtility::finalizeSpan(span, nullptr, stream_info, config); | ||
HttpTracerUtility::finalizeSpan(span, nullptr, nullptr, nullptr, stream_info, config); | ||
} | ||
|
||
TEST(HttpConnManFinalizerImpl, StreamInfoLogs) { | ||
|
@@ -222,7 +228,7 @@ TEST(HttpConnManFinalizerImpl, StreamInfoLogs) { | |
|
||
NiceMock<MockConfig> config; | ||
EXPECT_CALL(config, verbose).WillOnce(Return(true)); | ||
HttpTracerUtility::finalizeSpan(span, nullptr, stream_info, config); | ||
HttpTracerUtility::finalizeSpan(span, nullptr, nullptr, nullptr, stream_info, config); | ||
} | ||
|
||
TEST(HttpConnManFinalizerImpl, UpstreamClusterTagSet) { | ||
|
@@ -244,7 +250,7 @@ TEST(HttpConnManFinalizerImpl, UpstreamClusterTagSet) { | |
EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().RequestSize), Eq("10"))); | ||
|
||
NiceMock<MockConfig> config; | ||
HttpTracerUtility::finalizeSpan(span, nullptr, stream_info, config); | ||
HttpTracerUtility::finalizeSpan(span, nullptr, nullptr, nullptr, stream_info, config); | ||
} | ||
|
||
TEST(HttpConnManFinalizerImpl, SpanOptionalHeaders) { | ||
|
@@ -254,11 +260,13 @@ TEST(HttpConnManFinalizerImpl, SpanOptionalHeaders) { | |
{":path", "/test"}, | ||
{":method", "GET"}, | ||
{"x-forwarded-proto", "https"}}; | ||
Http::TestHeaderMapImpl response_headers; | ||
Http::TestHeaderMapImpl response_trailers; | ||
NiceMock<StreamInfo::MockStreamInfo> stream_info; | ||
|
||
absl::optional<Http::Protocol> protocol = Http::Protocol::Http10; | ||
EXPECT_CALL(stream_info, bytesReceived()).WillOnce(Return(10)); | ||
EXPECT_CALL(stream_info, protocol()).WillOnce(ReturnPointee(&protocol)); | ||
EXPECT_CALL(stream_info, protocol()).WillRepeatedly(ReturnPointee(&protocol)); | ||
const std::string service_node = "i-453"; | ||
|
||
// Check that span is populated correctly. | ||
|
@@ -282,7 +290,8 @@ TEST(HttpConnManFinalizerImpl, SpanOptionalHeaders) { | |
EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().UpstreamCluster), _)).Times(0); | ||
|
||
NiceMock<MockConfig> config; | ||
HttpTracerUtility::finalizeSpan(span, &request_headers, stream_info, config); | ||
HttpTracerUtility::finalizeSpan(span, &request_headers, &response_headers, &response_trailers, | ||
stream_info, config); | ||
} | ||
|
||
TEST(HttpConnManFinalizerImpl, SpanPopulatedFailureResponse) { | ||
|
@@ -291,6 +300,8 @@ TEST(HttpConnManFinalizerImpl, SpanPopulatedFailureResponse) { | |
{":path", "/test"}, | ||
{":method", "GET"}, | ||
{"x-forwarded-proto", "http"}}; | ||
Http::TestHeaderMapImpl response_headers; | ||
Http::TestHeaderMapImpl response_trailers; | ||
NiceMock<StreamInfo::MockStreamInfo> stream_info; | ||
|
||
request_headers.insertHost().value(std::string("api")); | ||
|
@@ -299,7 +310,7 @@ TEST(HttpConnManFinalizerImpl, SpanPopulatedFailureResponse) { | |
request_headers.insertClientTraceId().value(std::string("client_trace_id")); | ||
|
||
absl::optional<Http::Protocol> protocol = Http::Protocol::Http10; | ||
EXPECT_CALL(stream_info, protocol()).WillOnce(ReturnPointee(&protocol)); | ||
EXPECT_CALL(stream_info, protocol()).WillRepeatedly(ReturnPointee(&protocol)); | ||
EXPECT_CALL(stream_info, bytesReceived()).WillOnce(Return(10)); | ||
const std::string service_node = "i-453"; | ||
|
||
|
@@ -339,7 +350,80 @@ TEST(HttpConnManFinalizerImpl, SpanPopulatedFailureResponse) { | |
EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().ResponseFlags), Eq("UT"))); | ||
EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().UpstreamCluster), _)).Times(0); | ||
|
||
HttpTracerUtility::finalizeSpan(span, &request_headers, stream_info, config); | ||
HttpTracerUtility::finalizeSpan(span, &request_headers, &response_headers, &response_trailers, | ||
stream_info, config); | ||
} | ||
|
||
TEST(HttpConnManFinalizerImpl, GrpcOkStatus) { | ||
const std::string path_prefix = "http://"; | ||
NiceMock<MockSpan> span; | ||
|
||
Http::TestHeaderMapImpl request_headers{{":method", "POST"}, | ||
{":scheme", "http"}, | ||
{":path", "/pb.Foo/Bar"}, | ||
{":authority", "example.com:80"}, | ||
{"content-type", "application/grpc"}, | ||
{"te", "trailers"}}; | ||
|
||
Http::TestHeaderMapImpl response_headers{{":status", "200"}, | ||
{"content-type", "application/grpc"}}; | ||
Http::TestHeaderMapImpl response_trailers{{"grpc-status", "0"}, {"grpc-message", ""}}; | ||
NiceMock<StreamInfo::MockStreamInfo> stream_info; | ||
|
||
absl::optional<Http::Protocol> protocol = Http::Protocol::Http2; | ||
absl::optional<uint32_t> response_code(200); | ||
EXPECT_CALL(stream_info, responseCode()).WillRepeatedly(ReturnPointee(&response_code)); | ||
EXPECT_CALL(stream_info, bytesReceived()).WillOnce(Return(10)); | ||
EXPECT_CALL(stream_info, bytesSent()).WillOnce(Return(11)); | ||
EXPECT_CALL(stream_info, protocol()).WillRepeatedly(ReturnPointee(&protocol)); | ||
|
||
EXPECT_CALL(span, setTag(_, _)).Times(testing::AnyNumber()); | ||
EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().HttpMethod), Eq("POST"))); | ||
EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().HttpProtocol), Eq("HTTP/2"))); | ||
EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().HttpStatusCode), Eq("200"))); | ||
EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().GrpcStatusCode), Eq("0"))); | ||
EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().GrpcMessage), Eq(""))); | ||
|
||
NiceMock<MockConfig> config; | ||
HttpTracerUtility::finalizeSpan(span, &request_headers, &response_headers, &response_trailers, | ||
stream_info, config); | ||
} | ||
|
||
TEST(HttpConnManFinalizerImpl, GrpcError) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a test for trailer-only case too. |
||
const std::string path_prefix = "http://"; | ||
NiceMock<MockSpan> span; | ||
|
||
Http::TestHeaderMapImpl request_headers{{":method", "POST"}, | ||
{":scheme", "http"}, | ||
{":path", "/pb.Foo/Bar"}, | ||
{":authority", "example.com:80"}, | ||
{"content-type", "application/grpc"}, | ||
{"te", "trailers"}}; | ||
|
||
Http::TestHeaderMapImpl response_headers{{":status", "403"}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. :status is usually 200 even grpc-status is not 0. |
||
{"content-type", "application/grpc"}}; | ||
Http::TestHeaderMapImpl response_trailers{{"grpc-status", "7"}, | ||
{"grpc-message", "permission denied"}}; | ||
NiceMock<StreamInfo::MockStreamInfo> stream_info; | ||
|
||
absl::optional<Http::Protocol> protocol = Http::Protocol::Http2; | ||
absl::optional<uint32_t> response_code(403); | ||
EXPECT_CALL(stream_info, responseCode()).WillRepeatedly(ReturnPointee(&response_code)); | ||
EXPECT_CALL(stream_info, bytesReceived()).WillOnce(Return(10)); | ||
EXPECT_CALL(stream_info, bytesSent()).WillOnce(Return(11)); | ||
EXPECT_CALL(stream_info, protocol()).WillRepeatedly(ReturnPointee(&protocol)); | ||
|
||
EXPECT_CALL(span, setTag(_, _)).Times(testing::AnyNumber()); | ||
EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().Error), Eq(Tracing::Tags::get().True))); | ||
EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().HttpMethod), Eq("POST"))); | ||
EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().HttpProtocol), Eq("HTTP/2"))); | ||
EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().HttpStatusCode), Eq("403"))); | ||
EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().GrpcStatusCode), Eq("7"))); | ||
EXPECT_CALL(span, setTag(Eq(Tracing::Tags::get().GrpcMessage), Eq("permission denied"))); | ||
|
||
NiceMock<MockConfig> config; | ||
HttpTracerUtility::finalizeSpan(span, &request_headers, &response_headers, &response_trailers, | ||
stream_info, config); | ||
} | ||
|
||
TEST(HttpTracerUtilityTest, operationTypeToString) { | ||
|
@@ -352,6 +436,8 @@ TEST(HttpNullTracerTest, BasicFunctionality) { | |
MockConfig config; | ||
StreamInfo::MockStreamInfo stream_info; | ||
Http::TestHeaderMapImpl request_headers; | ||
Http::TestHeaderMapImpl response_headers; | ||
Http::TestHeaderMapImpl response_trailers; | ||
|
||
SpanPtr span_ptr = | ||
null_tracer.startSpan(config, request_headers, stream_info, {Reason::Sampling, true}); | ||
|
@@ -374,6 +460,8 @@ class HttpTracerImplTest : public testing::Test { | |
|
||
Http::TestHeaderMapImpl request_headers_{ | ||
{":path", "/"}, {":method", "GET"}, {"x-request-id", "foo"}, {":authority", "test"}}; | ||
Http::TestHeaderMapImpl response_headers; | ||
Http::TestHeaderMapImpl response_trailers; | ||
StreamInfo::MockStreamInfo stream_info_; | ||
NiceMock<LocalInfo::MockLocalInfo> local_info_; | ||
MockConfig config_; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: Don't you want to handle trailer only (header only) gRPC responses also? These would be useful error cases. cc @lizan
/wait-any
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right this should be extract first of non-null HeaderMap in (response trailers, response headers), I'd not limit the protocol = HTTP2 here since it is also useful cases for grpc_http1_bridge etc, and future gRPC over QUIC(HTTP/3?).