metric_registry_ut.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. #include "metric_registry.h"
  2. #include <library/cpp/monlib/encode/protobuf/protobuf.h>
  3. #include <library/cpp/monlib/encode/json/json.h>
  4. #include <library/cpp/resource/resource.h>
  5. #include <library/cpp/testing/unittest/registar.h>
  6. #include <util/stream/str.h>
  7. using namespace NMonitoring;
  8. template<>
  9. void Out<NMonitoring::NProto::TSingleSample::ValueCase>(IOutputStream& os, NMonitoring::NProto::TSingleSample::ValueCase val) {
  10. switch (val) {
  11. case NMonitoring::NProto::TSingleSample::ValueCase::kInt64:
  12. os << "Int64";
  13. break;
  14. case NMonitoring::NProto::TSingleSample::ValueCase::kUint64:
  15. os << "Uint64";
  16. break;
  17. case NMonitoring::NProto::TSingleSample::ValueCase::kHistogram:
  18. os << "Histogram";
  19. break;
  20. case NMonitoring::NProto::TSingleSample::ValueCase::kFloat64:
  21. os << "Float64";
  22. break;
  23. case NMonitoring::NProto::TSingleSample::ValueCase::kSummaryDouble:
  24. os << "DSummary";
  25. break;
  26. case NMonitoring::NProto::TSingleSample::ValueCase::kLogHistogram:
  27. os << "LogHistogram";
  28. break;
  29. case NMonitoring::NProto::TSingleSample::ValueCase::VALUE_NOT_SET:
  30. os << "NOT SET";
  31. break;
  32. }
  33. }
  34. namespace {
  35. template<typename F>
  36. auto EnsureIdempotent(F&& f) {
  37. auto firstResult = f();
  38. auto secondResult = f();
  39. UNIT_ASSERT_VALUES_EQUAL(firstResult, secondResult);
  40. return secondResult;
  41. }
  42. }
  43. Y_UNIT_TEST_SUITE(TMetricRegistryTest) {
  44. Y_UNIT_TEST(Gauge) {
  45. TMetricRegistry registry(TLabels{{"common", "label"}});
  46. TGauge* g = EnsureIdempotent([&] { return registry.Gauge({{"my", "gauge"}}); });
  47. UNIT_ASSERT_DOUBLES_EQUAL(g->Get(), 0.0, 1E-6);
  48. g->Set(12.34);
  49. UNIT_ASSERT_DOUBLES_EQUAL(g->Get(), 12.34, 1E-6);
  50. double val;
  51. val = g->Add(1.2);
  52. UNIT_ASSERT_DOUBLES_EQUAL(g->Get(), 13.54, 1E-6);
  53. UNIT_ASSERT_DOUBLES_EQUAL(g->Get(), val, 1E-6);
  54. val = g->Add(-3.47);
  55. UNIT_ASSERT_DOUBLES_EQUAL(g->Get(), 10.07, 1E-6);
  56. UNIT_ASSERT_DOUBLES_EQUAL(g->Get(), val, 1E-6);
  57. }
  58. Y_UNIT_TEST(LazyGauge) {
  59. TMetricRegistry registry(TLabels{{"common", "label"}});
  60. double val = 0.0;
  61. TLazyGauge* g = EnsureIdempotent([&] { return registry.LazyGauge({{"my", "lazyGauge"}}, [&val](){return val;}); });
  62. UNIT_ASSERT_DOUBLES_EQUAL(g->Get(), 0.0, 1E-6);
  63. val = 12.34;
  64. UNIT_ASSERT_DOUBLES_EQUAL(g->Get(), 12.34, 1E-6);
  65. val += 1.2;
  66. UNIT_ASSERT_DOUBLES_EQUAL(g->Get(), 13.54, 1E-6);
  67. UNIT_ASSERT_DOUBLES_EQUAL(g->Get(), val, 1E-6);
  68. val += -3.47;
  69. UNIT_ASSERT_DOUBLES_EQUAL(g->Get(), 10.07, 1E-6);
  70. UNIT_ASSERT_DOUBLES_EQUAL(g->Get(), val, 1E-6);
  71. }
  72. Y_UNIT_TEST(IntGauge) {
  73. TMetricRegistry registry(TLabels{{"common", "label"}});
  74. TIntGauge* g = EnsureIdempotent([&] { return registry.IntGauge({{"my", "gauge"}}); });
  75. UNIT_ASSERT_VALUES_EQUAL(g->Get(), 0);
  76. i64 val;
  77. val = g->Inc();
  78. UNIT_ASSERT_VALUES_EQUAL(g->Get(), 1);
  79. UNIT_ASSERT_VALUES_EQUAL(g->Get(), val);
  80. val = g->Dec();
  81. UNIT_ASSERT_VALUES_EQUAL(g->Get(), 0);
  82. UNIT_ASSERT_VALUES_EQUAL(g->Get(), val);
  83. val = g->Add(1);
  84. UNIT_ASSERT_VALUES_EQUAL(g->Get(), 1);
  85. UNIT_ASSERT_VALUES_EQUAL(g->Get(), val);
  86. val = g->Add(2);
  87. UNIT_ASSERT_VALUES_EQUAL(g->Get(), 3);
  88. UNIT_ASSERT_VALUES_EQUAL(g->Get(), val);
  89. val = g->Add(-5);
  90. UNIT_ASSERT_VALUES_EQUAL(g->Get(), -2);
  91. UNIT_ASSERT_VALUES_EQUAL(g->Get(), val);
  92. }
  93. Y_UNIT_TEST(LazyIntGauge) {
  94. TMetricRegistry registry(TLabels{{"common", "label"}});
  95. i64 val = 0;
  96. TLazyIntGauge* g = EnsureIdempotent([&] { return registry.LazyIntGauge({{"my", "gauge"}}, [&val](){return val;}); });
  97. UNIT_ASSERT_VALUES_EQUAL(g->Get(), 0);
  98. val += 1;
  99. UNIT_ASSERT_VALUES_EQUAL(g->Get(), 1);
  100. UNIT_ASSERT_VALUES_EQUAL(g->Get(), val);
  101. val -= 1;
  102. UNIT_ASSERT_VALUES_EQUAL(g->Get(), 0);
  103. UNIT_ASSERT_VALUES_EQUAL(g->Get(), val);
  104. val = 42;
  105. UNIT_ASSERT_VALUES_EQUAL(g->Get(), val);
  106. }
  107. Y_UNIT_TEST(Counter) {
  108. TMetricRegistry registry(TLabels{{"common", "label"}});
  109. TCounter* c = EnsureIdempotent([&] { return registry.Counter({{"my", "counter"}}); });
  110. UNIT_ASSERT_VALUES_EQUAL(c->Get(), 0);
  111. UNIT_ASSERT_VALUES_EQUAL(c->Inc(), 1);
  112. UNIT_ASSERT_VALUES_EQUAL(c->Get(), 1);
  113. UNIT_ASSERT_VALUES_EQUAL(c->Add(10), 11);
  114. UNIT_ASSERT_VALUES_EQUAL(c->Get(), 11);
  115. }
  116. Y_UNIT_TEST(LazyCounter) {
  117. TMetricRegistry registry(TLabels{{"common", "label"}});
  118. ui64 val = 0;
  119. TLazyCounter* c = EnsureIdempotent([&] { return registry.LazyCounter({{"my", "counter"}}, [&val](){return val;}); });
  120. UNIT_ASSERT_VALUES_EQUAL(c->Get(), 0);
  121. val = 42;
  122. UNIT_ASSERT_VALUES_EQUAL(c->Get(), 42);
  123. }
  124. Y_UNIT_TEST(LazyRate) {
  125. TMetricRegistry registry(TLabels{{"common", "label"}});
  126. ui64 val = 0;
  127. TLazyRate* r = EnsureIdempotent([&] { return registry.LazyRate({{"my", "rate"}}, [&val](){return val;}); });
  128. UNIT_ASSERT_VALUES_EQUAL(r->Get(), 0);
  129. val = 42;
  130. UNIT_ASSERT_VALUES_EQUAL(r->Get(), 42);
  131. }
  132. Y_UNIT_TEST(DoubleCounter) {
  133. TMetricRegistry registry(TLabels{{"common", "label"}});
  134. TCounter* c = registry.Counter({{"my", "counter"}});
  135. UNIT_ASSERT_VALUES_EQUAL(c->Get(), 0);
  136. c->Add(10);
  137. c = registry.Counter({{"my", "counter"}});
  138. UNIT_ASSERT_VALUES_EQUAL(c->Get(), 10);
  139. }
  140. Y_UNIT_TEST(Sample) {
  141. TMetricRegistry registry(TLabels{{"common", "label"}});
  142. TGauge* g = registry.Gauge({{"my", "gauge"}});
  143. g->Set(12.34);
  144. TCounter* c = registry.Counter({{"my", "counter"}});
  145. c->Add(10);
  146. NProto::TSingleSamplesList samples;
  147. auto encoder = EncoderProtobuf(&samples);
  148. auto now = TInstant::Now();
  149. registry.Accept(now, encoder.Get());
  150. UNIT_ASSERT_VALUES_EQUAL(samples.SamplesSize(), 2);
  151. UNIT_ASSERT_VALUES_EQUAL(samples.CommonLabelsSize(), 1);
  152. {
  153. const NProto::TLabel& label = samples.GetCommonLabels(0);
  154. UNIT_ASSERT_STRINGS_EQUAL(label.GetName(), "common");
  155. UNIT_ASSERT_STRINGS_EQUAL(label.GetValue(), "label");
  156. }
  157. for (const NProto::TSingleSample& sample : samples.GetSamples()) {
  158. UNIT_ASSERT_VALUES_EQUAL(sample.LabelsSize(), 1);
  159. UNIT_ASSERT_VALUES_EQUAL(sample.GetTime(), now.MilliSeconds());
  160. if (sample.GetMetricType() == NProto::GAUGE) {
  161. UNIT_ASSERT_VALUES_EQUAL(sample.GetValueCase(), NProto::TSingleSample::kFloat64);
  162. UNIT_ASSERT_DOUBLES_EQUAL(sample.GetFloat64(), 12.34, 1E-6);
  163. const NProto::TLabel& label = sample.GetLabels(0);
  164. UNIT_ASSERT_STRINGS_EQUAL(label.GetName(), "my");
  165. UNIT_ASSERT_STRINGS_EQUAL(label.GetValue(), "gauge");
  166. } else if (sample.GetMetricType() == NProto::COUNTER) {
  167. UNIT_ASSERT_VALUES_EQUAL(sample.GetValueCase(), NProto::TSingleSample::kUint64);
  168. UNIT_ASSERT_VALUES_EQUAL(sample.GetUint64(), 10);
  169. const NProto::TLabel& label = sample.GetLabels(0);
  170. UNIT_ASSERT_STRINGS_EQUAL(label.GetName(), "my");
  171. UNIT_ASSERT_STRINGS_EQUAL(label.GetValue(), "counter");
  172. } else {
  173. UNIT_FAIL("unexpected sample type");
  174. }
  175. }
  176. }
  177. Y_UNIT_TEST(Histograms) {
  178. TMetricRegistry registry(TLabels{{"common", "label"}});
  179. THistogram* h1 = EnsureIdempotent([&] {
  180. return registry.HistogramCounter(
  181. {{"sensor", "readTimeMillis"}},
  182. ExponentialHistogram(5, 2));
  183. });
  184. THistogram* h2 = EnsureIdempotent([&] {
  185. return registry.HistogramRate(
  186. {{"sensor", "writeTimeMillis"}},
  187. ExplicitHistogram({1, 5, 15, 20, 25}));
  188. });
  189. for (i64 i = 0; i < 100; i++) {
  190. h1->Record(i);
  191. h2->Record(i);
  192. }
  193. TStringStream ss;
  194. {
  195. auto encoder = EncoderJson(&ss, 2);
  196. registry.Accept(TInstant::Zero(), encoder.Get());
  197. }
  198. ss << '\n';
  199. UNIT_ASSERT_NO_DIFF(ss.Str(), NResource::Find("/histograms.json"));
  200. }
  201. Y_UNIT_TEST(HistogramsFabric) {
  202. TMetricRegistry registry(TLabels{{"common", "label"}});
  203. bool called = false;
  204. auto collector = [&]() {
  205. called = true;
  206. return ExponentialHistogram(5, 2);
  207. };
  208. THistogram* h1 = registry.HistogramCounter(
  209. {{"sensor", "readTimeMillis"}},
  210. collector);
  211. UNIT_ASSERT_VALUES_EQUAL(called, true);
  212. called = false;
  213. h1 = registry.HistogramCounter(
  214. {{"sensor", "readTimeMillis"}},
  215. collector);
  216. UNIT_ASSERT_VALUES_EQUAL(called, false);
  217. THistogram* h2 = registry.HistogramRate(
  218. {{"sensor", "writeTimeMillis"}},
  219. ExplicitHistogram({1, 5, 15, 20, 25}));
  220. for (i64 i = 0; i < 100; i++) {
  221. h1->Record(i);
  222. h2->Record(i);
  223. }
  224. TStringStream ss;
  225. {
  226. auto encoder = EncoderJson(&ss, 2);
  227. registry.Accept(TInstant::Zero(), encoder.Get());
  228. }
  229. ss << '\n';
  230. UNIT_ASSERT_NO_DIFF(ss.Str(), NResource::Find("/histograms.json"));
  231. }
  232. Y_UNIT_TEST(StreamingEncoderTest) {
  233. const TString expected {
  234. "{\"commonLabels\":{\"common\":\"label\"},"
  235. "\"sensors\":[{\"kind\":\"GAUGE\",\"labels\":{\"my\":\"gauge\"},\"value\":12.34}]}"
  236. };
  237. TMetricRegistry registry(TLabels{{"common", "label"}});
  238. TGauge* g = registry.Gauge({{"my", "gauge"}});
  239. g->Set(12.34);
  240. TStringStream os;
  241. auto encoder = EncoderJson(&os);
  242. registry.Accept(TInstant::Zero(), encoder.Get());
  243. UNIT_ASSERT_STRINGS_EQUAL(os.Str(), expected);
  244. }
  245. Y_UNIT_TEST(CreatingSameMetricWithDifferentTypesShouldThrow) {
  246. TMetricRegistry registry;
  247. registry.Gauge({{"foo", "bar"}});
  248. UNIT_ASSERT_EXCEPTION(registry.Counter({{"foo", "bar"}}), yexception);
  249. registry.HistogramCounter({{"bar", "baz"}}, ExponentialHistogram(5, 2));
  250. UNIT_ASSERT_EXCEPTION(registry.HistogramRate({{"bar", "baz"}}, ExponentialHistogram(5, 2)), yexception);
  251. }
  252. Y_UNIT_TEST(EncodeRegistryWithCommonLabels) {
  253. TMetricRegistry registry(TLabels{{"common", "label"}});
  254. TGauge* g = registry.Gauge({{"my", "gauge"}});
  255. g->Set(12.34);
  256. // Append() adds common labels to each metric, allowing to combine
  257. // several metric registries in one resulting blob
  258. {
  259. TStringStream os;
  260. auto encoder = EncoderJson(&os);
  261. encoder->OnStreamBegin();
  262. registry.Append(TInstant::Zero(), encoder.Get());
  263. encoder->OnStreamEnd();
  264. UNIT_ASSERT_STRINGS_EQUAL(
  265. os.Str(),
  266. "{\"sensors\":[{\"kind\":\"GAUGE\",\"labels\":{\"common\":\"label\",\"my\":\"gauge\"},\"value\":12.34}]}");
  267. }
  268. // Accept() adds common labels to the beginning of the blob
  269. {
  270. TStringStream os;
  271. auto encoder = EncoderJson(&os);
  272. registry.Accept(TInstant::Zero(), encoder.Get());
  273. UNIT_ASSERT_STRINGS_EQUAL(
  274. os.Str(),
  275. "{\"commonLabels\":{\"common\":\"label\"},"
  276. "\"sensors\":[{\"kind\":\"GAUGE\",\"labels\":{\"my\":\"gauge\"},\"value\":12.34}]}");
  277. }
  278. }
  279. Y_UNIT_TEST(MetricsRegistryClear) {
  280. TMetricRegistry registry;
  281. registry.Gauge({{"some", "label"}})->Add(1);
  282. NProto::TSingleSamplesList samples;
  283. auto encoder = EncoderProtobuf(&samples);
  284. registry.Accept(TInstant::Now(), encoder.Get());
  285. UNIT_ASSERT(samples.SamplesSize() == 1);
  286. samples = {};
  287. registry.Clear();
  288. registry.Accept(TInstant::Now(), encoder.Get());
  289. UNIT_ASSERT(samples.SamplesSize() == 0);
  290. }
  291. Y_UNIT_TEST(AssignNewRegistry) {
  292. TMetricRegistry registry;
  293. registry.Gauge({{"some", "label"}})->Add(1);
  294. NProto::TSingleSamplesList samples;
  295. auto encoder = EncoderProtobuf(&samples);
  296. registry.Accept(TInstant::Now(), encoder.Get());
  297. UNIT_ASSERT(samples.CommonLabelsSize() == 0);
  298. UNIT_ASSERT(samples.SamplesSize() == 1);
  299. samples = {};
  300. auto newRegistry = TMetricRegistry{{{"common", "label"}}};
  301. registry = std::move(newRegistry);
  302. registry.Accept(TInstant::Now(), encoder.Get());
  303. const auto& commonLabels = samples.GetCommonLabels();
  304. UNIT_ASSERT(samples.GetSamples().size() == 0);
  305. UNIT_ASSERT(commonLabels.size() == 1);
  306. UNIT_ASSERT(commonLabels[0].GetName() == "common");
  307. UNIT_ASSERT(commonLabels[0].GetValue() == "label");
  308. }
  309. }