// which makes it quite unwieldy. Thus, we'll package our own for the time being.
#include <atomic>
+#include <chrono>
+#include <deque>
#include <map>
#include <memory>
#include <mutex>
#include <string>
+#include <utility>
#include <vector>
class Histogram;
+class Summary;
// Prometheus recommends the use of timestamps instead of “time since event”,
// so you can use this to get the number of seconds since the epoch.
TYPE_COUNTER,
TYPE_GAUGE,
TYPE_HISTOGRAM, // Internal use only.
+ TYPE_SUMMARY, // Internal use only.
+ };
+ enum Laziness {
+ PRINT_ALWAYS,
+ PRINT_WHEN_NONEMPTY,
};
void add(const std::string &name, std::atomic<int64_t> *location, Type type = TYPE_COUNTER)
add(name, {}, location);
}
+ void add(const std::string &name, Summary *location)
+ {
+ add(name, {}, location);
+ }
+
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);
void add(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels, std::atomic<double> *location, Type type = TYPE_COUNTER);
- void add(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels, Histogram *location);
+ void add(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels, Histogram *location, Laziness laziness = PRINT_ALWAYS);
+ void add(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels, Summary *location, Laziness laziness = PRINT_ALWAYS);
void remove(const std::string &name)
{
DATA_TYPE_INT64,
DATA_TYPE_DOUBLE,
DATA_TYPE_HISTOGRAM,
+ DATA_TYPE_SUMMARY,
};
struct MetricKey {
MetricKey(const std::string &name, const std::vector<std::pair<std::string, std::string>> labels)
};
struct Metric {
DataType data_type;
+ Laziness laziness; // Only for TYPE_HISTOGRAM.
union {
std::atomic<int64_t> *location_int64;
std::atomic<double> *location_double;
Histogram *location_histogram;
+ Summary *location_summary;
};
};
mutable std::mutex mu;
std::map<std::string, Type> types; // Ordered the same as metrics.
std::map<MetricKey, Metric> metrics;
- std::vector<Histogram> histograms;
friend class Histogram;
+ friend class Summary;
};
class Histogram {
void init_uniform(size_t num_buckets); // Sets up buckets 0..(N-1).
void init_geometric(double min, double max, size_t num_buckets);
void count_event(double val);
- std::string serialize(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels) const;
+ std::string serialize(Metrics::Laziness laziness, const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels) const;
private:
// Bucket <i> counts number of events where val[i - 1] < x <= val[i].
std::atomic<int64_t> count_after_last_bucket{0};
};
+// This is a pretty dumb streaming quantile class, but it's exact, and we don't have
+// too many values (typically one per frame, and one-minute interval), so we don't
+// need anything fancy.
+class Summary {
+public:
+ void init(const std::vector<double> &quantiles, double window_seconds);
+ void count_event(double val);
+ std::string serialize(Metrics::Laziness laziness, const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels);
+
+private:
+ std::vector<double> quantiles;
+ std::chrono::duration<double> window;
+
+ mutable std::mutex mu;
+ std::deque<std::pair<std::chrono::steady_clock::time_point, double>> values;
+ std::atomic<double> sum{0.0};
+ std::atomic<int64_t> count{0};
+};
+
extern Metrics global_metrics;
#endif // !defined(_METRICS_H)