]> git.sesse.net Git - nageru/blob - metrics.h
Fix some compiler warnings.
[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 <chrono>
11 #include <deque>
12 #include <map>
13 #include <memory>
14 #include <mutex>
15 #include <string>
16 #include <utility>
17 #include <vector>
18
19 class Histogram;
20 class Summary;
21
22 // Prometheus recommends the use of timestamps instead of “time since event”,
23 // so you can use this to get the number of seconds since the epoch.
24 // Note that this will be wrong if your clock changes, so for non-metric use,
25 // you should use std::chrono::steady_clock instead.
26 double get_timestamp_for_metrics();
27
28 class Metrics {
29 public:
30         enum Type {
31                 TYPE_COUNTER,
32                 TYPE_GAUGE,
33                 TYPE_HISTOGRAM,  // Internal use only.
34                 TYPE_SUMMARY,  // Internal use only.
35         };
36         enum Laziness {
37                 PRINT_ALWAYS,
38                 PRINT_WHEN_NONEMPTY,
39         };
40
41         void add(const std::string &name, std::atomic<int64_t> *location, Type type = TYPE_COUNTER)
42         {
43                 add(name, {}, location, type);
44         }
45
46         void add(const std::string &name, std::atomic<double> *location, Type type = TYPE_COUNTER)
47         {
48                 add(name, {}, location, type);
49         }
50
51         void add(const std::string &name, Histogram *location)
52         {
53                 add(name, {}, location);
54         }
55
56         void add(const std::string &name, Summary *location)
57         {
58                 add(name, {}, location);
59         }
60
61         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);
62         void add(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels, std::atomic<double> *location, Type type = TYPE_COUNTER);
63         void add(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels, Histogram *location, Laziness laziness = PRINT_ALWAYS);
64         void add(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels, Summary *location, Laziness laziness = PRINT_ALWAYS);
65
66         void remove(const std::string &name)
67         {
68                 remove(name, {});
69         }
70
71         void remove(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels);
72
73         std::string serialize() const;
74
75 private:
76         static std::string serialize_name(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels);
77         static std::string serialize_labels(const std::vector<std::pair<std::string, std::string>> &labels);
78
79         enum DataType {
80                 DATA_TYPE_INT64,
81                 DATA_TYPE_DOUBLE,
82                 DATA_TYPE_HISTOGRAM,
83                 DATA_TYPE_SUMMARY,
84         };
85         struct MetricKey {
86                 MetricKey(const std::string &name, const std::vector<std::pair<std::string, std::string>> labels)
87                         : name(name), labels(labels), serialized_labels(serialize_labels(labels))
88                 {
89                 }
90
91                 bool operator< (const MetricKey &other) const
92                 {
93                         if (name != other.name)
94                                 return name < other.name;
95                         return serialized_labels < other.serialized_labels;
96                 }
97
98                 const std::string name;
99                 const std::vector<std::pair<std::string, std::string>> labels;
100                 const std::string serialized_labels;
101         };
102         struct Metric {
103                 DataType data_type;
104                 Laziness laziness;  // Only for TYPE_HISTOGRAM.
105                 union {
106                         std::atomic<int64_t> *location_int64;
107                         std::atomic<double> *location_double;
108                         Histogram *location_histogram;
109                         Summary *location_summary;
110                 };
111         };
112
113         mutable std::mutex mu;
114         std::map<std::string, Type> types;  // Ordered the same as metrics.
115         std::map<MetricKey, Metric> metrics;
116
117         friend class Histogram;
118         friend class Summary;
119 };
120
121 class Histogram {
122 public:
123         void init(const std::vector<double> &bucket_vals);
124         void init_uniform(size_t num_buckets);  // Sets up buckets 0..(N-1).
125         void init_geometric(double min, double max, size_t num_buckets);
126         void count_event(double val);
127         std::string serialize(Metrics::Laziness laziness, const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels) const;
128
129 private:
130         // Bucket <i> counts number of events where val[i - 1] < x <= val[i].
131         // The end histogram ends up being made into a cumulative one,
132         // but that's not how we store it here.
133         struct Bucket {
134                 double val;
135                 std::atomic<int64_t> count{0};
136         };
137         std::unique_ptr<Bucket[]> buckets;
138         size_t num_buckets;
139         std::atomic<double> sum{0.0};
140         std::atomic<int64_t> count_after_last_bucket{0};
141 };
142
143 // This is a pretty dumb streaming quantile class, but it's exact, and we don't have
144 // too many values (typically one per frame, and one-minute interval), so we don't
145 // need anything fancy.
146 class Summary {
147 public:
148         void init(const std::vector<double> &quantiles, double window_seconds);
149         void count_event(double val);
150         std::string serialize(Metrics::Laziness laziness, const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels);
151
152 private:
153         std::vector<double> quantiles;
154         std::chrono::duration<double> window;
155
156         mutable std::mutex mu;
157         std::deque<std::pair<std::chrono::steady_clock::time_point, double>> values;
158         std::atomic<double> sum{0.0};
159         std::atomic<int64_t> count{0};
160 };
161
162 extern Metrics global_metrics;
163
164 #endif  // !defined(_METRICS_H)