Line data Source code
1 : #include "source/common/tracing/http_tracer_impl.h"
2 :
3 : #include <string>
4 :
5 : #include "envoy/config/core/v3/base.pb.h"
6 : #include "envoy/network/address.h"
7 : #include "envoy/tracing/tracer.h"
8 : #include "envoy/type/metadata/v3/metadata.pb.h"
9 : #include "envoy/type/tracing/v3/custom_tag.pb.h"
10 :
11 : #include "source/common/common/assert.h"
12 : #include "source/common/common/fmt.h"
13 : #include "source/common/common/macros.h"
14 : #include "source/common/common/utility.h"
15 : #include "source/common/grpc/common.h"
16 : #include "source/common/http/codes.h"
17 : #include "source/common/http/header_map_impl.h"
18 : #include "source/common/http/header_utility.h"
19 : #include "source/common/http/headers.h"
20 : #include "source/common/http/utility.h"
21 : #include "source/common/protobuf/utility.h"
22 : #include "source/common/stream_info/utility.h"
23 :
24 : #include "absl/strings/str_cat.h"
25 :
26 : namespace Envoy {
27 : namespace Tracing {
28 :
29 : // TODO(perf): Avoid string creations/copies in this entire file.
30 0 : static std::string buildResponseCode(const StreamInfo::StreamInfo& info) {
31 0 : return info.responseCode() ? std::to_string(info.responseCode().value()) : "0";
32 0 : }
33 :
34 : static absl::string_view valueOrDefault(const Http::HeaderEntry* header,
35 0 : const char* default_value) {
36 0 : return header ? header->value().getStringView() : default_value;
37 0 : }
38 :
39 0 : static void addTagIfNotNull(Span& span, const std::string& tag, const Http::HeaderEntry* entry) {
40 0 : if (entry != nullptr) {
41 0 : span.setTag(tag, entry->value().getStringView());
42 0 : }
43 0 : }
44 :
45 0 : static void addGrpcRequestTags(Span& span, const Http::RequestHeaderMap& headers) {
46 0 : addTagIfNotNull(span, Tracing::Tags::get().GrpcPath, headers.Path());
47 0 : addTagIfNotNull(span, Tracing::Tags::get().GrpcAuthority, headers.Host());
48 0 : addTagIfNotNull(span, Tracing::Tags::get().GrpcContentType, headers.ContentType());
49 0 : addTagIfNotNull(span, Tracing::Tags::get().GrpcTimeout, headers.GrpcTimeout());
50 0 : }
51 :
52 0 : template <class T> static void addGrpcResponseTags(Span& span, const T& headers) {
53 0 : addTagIfNotNull(span, Tracing::Tags::get().GrpcStatusCode, headers.GrpcStatus());
54 0 : addTagIfNotNull(span, Tracing::Tags::get().GrpcMessage, headers.GrpcMessage());
55 : // Set error tag when Grpc status code represents an upstream error. See
56 : // https://github.com/envoyproxy/envoy/issues/18877.
57 0 : absl::optional<Grpc::Status::GrpcStatus> grpc_status_code = Grpc::Common::getGrpcStatus(headers);
58 0 : if (grpc_status_code.has_value()) {
59 0 : const auto& status = grpc_status_code.value();
60 0 : if (status != Grpc::Status::WellKnownGrpcStatus::InvalidCode) {
61 0 : switch (status) {
62 : // Each case below is considered to be a client side error, therefore should not be
63 : // tagged as an upstream error. See https://grpc.github.io/grpc/core/md_doc_statuscodes.html
64 : // for more details about how each Grpc status code is defined and whether it is an
65 : // upstream error or a client error.
66 0 : case Grpc::Status::WellKnownGrpcStatus::Ok:
67 0 : case Grpc::Status::WellKnownGrpcStatus::Canceled:
68 0 : case Grpc::Status::WellKnownGrpcStatus::InvalidArgument:
69 0 : case Grpc::Status::WellKnownGrpcStatus::NotFound:
70 0 : case Grpc::Status::WellKnownGrpcStatus::AlreadyExists:
71 0 : case Grpc::Status::WellKnownGrpcStatus::PermissionDenied:
72 0 : case Grpc::Status::WellKnownGrpcStatus::FailedPrecondition:
73 0 : case Grpc::Status::WellKnownGrpcStatus::Aborted:
74 0 : case Grpc::Status::WellKnownGrpcStatus::OutOfRange:
75 0 : case Grpc::Status::WellKnownGrpcStatus::Unauthenticated:
76 0 : break;
77 0 : case Grpc::Status::WellKnownGrpcStatus::Unknown:
78 0 : case Grpc::Status::WellKnownGrpcStatus::DeadlineExceeded:
79 0 : case Grpc::Status::WellKnownGrpcStatus::Unimplemented:
80 0 : case Grpc::Status::WellKnownGrpcStatus::ResourceExhausted:
81 0 : case Grpc::Status::WellKnownGrpcStatus::Internal:
82 0 : case Grpc::Status::WellKnownGrpcStatus::Unavailable:
83 0 : case Grpc::Status::WellKnownGrpcStatus::DataLoss:
84 0 : span.setTag(Tracing::Tags::get().Error, Tracing::Tags::get().True);
85 0 : break;
86 0 : }
87 0 : }
88 0 : }
89 0 : }
90 :
91 0 : static void annotateVerbose(Span& span, const StreamInfo::StreamInfo& stream_info) {
92 0 : const auto start_time = stream_info.startTime();
93 0 : StreamInfo::TimingUtility timing(stream_info);
94 0 : if (timing.lastDownstreamRxByteReceived()) {
95 0 : span.log(start_time + std::chrono::duration_cast<SystemTime::duration>(
96 0 : *timing.lastDownstreamRxByteReceived()),
97 0 : Tracing::Logs::get().LastDownstreamRxByteReceived);
98 0 : }
99 0 : if (timing.firstUpstreamTxByteSent()) {
100 0 : span.log(start_time + std::chrono::duration_cast<SystemTime::duration>(
101 0 : *timing.firstUpstreamTxByteSent()),
102 0 : Tracing::Logs::get().FirstUpstreamTxByteSent);
103 0 : }
104 0 : if (timing.lastUpstreamTxByteSent()) {
105 0 : span.log(start_time +
106 0 : std::chrono::duration_cast<SystemTime::duration>(*timing.lastUpstreamTxByteSent()),
107 0 : Tracing::Logs::get().LastUpstreamTxByteSent);
108 0 : }
109 0 : if (timing.firstUpstreamRxByteReceived()) {
110 0 : span.log(start_time + std::chrono::duration_cast<SystemTime::duration>(
111 0 : *timing.firstUpstreamRxByteReceived()),
112 0 : Tracing::Logs::get().FirstUpstreamRxByteReceived);
113 0 : }
114 0 : if (timing.lastUpstreamRxByteReceived()) {
115 0 : span.log(start_time + std::chrono::duration_cast<SystemTime::duration>(
116 0 : *timing.lastUpstreamRxByteReceived()),
117 0 : Tracing::Logs::get().LastUpstreamRxByteReceived);
118 0 : }
119 0 : if (timing.firstDownstreamTxByteSent()) {
120 0 : span.log(start_time + std::chrono::duration_cast<SystemTime::duration>(
121 0 : *timing.firstDownstreamTxByteSent()),
122 0 : Tracing::Logs::get().FirstDownstreamTxByteSent);
123 0 : }
124 0 : if (timing.lastDownstreamTxByteSent()) {
125 0 : span.log(start_time + std::chrono::duration_cast<SystemTime::duration>(
126 0 : *timing.lastDownstreamTxByteSent()),
127 0 : Tracing::Logs::get().LastDownstreamTxByteSent);
128 0 : }
129 0 : }
130 :
131 : void HttpTracerUtility::finalizeDownstreamSpan(Span& span,
132 : const Http::RequestHeaderMap* request_headers,
133 : const Http::ResponseHeaderMap* response_headers,
134 : const Http::ResponseTrailerMap* response_trailers,
135 : const StreamInfo::StreamInfo& stream_info,
136 0 : const Config& tracing_config) {
137 : // Pre response data.
138 0 : if (request_headers) {
139 0 : if (request_headers->RequestId()) {
140 0 : span.setTag(Tracing::Tags::get().GuidXRequestId, request_headers->getRequestIdValue());
141 0 : }
142 0 : span.setTag(
143 0 : Tracing::Tags::get().HttpUrl,
144 0 : Http::Utility::buildOriginalUri(*request_headers, tracing_config.maxPathTagLength()));
145 0 : span.setTag(Tracing::Tags::get().HttpMethod, request_headers->getMethodValue());
146 0 : span.setTag(Tracing::Tags::get().DownstreamCluster,
147 0 : valueOrDefault(request_headers->EnvoyDownstreamServiceCluster(), "-"));
148 0 : span.setTag(Tracing::Tags::get().UserAgent, valueOrDefault(request_headers->UserAgent(), "-"));
149 0 : span.setTag(
150 0 : Tracing::Tags::get().HttpProtocol,
151 0 : Formatter::SubstitutionFormatUtils::protocolToStringOrDefault(stream_info.protocol()));
152 :
153 0 : const auto& remote_address = stream_info.downstreamAddressProvider().directRemoteAddress();
154 :
155 0 : if (remote_address->type() == Network::Address::Type::Ip) {
156 0 : const auto remote_ip = remote_address->ip();
157 0 : span.setTag(Tracing::Tags::get().PeerAddress, remote_ip->addressAsString());
158 0 : } else {
159 0 : span.setTag(Tracing::Tags::get().PeerAddress, remote_address->logicalName());
160 0 : }
161 :
162 0 : if (request_headers->ClientTraceId()) {
163 0 : span.setTag(Tracing::Tags::get().GuidXClientTraceId,
164 0 : request_headers->getClientTraceIdValue());
165 0 : }
166 :
167 0 : if (Grpc::Common::isGrpcRequestHeaders(*request_headers)) {
168 0 : addGrpcRequestTags(span, *request_headers);
169 0 : }
170 0 : }
171 :
172 0 : span.setTag(Tracing::Tags::get().RequestSize, std::to_string(stream_info.bytesReceived()));
173 0 : span.setTag(Tracing::Tags::get().ResponseSize, std::to_string(stream_info.bytesSent()));
174 :
175 0 : setCommonTags(span, stream_info, tracing_config);
176 0 : onUpstreamResponseHeaders(span, response_headers);
177 0 : onUpstreamResponseTrailers(span, response_trailers);
178 :
179 0 : span.finishSpan();
180 0 : }
181 :
182 : void HttpTracerUtility::finalizeUpstreamSpan(Span& span, const StreamInfo::StreamInfo& stream_info,
183 0 : const Config& tracing_config) {
184 0 : span.setTag(
185 0 : Tracing::Tags::get().HttpProtocol,
186 0 : Formatter::SubstitutionFormatUtils::protocolToStringOrDefault(stream_info.protocol()));
187 :
188 0 : if (stream_info.upstreamInfo() && stream_info.upstreamInfo()->upstreamHost()) {
189 0 : auto upstream_address = stream_info.upstreamInfo()->upstreamHost()->address();
190 : // TODO(wbpcode): separated `upstream_address` may be meaningful to the downstream span.
191 : // But for the upstream span, `peer.address` should be used.
192 0 : span.setTag(Tracing::Tags::get().UpstreamAddress, upstream_address->asStringView());
193 : // TODO(wbpcode): may be set this tag in the setCommonTags.
194 0 : span.setTag(Tracing::Tags::get().PeerAddress, upstream_address->asStringView());
195 0 : }
196 :
197 0 : setCommonTags(span, stream_info, tracing_config);
198 :
199 0 : span.finishSpan();
200 0 : }
201 :
202 : void HttpTracerUtility::onUpstreamResponseHeaders(Span& span,
203 0 : const Http::ResponseHeaderMap* response_headers) {
204 0 : if (response_headers && response_headers->GrpcStatus() != nullptr) {
205 0 : addGrpcResponseTags(span, *response_headers);
206 0 : }
207 0 : }
208 :
209 : void HttpTracerUtility::onUpstreamResponseTrailers(
210 0 : Span& span, const Http::ResponseTrailerMap* response_trailers) {
211 0 : if (response_trailers && response_trailers->GrpcStatus() != nullptr) {
212 0 : addGrpcResponseTags(span, *response_trailers);
213 0 : }
214 0 : }
215 :
216 : void HttpTracerUtility::setCommonTags(Span& span, const StreamInfo::StreamInfo& stream_info,
217 0 : const Config& tracing_config) {
218 :
219 0 : span.setTag(Tracing::Tags::get().Component, Tracing::Tags::get().Proxy);
220 :
221 : // Cluster info.
222 0 : if (auto cluster_info = stream_info.upstreamClusterInfo();
223 0 : cluster_info.has_value() && cluster_info.value() != nullptr) {
224 0 : span.setTag(Tracing::Tags::get().UpstreamCluster, cluster_info.value()->name());
225 0 : span.setTag(Tracing::Tags::get().UpstreamClusterName,
226 0 : cluster_info.value()->observabilityName());
227 0 : }
228 :
229 : // Post response data.
230 0 : span.setTag(Tracing::Tags::get().HttpStatusCode, buildResponseCode(stream_info));
231 0 : span.setTag(Tracing::Tags::get().ResponseFlags,
232 0 : StreamInfo::ResponseFlagUtils::toShortString(stream_info));
233 :
234 0 : if (tracing_config.verbose()) {
235 0 : annotateVerbose(span, stream_info);
236 0 : }
237 :
238 0 : if (!stream_info.responseCode() || Http::CodeUtility::is5xx(stream_info.responseCode().value())) {
239 0 : span.setTag(Tracing::Tags::get().Error, Tracing::Tags::get().True);
240 0 : }
241 :
242 0 : ReadOnlyHttpTraceContext trace_context{stream_info.getRequestHeaders() != nullptr
243 0 : ? *stream_info.getRequestHeaders()
244 0 : : *Http::StaticEmptyHeaders::get().request_headers};
245 0 : CustomTagContext ctx{trace_context, stream_info};
246 0 : if (const CustomTagMap* custom_tag_map = tracing_config.customTags(); custom_tag_map) {
247 0 : for (const auto& it : *custom_tag_map) {
248 0 : it.second->applySpan(span, ctx);
249 0 : }
250 0 : }
251 0 : }
252 :
253 : } // namespace Tracing
254 : } // namespace Envoy
|