12 using namespace std::chrono;
14 Metrics global_metrics;
16 double get_timestamp_for_metrics()
18 return duration<double>(system_clock::now().time_since_epoch()).count();
21 string Metrics::serialize_name(const string &name, const vector<pair<string, string>> &labels)
24 return "nageru_" + name;
28 for (const pair<string, string> &label : labels) {
29 if (!label_str.empty()) {
32 label_str += label.first + "=\"" + label.second + "\"";
34 return "nageru_" + name + "{" + label_str + "}";
37 void Metrics::add(const string &name, const vector<pair<string, string>> &labels, atomic<int64_t> *location, Metrics::Type type)
40 metric.data_type = DATA_TYPE_INT64;
41 metric.location_int64 = location;
43 lock_guard<mutex> lock(mu);
44 metrics.emplace(MetricKey(name, labels), metric);
45 assert(types.count(name) == 0 || types[name] == type);
49 void Metrics::add(const string &name, const vector<pair<string, string>> &labels, atomic<double> *location, Metrics::Type type)
52 metric.data_type = DATA_TYPE_DOUBLE;
53 metric.location_double = location;
55 lock_guard<mutex> lock(mu);
56 metrics.emplace(MetricKey(name, labels), metric);
57 assert(types.count(name) == 0 || types[name] == type);
61 void Metrics::add(const string &name, const vector<pair<string, string>> &labels, Histogram *location)
64 metric.data_type = DATA_TYPE_HISTOGRAM;
65 metric.location_histogram = location;
67 lock_guard<mutex> lock(mu);
68 metrics.emplace(MetricKey(name, labels), metric);
69 assert(types.count(name) == 0 || types[name] == TYPE_HISTOGRAM);
70 types[name] = TYPE_HISTOGRAM;
73 string Metrics::serialize() const
76 ss.imbue(locale("C"));
79 lock_guard<mutex> lock(mu);
80 auto type_it = types.cbegin();
81 for (const auto &key_and_metric : metrics) {
82 const string &name = key_and_metric.first.serialized;
83 const Metric &metric = key_and_metric.second;
85 if (type_it != types.cend() &&
86 key_and_metric.first.name == type_it->first) {
87 // It's the first time we print out any metric with this name,
88 // so add the type header.
89 if (type_it->second == TYPE_GAUGE) {
90 ss << "# TYPE nageru_" << type_it->first << " gauge\n";
91 } else if (type_it->second == TYPE_HISTOGRAM) {
92 ss << "# TYPE nageru_" << type_it->first << " histogram\n";
97 if (metric.data_type == DATA_TYPE_INT64) {
98 ss << name << " " << metric.location_int64->load() << "\n";
99 } else if (metric.data_type == DATA_TYPE_DOUBLE) {
100 ss << name << " " << metric.location_double->load() << "\n";
102 ss << metric.location_histogram->serialize(key_and_metric.first.name, key_and_metric.first.labels);
109 void Histogram::init(const vector<double> &bucket_vals)
111 this->num_buckets = bucket_vals.size();
112 buckets.reset(new Bucket[num_buckets]);
113 for (size_t i = 0; i < num_buckets; ++i) {
114 buckets[i].val = bucket_vals[i];
118 void Histogram::init_uniform(size_t num_buckets)
120 this->num_buckets = num_buckets;
121 buckets.reset(new Bucket[num_buckets]);
122 for (size_t i = 0; i < num_buckets; ++i) {
127 void Histogram::init_geometric(double min, double max, size_t num_buckets)
129 this->num_buckets = num_buckets;
130 buckets.reset(new Bucket[num_buckets]);
131 for (size_t i = 0; i < num_buckets; ++i) {
132 buckets[i].val = min * pow(max / min, double(i) / (num_buckets - 1));
136 void Histogram::count_event(double val)
139 ref_bucket.val = val;
140 auto it = lower_bound(buckets.get(), buckets.get() + num_buckets, ref_bucket,
141 [](const Bucket &a, const Bucket &b) { return a.val < b.val; });
142 if (it == buckets.get() + num_buckets) {
143 ++count_after_last_bucket;
147 // Non-atomic add, but that's fine, since there are no concurrent writers.
151 string Histogram::serialize(const string &name, const vector<pair<string, string>> &labels) const
154 ss.imbue(locale("C"));
158 for (size_t bucket_idx = 0; bucket_idx < num_buckets; ++bucket_idx) {
160 le_ss.imbue(locale("C"));
162 le_ss << buckets[bucket_idx].val;
163 vector<pair<string, string>> bucket_labels = labels;
164 bucket_labels.emplace_back("le", le_ss.str());
166 count += buckets[bucket_idx].count.load();
167 ss << Metrics::serialize_name(name + "_bucket", bucket_labels) << " " << count << "\n";
170 count += count_after_last_bucket.load();
172 ss << Metrics::serialize_name(name + "_sum", labels) << " " << sum.load() << "\n";
173 ss << Metrics::serialize_name(name + "_count", labels) << " " << count << "\n";