Line data Source code
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 4805 : 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 0 : 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
|