Coverage Report

Created: 2023-11-12 09:30

/proc/self/cwd/source/common/tracing/http_tracer_impl.cc
Line
Count
Source (jump to first uncovered line)
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
95
static std::string buildResponseCode(const StreamInfo::StreamInfo& info) {
31
95
  return info.responseCode() ? std::to_string(info.responseCode().value()) : "0";
32
95
}
33
34
static absl::string_view valueOrDefault(const Http::HeaderEntry* header,
35
190
                                        const char* default_value) {
36
190
  return header ? header->value().getStringView() : default_value;
37
190
}
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
}
Unexecuted instantiation: http_tracer_impl.cc:void Envoy::Tracing::addGrpcResponseTags<Envoy::Http::ResponseHeaderMap>(Envoy::Tracing::Span&, Envoy::Http::ResponseHeaderMap const&)
Unexecuted instantiation: http_tracer_impl.cc:void Envoy::Tracing::addGrpcResponseTags<Envoy::Http::ResponseTrailerMap>(Envoy::Tracing::Span&, Envoy::Http::ResponseTrailerMap const&)
90
91
17
static void annotateVerbose(Span& span, const StreamInfo::StreamInfo& stream_info) {
92
17
  const auto start_time = stream_info.startTime();
93
17
  StreamInfo::TimingUtility timing(stream_info);
94
17
  if (timing.lastDownstreamRxByteReceived()) {
95
17
    span.log(start_time + std::chrono::duration_cast<SystemTime::duration>(
96
17
                              *timing.lastDownstreamRxByteReceived()),
97
17
             Tracing::Logs::get().LastDownstreamRxByteReceived);
98
17
  }
99
17
  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
17
  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
17
  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
17
  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
17
  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
17
  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
17
}
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
95
                                               const Config& tracing_config) {
137
  // Pre response data.
138
95
  if (request_headers) {
139
95
    if (request_headers->RequestId()) {
140
66
      span.setTag(Tracing::Tags::get().GuidXRequestId, request_headers->getRequestIdValue());
141
66
    }
142
95
    span.setTag(
143
95
        Tracing::Tags::get().HttpUrl,
144
95
        Http::Utility::buildOriginalUri(*request_headers, tracing_config.maxPathTagLength()));
145
95
    span.setTag(Tracing::Tags::get().HttpMethod, request_headers->getMethodValue());
146
95
    span.setTag(Tracing::Tags::get().DownstreamCluster,
147
95
                valueOrDefault(request_headers->EnvoyDownstreamServiceCluster(), "-"));
148
95
    span.setTag(Tracing::Tags::get().UserAgent, valueOrDefault(request_headers->UserAgent(), "-"));
149
95
    span.setTag(
150
95
        Tracing::Tags::get().HttpProtocol,
151
95
        Formatter::SubstitutionFormatUtils::protocolToStringOrDefault(stream_info.protocol()));
152
153
95
    const auto& remote_address = stream_info.downstreamAddressProvider().directRemoteAddress();
154
155
95
    if (remote_address->type() == Network::Address::Type::Ip) {
156
95
      const auto remote_ip = remote_address->ip();
157
95
      span.setTag(Tracing::Tags::get().PeerAddress, remote_ip->addressAsString());
158
95
    } else {
159
0
      span.setTag(Tracing::Tags::get().PeerAddress, remote_address->logicalName());
160
0
    }
161
162
95
    if (request_headers->ClientTraceId()) {
163
0
      span.setTag(Tracing::Tags::get().GuidXClientTraceId,
164
0
                  request_headers->getClientTraceIdValue());
165
0
    }
166
167
95
    if (Grpc::Common::isGrpcRequestHeaders(*request_headers)) {
168
0
      addGrpcRequestTags(span, *request_headers);
169
0
    }
170
95
  }
171
172
95
  span.setTag(Tracing::Tags::get().RequestSize, std::to_string(stream_info.bytesReceived()));
173
95
  span.setTag(Tracing::Tags::get().ResponseSize, std::to_string(stream_info.bytesSent()));
174
175
95
  setCommonTags(span, stream_info, tracing_config);
176
95
  onUpstreamResponseHeaders(span, response_headers);
177
95
  onUpstreamResponseTrailers(span, response_trailers);
178
179
95
  span.finishSpan();
180
95
}
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
95
                                                  const Http::ResponseHeaderMap* response_headers) {
204
95
  if (response_headers && response_headers->GrpcStatus() != nullptr) {
205
0
    addGrpcResponseTags(span, *response_headers);
206
0
  }
207
95
}
208
209
void HttpTracerUtility::onUpstreamResponseTrailers(
210
95
    Span& span, const Http::ResponseTrailerMap* response_trailers) {
211
95
  if (response_trailers && response_trailers->GrpcStatus() != nullptr) {
212
0
    addGrpcResponseTags(span, *response_trailers);
213
0
  }
214
95
}
215
216
void HttpTracerUtility::setCommonTags(Span& span, const StreamInfo::StreamInfo& stream_info,
217
95
                                      const Config& tracing_config) {
218
219
95
  span.setTag(Tracing::Tags::get().Component, Tracing::Tags::get().Proxy);
220
221
  // Cluster info.
222
95
  if (auto cluster_info = stream_info.upstreamClusterInfo();
223
95
      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
95
  span.setTag(Tracing::Tags::get().HttpStatusCode, buildResponseCode(stream_info));
231
95
  span.setTag(Tracing::Tags::get().ResponseFlags,
232
95
              StreamInfo::ResponseFlagUtils::toShortString(stream_info));
233
234
95
  if (tracing_config.verbose()) {
235
17
    annotateVerbose(span, stream_info);
236
17
  }
237
238
95
  if (!stream_info.responseCode() || Http::CodeUtility::is5xx(stream_info.responseCode().value())) {
239
95
    span.setTag(Tracing::Tags::get().Error, Tracing::Tags::get().True);
240
95
  }
241
242
95
  CustomTagContext ctx{stream_info.getRequestHeaders(), stream_info};
243
95
  if (const CustomTagMap* custom_tag_map = tracing_config.customTags(); custom_tag_map) {
244
0
    for (const auto& it : *custom_tag_map) {
245
0
      it.second->applySpan(span, ctx);
246
0
    }
247
0
  }
248
95
}
249
250
} // namespace Tracing
251
} // namespace Envoy