prometheus_encoder.cpp 16 KB

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