diff options
author | robot-ydb-importer <robot-ydb-importer@yandex-team.com> | 2024-02-14 19:47:36 +0300 |
---|---|---|
committer | robot-ydb-importer <robot-ydb-importer@yandex-team.com> | 2024-02-14 21:51:48 +0300 |
commit | ccc9ad1a6914b4cce50935b1b3fd868ed69fed13 (patch) | |
tree | 9dea935eaf96e944bf8262a295eb8bccb7bce077 /library/cpp/monlib/encode | |
parent | 37ca0ae098448d6f7d13b7c651f38c282915ad3a (diff) | |
download | ydb-ccc9ad1a6914b4cce50935b1b3fd868ed69fed13.tar.gz |
YDB Import 566
96265cd0cc64e1b9bb31fe97b915ed2a09caf1cb
Diffstat (limited to 'library/cpp/monlib/encode')
-rw-r--r-- | library/cpp/monlib/encode/unistat/unistat.h | 13 | ||||
-rw-r--r-- | library/cpp/monlib/encode/unistat/unistat_decoder.cpp | 312 | ||||
-rw-r--r-- | library/cpp/monlib/encode/unistat/unistat_ut.cpp | 341 | ||||
-rw-r--r-- | library/cpp/monlib/encode/unistat/ut/ya.make | 11 | ||||
-rw-r--r-- | library/cpp/monlib/encode/unistat/ya.make | 17 |
5 files changed, 694 insertions, 0 deletions
diff --git a/library/cpp/monlib/encode/unistat/unistat.h b/library/cpp/monlib/encode/unistat/unistat.h new file mode 100644 index 0000000000..300fb6270f --- /dev/null +++ b/library/cpp/monlib/encode/unistat/unistat.h @@ -0,0 +1,13 @@ +#pragma once + +#include <util/generic/fwd.h> +#include <util/datetime/base.h> + +namespace NMonitoring { + /// Decodes unistat-style metrics + /// https://wiki.yandex-team.ru/golovan/stat-handle + void DecodeUnistat(TStringBuf data, class IMetricConsumer* c, TStringBuf metricNameLabel = "sensor", TInstant ts = TInstant::Zero()); + + /// Assumes consumer's stream is open by the caller + void DecodeUnistatToStream(TStringBuf data, class IMetricConsumer* c, TStringBuf metricNameLabel = "sensor", TInstant ts = TInstant::Zero()); +} diff --git a/library/cpp/monlib/encode/unistat/unistat_decoder.cpp b/library/cpp/monlib/encode/unistat/unistat_decoder.cpp new file mode 100644 index 0000000000..a2b787365c --- /dev/null +++ b/library/cpp/monlib/encode/unistat/unistat_decoder.cpp @@ -0,0 +1,312 @@ +#include "unistat.h" + +#include <library/cpp/monlib/metrics/histogram_collector.h> +#include <library/cpp/monlib/metrics/labels.h> +#include <library/cpp/monlib/metrics/metric_type.h> +#include <library/cpp/monlib/metrics/metric_value.h> +#include <library/cpp/monlib/metrics/metric_consumer.h> + +#include <library/cpp/json/json_reader.h> + +#include <util/datetime/base.h> +#include <util/string/split.h> + +#include <contrib/libs/re2/re2/re2.h> + +using namespace NJson; + +const re2::RE2 NAME_RE{R"((?:[a-zA-Z0-9\.\-/@_]+_)+(?:[ad][vehmntx]{3}|summ|hgram|max))"}; + +namespace NMonitoring { + namespace { + bool IsNumber(const NJson::TJsonValue& j) { + switch (j.GetType()) { + case EJsonValueType::JSON_INTEGER: + case EJsonValueType::JSON_UINTEGER: + case EJsonValueType::JSON_DOUBLE: + return true; + + default: + return false; + } + } + + template <typename T> + T ExtractNumber(const TJsonValue& val) { + switch (val.GetType()) { + case EJsonValueType::JSON_INTEGER: + return static_cast<T>(val.GetInteger()); + case EJsonValueType::JSON_UINTEGER: + return static_cast<T>(val.GetUInteger()); + case EJsonValueType::JSON_DOUBLE: + return static_cast<T>(val.GetDouble()); + + default: + ythrow yexception() << "Expected number, but found " << val.GetType(); + } + } + + auto ExtractDouble = ExtractNumber<double>; + auto ExtractUi64 = ExtractNumber<ui64>; + + class THistogramBuilder { + public: + void Add(TBucketBound bound, TBucketValue value) { + /// XXX: yasm uses left-closed intervals, while in monlib we use right-closed ones, + /// so (-inf; 0) [0, 100) [100; +inf) + /// becomes (-inf; 0] (0, 100] (100; +inf) + /// but since we've already lost some information these no way to avoid this kind of error here + Bounds_.push_back(bound); + + /// this will always be 0 for the first bucket, + /// since there's no way to make (-inf; N) bucket in yasm + Values_.push_back(NextValue_); + + /// we will write this value into the next bucket so that [[0, 10], [100, 20], [200, 50]] + /// becomes (-inf; 0] -> 0; (0; 100] -> 10; (100; 200] -> 20; (200; +inf) -> 50 + NextValue_ = value; + } + + IHistogramSnapshotPtr Finalize() { + Bounds_.push_back(std::numeric_limits<TBucketBound>::max()); + Values_.push_back(NextValue_); + + return ExplicitHistogramSnapshot(Bounds_, Values_, true); + } + + public: + TBucketValue NextValue_ {0}; + TBucketBounds Bounds_; + TBucketValues Values_; + }; + + class LogHistogramBuilder { + public: + void Add(double value) { + while (Values_.size() < HISTOGRAM_MAX_BUCKETS_COUNT && value > MaxValueWithBickets(Values_.size())) { + Values_.push_back(0); + } + + ui32 index = 0; + if (value > MaxValueWithBickets(HISTOGRAM_MAX_BUCKETS_COUNT)) { + index = Values_.size() - 1; + } else if (value > MIN_VALUE) { + double logBase = std::log(value) / std::log(BASE); + index = static_cast<ui32>(std::ceil(logBase)); + } + ++Values_[index]; + } + + IHistogramSnapshotPtr Finalize() && { + return new TExponentialHistogramSnapshot(BASE, MIN_VALUE, std::move(Values_)); + } + + private: + static constexpr double BASE = 2; + static constexpr double MIN_VALUE = 1; + + static double MaxValueWithBickets(ui64 buckets) { + return std::pow(BASE, buckets - 2); + } + + private: + TBucketValues Values_ = TBucketValues(2, 0); + }; + + class TDecoderUnistat { + private: + public: + explicit TDecoderUnistat(IMetricConsumer* consumer, IInputStream* is, TStringBuf metricNameLabel, TInstant ts) + : Consumer_{consumer}, + MetricNameLabel(metricNameLabel), + Timestamp_{ts} { + ReadJsonTree(is, &Json_, /* throw */ true); + } + + void Decode() { + Y_ENSURE(Json_.IsArray(), "Expected array at the top level, but found " << Json_.GetType()); + + for (auto&& metric : Json_.GetArray()) { + Y_ENSURE(metric.IsArray(), "Metric must be an array"); + auto&& arr = metric.GetArray(); + Y_ENSURE(arr.size() == 2, "Metric must be an array of 2 elements"); + auto&& name = arr[0]; + auto&& value = arr[1]; + MetricContext_ = {}; + + ParseName(name.GetString()); + + if (value.IsArray()) { + const auto& array = value.GetArray(); + if (!array.empty() && IsNumber(array[0])) { + OnLogHistogram(value); + } else { + OnHistogram(value); + } + } else if (IsNumber(value)) { + if (MetricContext_.Name.EndsWith("_ahhh")) { + OnLogHistogram(value); + } else { + OnScalar(value); + } + } else { + ythrow yexception() << "Expected list or number, but found " << value.GetType(); + } + + WriteValue(); + } + } + + private: + void OnScalar(const TJsonValue& jsonValue) { + if (MetricContext_.IsDeriv) { + MetricContext_.Type = EMetricType::RATE; + MetricContext_.Value = TMetricValue{ExtractUi64(jsonValue)}; + } else { + MetricContext_.Type = EMetricType::GAUGE; + MetricContext_.Value = TMetricValue{ExtractDouble(jsonValue)}; + } + } + + void OnLogHistogram(const TJsonValue& value) { + Y_ENSURE(MetricContext_.Name.EndsWith("_ahhh"), "Values list is supported only for _ahhh metrics"); + MetricContext_.Type = EMetricType::HIST; + + LogHistogramBuilder histogramBuilder; + if (IsNumber(value)) { + histogramBuilder.Add(value.GetDouble()); + } else { + for (auto&& item: value.GetArray()) { + Y_ENSURE(IsNumber(item), "Expected a number, but found " << item.GetType()); + histogramBuilder.Add(item.GetDouble()); + } + } + + MetricContext_.Histogram = std::move(histogramBuilder).Finalize(); + MetricContext_.Value = TMetricValue{MetricContext_.Histogram.Get()}; + } + + void OnHistogram(const TJsonValue& jsonHist) { + if (MetricContext_.IsDeriv) { + MetricContext_.Type = EMetricType::HIST_RATE; + } else { + MetricContext_.Type = EMetricType::HIST; + } + + auto histogramBuilder = THistogramBuilder(); + + for (auto&& bucket : jsonHist.GetArray()) { + Y_ENSURE(bucket.IsArray(), "Expected an array, but found " << bucket.GetType()); + auto&& arr = bucket.GetArray(); + Y_ENSURE(arr.size() == 2, "Histogram bucket must be an array of 2 elements"); + const auto bound = ExtractDouble(arr[0]); + const auto weight = ExtractUi64(arr[1]); + histogramBuilder.Add(bound, weight); + } + + MetricContext_.Histogram = histogramBuilder.Finalize(); + MetricContext_.Value = TMetricValue{MetricContext_.Histogram.Get()}; + } + + bool IsDeriv(TStringBuf name) { + TStringBuf ignore, suffix; + name.RSplit('_', ignore, suffix); + + Y_ENSURE(suffix.size() >= 3 && suffix.size() <= 5, "Disallowed suffix value: " << suffix); + + if (suffix == TStringBuf("summ") || suffix == TStringBuf("hgram")) { + return true; + } else if (suffix == TStringBuf("max")) { + return false; + } + + return suffix[0] == 'd'; + } + + void ParseName(TStringBuf value) { + TVector<TStringBuf> parts; + StringSplitter(value).Split(';').SkipEmpty().Collect(&parts); + + Y_ENSURE(parts.size() >= 1 && parts.size() <= 16); + + TStringBuf name = parts.back(); + parts.pop_back(); + + Y_ENSURE(RE2::FullMatch(re2::StringPiece{name.data(), name.size()}, NAME_RE), + "Metric name " << name << " doesn't match regex " << NAME_RE.pattern()); + + MetricContext_.Name = name; + MetricContext_.IsDeriv = IsDeriv(MetricContext_.Name); + + for (auto tag : parts) { + TStringBuf n, v; + tag.Split('=', n, v); + Y_ENSURE(n && v, "Unexpected tag format in " << tag); + MetricContext_.Labels.Add(n, v); + } + } + + private: + void WriteValue() { + Consumer_->OnMetricBegin(MetricContext_.Type); + + Consumer_->OnLabelsBegin(); + Consumer_->OnLabel(MetricNameLabel, TString{MetricContext_.Name}); + for (auto&& l : MetricContext_.Labels) { + Consumer_->OnLabel(l.Name(), l.Value()); + } + + Consumer_->OnLabelsEnd(); + + switch (MetricContext_.Type) { + case EMetricType::GAUGE: + Consumer_->OnDouble(Timestamp_, MetricContext_.Value.AsDouble()); + break; + case EMetricType::RATE: + Consumer_->OnUint64(Timestamp_, MetricContext_.Value.AsUint64()); + break; + case EMetricType::HIST: + case EMetricType::HIST_RATE: + Consumer_->OnHistogram(Timestamp_, MetricContext_.Value.AsHistogram()); + break; + case EMetricType::LOGHIST: + case EMetricType::DSUMMARY: + case EMetricType::IGAUGE: + case EMetricType::COUNTER: + case EMetricType::UNKNOWN: + ythrow yexception() << "Unexpected metric type: " << MetricContext_.Type; + } + + Consumer_->OnMetricEnd(); + } + + private: + IMetricConsumer* Consumer_; + NJson::TJsonValue Json_; + TStringBuf MetricNameLabel; + TInstant Timestamp_; + + struct { + TStringBuf Name; + EMetricType Type{EMetricType::UNKNOWN}; + TMetricValue Value; + bool IsDeriv{false}; + TLabels Labels; + IHistogramSnapshotPtr Histogram; + } MetricContext_; + }; + + } + + void DecodeUnistat(TStringBuf data, IMetricConsumer* c, TStringBuf metricNameLabel, TInstant ts) { + c->OnStreamBegin(); + DecodeUnistatToStream(data, c, metricNameLabel, ts); + c->OnStreamEnd(); + } + + void DecodeUnistatToStream(TStringBuf data, IMetricConsumer* c, TStringBuf metricNameLabel, TInstant ts) { + TMemoryInput in{data.data(), data.size()}; + TDecoderUnistat decoder(c, &in, metricNameLabel, ts); + decoder.Decode(); + } +} diff --git a/library/cpp/monlib/encode/unistat/unistat_ut.cpp b/library/cpp/monlib/encode/unistat/unistat_ut.cpp new file mode 100644 index 0000000000..f15fc7c5b7 --- /dev/null +++ b/library/cpp/monlib/encode/unistat/unistat_ut.cpp @@ -0,0 +1,341 @@ +#include "unistat.h" + +#include <library/cpp/monlib/encode/protobuf/protobuf.h> +#include <library/cpp/monlib/metrics/labels.h> + +#include <library/cpp/testing/unittest/registar.h> + +using namespace NMonitoring; + +Y_UNIT_TEST_SUITE(TUnistatDecoderTest) { + Y_UNIT_TEST(MetricNameLabel) { + constexpr auto input = TStringBuf(R"([["something_axxx", 42]])"); + + NProto::TMultiSamplesList samples; + auto encoder = EncoderProtobuf(&samples); + + DecodeUnistat(input, encoder.Get(), "metric_name_label"); + + UNIT_ASSERT_VALUES_EQUAL(samples.SamplesSize(), 1); + auto sample = samples.GetSamples(0); + + auto label = sample.GetLabels(0); + UNIT_ASSERT_VALUES_EQUAL(label.GetName(), "metric_name_label"); + } + + Y_UNIT_TEST(ScalarMetric) { + constexpr auto input = TStringBuf(R"([["something_axxx", 42]])"); + + NProto::TMultiSamplesList samples; + auto encoder = EncoderProtobuf(&samples); + + DecodeUnistat(input, encoder.Get()); + + UNIT_ASSERT_VALUES_EQUAL(samples.SamplesSize(), 1); + auto sample = samples.GetSamples(0); + UNIT_ASSERT_EQUAL(sample.GetMetricType(), NProto::GAUGE); + UNIT_ASSERT_VALUES_EQUAL(sample.PointsSize(), 1); + UNIT_ASSERT_VALUES_EQUAL(sample.LabelsSize(), 1); + + auto label = sample.GetLabels(0); + auto point = sample.GetPoints(0); + UNIT_ASSERT_VALUES_EQUAL(point.GetFloat64(), 42.); + UNIT_ASSERT_VALUES_EQUAL(label.GetName(), "sensor"); + UNIT_ASSERT_VALUES_EQUAL(label.GetValue(), "something_axxx"); + } + + Y_UNIT_TEST(OverriddenTags) { + constexpr auto input = TStringBuf(R"([["ctype=foo;prj=bar;custom_tag=qwe;something_axxx", 42]])"); + + NProto::TMultiSamplesList samples; + auto encoder = EncoderProtobuf(&samples); + + DecodeUnistat(input, encoder.Get()); + + UNIT_ASSERT_VALUES_EQUAL(samples.SamplesSize(), 1); + auto sample = samples.GetSamples(0); + UNIT_ASSERT_VALUES_EQUAL(sample.PointsSize(), 1); + UNIT_ASSERT_VALUES_EQUAL(sample.LabelsSize(), 4); + + const auto& labels = sample.GetLabels(); + TLabels actual; + for (auto&& l : labels) { + actual.Add(l.GetName(), l.GetValue()); + } + + TLabels expected{{"ctype", "foo"}, {"prj", "bar"}, {"custom_tag", "qwe"}, {"sensor", "something_axxx"}}; + + UNIT_ASSERT_VALUES_EQUAL(actual.size(), expected.size()); + for (auto&& l : actual) { + UNIT_ASSERT(expected.Extract(l.Name())->Value() == l.Value()); + } + } + + Y_UNIT_TEST(ThrowsOnTopLevelObject) { + constexpr auto input = TStringBuf(R"({["something_axxx", 42]})"); + + NProto::TMultiSamplesList samples; + auto encoder = EncoderProtobuf(&samples); + + UNIT_ASSERT_EXCEPTION(DecodeUnistat(input, encoder.Get()), yexception); + } + + Y_UNIT_TEST(ThrowsOnUnwrappedMetric) { + constexpr auto input = TStringBuf(R"(["something_axxx", 42])"); + + NProto::TMultiSamplesList samples; + auto encoder = EncoderProtobuf(&samples); + + UNIT_ASSERT_EXCEPTION(DecodeUnistat(input, encoder.Get()), yexception); + } + + Y_UNIT_TEST(HistogramMetric) { + constexpr auto input = TStringBuf(R"([["something_hgram", [[0, 1], [200, 2], [500, 3]] ]])"); + + NProto::TMultiSamplesList samples; + auto encoder = EncoderProtobuf(&samples); + + DecodeUnistat(input, encoder.Get()); + + auto sample = samples.GetSamples(0); + UNIT_ASSERT_EQUAL(sample.GetMetricType(), NProto::HIST_RATE); + UNIT_ASSERT_VALUES_EQUAL(sample.PointsSize(), 1); + UNIT_ASSERT_VALUES_EQUAL(sample.LabelsSize(), 1); + + auto label = sample.GetLabels(0); + const auto point = sample.GetPoints(0); + const auto histogram = point.GetHistogram(); + const auto size = histogram.BoundsSize(); + UNIT_ASSERT_VALUES_EQUAL(size, 4); + + const TVector<double> expectedBounds {0, 200, 500, std::numeric_limits<double>::max()}; + const TVector<ui64> expectedValues {0, 1, 2, 3}; + + for (auto i = 0; i < 4; ++i) { + UNIT_ASSERT_VALUES_EQUAL(histogram.GetBounds(i), expectedBounds[i]); + UNIT_ASSERT_VALUES_EQUAL(histogram.GetValues(i), expectedValues[i]); + } + + UNIT_ASSERT_VALUES_EQUAL(label.GetName(), "sensor"); + UNIT_ASSERT_VALUES_EQUAL(label.GetValue(), "something_hgram"); + } + + Y_UNIT_TEST(AbsoluteHistogram) { + constexpr auto input = TStringBuf(R"([["something_ahhh", [[0, 1], [200, 2], [500, 3]] ]])"); + + NProto::TMultiSamplesList samples; + auto encoder = EncoderProtobuf(&samples); + + DecodeUnistat(input, encoder.Get()); + + auto sample = samples.GetSamples(0); + UNIT_ASSERT_EQUAL(sample.GetMetricType(), NProto::HISTOGRAM); + UNIT_ASSERT_VALUES_EQUAL(sample.PointsSize(), 1); + UNIT_ASSERT_VALUES_EQUAL(sample.LabelsSize(), 1); + } + + Y_UNIT_TEST(LogHistogram) { + constexpr auto input = TStringBuf(R"([["something_ahhh", [1, 2, 3] ]])"); + + NProto::TMultiSamplesList samples; + auto encoder = EncoderProtobuf(&samples); + + DecodeUnistat(input, encoder.Get()); + + auto sample = samples.GetSamples(0); + UNIT_ASSERT_EQUAL(sample.GetMetricType(), NProto::HISTOGRAM); + UNIT_ASSERT_VALUES_EQUAL(sample.PointsSize(), 1); + UNIT_ASSERT_VALUES_EQUAL(sample.LabelsSize(), 1); + + auto histogram = sample.GetPoints(0).GetHistogram(); + UNIT_ASSERT_VALUES_EQUAL(histogram.BoundsSize(), 4); + TVector<double> expectedBounds = {1, 2, 4, std::numeric_limits<double>::max()}; + TVector<ui64> expectedValues = {1, 1, 1, 0}; + + for (auto i = 0; i < 4; ++i) { + UNIT_ASSERT_VALUES_EQUAL(histogram.GetBounds(i), expectedBounds[i]); + UNIT_ASSERT_VALUES_EQUAL(histogram.GetValues(i), expectedValues[i]); + } + } + + Y_UNIT_TEST(LogHistogramOverflow) { + constexpr auto input = TStringBuf(R"([["something_ahhh", [1125899906842624, 2251799813685248] ]])"); + + NProto::TMultiSamplesList samples; + auto encoder = EncoderProtobuf(&samples); + + DecodeUnistat(input, encoder.Get()); + + auto sample = samples.GetSamples(0); + UNIT_ASSERT_EQUAL(sample.GetMetricType(), NProto::HISTOGRAM); + UNIT_ASSERT_VALUES_EQUAL(sample.PointsSize(), 1); + UNIT_ASSERT_VALUES_EQUAL(sample.LabelsSize(), 1); + + auto histogram = sample.GetPoints(0).GetHistogram(); + UNIT_ASSERT_VALUES_EQUAL(histogram.BoundsSize(), HISTOGRAM_MAX_BUCKETS_COUNT); + + TVector<double> expectedBounds; + for (ui64 i = 0; i < 50; ++i) { + expectedBounds.push_back(std::pow(2, i)); + } + expectedBounds.push_back(std::numeric_limits<double>::max()); + + TVector<ui64> expectedValues; + for (ui64 i = 0; i < 50; ++i) { + expectedValues.push_back(0); + } + expectedValues.push_back(2); + + for (auto i = 0; i < 4; ++i) { + UNIT_ASSERT_VALUES_EQUAL(histogram.GetBounds(i), expectedBounds[i]); + UNIT_ASSERT_VALUES_EQUAL(histogram.GetValues(i), expectedValues[i]); + } + } + + Y_UNIT_TEST(LogHistogramSingle) { + constexpr auto input = TStringBuf(R"([["something_ahhh", 4 ]])"); + + NProto::TMultiSamplesList samples; + auto encoder = EncoderProtobuf(&samples); + + DecodeUnistat(input, encoder.Get()); + + auto sample = samples.GetSamples(0); + UNIT_ASSERT_EQUAL(sample.GetMetricType(), NProto::HISTOGRAM); + UNIT_ASSERT_VALUES_EQUAL(sample.PointsSize(), 1); + UNIT_ASSERT_VALUES_EQUAL(sample.LabelsSize(), 1); + + auto histogram = sample.GetPoints(0).GetHistogram(); + UNIT_ASSERT_VALUES_EQUAL(histogram.BoundsSize(), 4); + TVector<double> expectedBounds = {1, 2, 4, std::numeric_limits<double>::max()}; + TVector<ui64> expectedValues = {0, 0, 1, 0}; + + for (auto i = 0; i < 4; ++i) { + UNIT_ASSERT_VALUES_EQUAL(histogram.GetBounds(i), expectedBounds[i]); + UNIT_ASSERT_VALUES_EQUAL(histogram.GetValues(i), expectedValues[i]); + } + } + + Y_UNIT_TEST(LogHistogramInvalid) { + NProto::TMultiSamplesList samples; + auto encoder = EncoderProtobuf(&samples); + + { + constexpr auto input = TStringBuf(R"([["something_ahhh", [1, 2, [3, 4]] ]])"); + UNIT_ASSERT_EXCEPTION(DecodeUnistat(input, encoder.Get()), yexception); + } + + { + constexpr auto input = TStringBuf(R"([["something_ahhh", [[3, 4], 1, 2] ]])"); + UNIT_ASSERT_EXCEPTION(DecodeUnistat(input, encoder.Get()), yexception); + } + + { + constexpr auto input = TStringBuf(R"([["something_hgram", 1, 2, 3, 4 ]])"); + UNIT_ASSERT_EXCEPTION(DecodeUnistat(input, encoder.Get()), yexception); + } + } + + + Y_UNIT_TEST(AllowedMetricNames) { + NProto::TMultiSamplesList samples; + auto encoder = EncoderProtobuf(&samples); + + { + constexpr auto input = TStringBuf(R"([["a/A-b/c_D/__G_dmmm", [[0, 1], [200, 2], [500, 3]] ]])"); + UNIT_ASSERT_NO_EXCEPTION(DecodeUnistat(input, encoder.Get())); + } + } + + Y_UNIT_TEST(DisallowedMetricNames) { + NProto::TMultiSamplesList samples; + auto encoder = EncoderProtobuf(&samples); + + { + constexpr auto input = TStringBuf(R"([["someth!ng_ahhh", [[0, 1], [200, 2], [500, 3]] ]])"); + UNIT_ASSERT_EXCEPTION(DecodeUnistat(input, encoder.Get()), yexception); + } + + { + constexpr auto input = TStringBuf(R"([["foo_a", [[0, 1], [200, 2], [500, 3]] ]])"); + UNIT_ASSERT_EXCEPTION(DecodeUnistat(input, encoder.Get()), yexception); + } + + { + constexpr auto input = TStringBuf(R"([["foo_ahhh;tag=value", [[0, 1], [200, 2], [500, 3]] ]])"); + UNIT_ASSERT_EXCEPTION(DecodeUnistat(input, encoder.Get()), yexception); + } + } + + Y_UNIT_TEST(MultipleMetrics) { + constexpr auto input = TStringBuf(R"([["something_axxx", 42], ["some-other_dhhh", 53]])"); + + NProto::TMultiSamplesList samples; + auto encoder = EncoderProtobuf(&samples); + + DecodeUnistat(input, encoder.Get()); + + UNIT_ASSERT_VALUES_EQUAL(samples.SamplesSize(), 2); + auto sample = samples.GetSamples(0); + UNIT_ASSERT_EQUAL(sample.GetMetricType(), NProto::GAUGE); + UNIT_ASSERT_VALUES_EQUAL(sample.PointsSize(), 1); + UNIT_ASSERT_VALUES_EQUAL(sample.LabelsSize(), 1); + + auto label = sample.GetLabels(0); + auto point = sample.GetPoints(0); + UNIT_ASSERT_VALUES_EQUAL(point.GetFloat64(), 42.); + UNIT_ASSERT_VALUES_EQUAL(label.GetName(), "sensor"); + UNIT_ASSERT_VALUES_EQUAL(label.GetValue(), "something_axxx"); + + sample = samples.GetSamples(1); + UNIT_ASSERT_EQUAL(sample.GetMetricType(), NProto::RATE); + UNIT_ASSERT_VALUES_EQUAL(sample.PointsSize(), 1); + UNIT_ASSERT_VALUES_EQUAL(sample.LabelsSize(), 1); + + label = sample.GetLabels(0); + point = sample.GetPoints(0); + UNIT_ASSERT_VALUES_EQUAL(point.GetUint64(), 53); + UNIT_ASSERT_VALUES_EQUAL(label.GetName(), "sensor"); + UNIT_ASSERT_VALUES_EQUAL(label.GetValue(), "some-other_dhhh"); + } + + Y_UNIT_TEST(UnderscoreName) { + constexpr auto input = TStringBuf(R"([["something_anything_dmmm", 42]])"); + + NProto::TMultiSamplesList samples; + auto encoder = EncoderProtobuf(&samples); + DecodeUnistat(input, encoder.Get()); + + UNIT_ASSERT_VALUES_EQUAL(samples.SamplesSize(), 1); + auto sample = samples.GetSamples(0); + UNIT_ASSERT_EQUAL(sample.GetMetricType(), NProto::RATE); + UNIT_ASSERT_VALUES_EQUAL(sample.PointsSize(), 1); + UNIT_ASSERT_VALUES_EQUAL(sample.LabelsSize(), 1); + + auto label = sample.GetLabels(0); + auto point = sample.GetPoints(0); + UNIT_ASSERT_VALUES_EQUAL(point.GetUint64(), 42); + UNIT_ASSERT_VALUES_EQUAL(label.GetName(), "sensor"); + UNIT_ASSERT_VALUES_EQUAL(label.GetValue(), "something_anything_dmmm"); + } + + Y_UNIT_TEST(MaxAggr) { + constexpr auto input = TStringBuf(R"([["something_anything_max", 42]])"); + + NProto::TMultiSamplesList samples; + auto encoder = EncoderProtobuf(&samples); + DecodeUnistat(input, encoder.Get()); + + UNIT_ASSERT_VALUES_EQUAL(samples.SamplesSize(), 1); + auto sample = samples.GetSamples(0); + UNIT_ASSERT_EQUAL(sample.GetMetricType(), NProto::GAUGE); + UNIT_ASSERT_VALUES_EQUAL(sample.PointsSize(), 1); + UNIT_ASSERT_VALUES_EQUAL(sample.LabelsSize(), 1); + + auto label = sample.GetLabels(0); + auto point = sample.GetPoints(0); + UNIT_ASSERT_VALUES_EQUAL(point.GetFloat64(), 42.); + UNIT_ASSERT_VALUES_EQUAL(label.GetName(), "sensor"); + UNIT_ASSERT_VALUES_EQUAL(label.GetValue(), "something_anything_max"); + } +} diff --git a/library/cpp/monlib/encode/unistat/ut/ya.make b/library/cpp/monlib/encode/unistat/ut/ya.make new file mode 100644 index 0000000000..1f4590b3fa --- /dev/null +++ b/library/cpp/monlib/encode/unistat/ut/ya.make @@ -0,0 +1,11 @@ +UNITTEST_FOR(library/cpp/monlib/encode/unistat) + +SRCS( + unistat_ut.cpp +) + +PEERDIR( + library/cpp/monlib/encode/protobuf +) + +END() diff --git a/library/cpp/monlib/encode/unistat/ya.make b/library/cpp/monlib/encode/unistat/ya.make new file mode 100644 index 0000000000..e054cd707e --- /dev/null +++ b/library/cpp/monlib/encode/unistat/ya.make @@ -0,0 +1,17 @@ +LIBRARY() + +PEERDIR( + contrib/libs/re2 + library/cpp/json + library/cpp/monlib/metrics +) + +SRCS( + unistat_decoder.cpp +) + +END() + +RECURSE_FOR_TESTS( + ut +) |