LogIt++
Header-only, macro-first C++ logging library
Loading...
Searching...
No Matches
PrometheusTextSerializer.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef LOGIT_CPP_HEADER_LOGIT_CPP_LOGIT_LOGGERS_PROMETHEUS_PROMETHEUSTEXTSERIALIZER_HPP_INCLUDED
3#define LOGIT_CPP_HEADER_LOGIT_CPP_LOGIT_LOGGERS_PROMETHEUS_PROMETHEUSTEXTSERIALIZER_HPP_INCLUDED
4
7
9
10#include <cctype>
11#include <cmath>
12#include <cstdint>
13#include <iomanip>
14#include <limits>
15#include <sstream>
16#include <string>
17#include <vector>
18
19namespace logit {
20
24 inline std::string prometheus_escape_help(const std::string& value) {
25 std::string out;
26 out.reserve(value.size());
27 for (size_t i = 0; i < value.size(); ++i) {
28 const char c = value[i];
29 if (c == '\\') {
30 out += "\\\\";
31 } else if (c == '\n') {
32 out += "\\n";
33 } else {
34 out += c;
35 }
36 }
37 return out;
38 }
39
43 inline std::string prometheus_escape_label_value(const std::string& value) {
44 std::string out;
45 out.reserve(value.size());
46 for (size_t i = 0; i < value.size(); ++i) {
47 const char c = value[i];
48 if (c == '\\') {
49 out += "\\\\";
50 } else if (c == '"') {
51 out += "\\\"";
52 } else if (c == '\n') {
53 out += "\\n";
54 } else {
55 out += c;
56 }
57 }
58 return out;
59 }
60
64 inline std::string prometheus_sanitize_metric_name(const std::string& name) {
65 std::string out;
66 out.reserve(name.size());
67 for (size_t i = 0; i < name.size(); ++i) {
68 const unsigned char c = static_cast<unsigned char>(name[i]);
69 if (i == 0) {
70 if (std::isalpha(c) || c == '_' || c == ':') {
71 out += static_cast<char>(c);
72 } else {
73 out += '_';
74 }
75 } else {
76 if (std::isalnum(c) || c == '_' || c == ':') {
77 out += static_cast<char>(c);
78 } else {
79 out += '_';
80 }
81 }
82 }
83 if (out.empty()) {
84 out = "_";
85 }
86 return out;
87 }
88
92 inline std::string prometheus_sanitize_label_name(const std::string& name) {
93 std::string out;
94 out.reserve(name.size());
95 for (size_t i = 0; i < name.size(); ++i) {
96 const unsigned char c = static_cast<unsigned char>(name[i]);
97 if (i == 0) {
98 if (std::isalpha(c) || c == '_') {
99 out += static_cast<char>(c);
100 } else {
101 out += '_';
102 }
103 } else {
104 if (std::isalnum(c) || c == '_') {
105 out += static_cast<char>(c);
106 } else {
107 out += '_';
108 }
109 }
110 }
111 if (out.empty()) {
112 out = "_";
113 }
114 return out;
115 }
116
120 inline void prometheus_format_value(std::ostringstream& os, double value) {
121 if (std::isnan(value)) {
122 os << "NaN";
123 } else if (std::isinf(value)) {
124 if (value > 0) {
125 os << "+Inf";
126 } else {
127 os << "-Inf";
128 }
129 } else {
130 os << std::setprecision(std::numeric_limits<double>::max_digits10) << value;
131 }
132 }
133
139 std::ostringstream& os,
140 const PrometheusMetricFamily& family,
141 const PrometheusTextFormatConfig& config) {
142 const std::string safe_name = prometheus_sanitize_metric_name(family.name);
143
144 if (config.include_help && !family.help.empty()) {
145 os << "# HELP " << safe_name << " " << prometheus_escape_help(family.help) << "\n";
146 }
147
148 if (config.include_type) {
149 const char* type_str = "untyped";
150 switch (family.type) {
151 case PrometheusMetricType::Counter: type_str = "counter"; break;
152 case PrometheusMetricType::Gauge: type_str = "gauge"; break;
153 default: break;
154 }
155 os << "# TYPE " << safe_name << " " << type_str << "\n";
156 }
157
158 for (size_t si = 0; si < family.samples.size(); ++si) {
159 const PrometheusSample& sample = family.samples[si];
160 const std::string sample_name = prometheus_sanitize_metric_name(sample.name);
161
162 os << sample_name;
163
164 // Merge labels: const_labels first, then sample labels, then logger/instance.
165 std::vector<PrometheusLabel> merged;
166 if (!config.const_labels.empty()) {
167 merged.insert(merged.end(), config.const_labels.begin(), config.const_labels.end());
168 }
169 if (!sample.labels.empty()) {
170 merged.insert(merged.end(), sample.labels.begin(), sample.labels.end());
171 }
172
173 if (!merged.empty()) {
174 os << "{";
175 for (size_t li = 0; li < merged.size(); ++li) {
176 if (li != 0) {
177 os << ",";
178 }
179 os << prometheus_sanitize_label_name(merged[li].name)
180 << "=\"" << prometheus_escape_label_value(merged[li].value) << "\"";
181 }
182 os << "}";
183 }
184
185 os << " ";
186 prometheus_format_value(os, sample.value);
187
188 if (config.include_timestamp && sample.timestamp_ms != 0) {
189 os << " " << sample.timestamp_ms;
190 }
191
192 os << "\n";
193 }
194 }
195
201 const std::vector<PrometheusMetricFamily>& families,
202 const PrometheusTextFormatConfig& config) {
203 std::ostringstream os;
204 for (size_t i = 0; i < families.size(); ++i) {
205 prometheus_write_metric_family(os, families[i], config);
206 }
207 return os.str();
208 }
209
210} // namespace logit
211
212#endif // LOGIT_CPP_HEADER_LOGIT_CPP_LOGIT_LOGGERS_PROMETHEUS_PROMETHEUSTEXTSERIALIZER_HPP_INCLUDED
Defines Prometheus text exposition format types and configuration.
The primary namespace for the LogIt++ library.
std::string prometheus_escape_label_value(const std::string &value)
Escapes label value for Prometheus text format.
std::string prometheus_sanitize_metric_name(const std::string &name)
Sanitizes a metric name to match Prometheus naming rules.
std::string prometheus_sanitize_label_name(const std::string &name)
Sanitizes a label name to match Prometheus naming rules.
std::string prometheus_escape_help(const std::string &value)
Escapes help string for Prometheus text format.
void prometheus_format_value(std::ostringstream &os, double value)
Formats a Prometheus sample value according to text format rules.
std::string build_prometheus_text_payload(const std::vector< PrometheusMetricFamily > &families, const PrometheusTextFormatConfig &config)
Builds a complete Prometheus text exposition format payload.
void prometheus_write_metric_family(std::ostringstream &os, const PrometheusMetricFamily &family, const PrometheusTextFormatConfig &config)
Writes one PrometheusMetricFamily to an output stream.
A group of related samples sharing a name, help, and type.
std::vector< PrometheusSample > samples
Samples in this family.
std::string name
Metric family name.
PrometheusMetricType type
Metric type.
A single metric sample with optional labels and timestamp.
std::string name
Full metric name (including suffixes).
std::vector< PrometheusLabel > labels
Labels for this sample.
int64_t timestamp_ms
Timestamp in ms; 0 = omit.
Configuration for Prometheus text exposition format output.
std::vector< PrometheusLabel > const_labels
Labels added to every sample.