prometheus_encoder.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. #include "prometheus.h"
  2. #include "prometheus_model.h"
  3. #include <library/cpp/monlib/encode/encoder_state.h>
  4. #include <library/cpp/monlib/metrics/labels.h>
  5. #include <library/cpp/monlib/metrics/metric_value.h>
  6. #include <util/string/cast.h>
  7. #include <util/generic/hash_set.h>
  8. namespace NMonitoring {
  9. namespace {
  10. ///////////////////////////////////////////////////////////////////////
  11. // TPrometheusWriter
  12. ///////////////////////////////////////////////////////////////////////
  13. class TPrometheusWriter {
  14. public:
  15. explicit TPrometheusWriter(IOutputStream* out)
  16. : Out_(out)
  17. {
  18. }
  19. void WriteType(EMetricType type, const TString& name) {
  20. auto r = WrittenTypes_.insert(name);
  21. if (!r.second) {
  22. // type for this metric was already written
  23. return;
  24. }
  25. Out_->Write("# TYPE ");
  26. WriteMetricName(name);
  27. Out_->Write(' ');
  28. switch (type) {
  29. case EMetricType::GAUGE:
  30. case EMetricType::IGAUGE:
  31. Out_->Write("gauge");
  32. break;
  33. case EMetricType::RATE:
  34. case EMetricType::COUNTER:
  35. Out_->Write("counter");
  36. break;
  37. case EMetricType::HIST:
  38. case EMetricType::HIST_RATE:
  39. Out_->Write("histogram");
  40. break;
  41. case EMetricType::LOGHIST:
  42. // TODO(@kbalakirev): implement this case
  43. break;
  44. case EMetricType::DSUMMARY:
  45. ythrow yexception() << "writing summary type is forbiden";
  46. case EMetricType::UNKNOWN:
  47. ythrow yexception() << "unknown metric type: " << MetricTypeToStr(type)
  48. << ", name: " << name;
  49. }
  50. Out_->Write('\n');
  51. }
  52. void WriteDouble(TStringBuf name, const TLabels& labels, TInstant time, double value) {
  53. WriteValue(name, "", labels, "", "", time, value);
  54. }
  55. void WriteHistogram(TStringBuf name, const TLabels& labels, TInstant time, IHistogramSnapshot* h) {
  56. Y_ENSURE(!labels.Has(NPrometheus::BUCKET_LABEL),
  57. "histogram metric " << name << " has label '" <<
  58. NPrometheus::BUCKET_LABEL << "' which is reserved in Prometheus");
  59. double totalCount = 0;
  60. for (ui32 i = 0, count = h->Count(); i < count; i++) {
  61. TBucketBound bound = h->UpperBound(i);
  62. TStringBuf boundStr;
  63. if (bound == HISTOGRAM_INF_BOUND) {
  64. boundStr = TStringBuf("+Inf");
  65. } else {
  66. size_t len = FloatToString(bound, TmpBuf_, Y_ARRAY_SIZE(TmpBuf_));
  67. boundStr = TStringBuf(TmpBuf_, len);
  68. }
  69. TBucketValue value = h->Value(i);
  70. totalCount += static_cast<double>(value);
  71. WriteValue(
  72. name, NPrometheus::BUCKET_SUFFIX,
  73. labels, NPrometheus::BUCKET_LABEL, boundStr,
  74. time,
  75. totalCount);
  76. }
  77. WriteValue(name, NPrometheus::COUNT_SUFFIX, labels, "", "", time, totalCount);
  78. }
  79. void WriteSummaryDouble(TStringBuf name, const TLabels& labels, TInstant time, ISummaryDoubleSnapshot* s) {
  80. WriteValue(name, NPrometheus::SUM_SUFFIX, labels, "", "", time, s->GetSum());
  81. WriteValue(name, NPrometheus::MIN_SUFFIX, labels, "", "", time, s->GetMin());
  82. WriteValue(name, NPrometheus::MAX_SUFFIX, labels, "", "", time, s->GetMax());
  83. WriteValue(name, NPrometheus::LAST_SUFFIX, labels, "", "", time, s->GetLast());
  84. WriteValue(name, NPrometheus::COUNT_SUFFIX, labels, "", "", time, s->GetCount());
  85. }
  86. void WriteLn() {
  87. Out_->Write('\n');
  88. }
  89. private:
  90. // will replace invalid chars with '_'
  91. void WriteMetricName(TStringBuf name) {
  92. Y_ENSURE(!name.Empty(), "trying to write metric with empty name");
  93. char ch = name[0];
  94. if (NPrometheus::IsValidMetricNameStart(ch)) {
  95. Out_->Write(ch);
  96. } else {
  97. Out_->Write('_');
  98. }
  99. for (size_t i = 1, len = name.length(); i < len; i++) {
  100. ch = name[i];
  101. if (NPrometheus::IsValidMetricNameContinuation(ch)) {
  102. Out_->Write(ch);
  103. } else {
  104. Out_->Write('_');
  105. }
  106. }
  107. }
  108. void WriteLabels(const TLabels& labels, TStringBuf addLabelKey, TStringBuf addLabelValue) {
  109. Out_->Write('{');
  110. for (auto&& l: labels) {
  111. Out_->Write(l.Name());
  112. Out_->Write('=');
  113. WriteLabelValue(l.Value());
  114. Out_->Write(", "); // trailign comma is supported in parsers
  115. }
  116. if (!addLabelKey.Empty() && !addLabelValue.Empty()) {
  117. Out_->Write(addLabelKey);
  118. Out_->Write('=');
  119. WriteLabelValue(addLabelValue);
  120. }
  121. Out_->Write('}');
  122. }
  123. void WriteLabelValue(TStringBuf value) {
  124. Out_->Write('"');
  125. for (char ch: value) {
  126. if (ch == '"') {
  127. Out_->Write("\\\"");
  128. } else if (ch == '\\') {
  129. Out_->Write("\\\\");
  130. } else if (ch == '\n') {
  131. Out_->Write("\\n");
  132. } else {
  133. Out_->Write(ch);
  134. }
  135. }
  136. Out_->Write('"');
  137. }
  138. void WriteValue(
  139. TStringBuf name, TStringBuf suffix,
  140. const TLabels& labels, TStringBuf addLabelKey, TStringBuf addLabelValue,
  141. TInstant time, double value)
  142. {
  143. // (1) name
  144. WriteMetricName(name);
  145. if (!suffix.Empty()) {
  146. Out_->Write(suffix);
  147. }
  148. // (2) labels
  149. if (!labels.Empty() || !addLabelKey.Empty()) {
  150. WriteLabels(labels, addLabelKey, addLabelValue);
  151. }
  152. Out_->Write(' ');
  153. // (3) value
  154. {
  155. size_t len = FloatToString(value, TmpBuf_, Y_ARRAY_SIZE(TmpBuf_));
  156. Out_->Write(TmpBuf_, len);
  157. }
  158. // (4) time
  159. if (ui64 timeMillis = time.MilliSeconds()) {
  160. Out_->Write(' ');
  161. size_t len = IntToString<10>(timeMillis, TmpBuf_, Y_ARRAY_SIZE(TmpBuf_));
  162. Out_->Write(TmpBuf_, len);
  163. }
  164. Out_->Write('\n');
  165. }
  166. private:
  167. IOutputStream* Out_;
  168. THashSet<TString> WrittenTypes_;
  169. char TmpBuf_[512]; // used to convert doubles to strings
  170. };
  171. ///////////////////////////////////////////////////////////////////////
  172. // TMetricState
  173. ///////////////////////////////////////////////////////////////////////
  174. struct TMetricState {
  175. EMetricType Type = EMetricType::UNKNOWN;
  176. TLabels Labels;
  177. TInstant Time = TInstant::Zero();
  178. EMetricValueType ValueType = EMetricValueType::UNKNOWN;
  179. TMetricValue Value;
  180. ~TMetricState() {
  181. ClearValue();
  182. }
  183. void Clear() {
  184. Type = EMetricType::UNKNOWN;
  185. Labels.Clear();
  186. Time = TInstant::Zero();
  187. ClearValue();
  188. }
  189. void ClearValue() {
  190. // TMetricValue does not keep ownership of histogram
  191. if (ValueType == EMetricValueType::HISTOGRAM) {
  192. Value.AsHistogram()->UnRef();
  193. } else if (ValueType == EMetricValueType::SUMMARY) {
  194. Value.AsSummaryDouble()->UnRef();
  195. }
  196. ValueType = EMetricValueType::UNKNOWN;
  197. Value = {};
  198. }
  199. template <typename T>
  200. void SetValue(T value) {
  201. // TMetricValue does not keep ownership of histogram
  202. if (ValueType == EMetricValueType::HISTOGRAM) {
  203. Value.AsHistogram()->UnRef();
  204. } else if (ValueType == EMetricValueType::SUMMARY) {
  205. Value.AsSummaryDouble()->UnRef();
  206. }
  207. ValueType = TValueType<T>::Type;
  208. Value = TMetricValue(value);
  209. if (ValueType == EMetricValueType::HISTOGRAM) {
  210. Value.AsHistogram()->Ref();
  211. } else if (ValueType == EMetricValueType::SUMMARY) {
  212. Value.AsSummaryDouble()->Ref();
  213. }
  214. }
  215. };
  216. ///////////////////////////////////////////////////////////////////////
  217. // TPrometheusEncoder
  218. ///////////////////////////////////////////////////////////////////////
  219. class TPrometheusEncoder final: public IMetricEncoder {
  220. public:
  221. explicit TPrometheusEncoder(IOutputStream* out, TStringBuf metricNameLabel)
  222. : Writer_(out)
  223. , MetricNameLabel_(metricNameLabel)
  224. {
  225. }
  226. private:
  227. void OnStreamBegin() override {
  228. State_.Expect(TEncoderState::EState::ROOT);
  229. }
  230. void OnStreamEnd() override {
  231. State_.Expect(TEncoderState::EState::ROOT);
  232. Writer_.WriteLn();
  233. }
  234. void OnCommonTime(TInstant time) override {
  235. State_.Expect(TEncoderState::EState::ROOT);
  236. CommonTime_ = time;
  237. }
  238. void OnMetricBegin(EMetricType type) override {
  239. State_.Switch(TEncoderState::EState::ROOT, TEncoderState::EState::METRIC);
  240. MetricState_.Clear();
  241. MetricState_.Type = type;
  242. }
  243. void OnMetricEnd() override {
  244. State_.Switch(TEncoderState::EState::METRIC, TEncoderState::EState::ROOT);
  245. WriteMetric();
  246. }
  247. void OnLabelsBegin() override {
  248. if (State_ == TEncoderState::EState::METRIC) {
  249. State_ = TEncoderState::EState::METRIC_LABELS;
  250. } else if (State_ == TEncoderState::EState::ROOT) {
  251. State_ = TEncoderState::EState::COMMON_LABELS;
  252. } else {
  253. State_.ThrowInvalid("expected METRIC or ROOT");
  254. }
  255. }
  256. void OnLabelsEnd() override {
  257. if (State_ == TEncoderState::EState::METRIC_LABELS) {
  258. State_ = TEncoderState::EState::METRIC;
  259. } else if (State_ == TEncoderState::EState::COMMON_LABELS) {
  260. State_ = TEncoderState::EState::ROOT;
  261. } else {
  262. State_.ThrowInvalid("expected LABELS or COMMON_LABELS");
  263. }
  264. }
  265. void OnLabel(TStringBuf name, TStringBuf value) override {
  266. if (State_ == TEncoderState::EState::METRIC_LABELS) {
  267. MetricState_.Labels.Add(name, value);
  268. } else if (State_ == TEncoderState::EState::COMMON_LABELS) {
  269. CommonLabels_.Add(name, value);
  270. } else {
  271. State_.ThrowInvalid("expected LABELS or COMMON_LABELS");
  272. }
  273. }
  274. void OnDouble(TInstant time, double value) override {
  275. State_.Expect(TEncoderState::EState::METRIC);
  276. MetricState_.Time = time;
  277. MetricState_.SetValue(value);
  278. }
  279. void OnInt64(TInstant time, i64 value) override {
  280. State_.Expect(TEncoderState::EState::METRIC);
  281. MetricState_.Time = time;
  282. MetricState_.SetValue(value);
  283. }
  284. void OnUint64(TInstant time, ui64 value) override {
  285. State_.Expect(TEncoderState::EState::METRIC);
  286. MetricState_.Time = time;
  287. MetricState_.SetValue(value);
  288. }
  289. void OnHistogram(TInstant time, IHistogramSnapshotPtr snapshot) override {
  290. State_.Expect(TEncoderState::EState::METRIC);
  291. MetricState_.Time = time;
  292. MetricState_.SetValue(snapshot.Get());
  293. }
  294. void OnSummaryDouble(TInstant time, ISummaryDoubleSnapshotPtr snapshot) override {
  295. State_.Expect(TEncoderState::EState::METRIC);
  296. MetricState_.Time = time;
  297. MetricState_.SetValue(snapshot.Get());
  298. }
  299. void OnLogHistogram(TInstant, TLogHistogramSnapshotPtr) override {
  300. // TODO(@kbalakirev): implement this function
  301. }
  302. void Close() override {
  303. }
  304. void WriteMetric() {
  305. if (MetricState_.ValueType == EMetricValueType::UNKNOWN) {
  306. return;
  307. }
  308. // XXX: poor performace
  309. for (auto&& l: CommonLabels_) {
  310. MetricState_.Labels.Add(l.Name(), l.Value());
  311. }
  312. TMaybe<TLabel> nameLabel = MetricState_.Labels.Extract(MetricNameLabel_);
  313. Y_ENSURE(nameLabel,
  314. "labels " << MetricState_.Labels <<
  315. " does not contain label '" << MetricNameLabel_ << '\'');
  316. const TString& metricName = ToString(nameLabel->Value());
  317. if (MetricState_.Type != EMetricType::DSUMMARY) {
  318. Writer_.WriteType(MetricState_.Type, metricName);
  319. }
  320. if (MetricState_.Time == TInstant::Zero()) {
  321. MetricState_.Time = CommonTime_;
  322. }
  323. EMetricType type = MetricState_.Type;
  324. if (type == EMetricType::HIST || type == EMetricType::HIST_RATE) {
  325. Y_ENSURE(MetricState_.ValueType == EMetricValueType::HISTOGRAM,
  326. "invalid value type for histogram: " << int(MetricState_.ValueType)); // TODO: to string conversion
  327. Writer_.WriteHistogram(
  328. metricName,
  329. MetricState_.Labels,
  330. MetricState_.Time,
  331. MetricState_.Value.AsHistogram());
  332. } else if (type == EMetricType::DSUMMARY) {
  333. Writer_.WriteSummaryDouble(
  334. metricName,
  335. MetricState_.Labels,
  336. MetricState_.Time,
  337. MetricState_.Value.AsSummaryDouble());
  338. } else {
  339. Writer_.WriteDouble(
  340. metricName,
  341. MetricState_.Labels,
  342. MetricState_.Time,
  343. MetricState_.Value.AsDouble(MetricState_.ValueType));
  344. }
  345. }
  346. private:
  347. TEncoderState State_;
  348. TPrometheusWriter Writer_;
  349. TString MetricNameLabel_;
  350. TInstant CommonTime_ = TInstant::Zero();
  351. TLabels CommonLabels_;
  352. TMetricState MetricState_;
  353. };
  354. }
  355. IMetricEncoderPtr EncoderPrometheus(IOutputStream* out, TStringBuf metricNameLabel) {
  356. return MakeHolder<TPrometheusEncoder>(out, metricNameLabel);
  357. }
  358. } // namespace NMonitoring