prometheus_encoder.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  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(ch);
  97. } else {
  98. Out_->Write('_');
  99. }
  100. for (size_t i = 1, len = name.length(); i < len; i++) {
  101. ch = name[i];
  102. if (NPrometheus::IsValidMetricNameContinuation(ch)) {
  103. Out_->Write(ch);
  104. } else {
  105. Out_->Write('_');
  106. }
  107. }
  108. }
  109. void WriteLabels(const TLabels& labels, TStringBuf addLabelKey, TStringBuf addLabelValue) {
  110. Out_->Write('{');
  111. for (auto&& l: labels) {
  112. Out_->Write(l.Name());
  113. Out_->Write('=');
  114. WriteLabelValue(l.Value());
  115. Out_->Write(", "); // trailign comma is supported in parsers
  116. }
  117. if (!addLabelKey.Empty() && !addLabelValue.Empty()) {
  118. Out_->Write(addLabelKey);
  119. Out_->Write('=');
  120. WriteLabelValue(addLabelValue);
  121. }
  122. Out_->Write('}');
  123. }
  124. void WriteLabelValue(TStringBuf value) {
  125. Out_->Write('"');
  126. for (char ch: value) {
  127. if (ch == '"') {
  128. Out_->Write("\\\"");
  129. } else if (ch == '\\') {
  130. Out_->Write("\\\\");
  131. } else if (ch == '\n') {
  132. Out_->Write("\\n");
  133. } else {
  134. Out_->Write(ch);
  135. }
  136. }
  137. Out_->Write('"');
  138. }
  139. void WriteValue(
  140. TStringBuf name, TStringBuf suffix,
  141. const TLabels& labels, TStringBuf addLabelKey, TStringBuf addLabelValue,
  142. TInstant time, double value)
  143. {
  144. // (1) name
  145. WriteMetricName(name);
  146. if (!suffix.Empty()) {
  147. Out_->Write(suffix);
  148. }
  149. // (2) labels
  150. if (!labels.Empty() || !addLabelKey.Empty()) {
  151. WriteLabels(labels, addLabelKey, addLabelValue);
  152. }
  153. Out_->Write(' ');
  154. // (3) value
  155. {
  156. size_t len = FloatToString(value, TmpBuf_, Y_ARRAY_SIZE(TmpBuf_));
  157. Out_->Write(TmpBuf_, len);
  158. }
  159. // (4) time
  160. if (ui64 timeMillis = time.MilliSeconds()) {
  161. Out_->Write(' ');
  162. size_t len = IntToString<10>(timeMillis, TmpBuf_, Y_ARRAY_SIZE(TmpBuf_));
  163. Out_->Write(TmpBuf_, len);
  164. }
  165. Out_->Write('\n');
  166. }
  167. private:
  168. IOutputStream* Out_;
  169. THashSet<TString> WrittenTypes_;
  170. char TmpBuf_[512]; // used to convert doubles to strings
  171. };
  172. ///////////////////////////////////////////////////////////////////////
  173. // TMetricState
  174. ///////////////////////////////////////////////////////////////////////
  175. struct TMetricState {
  176. EMetricType Type = EMetricType::UNKNOWN;
  177. TLabels Labels;
  178. TInstant Time = TInstant::Zero();
  179. EMetricValueType ValueType = EMetricValueType::UNKNOWN;
  180. TMetricValue Value;
  181. ~TMetricState() {
  182. ClearValue();
  183. }
  184. void Clear() {
  185. Type = EMetricType::UNKNOWN;
  186. Labels.Clear();
  187. Time = TInstant::Zero();
  188. ClearValue();
  189. }
  190. void ClearValue() {
  191. // TMetricValue does not keep ownership of histogram
  192. if (ValueType == EMetricValueType::HISTOGRAM) {
  193. Value.AsHistogram()->UnRef();
  194. } else if (ValueType == EMetricValueType::SUMMARY) {
  195. Value.AsSummaryDouble()->UnRef();
  196. }
  197. ValueType = EMetricValueType::UNKNOWN;
  198. Value = {};
  199. }
  200. template <typename T>
  201. void SetValue(T value) {
  202. // TMetricValue does not keep ownership of histogram
  203. if (ValueType == EMetricValueType::HISTOGRAM) {
  204. Value.AsHistogram()->UnRef();
  205. } else if (ValueType == EMetricValueType::SUMMARY) {
  206. Value.AsSummaryDouble()->UnRef();
  207. }
  208. ValueType = TValueType<T>::Type;
  209. Value = TMetricValue(value);
  210. if (ValueType == EMetricValueType::HISTOGRAM) {
  211. Value.AsHistogram()->Ref();
  212. } else if (ValueType == EMetricValueType::SUMMARY) {
  213. Value.AsSummaryDouble()->Ref();
  214. }
  215. }
  216. };
  217. ///////////////////////////////////////////////////////////////////////
  218. // TPrometheusEncoder
  219. ///////////////////////////////////////////////////////////////////////
  220. class TPrometheusEncoder final: public IMetricEncoder {
  221. public:
  222. explicit TPrometheusEncoder(IOutputStream* out, TStringBuf metricNameLabel)
  223. : Writer_(out)
  224. , MetricNameLabel_(metricNameLabel)
  225. {
  226. }
  227. private:
  228. void OnStreamBegin() override {
  229. State_.Expect(TEncoderState::EState::ROOT);
  230. }
  231. void OnStreamEnd() override {
  232. State_.Expect(TEncoderState::EState::ROOT);
  233. Writer_.WriteLn();
  234. }
  235. void OnCommonTime(TInstant time) override {
  236. State_.Expect(TEncoderState::EState::ROOT);
  237. CommonTime_ = time;
  238. }
  239. void OnMetricBegin(EMetricType type) override {
  240. State_.Switch(TEncoderState::EState::ROOT, TEncoderState::EState::METRIC);
  241. MetricState_.Clear();
  242. MetricState_.Type = type;
  243. }
  244. void OnMetricEnd() override {
  245. State_.Switch(TEncoderState::EState::METRIC, TEncoderState::EState::ROOT);
  246. WriteMetric();
  247. }
  248. void OnLabelsBegin() override {
  249. if (State_ == TEncoderState::EState::METRIC) {
  250. State_ = TEncoderState::EState::METRIC_LABELS;
  251. } else if (State_ == TEncoderState::EState::ROOT) {
  252. State_ = TEncoderState::EState::COMMON_LABELS;
  253. } else {
  254. State_.ThrowInvalid("expected METRIC or ROOT");
  255. }
  256. }
  257. void OnLabelsEnd() override {
  258. if (State_ == TEncoderState::EState::METRIC_LABELS) {
  259. State_ = TEncoderState::EState::METRIC;
  260. } else if (State_ == TEncoderState::EState::COMMON_LABELS) {
  261. State_ = TEncoderState::EState::ROOT;
  262. } else {
  263. State_.ThrowInvalid("expected LABELS or COMMON_LABELS");
  264. }
  265. }
  266. void OnLabel(TStringBuf name, TStringBuf value) override {
  267. if (State_ == TEncoderState::EState::METRIC_LABELS) {
  268. MetricState_.Labels.Add(name, value);
  269. } else if (State_ == TEncoderState::EState::COMMON_LABELS) {
  270. CommonLabels_.Add(name, value);
  271. } else {
  272. State_.ThrowInvalid("expected LABELS or COMMON_LABELS");
  273. }
  274. }
  275. void OnLabel(ui32 name, ui32 value) override {
  276. OnLabel(LabelNamesPool_.Get(name), LabelValuesPool_.Get(value));
  277. }
  278. std::pair<ui32, ui32> PrepareLabel(TStringBuf name, TStringBuf value) override {
  279. auto nameLabel = LabelNamesPool_.PutIfAbsent(name);
  280. auto valueLabel = LabelValuesPool_.PutIfAbsent(value);
  281. return std::make_pair(nameLabel->Index, valueLabel->Index);
  282. }
  283. void OnDouble(TInstant time, double value) override {
  284. State_.Expect(TEncoderState::EState::METRIC);
  285. MetricState_.Time = time;
  286. MetricState_.SetValue(value);
  287. }
  288. void OnInt64(TInstant time, i64 value) override {
  289. State_.Expect(TEncoderState::EState::METRIC);
  290. MetricState_.Time = time;
  291. MetricState_.SetValue(value);
  292. }
  293. void OnUint64(TInstant time, ui64 value) override {
  294. State_.Expect(TEncoderState::EState::METRIC);
  295. MetricState_.Time = time;
  296. MetricState_.SetValue(value);
  297. }
  298. void OnHistogram(TInstant time, IHistogramSnapshotPtr snapshot) override {
  299. State_.Expect(TEncoderState::EState::METRIC);
  300. MetricState_.Time = time;
  301. MetricState_.SetValue(snapshot.Get());
  302. }
  303. void OnSummaryDouble(TInstant time, ISummaryDoubleSnapshotPtr snapshot) override {
  304. State_.Expect(TEncoderState::EState::METRIC);
  305. MetricState_.Time = time;
  306. MetricState_.SetValue(snapshot.Get());
  307. }
  308. void OnLogHistogram(TInstant, TLogHistogramSnapshotPtr) override {
  309. // TODO(@kbalakirev): implement this function
  310. }
  311. void Close() override {
  312. }
  313. void WriteMetric() {
  314. if (MetricState_.ValueType == EMetricValueType::UNKNOWN) {
  315. return;
  316. }
  317. // XXX: poor performace
  318. for (auto&& l: CommonLabels_) {
  319. MetricState_.Labels.Add(l.Name(), l.Value());
  320. }
  321. TMaybe<TLabel> nameLabel = MetricState_.Labels.Extract(MetricNameLabel_);
  322. if (!nameLabel) {
  323. return;
  324. }
  325. const TString& metricName = ToString(nameLabel->Value());
  326. if (MetricState_.Type != EMetricType::DSUMMARY) {
  327. Writer_.WriteType(MetricState_.Type, metricName);
  328. }
  329. if (MetricState_.Time == TInstant::Zero()) {
  330. MetricState_.Time = CommonTime_;
  331. }
  332. EMetricType type = MetricState_.Type;
  333. if (type == EMetricType::HIST || type == EMetricType::HIST_RATE) {
  334. Y_ENSURE(MetricState_.ValueType == EMetricValueType::HISTOGRAM,
  335. "invalid value type for histogram: " << int(MetricState_.ValueType)); // TODO: to string conversion
  336. Writer_.WriteHistogram(
  337. metricName,
  338. MetricState_.Labels,
  339. MetricState_.Time,
  340. MetricState_.Value.AsHistogram());
  341. } else if (type == EMetricType::DSUMMARY) {
  342. Writer_.WriteSummaryDouble(
  343. metricName,
  344. MetricState_.Labels,
  345. MetricState_.Time,
  346. MetricState_.Value.AsSummaryDouble());
  347. } else {
  348. Writer_.WriteDouble(
  349. metricName,
  350. MetricState_.Labels,
  351. MetricState_.Time,
  352. MetricState_.Value.AsDouble(MetricState_.ValueType));
  353. }
  354. }
  355. private:
  356. TEncoderState State_;
  357. TPrometheusWriter Writer_;
  358. TString MetricNameLabel_;
  359. TInstant CommonTime_ = TInstant::Zero();
  360. TLabels CommonLabels_;
  361. TMetricState MetricState_;
  362. TStringPoolBuilder LabelNamesPool_;
  363. TStringPoolBuilder LabelValuesPool_;
  364. };
  365. }
  366. IMetricEncoderPtr EncoderPrometheus(IOutputStream* out, TStringBuf metricNameLabel) {
  367. return MakeHolder<TPrometheusEncoder>(out, metricNameLabel);
  368. }
  369. } // namespace NMonitoring