]> git.sesse.net Git - nageru/blob - metrics.h
Update the queue length metric after trimming, not before.
[nageru] / metrics.h
1 #ifndef _METRICS_H
2 #define _METRICS_H 1
3
4 // A simple global class to keep track of metrics export in Prometheus format.
5 // It would be better to use a more full-featured Prometheus client library for this,
6 // but it would introduce a dependency that is not commonly packaged in distributions,
7 // which makes it quite unwieldy. Thus, we'll package our own for the time being.
8
9 #include <atomic>
10 #include <map>
11 #include <memory>
12 #include <mutex>
13 #include <string>
14 #include <vector>
15
16 class Histogram;
17
18 // Prometheus recommends the use of timestamps instead of “time since event”,
19 // so you can use this to get the number of seconds since the epoch.
20 // Note that this will be wrong if your clock changes, so for non-metric use,
21 // you should use std::chrono::steady_clock instead.
22 double get_timestamp_for_metrics();
23
24 class Metrics {
25 public:
26         enum Type {
27                 TYPE_COUNTER,
28                 TYPE_GAUGE,
29                 TYPE_HISTOGRAM,  // Internal use only.
30         };
31         enum Laziness {
32                 PRINT_ALWAYS,
33                 PRINT_WHEN_NONEMPTY,
34         };
35
36         void add(const std::string &name, std::atomic<int64_t> *location, Type type = TYPE_COUNTER)
37         {
38                 add(name, {}, location, type);
39         }
40
41         void add(const std::string &name, std::atomic<double> *location, Type type = TYPE_COUNTER)
42         {
43                 add(name, {}, location, type);
44         }
45
46         void add(const std::string &name, Histogram *location)
47         {
48                 add(name, {}, location);
49         }
50
51         void add(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels, std::atomic<int64_t> *location, Type type = TYPE_COUNTER);
52         void add(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels, std::atomic<double> *location, Type type = TYPE_COUNTER);
53         void add(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels, Histogram *location, Laziness laziness = PRINT_ALWAYS);
54
55         void remove(const std::string &name)
56         {
57                 remove(name, {});
58         }
59
60         void remove(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels);
61
62         std::string serialize() const;
63
64 private:
65         static std::string serialize_name(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels);
66         static std::string serialize_labels(const std::vector<std::pair<std::string, std::string>> &labels);
67
68         enum DataType {
69                 DATA_TYPE_INT64,
70                 DATA_TYPE_DOUBLE,
71                 DATA_TYPE_HISTOGRAM,
72         };
73         struct MetricKey {
74                 MetricKey(const std::string &name, const std::vector<std::pair<std::string, std::string>> labels)
75                         : name(name), labels(labels), serialized_labels(serialize_labels(labels))
76                 {
77                 }
78
79                 bool operator< (const MetricKey &other) const
80                 {
81                         if (name != other.name)
82                                 return name < other.name;
83                         return serialized_labels < other.serialized_labels;
84                 }
85
86                 const std::string name;
87                 const std::vector<std::pair<std::string, std::string>> labels;
88                 const std::string serialized_labels;
89         };
90         struct Metric {
91                 DataType data_type;
92                 Laziness laziness;  // Only for TYPE_HISTOGRAM.
93                 union {
94                         std::atomic<int64_t> *location_int64;
95                         std::atomic<double> *location_double;
96                         Histogram *location_histogram;
97                 };
98         };
99
100         mutable std::mutex mu;
101         std::map<std::string, Type> types;  // Ordered the same as metrics.
102         std::map<MetricKey, Metric> metrics;
103         std::vector<Histogram> histograms;
104
105         friend class Histogram;
106 };
107
108 class Histogram {
109 public:
110         void init(const std::vector<double> &bucket_vals);
111         void init_uniform(size_t num_buckets);  // Sets up buckets 0..(N-1).
112         void init_geometric(double min, double max, size_t num_buckets);
113         void count_event(double val);
114         std::string serialize(Metrics::Laziness laziness, const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels) const;
115
116 private:
117         // Bucket <i> counts number of events where val[i - 1] < x <= val[i].
118         // The end histogram ends up being made into a cumulative one,
119         // but that's not how we store it here.
120         struct Bucket {
121                 double val;
122                 std::atomic<int64_t> count{0};
123         };
124         std::unique_ptr<Bucket[]> buckets;
125         size_t num_buckets;
126         std::atomic<double> sum{0.0};
127         std::atomic<int64_t> count_after_last_bucket{0};
128 };
129
130 extern Metrics global_metrics;
131
132 #endif  // !defined(_METRICS_H)