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/formatter/substitution_format_utility.h"
16
#include "source/common/grpc/common.h"
17
#include "source/common/http/codes.h"
18
#include "source/common/http/header_map_impl.h"
19
#include "source/common/http/header_utility.h"
20
#include "source/common/http/headers.h"
21
#include "source/common/http/utility.h"
22
#include "source/common/protobuf/utility.h"
23
#include "source/common/stream_info/utility.h"
24

            
25
#include "absl/strings/string_view.h"
26

            
27
namespace Envoy {
28
namespace Tracing {
29

            
30
namespace {
31
constexpr absl::string_view HttpResponseCode0 = "0";
32
constexpr absl::string_view HttpResponseCode200 = "200";
33
constexpr absl::string_view DefaultValue = "-";
34
} // namespace
35

            
36
// Helper method to set http status code.
37
495
static void setSpanHttpStatusCode(Span& span, const StreamInfo::StreamInfo& stream_info) {
38
495
  if (!stream_info.responseCode()) {
39
44
    span.setTag(Tracing::Tags::get().HttpStatusCode, HttpResponseCode0);
40
469
  } else {
41
451
    const uint16_t code = stream_info.responseCode().value();
42
451
    if (code == 200) {
43
      // Optimization for the hottest path to avoid string conversion.
44
329
      span.setTag(Tracing::Tags::get().HttpStatusCode, HttpResponseCode200);
45
355
    } else {
46
122
      span.setTag(Tracing::Tags::get().HttpStatusCode, std::to_string(code));
47
122
    }
48
451
  }
49
495
}
50

            
51
static absl::string_view valueOrDefault(const Http::HeaderEntry* header,
52
126
                                        absl::string_view default_value) {
53
126
  return (header != nullptr) ? header->value().getStringView() : default_value;
54
126
}
55

            
56
24
static void addTagIfNotNull(Span& span, absl::string_view tag, const Http::HeaderEntry* entry) {
57
24
  if (entry != nullptr) {
58
22
    span.setTag(tag, entry->value().getStringView());
59
22
  }
60
24
}
61

            
62
3
static void addGrpcRequestTags(Span& span, const Http::RequestHeaderMap& headers) {
63
3
  addTagIfNotNull(span, Tracing::Tags::get().GrpcPath, headers.Path());
64
3
  addTagIfNotNull(span, Tracing::Tags::get().GrpcAuthority, headers.Host());
65
3
  addTagIfNotNull(span, Tracing::Tags::get().GrpcContentType, headers.ContentType());
66
3
  addTagIfNotNull(span, Tracing::Tags::get().GrpcTimeout, headers.GrpcTimeout());
67
3
}
68

            
69
6
template <class T> static void addGrpcResponseTags(Span& span, const T& headers) {
70
6
  addTagIfNotNull(span, Tracing::Tags::get().GrpcStatusCode, headers.GrpcStatus());
71
6
  addTagIfNotNull(span, Tracing::Tags::get().GrpcMessage, headers.GrpcMessage());
72
  // Set error tag when Grpc status code represents an upstream error. See
73
  // https://github.com/envoyproxy/envoy/issues/18877.
74
6
  absl::optional<Grpc::Status::GrpcStatus> grpc_status_code = Grpc::Common::getGrpcStatus(headers);
75
6
  if (grpc_status_code.has_value()) {
76
6
    const auto& status = grpc_status_code.value();
77
6
    if (status != Grpc::Status::WellKnownGrpcStatus::InvalidCode) {
78
6
      switch (status) {
79
      // Each case below is considered to be a client side error, therefore should not be
80
      // tagged as an upstream error. See https://grpc.github.io/grpc/core/md_doc_statuscodes.html
81
      // for more details about how each Grpc status code is defined and whether it is an
82
      // upstream error or a client error.
83
1
      case Grpc::Status::WellKnownGrpcStatus::Ok:
84
3
      case Grpc::Status::WellKnownGrpcStatus::Canceled:
85
3
      case Grpc::Status::WellKnownGrpcStatus::InvalidArgument:
86
3
      case Grpc::Status::WellKnownGrpcStatus::NotFound:
87
3
      case Grpc::Status::WellKnownGrpcStatus::AlreadyExists:
88
3
      case Grpc::Status::WellKnownGrpcStatus::PermissionDenied:
89
3
      case Grpc::Status::WellKnownGrpcStatus::FailedPrecondition:
90
3
      case Grpc::Status::WellKnownGrpcStatus::Aborted:
91
3
      case Grpc::Status::WellKnownGrpcStatus::OutOfRange:
92
3
      case Grpc::Status::WellKnownGrpcStatus::Unauthenticated:
93
3
        break;
94
      case Grpc::Status::WellKnownGrpcStatus::Unknown:
95
      case Grpc::Status::WellKnownGrpcStatus::DeadlineExceeded:
96
      case Grpc::Status::WellKnownGrpcStatus::Unimplemented:
97
      case Grpc::Status::WellKnownGrpcStatus::ResourceExhausted:
98
      case Grpc::Status::WellKnownGrpcStatus::Internal:
99
3
      case Grpc::Status::WellKnownGrpcStatus::Unavailable:
100
3
      case Grpc::Status::WellKnownGrpcStatus::DataLoss:
101
3
        span.setTag(Tracing::Tags::get().Error, Tracing::Tags::get().True);
102
3
        break;
103
6
      }
104
6
    }
105
6
  }
106
6
}
107

            
108
1
static void annotateVerbose(Span& span, const StreamInfo::StreamInfo& stream_info) {
109
1
  const auto start_time = stream_info.startTime();
110
1
  StreamInfo::TimingUtility timing(stream_info);
111
1
  if (timing.lastDownstreamRxByteReceived()) {
112
1
    span.log(start_time + std::chrono::duration_cast<SystemTime::duration>(
113
1
                              *timing.lastDownstreamRxByteReceived()),
114
1
             Tracing::Logs::get().LastDownstreamRxByteReceived);
115
1
  }
116
1
  if (timing.firstUpstreamTxByteSent()) {
117
1
    span.log(start_time + std::chrono::duration_cast<SystemTime::duration>(
118
1
                              *timing.firstUpstreamTxByteSent()),
119
1
             Tracing::Logs::get().FirstUpstreamTxByteSent);
120
1
  }
121
1
  if (timing.lastUpstreamTxByteSent()) {
122
1
    span.log(start_time +
123
1
                 std::chrono::duration_cast<SystemTime::duration>(*timing.lastUpstreamTxByteSent()),
124
1
             Tracing::Logs::get().LastUpstreamTxByteSent);
125
1
  }
126
1
  if (timing.firstUpstreamRxByteReceived()) {
127
1
    span.log(start_time + std::chrono::duration_cast<SystemTime::duration>(
128
1
                              *timing.firstUpstreamRxByteReceived()),
129
1
             Tracing::Logs::get().FirstUpstreamRxByteReceived);
130
1
  }
131
1
  if (timing.lastUpstreamRxByteReceived()) {
132
1
    span.log(start_time + std::chrono::duration_cast<SystemTime::duration>(
133
1
                              *timing.lastUpstreamRxByteReceived()),
134
1
             Tracing::Logs::get().LastUpstreamRxByteReceived);
135
1
  }
136
1
  if (timing.firstDownstreamTxByteSent()) {
137
1
    span.log(start_time + std::chrono::duration_cast<SystemTime::duration>(
138
1
                              *timing.firstDownstreamTxByteSent()),
139
1
             Tracing::Logs::get().FirstDownstreamTxByteSent);
140
1
  }
141
1
  if (timing.lastDownstreamTxByteSent()) {
142
1
    span.log(start_time + std::chrono::duration_cast<SystemTime::duration>(
143
1
                              *timing.lastDownstreamTxByteSent()),
144
1
             Tracing::Logs::get().LastDownstreamTxByteSent);
145
1
  }
146
1
}
147

            
148
void HttpTracerUtility::finalizeDownstreamSpan(Span& span,
149
                                               const Http::RequestHeaderMap* request_headers,
150
                                               const Http::ResponseHeaderMap* response_headers,
151
                                               const Http::ResponseTrailerMap* response_trailers,
152
                                               const StreamInfo::StreamInfo& stream_info,
153
67
                                               const Config& tracing_config) {
154
  // Pre response data.
155
67
  if (request_headers) {
156
63
    if (request_headers->RequestId()) {
157
57
      span.setTag(Tracing::Tags::get().GuidXRequestId, request_headers->getRequestIdValue());
158
57
    }
159
63
    span.setTag(
160
63
        Tracing::Tags::get().HttpUrl,
161
63
        Http::Utility::buildOriginalUri(*request_headers, tracing_config.maxPathTagLength()));
162
63
    span.setTag(Tracing::Tags::get().HttpMethod, request_headers->getMethodValue());
163
63
    span.setTag(Tracing::Tags::get().DownstreamCluster,
164
63
                valueOrDefault(request_headers->EnvoyDownstreamServiceCluster(), DefaultValue));
165
63
    span.setTag(Tracing::Tags::get().UserAgent,
166
63
                valueOrDefault(request_headers->UserAgent(), DefaultValue));
167
63
    span.setTag(
168
63
        Tracing::Tags::get().HttpProtocol,
169
63
        Formatter::SubstitutionFormatUtils::protocolToStringOrDefault(stream_info.protocol()));
170

            
171
63
    const auto& remote_address = stream_info.downstreamAddressProvider().directRemoteAddress();
172

            
173
63
    if (remote_address->type() == Network::Address::Type::Ip) {
174
62
      span.setTag(Tracing::Tags::get().PeerAddress, remote_address->ip()->addressAsString());
175
62
    } else {
176
1
      span.setTag(Tracing::Tags::get().PeerAddress, remote_address->logicalName());
177
1
    }
178

            
179
63
    if (request_headers->ClientTraceId()) {
180
1
      span.setTag(Tracing::Tags::get().GuidXClientTraceId,
181
1
                  request_headers->getClientTraceIdValue());
182
1
    }
183

            
184
63
    if (Grpc::Common::isGrpcRequestHeaders(*request_headers)) {
185
3
      addGrpcRequestTags(span, *request_headers);
186
3
    }
187
63
  }
188

            
189
67
  span.setTag(Tracing::Tags::get().RequestSize, std::to_string(stream_info.bytesReceived()));
190
67
  span.setTag(Tracing::Tags::get().ResponseSize, std::to_string(stream_info.bytesSent()));
191

            
192
67
  setCommonTags(span, stream_info, tracing_config, false);
193
67
  onUpstreamResponseHeaders(span, response_headers);
194
67
  onUpstreamResponseTrailers(span, response_trailers);
195

            
196
67
  span.finishSpan();
197
67
}
198

            
199
void HttpTracerUtility::finalizeUpstreamSpan(Span& span, const StreamInfo::StreamInfo& stream_info,
200
428
                                             const Config& tracing_config) {
201
428
  span.setTag(
202
428
      Tracing::Tags::get().HttpProtocol,
203
428
      Formatter::SubstitutionFormatUtils::protocolToStringOrDefault(stream_info.protocol()));
204

            
205
428
  if (stream_info.upstreamInfo() && stream_info.upstreamInfo()->upstreamHost()) {
206
423
    auto upstream_address = stream_info.upstreamInfo()->upstreamHost()->address();
207
    // TODO(wbpcode): separated `upstream_address` may be meaningful to the downstream span.
208
    // But for the upstream span, `peer.address` should be used.
209
423
    span.setTag(Tracing::Tags::get().UpstreamAddress, upstream_address->asStringView());
210
    // TODO(wbpcode): may be set this tag in the setCommonTags.
211
423
    span.setTag(Tracing::Tags::get().PeerAddress, upstream_address->asStringView());
212
423
  }
213

            
214
428
  setCommonTags(span, stream_info, tracing_config, true);
215

            
216
428
  span.finishSpan();
217
428
}
218

            
219
void HttpTracerUtility::onUpstreamResponseHeaders(Span& span,
220
88
                                                  const Http::ResponseHeaderMap* response_headers) {
221
88
  if (response_headers && response_headers->GrpcStatus() != nullptr) {
222
4
    addGrpcResponseTags(span, *response_headers);
223
4
  }
224
88
}
225

            
226
void HttpTracerUtility::onUpstreamResponseTrailers(
227
68
    Span& span, const Http::ResponseTrailerMap* response_trailers) {
228
68
  if (response_trailers && response_trailers->GrpcStatus() != nullptr) {
229
2
    addGrpcResponseTags(span, *response_trailers);
230
2
  }
231
68
}
232

            
233
void HttpTracerUtility::setCommonTags(Span& span, const StreamInfo::StreamInfo& stream_info,
234
495
                                      const Config& tracing_config, bool upstream_span) {
235

            
236
495
  span.setTag(Tracing::Tags::get().Component, Tracing::Tags::get().Proxy);
237

            
238
  // Cluster info.
239
495
  if (auto cluster_info = stream_info.upstreamClusterInfo();
240
495
      cluster_info.has_value() && cluster_info.value() != nullptr) {
241
477
    span.setTag(Tracing::Tags::get().UpstreamCluster, cluster_info.value()->name());
242
477
    span.setTag(Tracing::Tags::get().UpstreamClusterName,
243
477
                cluster_info.value()->observabilityName());
244
477
  }
245

            
246
495
  setSpanHttpStatusCode(span, stream_info);
247

            
248
495
  span.setTag(Tracing::Tags::get().ResponseFlags,
249
495
              StreamInfo::ResponseFlagUtils::toShortString(stream_info));
250

            
251
495
  if (tracing_config.verbose()) {
252
1
    annotateVerbose(span, stream_info);
253
1
  }
254

            
255
495
  if (!stream_info.responseCode() || Http::CodeUtility::is5xx(stream_info.responseCode().value())) {
256
140
    span.setTag(Tracing::Tags::get().Error, Tracing::Tags::get().True);
257
140
  }
258

            
259
495
  tracing_config.modifySpan(span, upstream_span);
260
495
}
261

            
262
} // namespace Tracing
263
} // namespace Envoy