Line data Source code
1 : #pragma once
2 :
3 : #include <algorithm>
4 : #include <cstring>
5 : #include <functional>
6 : #include <string>
7 :
8 : #include "envoy/stats/stats.h"
9 : #include "envoy/stats/store.h"
10 :
11 : #include "source/common/common/utility.h"
12 : #include "source/common/stats/allocator_impl.h"
13 : #include "source/common/stats/null_counter.h"
14 : #include "source/common/stats/null_gauge.h"
15 : #include "source/common/stats/symbol_table.h"
16 : #include "source/common/stats/tag_utility.h"
17 : #include "source/common/stats/utility.h"
18 :
19 : #include "absl/container/flat_hash_map.h"
20 :
21 : namespace Envoy {
22 : namespace Stats {
23 :
24 : /**
25 : * A stats cache template that is used by the isolated store.
26 : */
27 : template <class Base> class IsolatedStatsCache {
28 : public:
29 : using CounterAllocator = std::function<RefcountPtr<Base>(
30 : const TagUtility::TagStatNameJoiner& joiner, StatNameTagVectorOptConstRef tags)>;
31 : using GaugeAllocator =
32 : std::function<RefcountPtr<Base>(const TagUtility::TagStatNameJoiner& joiner,
33 : StatNameTagVectorOptConstRef tags, Gauge::ImportMode)>;
34 : using HistogramAllocator =
35 : std::function<RefcountPtr<Base>(const TagUtility::TagStatNameJoiner& joiner,
36 : StatNameTagVectorOptConstRef tags, Histogram::Unit)>;
37 : using TextReadoutAllocator =
38 : std::function<RefcountPtr<Base>(const TagUtility::TagStatNameJoiner& joiner,
39 : StatNameTagVectorOptConstRef tags, TextReadout::Type)>;
40 : using BaseOptConstRef = absl::optional<std::reference_wrapper<const Base>>;
41 :
42 21325 : IsolatedStatsCache(CounterAllocator alloc) : counter_alloc_(alloc) {}
43 21325 : IsolatedStatsCache(GaugeAllocator alloc) : gauge_alloc_(alloc) {}
44 21325 : IsolatedStatsCache(HistogramAllocator alloc) : histogram_alloc_(alloc) {}
45 21325 : IsolatedStatsCache(TextReadoutAllocator alloc) : text_readout_alloc_(alloc) {}
46 :
47 : Base& get(StatName prefix, StatName basename, StatNameTagVectorOptConstRef tags,
48 264650 : SymbolTable& symbol_table) {
49 264650 : TagUtility::TagStatNameJoiner joiner(prefix, basename, tags, symbol_table);
50 264650 : StatName name = joiner.nameWithTags();
51 264650 : auto stat = stats_.find(name);
52 264650 : if (stat != stats_.end()) {
53 41988 : return *stat->second;
54 41988 : }
55 :
56 222662 : RefcountPtr<Base> new_stat = counter_alloc_(joiner, tags);
57 222662 : stats_.emplace(new_stat->statName(), new_stat);
58 222662 : return *new_stat;
59 264650 : }
60 :
61 : Base& get(StatName prefix, StatName basename, StatNameTagVectorOptConstRef tags,
62 68595 : SymbolTable& symbol_table, Gauge::ImportMode import_mode) {
63 68595 : TagUtility::TagStatNameJoiner joiner(prefix, basename, tags, symbol_table);
64 68595 : StatName name = joiner.nameWithTags();
65 68595 : auto stat = stats_.find(name);
66 68595 : if (stat != stats_.end()) {
67 8675 : return *stat->second;
68 8675 : }
69 :
70 59920 : RefcountPtr<Base> new_stat = gauge_alloc_(joiner, tags, import_mode);
71 59920 : stats_.emplace(new_stat->statName(), new_stat);
72 59920 : return *new_stat;
73 68595 : }
74 :
75 : Base& get(StatName prefix, StatName basename, StatNameTagVectorOptConstRef tags,
76 18922 : SymbolTable& symbol_table, Histogram::Unit unit) {
77 18922 : TagUtility::TagStatNameJoiner joiner(prefix, basename, tags, symbol_table);
78 18922 : StatName name = joiner.nameWithTags();
79 18922 : auto stat = stats_.find(name);
80 18922 : if (stat != stats_.end()) {
81 0 : return *stat->second;
82 0 : }
83 :
84 18922 : RefcountPtr<Base> new_stat = histogram_alloc_(joiner, tags, unit);
85 18922 : stats_.emplace(new_stat->statName(), new_stat);
86 18922 : return *new_stat;
87 18922 : }
88 :
89 : Base& get(StatName prefix, StatName basename, StatNameTagVectorOptConstRef tags,
90 2 : SymbolTable& symbol_table, TextReadout::Type type) {
91 2 : TagUtility::TagStatNameJoiner joiner(prefix, basename, tags, symbol_table);
92 2 : StatName name = joiner.nameWithTags();
93 2 : auto stat = stats_.find(name);
94 2 : if (stat != stats_.end()) {
95 0 : return *stat->second;
96 0 : }
97 :
98 2 : RefcountPtr<Base> new_stat = text_readout_alloc_(joiner, tags, type);
99 2 : stats_.emplace(new_stat->statName(), new_stat);
100 2 : return *new_stat;
101 2 : }
102 :
103 0 : std::vector<RefcountPtr<Base>> toVector() const {
104 0 : std::vector<RefcountPtr<Base>> vec;
105 0 : vec.reserve(stats_.size());
106 0 : for (auto& stat : stats_) {
107 0 : vec.push_back(stat.second);
108 0 : }
109 :
110 0 : return vec;
111 0 : }
112 :
113 0 : bool iterate(const IterateFn<Base>& fn) const {
114 0 : for (auto& stat : stats_) {
115 0 : if (!fn(stat.second)) {
116 0 : return false;
117 0 : }
118 0 : }
119 0 : return true;
120 0 : }
121 :
122 39 : void forEachStat(SizeFn f_size, StatFn<Base> f_stat) const {
123 39 : if (f_size != nullptr) {
124 39 : f_size(stats_.size());
125 39 : }
126 1630 : for (auto const& stat : stats_) {
127 1630 : f_stat(*stat.second);
128 1630 : }
129 39 : }
130 :
131 0 : BaseOptConstRef find(StatName name) const {
132 0 : auto stat = stats_.find(name);
133 0 : if (stat == stats_.end()) {
134 0 : return absl::nullopt;
135 0 : }
136 0 : return std::cref(*stat->second);
137 0 : }
138 :
139 : private:
140 : StatNameHashMap<RefcountPtr<Base>> stats_;
141 : CounterAllocator counter_alloc_;
142 : GaugeAllocator gauge_alloc_;
143 : HistogramAllocator histogram_alloc_;
144 : TextReadoutAllocator text_readout_alloc_;
145 : };
146 :
147 : // Isolated implementation of Stats::Store. This class is not thread-safe by
148 : // itself, but a thread-safe wrapper can be built, e.g. TestIsolatedStoreImpl
149 : // in test/integration/server.h.
150 : class IsolatedStoreImpl : public Store {
151 : public:
152 : IsolatedStoreImpl();
153 : explicit IsolatedStoreImpl(SymbolTable& symbol_table);
154 : ~IsolatedStoreImpl() override;
155 :
156 : // Stats::Store
157 0 : const SymbolTable& constSymbolTable() const override { return alloc_.constSymbolTable(); }
158 1575829 : SymbolTable& symbolTable() override { return alloc_.symbolTable(); }
159 :
160 360 : void deliverHistogramToSinks(const Histogram&, uint64_t) override {}
161 : ScopeSharedPtr rootScope() override;
162 : ConstScopeSharedPtr constRootScope() const override;
163 0 : std::vector<CounterSharedPtr> counters() const override { return counters_.toVector(); }
164 0 : std::vector<GaugeSharedPtr> gauges() const override {
165 : // TODO(jmarantz): should we filter out gauges where
166 : // gauge.importMode() != Gauge::ImportMode::Uninitialized ?
167 : // I don't think this matters because that should only occur for gauges
168 : // received in a hot-restart transfer, and isolated-store gauges should
169 : // never be transmitted that way.
170 0 : return gauges_.toVector();
171 0 : }
172 0 : std::vector<ParentHistogramSharedPtr> histograms() const override {
173 0 : return std::vector<ParentHistogramSharedPtr>{};
174 0 : }
175 0 : std::vector<TextReadoutSharedPtr> textReadouts() const override {
176 0 : return text_readouts_.toVector();
177 0 : }
178 :
179 13 : void forEachCounter(SizeFn f_size, StatFn<Counter> f_stat) const override {
180 13 : counters_.forEachStat(f_size, f_stat);
181 13 : }
182 :
183 13 : void forEachGauge(SizeFn f_size, StatFn<Gauge> f_stat) const override {
184 13 : gauges_.forEachStat(f_size, f_stat);
185 13 : }
186 :
187 13 : void forEachTextReadout(SizeFn f_size, StatFn<TextReadout> f_stat) const override {
188 13 : text_readouts_.forEachStat(f_size, f_stat);
189 13 : }
190 :
191 0 : void forEachHistogram(SizeFn f_size, StatFn<ParentHistogram> f_stat) const override {
192 0 : UNREFERENCED_PARAMETER(f_size);
193 0 : UNREFERENCED_PARAMETER(f_stat);
194 0 : }
195 :
196 0 : void forEachScope(SizeFn f_size, StatFn<const Scope> f_stat) const override {
197 0 : if (f_size != nullptr) {
198 0 : f_size(scopes_.size() + 1);
199 0 : }
200 0 : f_stat(*constRootScope());
201 0 : for (const ScopeSharedPtr& scope : scopes_) {
202 0 : f_stat(*scope);
203 0 : }
204 0 : }
205 :
206 13 : void forEachSinkedCounter(SizeFn f_size, StatFn<Counter> f_stat) const override {
207 13 : forEachCounter(f_size, f_stat);
208 13 : }
209 :
210 13 : void forEachSinkedGauge(SizeFn f_size, StatFn<Gauge> f_stat) const override {
211 13 : forEachGauge(f_size, f_stat);
212 13 : }
213 :
214 13 : void forEachSinkedTextReadout(SizeFn f_size, StatFn<TextReadout> f_stat) const override {
215 13 : forEachTextReadout(f_size, f_stat);
216 13 : }
217 :
218 13 : void forEachSinkedHistogram(SizeFn f_size, StatFn<ParentHistogram> f_stat) const override {
219 13 : UNREFERENCED_PARAMETER(f_size);
220 13 : UNREFERENCED_PARAMETER(f_stat);
221 13 : }
222 :
223 0 : NullCounterImpl& nullCounter() override { return *null_counter_; }
224 0 : NullGaugeImpl& nullGauge() override { return *null_gauge_; }
225 :
226 0 : bool iterate(const IterateFn<Counter>& fn) const override {
227 0 : return constRootScope()->iterate(fn);
228 0 : }
229 0 : bool iterate(const IterateFn<Gauge>& fn) const override { return constRootScope()->iterate(fn); }
230 0 : bool iterate(const IterateFn<Histogram>& fn) const override {
231 0 : return constRootScope()->iterate(fn);
232 0 : }
233 0 : bool iterate(const IterateFn<TextReadout>& fn) const override {
234 0 : return constRootScope()->iterate(fn);
235 0 : }
236 :
237 0 : void extractAndAppendTags(StatName, StatNamePool&, StatNameTagVector&) override {}
238 0 : void extractAndAppendTags(absl::string_view, StatNamePool&, StatNameTagVector&) override {}
239 0 : const TagVector& fixedTags() override { CONSTRUCT_ON_FIRST_USE(TagVector); }
240 :
241 : protected:
242 : /**
243 : * Provides a hook for sub-classes to define how to create new scopes. When
244 : * subclassing IsolatedStoreImpl you likely want to also subclass
245 : * IsolatedScopeImpl. Overriding this method enables scopes of the appropriate
246 : * type to be created from Store::rootScope(), Scope::scopeFromStatName, and
247 : * Scope::createScope, without needing to override those. makeScope is usually
248 : * implemented by "return std::make_shared<YourScopeType>(name, *this)".
249 : *
250 : * @param name the fully qualified stat name -- no further prefixing needed.
251 : */
252 : virtual ScopeSharedPtr makeScope(StatName name);
253 :
254 : private:
255 : friend class IsolatedScopeImpl;
256 :
257 : IsolatedStoreImpl(std::unique_ptr<SymbolTable>&& symbol_table);
258 :
259 : SymbolTablePtr symbol_table_storage_;
260 : AllocatorImpl alloc_;
261 : IsolatedStatsCache<Counter> counters_;
262 : IsolatedStatsCache<Gauge> gauges_;
263 : IsolatedStatsCache<Histogram> histograms_;
264 : IsolatedStatsCache<TextReadout> text_readouts_;
265 : RefcountPtr<NullCounterImpl> null_counter_;
266 : RefcountPtr<NullGaugeImpl> null_gauge_;
267 :
268 : // We construct the default-scope lazily to allow subclasses to override
269 : // makeScope(), making it easier to share infrastructure across subclasses
270 : // around managing scopes. Since you can't call an overridden virtual method
271 : // from a parent-class constructor, we instantiate this lazily after the
272 : // object has been fully constructed.
273 : //
274 : // This technically does not need to be declared mutable, but is conceptually
275 : // mutable because we can update this in the const method constRootScope().
276 : mutable ScopeSharedPtr lazy_default_scope_;
277 :
278 : std::vector<ScopeSharedPtr> scopes_;
279 : };
280 :
281 : class IsolatedScopeImpl : public Scope {
282 : public:
283 : IsolatedScopeImpl(const std::string& prefix, IsolatedStoreImpl& store)
284 0 : : prefix_(prefix, store.symbolTable()), store_(store) {}
285 :
286 : IsolatedScopeImpl(StatName prefix, IsolatedStoreImpl& store)
287 24713 : : prefix_(prefix, store.symbolTable()), store_(store) {}
288 :
289 24713 : ~IsolatedScopeImpl() override { prefix_.free(store_.symbolTable()); }
290 :
291 : // Stats::Scope
292 1428747 : SymbolTable& symbolTable() override { return store_.symbolTable(); }
293 0 : const SymbolTable& constSymbolTable() const override { return store_.symbolTable(); }
294 : Counter& counterFromStatNameWithTags(const StatName& name,
295 264650 : StatNameTagVectorOptConstRef tags) override {
296 264650 : return store_.counters_.get(prefix(), name, tags, symbolTable());
297 264650 : }
298 : ScopeSharedPtr createScope(const std::string& name) override;
299 : ScopeSharedPtr scopeFromStatName(StatName name) override;
300 : Gauge& gaugeFromStatNameWithTags(const StatName& name, StatNameTagVectorOptConstRef tags,
301 68595 : Gauge::ImportMode import_mode) override {
302 68595 : Gauge& gauge = store_.gauges_.get(prefix(), name, tags, symbolTable(), import_mode);
303 68595 : gauge.mergeImportMode(import_mode);
304 68595 : return gauge;
305 68595 : }
306 : Histogram& histogramFromStatNameWithTags(const StatName& name, StatNameTagVectorOptConstRef tags,
307 18922 : Histogram::Unit unit) override {
308 18922 : return store_.histograms_.get(prefix(), name, tags, symbolTable(), unit);
309 18922 : }
310 : TextReadout& textReadoutFromStatNameWithTags(const StatName& name,
311 2 : StatNameTagVectorOptConstRef tags) override {
312 2 : return store_.text_readouts_.get(prefix(), name, tags, symbolTable(),
313 2 : TextReadout::Type::Default);
314 2 : }
315 0 : CounterOptConstRef findCounter(StatName name) const override {
316 0 : return store_.counters_.find(name);
317 0 : }
318 0 : GaugeOptConstRef findGauge(StatName name) const override { return store_.gauges_.find(name); }
319 0 : HistogramOptConstRef findHistogram(StatName name) const override {
320 0 : return store_.histograms_.find(name);
321 0 : }
322 0 : TextReadoutOptConstRef findTextReadout(StatName name) const override {
323 0 : return store_.text_readouts_.find(name);
324 0 : }
325 :
326 0 : bool iterate(const IterateFn<Counter>& fn) const override {
327 0 : return store_.counters_.iterate(iterFilter(fn));
328 0 : }
329 0 : bool iterate(const IterateFn<Gauge>& fn) const override {
330 0 : return store_.gauges_.iterate(iterFilter(fn));
331 0 : }
332 0 : bool iterate(const IterateFn<Histogram>& fn) const override {
333 0 : return store_.histograms_.iterate(iterFilter(fn));
334 0 : }
335 0 : bool iterate(const IterateFn<TextReadout>& fn) const override {
336 0 : return store_.text_readouts_.iterate(iterFilter(fn));
337 0 : }
338 :
339 63665 : Counter& counterFromString(const std::string& name) override {
340 63665 : StatNameManagedStorage storage(name, symbolTable());
341 63665 : return counterFromStatName(storage.statName());
342 63665 : }
343 15964 : Gauge& gaugeFromString(const std::string& name, Gauge::ImportMode import_mode) override {
344 15964 : StatNameManagedStorage storage(name, symbolTable());
345 15964 : return gaugeFromStatName(storage.statName(), import_mode);
346 15964 : }
347 923 : Histogram& histogramFromString(const std::string& name, Histogram::Unit unit) override {
348 923 : StatNameManagedStorage storage(name, symbolTable());
349 923 : return histogramFromStatName(storage.statName(), unit);
350 923 : }
351 0 : TextReadout& textReadoutFromString(const std::string& name) override {
352 0 : StatNameManagedStorage storage(name, symbolTable());
353 0 : return textReadoutFromStatName(storage.statName());
354 0 : }
355 :
356 623697 : StatName prefix() const override { return prefix_.statName(); }
357 0 : IsolatedStoreImpl& store() override { return store_; }
358 0 : const IsolatedStoreImpl& constStore() const override { return store_; }
359 :
360 : protected:
361 5127 : void addScopeToStore(const ScopeSharedPtr& scope) { store_.scopes_.push_back(scope); }
362 :
363 : private:
364 0 : template <class StatType> IterateFn<StatType> iterFilter(const IterateFn<StatType>& fn) const {
365 : // We determine here what's in the scope by looking at name
366 : // prefixes. Strictly speaking this is not correct, as the same stat can be
367 : // in different scopes, e.g. counter "b.c" in scope "a", and counter "c"
368 : // created in scope "a.b".
369 : //
370 : // There is currently no mechanism to resurrect actual membership of a stat
371 : // in a scope, so we go by name matching. Note that this hack is not needed
372 : // in `ThreadLocalStore`, which has accurate maps describing which stats are
373 : // in which scopes.
374 : //
375 : // TODO(jmarantz): In the scope of this limited implementation, it would be
376 : // faster to match on the StatName prefix. This would be possible if
377 : // SymbolTable exposed a split() method.
378 0 : std::string prefix_str = constSymbolTable().toString(prefix_.statName());
379 0 : if (!prefix_str.empty() && !absl::EndsWith(prefix_str, ".")) {
380 0 : prefix_str += ".";
381 0 : }
382 0 : return [fn, prefix_str](const RefcountPtr<StatType>& stat) -> bool {
383 0 : return !absl::StartsWith(stat->name(), prefix_str) || fn(stat);
384 0 : };
385 0 : }
386 :
387 : StatNameStorage prefix_;
388 : IsolatedStoreImpl& store_;
389 : };
390 :
391 : } // namespace Stats
392 : } // namespace Envoy
|