Coverage Report

Created: 2024-09-19 09:45

/proc/self/cwd/source/common/stats/utility.h
Line
Count
Source
1
#pragma once
2
3
#include <string>
4
5
#include "envoy/stats/scope.h"
6
#include "envoy/stats/stats.h"
7
8
#include "source/common/common/thread.h"
9
#include "source/common/stats/symbol_table.h"
10
11
#include "absl/container/inlined_vector.h"
12
#include "absl/strings/string_view.h"
13
#include "absl/types/optional.h"
14
15
namespace Envoy {
16
namespace Stats {
17
18
/**
19
 * Represents a dynamically created stat name token based on absl::string_view.
20
 * This class wrapper is used in the 'Element' variant so that call-sites
21
 * can express explicit intent to create dynamic stat names, which are more
22
 * expensive than symbolic stat names. We use dynamic stat names only for
23
 * building stats based on names discovered in the line of a request.
24
 */
25
class DynamicName : public absl::string_view {
26
public:
27
  // This is intentionally left as an implicit conversion from string_view to
28
  // make call-sites easier to read, e.g.
29
  //    Utility::counterFromElements(*scope, {DynamicName("a"), DynamicName("b")});
30
8.00k
  explicit DynamicName(absl::string_view str) : absl::string_view(str) {}
31
};
32
33
/**
34
 * Represents a dynamically created stat name token based on std::string.
35
 * This class wrapper is used in the 'Element' variant so that call-sites
36
 * can express explicit intent to create dynamic stat names, which are more
37
 * expensive than symbolic stat names. We use dynamic stat names only for
38
 * building stats based on names discovered in the line of a request.
39
 *
40
 * Specifically, this class should be used only when the content of the string
41
 * can be changed between the object creation and usage.
42
 */
43
class DynamicSavedName : public std::string {
44
public:
45
  // This is intentionally left as an implicit conversion from string_view to
46
  // make call-sites easier to read, e.g.
47
  //    Utility::counterFromElements(*scope, {DynamicSavedName("a"), DynamicSavedName("b")});
48
2
  explicit DynamicSavedName(absl::string_view str) : std::string(str.begin(), str.end()) {}
49
};
50
51
/**
52
 * Holds either a symbolic StatName or a dynamic string, for the purpose of
53
 * composing a vector to pass to Utility::counterFromElements, etc. This is
54
 * a programming convenience to create joined stat names. It is easier to
55
 * call the above helpers than to use SymbolTable::join(), because the helpers
56
 * hide the memory management of the joined storage, and they allow easier
57
 * co-mingling of symbolic and dynamic stat-name components.
58
 */
59
using Element = absl::variant<StatName, DynamicName, DynamicSavedName>;
60
using ElementVec = absl::InlinedVector<Element, 8>;
61
62
/**
63
 * Common stats utility routines.
64
 */
65
namespace Utility {
66
/**
67
 * ':' is a reserved char in statsd. Do a character replacement to avoid
68
 * costly inline translations later.
69
 *
70
 * @param name the stat name to sanitize.
71
 * @return the sanitized stat name.
72
 */
73
std::string sanitizeStatsName(absl::string_view name);
74
75
/**
76
 * Finds a metric tag with the specified name.
77
 *
78
 * @param metric The metric in which the tag is expected to exist.
79
 * @param find_tag_name The name of the tag to search for.
80
 * @return The value of the tag, if found.
81
 */
82
absl::optional<StatName> findTag(const Metric& metric, StatName find_tag_name);
83
84
/**
85
 * Creates a nested scope from a vector of StatNames which are used to create the
86
 * name.
87
 *
88
 * See also scopeFromElements, which is slightly slower but allows
89
 * passing DynamicName(string)s as names.
90
 *
91
 * @param scope The scope in which to create the counter.
92
 * @param elements The vector of mixed DynamicName and StatName
93
 * @return A scope named using the joined elements.
94
 */
95
ScopeSharedPtr scopeFromStatNames(Scope& scope, const StatNameVec& names);
96
97
/**
98
 * Creates a counter from a vector of tokens which are used to create the
99
 * name. The tokens can be specified as DynamicName or StatName. For
100
 * tokens specified as DynamicName, a dynamic StatName will be created. See
101
 * https://github.com/envoyproxy/envoy/blob/main/source/docs/stats.md#dynamic-stat-tokens
102
 * for more detail on why symbolic StatNames are preferred when possible.
103
 *
104
 * See also counterFromStatNames, which is slightly faster but does not allow
105
 * passing DynamicName(string)s as names.
106
 *
107
 * @param scope The scope in which to create the counter.
108
 * @param elements The vector of mixed DynamicName and StatName
109
 * @param tags optionally specified tags.
110
 * @return A counter named using the joined elements.
111
 */
112
Counter& counterFromElements(Scope& scope, const ElementVec& elements,
113
                             StatNameTagVectorOptConstRef tags = absl::nullopt);
114
115
/**
116
 * Creates a counter from a vector of tokens which are used to create the
117
 * name. The tokens must be of type StatName.
118
 *
119
 * See also counterFromElements, which is slightly slower, but allows
120
 * passing DynamicName(string)s as elements.
121
 *
122
 * @param scope The scope in which to create the counter.
123
 * @param names The vector of StatNames
124
 * @param tags optionally specified tags.
125
 * @return A counter named using the joined elements.
126
 */
127
Counter& counterFromStatNames(Scope& scope, const StatNameVec& names,
128
                              StatNameTagVectorOptConstRef tags = absl::nullopt);
129
130
/**
131
 * Creates a gauge from a vector of tokens which are used to create the
132
 * name. The tokens can be specified as DynamicName or StatName. For
133
 * tokens specified as DynamicName, a dynamic StatName will be created. See
134
 * https://github.com/envoyproxy/envoy/blob/main/source/docs/stats.md#dynamic-stat-tokens
135
 * for more detail on why symbolic StatNames are preferred when possible.
136
 *
137
 * See also gaugeFromStatNames, which is slightly faster but does not allow
138
 * passing DynamicName(string)s as names.
139
 *
140
 * @param scope The scope in which to create the counter.
141
 * @param elements The vector of mixed DynamicName and StatName
142
 * @param import_mode Whether hot-restart should accumulate this value.
143
 * @param tags optionally specified tags.
144
 * @return A gauge named using the joined elements.
145
 */
146
Gauge& gaugeFromElements(Scope& scope, const ElementVec& elements, Gauge::ImportMode import_mode,
147
                         StatNameTagVectorOptConstRef tags = absl::nullopt);
148
149
/**
150
 * Creates a gauge from a vector of tokens which are used to create the
151
 * name. The tokens must be of type StatName.
152
 *
153
 * See also gaugeFromElements, which is slightly slower, but allows
154
 * passing DynamicName(string)s as elements.
155
 *
156
 * @param scope The scope in which to create the counter.
157
 * @param names The vector of StatNames
158
 * @param import_mode Whether hot-restart should accumulate this value.
159
 * @param tags optionally specified tags.
160
 * @return A gauge named using the joined elements.
161
 */
162
Gauge& gaugeFromStatNames(Scope& scope, const StatNameVec& elements, Gauge::ImportMode import_mode,
163
                          StatNameTagVectorOptConstRef tags = absl::nullopt);
164
165
/**
166
 * Creates a histogram from a vector of tokens which are used to create the
167
 * name. The tokens can be specified as DynamicName or StatName. For
168
 * tokens specified as DynamicName, a dynamic StatName will be created. See
169
 * https://github.com/envoyproxy/envoy/blob/main/source/docs/stats.md#dynamic-stat-tokens
170
 * for more detail on why symbolic StatNames are preferred when possible.
171
 *
172
 * See also histogramFromStatNames, which is slightly faster but does not allow
173
 * passing DynamicName(string)s as names.
174
 *
175
 * @param scope The scope in which to create the counter.
176
 * @param elements The vector of mixed DynamicName and StatName
177
 * @param unit The unit of measurement.
178
 * @param tags optionally specified tags.
179
 * @return A histogram named using the joined elements.
180
 */
181
Histogram& histogramFromElements(Scope& scope, const ElementVec& elements, Histogram::Unit unit,
182
                                 StatNameTagVectorOptConstRef tags = absl::nullopt);
183
184
/**
185
 * Creates a histogram from a vector of tokens which are used to create the
186
 * name. The tokens must be of type StatName.
187
 *
188
 * See also histogramFromElements, which is slightly slower, but allows
189
 * passing DynamicName(string)s as elements.
190
 *
191
 * @param scope The scope in which to create the counter.
192
 * @param elements The vector of mixed DynamicName and StatName
193
 * @param unit The unit of measurement.
194
 * @param tags optionally specified tags.
195
 * @return A histogram named using the joined elements.
196
 */
197
Histogram& histogramFromStatNames(Scope& scope, const StatNameVec& elements, Histogram::Unit unit,
198
                                  StatNameTagVectorOptConstRef tags = absl::nullopt);
199
200
/**
201
 * Creates a TextReadout from a vector of tokens which are used to create the
202
 * name. The tokens can be specified as DynamicName or StatName. For
203
 * tokens specified as DynamicName, a dynamic StatName will be created. See
204
 * https://github.com/envoyproxy/envoy/blob/main/source/docs/stats.md#dynamic-stat-tokens
205
 * for more detail on why symbolic StatNames are preferred when possible.
206
 *
207
 * See also TextReadoutFromStatNames, which is slightly faster but does not allow
208
 * passing DynamicName(string)s as names.
209
 *
210
 * @param scope The scope in which to create the counter.
211
 * @param elements The vector of mixed DynamicName and StatName
212
 * @param unit The unit of measurement.
213
 * @param tags optionally specified tags.
214
 * @return A TextReadout named using the joined elements.
215
 */
216
TextReadout& textReadoutFromElements(Scope& scope, const ElementVec& elements,
217
                                     StatNameTagVectorOptConstRef tags = absl::nullopt);
218
219
/**
220
 * Creates a TextReadout from a vector of tokens which are used to create the
221
 * name. The tokens must be of type StatName.
222
 *
223
 * See also TextReadoutFromElements, which is slightly slower, but allows
224
 * passing DynamicName(string)s as elements.
225
 *
226
 * @param scope The scope in which to create the counter.
227
 * @param elements The vector of mixed DynamicName and StatName
228
 * @param unit The unit of measurement.
229
 * @param tags optionally specified tags.
230
 * @return A TextReadout named using the joined elements.
231
 */
232
TextReadout& textReadoutFromStatNames(Scope& scope, const StatNameVec& elements,
233
                                      StatNameTagVectorOptConstRef tags = absl::nullopt);
234
235
} // namespace Utility
236
237
/**
238
 * Holds a reference to a stat by name. Note that the stat may not be created
239
 * yet at the time CachedReference is created. Calling get() then does a lazy
240
 * lookup, potentially returning absl::nullopt if the stat doesn't exist yet.
241
 * StatReference works whether the name was constructed symbolically, or with
242
 * StatNameDynamicStorage.
243
 *
244
 * Lookups are very slow, taking time proportional to the size of the scope,
245
 * holding mutexes during the lookup. However once the lookup succeeds, the
246
 * result is cached atomically, and further calls to get() are thus fast and
247
 * mutex-free. The implementation may be faster for stats that are named
248
 * symbolically.
249
 *
250
 * CachedReference is valid for the lifetime of the Scope. When the Scope
251
 * becomes invalid, CachedReferences must also be dropped as they will hold
252
 * pointers into the scope.
253
 */
254
template <class StatType> class CachedReference {
255
public:
256
  CachedReference(Scope& scope, absl::string_view name) : scope_(scope), name_(std::string(name)) {}
257
258
  /**
259
   * Finds the named stat, if it exists, returning it as an optional.
260
   */
261
  absl::optional<std::reference_wrapper<StatType>> get() {
262
    StatType* stat = stat_.get([this]() -> StatType* {
263
      StatType* stat = nullptr;
264
      IterateFn<StatType> check_stat = [this,
265
                                        &stat](const RefcountPtr<StatType>& shared_stat) -> bool {
266
        if (shared_stat->name() == name_) {
267
          stat = shared_stat.get();
268
          return false; // Stop iteration.
269
        }
270
        return true;
271
      };
272
      scope_.iterate(check_stat);
273
      return stat;
274
    });
275
    if (stat == nullptr) {
276
      return absl::nullopt;
277
    }
278
    return *stat;
279
  }
280
281
private:
282
  Scope& scope_;
283
  const std::string name_;
284
  Thread::AtomicPtr<StatType, Thread::AtomicPtrAllocMode::DoNotDelete> stat_;
285
};
286
287
} // namespace Stats
288
} // namespace Envoy