Branch data Line data Source code
1 : : // Copyright 2026 HPActor Contributors
2 : : //
3 : : // Licensed under the Apache License, Version 2.0 (the "License");
4 : : // you may not use this file except in compliance with the License.
5 : : // You may obtain a copy of the License at
6 : : //
7 : : // http://www.apache.org/licenses/LICENSE-2.0
8 : : //
9 : : // Unless required by applicable law or agreed to in writing, software
10 : : // distributed under the License is distributed on an "AS IS" BASIS,
11 : : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : : // See the License for the specific language governing permissions and
13 : : // limitations under the License.
14 : :
15 : : #include <hpactor/metrics/metrics_formatter.hpp>
16 : : #include <cstdio>
17 : :
18 : : namespace hpactor::metrics {
19 : :
20 : 6 : std::string OpenMetricsFormatter::escape_label_value(const std::string& s) {
21 : 6 : std::string out;
22 : 6 : out.reserve(s.size());
23 : 36 : for (char c : s) {
24 : 30 : switch (c) {
25 : 0 : case '\\': out += "\\\\"; break;
26 : 0 : case '"': out += "\\\""; break;
27 : 0 : case '\n': out += "\\n"; break;
28 : 30 : default: out += c;
29 : : }
30 : : }
31 : 6 : return out;
32 : : }
33 : :
34 : 3 : std::string OpenMetricsFormatter::format_labels(const LabelSet& ls) {
35 : 3 : if (ls.labels.empty()) return "";
36 : 3 : std::string out = "{";
37 : 9 : for (size_t i = 0; i < ls.labels.size(); ++i) {
38 : 6 : if (i > 0) out += ",";
39 : 6 : out += ls.labels[i].first;
40 : 6 : out += "=\"";
41 : 6 : out += escape_label_value(ls.labels[i].second);
42 : 6 : out += "\"";
43 : : }
44 : 3 : out += "}";
45 : 3 : return out;
46 : 3 : }
47 : :
48 : 1 : std::string OpenMetricsFormatter::format(const MetricRegistry::Snapshot& snapshot) const {
49 : 1 : std::string out;
50 : 1 : out.reserve(8192);
51 : :
52 : : char buf[256];
53 : :
54 : 4 : for (const auto& fam : snapshot.families) {
55 : 3 : out += "# HELP ";
56 : 3 : out += fam.name;
57 : 3 : out += " ";
58 : 3 : out += fam.help;
59 : 3 : out += "\n";
60 : :
61 : 3 : switch (fam.type) {
62 : 1 : case MetricType::kCounter: {
63 : 1 : out += "# TYPE ";
64 : 1 : out += fam.name;
65 : 1 : out += " counter\n";
66 : 2 : for (const auto& [ls, val] : fam.counters) {
67 : 1 : int n = snprintf(buf, sizeof(buf), "%s%s %llu\n",
68 : 2 : fam.name.c_str(), format_labels(ls).c_str(),
69 : : static_cast<unsigned long long>(val));
70 : 1 : out.append(buf, static_cast<size_t>(n));
71 : : }
72 : 1 : break;
73 : : }
74 : 1 : case MetricType::kGauge: {
75 : 1 : out += "# TYPE ";
76 : 1 : out += fam.name;
77 : 1 : out += " gauge\n";
78 : 2 : for (const auto& [ls, val] : fam.gauges) {
79 : 1 : int n = snprintf(buf, sizeof(buf), "%s%s %lld\n",
80 : 2 : fam.name.c_str(), format_labels(ls).c_str(),
81 : : static_cast<long long>(val));
82 : 1 : out.append(buf, static_cast<size_t>(n));
83 : : }
84 : 1 : break;
85 : : }
86 : 1 : case MetricType::kHistogram: {
87 : 1 : out += "# TYPE ";
88 : 1 : out += fam.name;
89 : 1 : out += " histogram\n";
90 : 2 : for (const auto& he : fam.histograms) {
91 : 1 : std::string base_label_str = format_labels(he.labels);
92 : 1 : uint64_t cumulative = 0;
93 : 16 : for (size_t i = 0; i < kHistogramNumBuckets - 1; ++i) {
94 : 15 : cumulative += he.buckets[i];
95 : 15 : std::string extra_label;
96 : 15 : if (he.labels.labels.empty()) {
97 : 0 : extra_label = "{le=\"";
98 : : } else {
99 : 15 : extra_label = base_label_str;
100 : 15 : extra_label.pop_back(); // remove trailing }
101 : 15 : extra_label += ",le=\"";
102 : : }
103 : 15 : int n = snprintf(buf, sizeof(buf),
104 : : "%s_bucket%s%g\"} %llu\n",
105 : : fam.name.c_str(), extra_label.c_str(),
106 : 15 : kBucketBounds[i],
107 : : static_cast<unsigned long long>(cumulative));
108 : 15 : out.append(buf, static_cast<size_t>(n));
109 : 15 : }
110 : 1 : cumulative += he.buckets[kHistogramNumBuckets - 1];
111 : : {
112 : 1 : std::string extra_label;
113 : 1 : if (he.labels.labels.empty()) {
114 : 0 : extra_label = "{le=\"+Inf\"}";
115 : : } else {
116 : 1 : extra_label = base_label_str;
117 : 1 : extra_label.pop_back();
118 : 1 : extra_label += ",le=\"+Inf\"}";
119 : : }
120 : 1 : int n = snprintf(buf, sizeof(buf),
121 : : "%s_bucket%s %llu\n",
122 : : fam.name.c_str(), extra_label.c_str(),
123 : : static_cast<unsigned long long>(cumulative));
124 : 1 : out.append(buf, static_cast<size_t>(n));
125 : 1 : }
126 : : {
127 : 1 : int n = snprintf(buf, sizeof(buf), "%s_sum%s %g\n",
128 : : fam.name.c_str(), base_label_str.c_str(),
129 : 1 : he.sum_seconds);
130 : 1 : out.append(buf, static_cast<size_t>(n));
131 : : }
132 : : {
133 : 1 : int n = snprintf(buf, sizeof(buf), "%s_count%s %llu\n",
134 : : fam.name.c_str(), base_label_str.c_str(),
135 : 1 : static_cast<unsigned long long>(he.count));
136 : 1 : out.append(buf, static_cast<size_t>(n));
137 : : }
138 : 1 : }
139 : 1 : break;
140 : : }
141 : : }
142 : 3 : out += "\n";
143 : : }
144 : 1 : out += "# EOF\n";
145 : 1 : return out;
146 : : }
147 : :
148 : : } // namespace hpactor::metrics
|