1
#include "source/common/grpc/context_impl.h"
2

            
3
#include <cstdint>
4
#include <memory>
5
#include <string>
6
#include <tuple>
7
#include <utility>
8

            
9
#include "source/common/grpc/common.h"
10
#include "source/common/stats/utility.h"
11

            
12
#include "absl/strings/str_replace.h"
13

            
14
namespace Envoy {
15
namespace Grpc {
16

            
17
ContextImpl::ContextImpl(Stats::SymbolTable& symbol_table)
18
50877
    : stat_name_pool_(symbol_table), grpc_(stat_name_pool_.add("grpc")),
19
50877
      grpc_web_(stat_name_pool_.add("grpc-web")), success_(stat_name_pool_.add("success")),
20
50877
      failure_(stat_name_pool_.add("failure")), total_(stat_name_pool_.add("total")),
21
50877
      zero_(stat_name_pool_.add("0")),
22
50877
      request_message_count_(stat_name_pool_.add("request_message_count")),
23
50877
      response_message_count_(stat_name_pool_.add("response_message_count")),
24
50877
      upstream_rq_time_(stat_name_pool_.add("upstream_rq_time")), stat_names_(symbol_table) {}
25

            
26
// Gets the stat prefix and underlying storage, depending on whether request_names is empty
27
Stats::ElementVec ContextImpl::statElements(Protocol protocol,
28
                                            const absl::optional<RequestStatNames>& request_names,
29
111
                                            Stats::Element suffix) {
30
111
  const Stats::StatName protocolName = protocolStatName(protocol);
31
111
  if (request_names) {
32
101
    return Stats::ElementVec{protocolName, request_names->service_, request_names->method_, suffix};
33
101
  }
34
10
  return Stats::ElementVec{protocolName, suffix};
35
111
}
36

            
37
void ContextImpl::chargeStat(const Upstream::ClusterInfo& cluster, Protocol protocol,
38
                             const absl::optional<RequestStatNames>& request_names,
39
108
                             const Http::HeaderEntry* grpc_status) {
40
108
  if (!grpc_status) {
41
64
    return;
42
64
  }
43

            
44
44
  absl::string_view status_str = grpc_status->value().getStringView();
45
44
  auto iter = stat_names_.status_names_.find(status_str);
46
44
  Stats::ElementVec elements =
47
44
      statElements(protocol, request_names,
48
44
                   (iter != stat_names_.status_names_.end()) ? Stats::Element(iter->second)
49
44
                                                             : Stats::DynamicName(status_str));
50
44
  Stats::Utility::counterFromElements(cluster.statsScope(), elements).inc();
51
44
  chargeStat(cluster, protocol, request_names, (status_str == "0"));
52
44
}
53

            
54
void ContextImpl::chargeStat(const Upstream::ClusterInfo& cluster, Protocol protocol,
55
53
                             const absl::optional<RequestStatNames>& request_names, bool success) {
56
53
  Stats::ElementVec elements = statElements(protocol, request_names, successStatName(success));
57
53
  Stats::Utility::counterFromElements(cluster.statsScope(), elements).inc();
58
53
  elements.back() = total_;
59
53
  Stats::Utility::counterFromElements(cluster.statsScope(), elements).inc();
60
53
}
61

            
62
void ContextImpl::chargeStat(const Upstream::ClusterInfo& cluster,
63
2
                             const absl::optional<RequestStatNames>& request_names, bool success) {
64
2
  chargeStat(cluster, Protocol::Grpc, request_names, success);
65
2
}
66

            
67
void ContextImpl::chargeRequestMessageStat(const Upstream::ClusterInfo& cluster,
68
                                           const absl::optional<RequestStatNames>& request_names,
69
6
                                           uint64_t amount) {
70
6
  Stats::ElementVec elements = statElements(Protocol::Grpc, request_names, request_message_count_);
71
6
  Stats::Utility::counterFromElements(cluster.statsScope(), elements).add(amount);
72
6
}
73

            
74
void ContextImpl::chargeResponseMessageStat(const Upstream::ClusterInfo& cluster,
75
                                            const absl::optional<RequestStatNames>& request_names,
76
6
                                            uint64_t amount) {
77
6
  Stats::ElementVec elements = statElements(Protocol::Grpc, request_names, response_message_count_);
78
6
  Stats::Utility::counterFromElements(cluster.statsScope(), elements).add(amount);
79
6
}
80

            
81
void ContextImpl::chargeUpstreamStat(const Upstream::ClusterInfo& cluster,
82
                                     const absl::optional<RequestStatNames>& request_names,
83
2
                                     std::chrono::milliseconds duration) {
84
2
  Stats::ElementVec elements = statElements(Protocol::Grpc, request_names, upstream_rq_time_);
85
2
  Stats::Utility::histogramFromElements(cluster.statsScope(), elements,
86
2
                                        Stats::Histogram::Unit::Milliseconds)
87
2
      .recordValue(duration.count());
88
2
}
89

            
90
absl::optional<ContextImpl::RequestStatNames>
91
167
ContextImpl::resolveDynamicServiceAndMethod(const Http::HeaderEntry* path) {
92
167
  absl::optional<Common::RequestNames> request_names = Common::resolveServiceAndMethod(path);
93

            
94
167
  if (!request_names) {
95
103
    return {};
96
103
  }
97

            
98
  // service/method will live until the request is finished to emit resulting stats (e.g. request
99
  // status) values in request_names_ might get changed, for example by routing rules (i.e.
100
  // "prefix_rewrite"), so we copy them here to preserve the initial value and get a proper stat
101
64
  Stats::Element service = Stats::DynamicSavedName(request_names->service_);
102
64
  Stats::Element method = Stats::DynamicSavedName(request_names->method_);
103
64
  return RequestStatNames{service, method};
104
167
}
105

            
106
absl::optional<ContextImpl::RequestStatNames>
107
7
ContextImpl::resolveDynamicServiceAndMethodWithDotReplaced(const Http::HeaderEntry* path) {
108
7
  absl::optional<Common::RequestNames> request_names = Common::resolveServiceAndMethod(path);
109
7
  if (!request_names) {
110
5
    return {};
111
5
  }
112
2
  Stats::Element service =
113
2
      Stats::DynamicSavedName(absl::StrReplaceAll(request_names->service_, {{".", "_"}}));
114
2
  Stats::Element method = Stats::DynamicSavedName(request_names->method_);
115
2
  return RequestStatNames{service, method};
116
7
}
117

            
118
} // namespace Grpc
119
} // namespace Envoy