text_serializer.h 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. #pragma once
  2. #include <iosfwd>
  3. #include <vector>
  4. #include <array>
  5. #include <math.h>
  6. #include <ostream>
  7. #include "prometheus/metric_family.h"
  8. #if __cpp_lib_to_chars >= 201611L
  9. #include <charconv>
  10. #endif
  11. namespace prometheus {
  12. class TextSerializer {
  13. // Write a double as a string, with proper formatting for infinity and NaN
  14. static void WriteValue (std::ostream& out, double value) {
  15. if (std::isnan(value))
  16. out << "Nan";
  17. else if (std::isinf(value))
  18. out << (value < 0 ? "-Inf" : "+Inf");
  19. else {
  20. std::array<char, 128> buffer;
  21. #if __cpp_lib_to_chars >= 201611L
  22. auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value);
  23. if (ec != std::errc()) {
  24. throw std::runtime_error("Could not convert double to string: " +
  25. std::make_error_code(ec).message());
  26. }
  27. out.write(buffer.data(), ptr - buffer.data());
  28. #else
  29. int wouldHaveWritten = std::snprintf(buffer.data(), buffer.size(), "%.*g", std::numeric_limits<double>::max_digits10 - 1, value);
  30. if (wouldHaveWritten <= 0 || static_cast<std::size_t>(wouldHaveWritten) >= buffer.size()) {
  31. throw std::runtime_error("Could not convert double to string");
  32. }
  33. out.write(buffer.data(), wouldHaveWritten);
  34. #endif
  35. }
  36. }
  37. static void WriteValue(std::ostream& out, const std::string& value) {
  38. for (auto c : value) {
  39. switch (c) {
  40. case '\n': out << '\\' << 'n'; break;
  41. case '\\': out << '\\' << c; break;
  42. case '"': out << '\\' << c; break;
  43. default: out << c; break;
  44. }
  45. }
  46. }
  47. // Write a line header: metric name and labels
  48. template <typename T = std::string>
  49. static void WriteHead(
  50. std::ostream& out,
  51. const MetricFamily& family,
  52. const ClientMetric& metric,
  53. const std::string& suffix = "",
  54. const std::string& extraLabelName = "",
  55. const T& extraLabelValue = T()) {
  56. out << family.name << suffix;
  57. if (!metric.label.empty() || !extraLabelName.empty()) {
  58. out << "{";
  59. const char* prefix = "";
  60. for (auto& lp : metric.label) {
  61. out << prefix << lp.name << "=\"";
  62. WriteValue(out, lp.value);
  63. out << "\"";
  64. prefix = ",";
  65. }
  66. if (!extraLabelName.empty()) {
  67. out << prefix << extraLabelName << "=\"";
  68. WriteValue(out, extraLabelValue);
  69. out << "\"";
  70. }
  71. out << "}";
  72. }
  73. out << " ";
  74. }
  75. // Write a line trailer: timestamp
  76. static void WriteTail(std::ostream& out, const ClientMetric& metric) {
  77. if (metric.timestamp_ms != 0) {
  78. out << " " << metric.timestamp_ms;
  79. }
  80. out << "\n";
  81. }
  82. static void SerializeCounter(std::ostream& out, const MetricFamily& family, const ClientMetric& metric) {
  83. WriteHead(out, family, metric);
  84. WriteValue(out, metric.counter.value);
  85. WriteTail(out, metric);
  86. }
  87. static void SerializeGauge(std::ostream& out, const MetricFamily& family, const ClientMetric& metric) {
  88. WriteHead(out, family, metric);
  89. WriteValue(out, metric.gauge.value);
  90. WriteTail(out, metric);
  91. }
  92. static void SerializeSummary(std::ostream& out, const MetricFamily& family, const ClientMetric& metric) {
  93. auto& sum = metric.summary;
  94. WriteHead(out, family, metric, "_count");
  95. out << sum.sample_count;
  96. WriteTail(out, metric);
  97. WriteHead(out, family, metric, "_sum");
  98. WriteValue(out, sum.sample_sum);
  99. WriteTail(out, metric);
  100. for (auto& q : sum.quantile) {
  101. WriteHead(out, family, metric, "", "quantile", q.quantile);
  102. WriteValue(out, q.value);
  103. WriteTail(out, metric);
  104. }
  105. }
  106. static void SerializeUntyped(std::ostream& out, const MetricFamily& family, const ClientMetric& metric) {
  107. WriteHead(out, family, metric);
  108. WriteValue(out, metric.untyped.value);
  109. WriteTail(out, metric);
  110. }
  111. static void SerializeHistogram(std::ostream& out, const MetricFamily& family, const ClientMetric& metric) {
  112. auto& hist = metric.histogram;
  113. WriteHead(out, family, metric, "_count");
  114. out << hist.sample_count;
  115. WriteTail(out, metric);
  116. WriteHead(out, family, metric, "_sum");
  117. WriteValue(out, hist.sample_sum);
  118. WriteTail(out, metric);
  119. double last = -std::numeric_limits<double>::infinity();
  120. for (auto& b : hist.bucket) {
  121. WriteHead(out, family, metric, "_bucket", "le", b.upper_bound);
  122. last = b.upper_bound;
  123. out << b.cumulative_count;
  124. WriteTail(out, metric);
  125. }
  126. if (last != std::numeric_limits<double>::infinity()) {
  127. WriteHead(out, family, metric, "_bucket", "le", "+Inf");
  128. out << hist.sample_count;
  129. WriteTail(out, metric);
  130. }
  131. }
  132. static void SerializeFamily(std::ostream& out, const MetricFamily& family) {
  133. if (!family.help.empty()) {
  134. out << "# HELP " << family.name << " " << family.help << "\n";
  135. }
  136. switch (family.type) {
  137. case Metric::Type::Counter:
  138. out << "# TYPE " << family.name << " counter\n";
  139. for (auto& metric : family.metric) {
  140. SerializeCounter(out, family, metric);
  141. }
  142. break;
  143. case Metric::Type::Gauge:
  144. out << "# TYPE " << family.name << " gauge\n";
  145. for (auto& metric : family.metric) {
  146. SerializeGauge(out, family, metric);
  147. }
  148. break;
  149. case Metric::Type::Summary:
  150. out << "# TYPE " << family.name << " summary\n";
  151. for (auto& metric : family.metric) {
  152. SerializeSummary(out, family, metric);
  153. }
  154. break;
  155. case Metric::Type::Untyped:
  156. out << "# TYPE " << family.name << " untyped\n";
  157. for (auto& metric : family.metric) {
  158. SerializeUntyped(out, family, metric);
  159. }
  160. break;
  161. case Metric::Type::Histogram:
  162. out << "# TYPE " << family.name << " histogram\n";
  163. for (auto& metric : family.metric) {
  164. SerializeHistogram(out, family, metric);
  165. }
  166. break;
  167. }
  168. }
  169. public:
  170. static void Serialize (std::ostream& out, const std::vector<MetricFamily>& metrics) {
  171. std::locale saved_locale = out.getloc();
  172. out.imbue(std::locale::classic());
  173. for (auto& family : metrics) {
  174. SerializeFamily(out, family);
  175. }
  176. out.imbue(saved_locale);
  177. }
  178. };
  179. } // namespace prometheus