aboutsummaryrefslogtreecommitdiffstats
path: root/metrics/src/vespa/metrics/metric.h
blob: 89b772bf75079dcb27993480f5ba1466e3755678 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once

#include "name_repo.h"
#include <vespa/vespalib/util/printable.h>
#include <vespa/vespalib/stllike/string.h>
#include <memory>

namespace metrics {

struct AbstractCountMetric;
struct AbstractValueMetric;
class Metric;
class MetricSet;
class MetricSnapshot;
class MemoryConsumption;

/** Implement class to visit metrics. */
struct MetricVisitor {
    virtual ~MetricVisitor() {}

    /**
     * Visit a snapshot. Return true to visit content of the snapshot
     */
    virtual bool visitSnapshot(const MetricSnapshot&) { return true; }
    virtual void doneVisitingSnapshot(const MetricSnapshot&) {}

    /**
     * Visit a metric set.
     *
     * @param autoGenerated True for metric sets that are generated on the
     *                      fly such as in sum metrics.
     * @return True if you want to visit the content of this metric set.
     */
    virtual bool visitMetricSet(const MetricSet&, bool autoGenerated)
        { (void) autoGenerated; return true; }

    /**
     * Callback visitors can use if they need to know the tree traversal of
     * metric sets. This function is not called if visitMetricSet returned
     * false.
     */
    virtual void doneVisitingMetricSet(const MetricSet&) {}

    /**
     * Visit a primitive metric within an accepted metric set.
     *
     * @return True if you want to continue visiting, false to abort.
     */
    virtual bool visitCountMetric(const AbstractCountMetric& m,
                                  bool autoGenerated);
    /**
     * Visit a primitive metric within an accepted metric set.
     *
     * @return True if you want to continue visiting, false to abort.
     */
    virtual bool visitValueMetric(const AbstractValueMetric& m,
                                  bool autoGenerated);

    /**
     * Visit function for visiting primitive metrics in one function. Only
     * called if not overriding specific primitive metric functions.
     */
    virtual bool visitMetric(const Metric&, bool autoGenerated);

    /**
     * To avoid having to destruct the visitor in order to get any post
     * processing done you can use this hook. (Only called by functions knowing
     * they are top level, so you have to call this manually if visiting metrics
     * directly, and not through manager)
     */
    virtual void doneVisiting() {}
};

/**
 * A tag is a simple key-value mapping associated with a metric. Tags are used
 * for either constraining the set of metrics reported to a consumer (e.g. "do
 * not include metrics tagged as 'partofsum'") or to represent dimensions for
 * metric sets or individual metrics. In the former case, the key represents
 * the tag and the value remains empty. In the latter (dimension) case, the key
 * is the dimension name and the value is the dimension value.
 */
struct Tag
{
    const vespalib::string& key() const { return NameRepo::tagKey(_key); }
    const vespalib::string& value() const { return NameRepo::tagValue(_value); }

    Tag(vespalib::stringref k);
    Tag(vespalib::stringref k, vespalib::stringref v);
    Tag(const Tag &) noexcept;
    Tag & operator = (const Tag &);
    Tag(Tag &&) noexcept = default;
    Tag & operator = (Tag &&) = default;
    ~Tag();

    bool hasValue() const { return (_value != TagValueId::empty_handle); }

private:
    TagKeyId _key;
    TagValueId _value;
};

class Metric : public vespalib::Printable
{
public:
    using String = vespalib::string;
    using stringref = vespalib::stringref;
    using UP = std::unique_ptr<Metric>;
    using SP = std::shared_ptr<Metric>;
    using Tags = std::vector<Tag>;

    Metric(const String& name,
           Tags dimensions,
           const String& description,
           MetricSet* owner = 0);

    Metric(const Metric& other, MetricSet* owner);
    Metric(const Metric& rhs);
    Metric & operator = (const Metric& rhs);
    Metric(Metric && rhs) = default;
    Metric & operator = (Metric && rhs) = default;
    ~Metric();

    const vespalib::string& getName() const { return NameRepo::metricName(_name); }
    /**
     * Get mangled name iff the metric contains any dimensions, otherwise
     * the original metric name is returned.
     */
    const vespalib::string& getMangledName() const {
        return NameRepo::metricName(_mangledName);
    }
    vespalib::string getPath() const;
    std::vector<String> getPathVector() const;
    const vespalib::string& getDescription() const { return NameRepo::description(_description); }
    const Tags& getTags() const { return _tags; }
    /** Return whether there exists a tag with a key equal to 'tag' */
    bool hasTag(const String& tag) const;

    enum CopyType { CLONE, INACTIVE };
    /**
     * The clone function will clone metrics to an identical subtree of
     * metrics. Clone is primarily used for load metrics that wants to clone
     * a template metric for each loadtype. But it should work generically.
     *
     * @param type If set to inactive, sum metrics will evaluate to primitives
     *             and metrics can save memory by knowing no updates are coming.
     * @param includeUnused When creating snapshots we do not want to include
     *             unused metrics, but while generating sum metric sum in active
     *             metrics we want to. This has no affect if type is CLONE.
     */
    virtual Metric* clone(std::vector<Metric::UP> &ownerList,
                          CopyType type, MetricSet* owner,
                          bool includeUnused = false) const = 0;

    /**
     * Utility function for assigning values from one metric of identical type
     * to this metric. For simplicity sake it does a const cast and calls
     * addToSnapshot, which should not alter source if reset is false. This can
     * not be used to copy between active metrics and inactive copies.
     */
    virtual Metric* assignValues(const Metric& m);

    /** Reset all metric values. */
    virtual void reset() = 0;

    void print(std::ostream& out, bool verbose,
                       const std::string& indent) const override {
        print(out, verbose, indent, 0);
    }
    virtual void print(std::ostream&, bool verbose, const std::string& indent,
                       uint64_t secondsPassed) const = 0;

    /**
     * Most metrics report numbers of some kind. To be able to report numbers
     * without having code to handle each possible metric type, these functions
     * exist to extract raw data to present easily.
     * @param id The part of the metric to extract. For instance, an average
     *           metric have average,
     */
    virtual int64_t getLongValue(stringref id) const = 0;
    virtual double getDoubleValue(stringref id) const = 0;

    /**
     * When snapshotting we need to be able to add data from one set of metrics
     * to another set of metrics taken at another time. MetricSet doesn't know
     * the type of the metrics it contains, so we need a generic function for
     * doing this. This function assumes metric given as input is of the exact
     * same type as the one it is called on for simplicity. This is true when
     * adding to snapshots as they have been created with clone and is thus
     * always exactly equal.
     *
     * @param m Metric of exact same type as this one. (Will core if wrong)
     * @param ownerList In case snapshot doesn't contain given metric, it can
     *                  create them and add them to ownerlist.
     */
    virtual void addToSnapshot(Metric& m, std::vector<Metric::UP> &ownerList) const = 0;

    /**
     * For sum metrics to work with metric sets, metric sets need operator+=.
     * To implement this, we need a function to add any metric type together.
     * This is different from adding to snapshot. When adding to snapshots we
     * add different time periods to the same metric, but when adding parts
     * together we add different metrics for the same time. For instance, an
     * average metric of queuesize, should just add new values to create new
     * average when adding to snapshot, but when adding parts, the averages
     * themselves should be added together.
     *
     * @param m Metric of exact same type as this one. (Will core if wrong)
     */
    virtual void addToPart(Metric& m) const = 0;

    virtual bool visit(MetricVisitor& visitor, bool tagAsAutoGenerated = false) const = 0;

    /** Used by sum metric to alter name of cloned metric for sum. */
    void setName(const String& name) {
        MetricNameId newName = NameRepo::metricId(name);
        _name = newName;
        assignMangledNameWithDimensions();
    }

    /** Used by sum metric to alter description of cloned metric for sum. */
    void setDescription(const vespalib::string& d) {
        _description = NameRepo::descriptionId(d);
    }
    /** Used by sum metric to alter tag of cloned metric for sum. */
    void setTags(Tags tags) {
        _tags = std::move(tags);
        assignMangledNameWithDimensions(); 
    }

    /** Set whether metrics have ever been set. */
    virtual bool used() const = 0;

    /** Returns true if this metric is registered in a metric set. */
    bool isRegistered() const { return (_owner != 0); }

    const MetricSet* getOwner() const { return _owner; }
    const MetricSet* getRoot() const;

    /** Used by metric set in register functions. */
    void setRegistered(MetricSet* owner) { _owner = owner; }

    virtual void addMemoryUsage(MemoryConsumption&) const;

    /** Print debug information of the metric tree. */
    virtual void printDebug(std::ostream&, const std::string& indent="") const;

    virtual bool isMetricSet() const { return false; }

    virtual bool is_sum_metric() const;

private:

    /**
     * A Tag instance is only considered to be a valid dimension if it contains
     * a value (i.e. not just a key as with legacy tags).
     */
    bool tagsSpecifyAtLeastOneDimension(const Tags&) const;

    /**
     * Iff _tags contains at least one valid dimension, update _mangledName to
     * be a string of the form 'metricname{key_0=value_0,key_1=value_1}' for
     * each pair of valid dimension key/values. _mangledName is left untouched
     * if no such valid dimension exists.
     */
    void assignMangledNameWithDimensions();

    /**
     * Sort _tags so that its entries are order in increasing lexicographic
     * order. Dimensions/tags must be in deterministic order for duplicate
     * detection based on name to work correctly.
     */
    void sortTagsInDeterministicOrder();

    vespalib::string createMangledNameWithDimensions() const;

    void verifyConstructionParameters();
    /**
     * Registers with metric set iff owner is not nullptr. This will implicitly
     * set _owner via a metric set callback to Metric::setRegistered().
     */
    void registerWithOwnerIfRequired(MetricSet* owner);

protected:
    MetricNameId _name;
    MetricNameId _mangledName;
    DescriptionId _description;
    std::vector<Tag> _tags;
    MetricSet* _owner;

};

} // metrics