]> git.sesse.net Git - nageru/blob - metrics.h
Start times should be gauges, not counters.
[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
32         void add(const std::string &name, std::atomic<int64_t> *location, Type type = TYPE_COUNTER)
33         {
34                 add(name, {}, location, type);
35         }
36
37         void add(const std::string &name, std::atomic<double> *location, Type type = TYPE_COUNTER)
38         {
39                 add(name, {}, location, type);
40         }
41
42         void add(const std::string &name, Histogram *location)
43         {
44                 add(name, {}, location);
45         }
46
47         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);
48         void add(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels, std::atomic<double> *location, Type type = TYPE_COUNTER);
49         void add(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels, Histogram *location);
50
51         std::string serialize() const;
52
53 private:
54         enum DataType {
55                 DATA_TYPE_INT64,
56                 DATA_TYPE_DOUBLE,
57                 DATA_TYPE_HISTOGRAM,
58         };
59
60         struct Metric {
61                 DataType data_type;
62                 std::string name;
63                 std::vector<std::pair<std::string, std::string>> labels;
64                 union {
65                         std::atomic<int64_t> *location_int64;
66                         std::atomic<double> *location_double;
67                         Histogram *location_histogram;
68                 };
69         };
70
71         mutable std::mutex mu;
72         std::map<std::string, Type> types;
73         std::vector<Metric> metrics;
74         std::vector<Histogram> histograms;
75 };
76
77 class Histogram {
78 public:
79         void init(const std::vector<double> &bucket_vals);
80         void init_uniform(size_t num_buckets);  // Sets up buckets 0..(N-1).
81         void init_geometric(double min, double max, size_t num_buckets);
82         void count_event(double val);
83         std::string serialize(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels) const;
84
85 private:
86         // Bucket <i> counts number of events where val[i - 1] < x <= val[i].
87         // The end histogram ends up being made into a cumulative one,
88         // but that's not how we store it here.
89         struct Bucket {
90                 double val;
91                 std::atomic<int64_t> count{0};
92         };
93         std::unique_ptr<Bucket[]> buckets;
94         size_t num_buckets;
95         std::atomic<double> sum{0.0};
96         std::atomic<int64_t> count_after_last_bucket{0};
97 };
98
99 extern Metrics global_metrics;
100
101 #endif  // !defined(_METRICS_H)