summary.h 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. #pragma once
  2. #include <chrono>
  3. #include <cstdint>
  4. #include <mutex>
  5. #include <vector>
  6. #include "prometheus/metric.h"
  7. #include "prometheus/family.h"
  8. #include "prometheus/detail/ckms_quantiles.h"
  9. #include "prometheus/detail/time_window_quantiles.h"
  10. #include "prometheus/builder.h"
  11. namespace prometheus {
  12. /// \brief A summary metric samples observations over a sliding window of time.
  13. ///
  14. /// This class represents the metric type summary:
  15. /// https://prometheus.io/docs/instrumenting/writing_clientlibs/#summary
  16. ///
  17. /// A summary provides a total count of observations and a sum of all observed
  18. /// values. In contrast to a histogram metric it also calculates configurable
  19. /// Phi-quantiles over a sliding window of time.
  20. ///
  21. /// The essential difference between summaries and histograms is that summaries
  22. /// calculate streaming Phi-quantiles on the client side and expose them
  23. /// directly, while histograms expose bucketed observation counts and the
  24. /// calculation of quantiles from the buckets of a histogram happens on the
  25. /// server side:
  26. /// https://prometheus.io/docs/prometheus/latest/querying/functions/#histogram_quantile.
  27. ///
  28. /// Note that Phi designates the probability density function of the standard
  29. /// Gaussian distribution.
  30. ///
  31. /// See https://prometheus.io/docs/practices/histograms/ for detailed
  32. /// explanations of Phi-quantiles, summary usage, and differences to histograms.
  33. ///
  34. /// The class is thread-safe. No concurrent call to any API of this type causes
  35. /// a data race.
  36. class Summary : Metric {
  37. public:
  38. using Value = double;
  39. using Family = CustomFamily<Summary>;
  40. static const Metric::Type static_type = Metric::Type::Summary;
  41. using Quantiles = std::vector<detail::CKMSQuantiles::Quantile>;
  42. const Quantiles quantiles_;
  43. mutable std::mutex mutex_;
  44. std::uint64_t count_;
  45. double sum_;
  46. detail::TimeWindowQuantiles quantile_values_;
  47. public:
  48. /// \brief Create a summary metric.
  49. ///
  50. /// \param quantiles A list of 'targeted' Phi-quantiles. A targeted
  51. /// Phi-quantile is specified in the form of a Phi-quantile and tolerated
  52. /// error. For example a Quantile{0.5, 0.1} means that the median (= 50th
  53. /// percentile) should be returned with 10 percent error or a Quantile{0.2,
  54. /// 0.05} means the 20th percentile with 5 percent tolerated error. Note that
  55. /// percentiles and quantiles are the same concept, except percentiles are
  56. /// expressed as percentages. The Phi-quantile must be in the interval [0, 1].
  57. /// Note that a lower tolerated error for a Phi-quantile results in higher
  58. /// usage of resources (memory and cpu) to calculate the summary.
  59. ///
  60. /// The Phi-quantiles are calculated over a sliding window of time. The
  61. /// sliding window of time is configured by max_age and age_buckets.
  62. ///
  63. /// \param max_age Set the duration of the time window, i.e., how long
  64. /// observations are kept before they are discarded. The default value is 60
  65. /// seconds.
  66. ///
  67. /// \param age_buckets Set the number of buckets of the time window. It
  68. /// determines the number of buckets used to exclude observations that are
  69. /// older than max_age from the summary, e.g., if max_age is 60 seconds and
  70. /// age_buckets is 5, buckets will be switched every 12 seconds. The value is
  71. /// a trade-off between resources (memory and cpu for maintaining the bucket)
  72. /// and how smooth the time window is moved. With only one age bucket it
  73. /// effectively results in a complete reset of the summary each time max_age
  74. /// has passed. The default value is 5.
  75. Summary(const Quantiles& quantiles, std::chrono::milliseconds max_age = std::chrono::seconds{ 60 }, int age_buckets = 5)
  76. : Metric(static_type), quantiles_{ quantiles }, count_{ 0 }, sum_{ 0 }, quantile_values_(quantiles_, max_age, age_buckets) {}
  77. /// \brief Observe the given amount.
  78. void Observe(const double value) {
  79. std::lock_guard<std::mutex> lock(mutex_);
  80. count_ += 1;
  81. sum_ += value;
  82. quantile_values_.insert(value);
  83. }
  84. /// \brief Get the current value of the summary.
  85. ///
  86. /// Collect is called by the Registry when collecting metrics.
  87. virtual ClientMetric Collect() const {
  88. auto metric = ClientMetric{};
  89. std::lock_guard<std::mutex> lock(mutex_);
  90. metric.summary.quantile.reserve(quantiles_.size());
  91. for (const auto& quantile : quantiles_) {
  92. auto metricQuantile = ClientMetric::Quantile{};
  93. metricQuantile.quantile = quantile.quantile;
  94. metricQuantile.value = quantile_values_.get(quantile.quantile);
  95. metric.summary.quantile.push_back(std::move(metricQuantile));
  96. }
  97. metric.summary.sample_count = count_;
  98. metric.summary.sample_sum = sum_;
  99. return metric;
  100. }
  101. };
  102. /// \brief Return a builder to configure and register a Summary metric.
  103. ///
  104. /// @copydetails Family<>::Family()
  105. ///
  106. /// Example usage:
  107. ///
  108. /// \code
  109. /// auto registry = std::make_shared<Registry>();
  110. /// auto& summary_family = prometheus::BuildSummary()
  111. /// .Name("some_name")
  112. /// .Help("Additional description.")
  113. /// .Labels({{"key", "value"}})
  114. /// .Register(*registry);
  115. ///
  116. /// ...
  117. /// \endcode
  118. ///
  119. /// \return An object of unspecified type T, i.e., an implementation detail
  120. /// except that it has the following members:
  121. ///
  122. /// - Name(const std::string&) to set the metric name,
  123. /// - Help(const std::string&) to set an additional description.
  124. /// - Label(const std::map<std::string, std::string>&) to assign a set of
  125. /// key-value pairs (= labels) to the metric.
  126. ///
  127. /// To finish the configuration of the Summary metric register it with
  128. /// Register(Registry&).
  129. using BuildSummary = Builder<Summary>;
  130. } // namespace prometheus