diff --git a/core/models/MetricEvent.cpp b/core/models/MetricEvent.cpp index 54d0fa3685..d1ae8e139d 100644 --- a/core/models/MetricEvent.cpp +++ b/core/models/MetricEvent.cpp @@ -24,7 +24,11 @@ MetricEvent::MetricEvent(PipelineEventGroup* ptr) : PipelineEvent(Type::METRIC, } unique_ptr MetricEvent::Copy() const { - return make_unique(*this); + unique_ptr newPtr = make_unique(*this); + if (newPtr->Is()) { + newPtr->MutableValue()->ResetPipelineEvent(newPtr.get()); + } + return newPtr; } void MetricEvent::Reset() { @@ -88,7 +92,21 @@ Json::Value MetricEvent::ToJson(bool enableEventMeta) const { root["timestampNanosecond"] = static_cast(GetTimestampNanosecond().value()); } root["name"] = mName.to_string(); - root["value"] = MetricValueToJson(mValue); + root["value"] = Json::Value(); + visit( + [&](auto&& arg) { + using T = decay_t; + if constexpr (is_same_v) { + root["value"]["type"] = "untyped_single_value"; + root["value"]["detail"] = get(mValue).ToJson(); + } else if constexpr (is_same_v) { + root["value"]["type"] = "untyped_multi_double_values"; + root["value"]["detail"] = get(mValue).ToJson(); + } else if constexpr (is_same_v) { + root["value"]["type"] = "unknown"; + } + }, + mValue); if (!mTags.mInner.empty()) { Json::Value& tags = root["tags"]; for (const auto& tag : mTags.mInner) { @@ -106,7 +124,15 @@ bool MetricEvent::FromJson(const Json::Value& root) { } SetName(root["name"].asString()); const Json::Value& value = root["value"]; - SetValue(JsonToMetricValue(value["type"].asString(), value["detail"])); + if (value["type"].asString() == "untyped_single_value") { + UntypedSingleValue v; + v.FromJson(value["detail"]); + SetValue(v); + } else if (value["type"].asString() == "untyped_multi_double_values") { + UntypedMultiDoubleValues v(this); + v.FromJson(value["detail"]); + SetValue(v); + } if (root.isMember("tags")) { Json::Value tags = root["tags"]; for (const auto& key : tags.getMemberNames()) { diff --git a/core/models/MetricEvent.h b/core/models/MetricEvent.h index 0c4779ac44..63ed412e14 100644 --- a/core/models/MetricEvent.h +++ b/core/models/MetricEvent.h @@ -48,6 +48,11 @@ class MetricEvent : public PipelineEvent { return std::get_if(&mValue); } + template + constexpr std::add_pointer_t MutableValue() noexcept { + return std::get_if(&mValue); + } + template void SetValue(const T& value) { mValue = value; @@ -58,6 +63,14 @@ class MetricEvent : public PipelineEvent { mValue = T{std::forward(args)...}; } + void SetValue(const std::map& multiDoubleValues) { + mValue = UntypedMultiDoubleValues{multiDoubleValues, this}; + } + + void SetValue(const UntypedMultiDoubleValues& multiDoubleValues) { + mValue = UntypedMultiDoubleValues{multiDoubleValues.mValues, this}; + } + StringView GetTag(StringView key) const; bool HasTag(StringView key) const; void SetTag(StringView key, StringView val); diff --git a/core/models/MetricValue.cpp b/core/models/MetricValue.cpp index 2c8022a831..c262367e07 100644 --- a/core/models/MetricValue.cpp +++ b/core/models/MetricValue.cpp @@ -20,6 +20,62 @@ using namespace std; namespace logtail { +bool UntypedMultiDoubleValues::GetValue(StringView key, double& val) const { + if (mValues.find(key) != mValues.end()) { + val = mValues.at(key); + return true; + } + return false; +} + +bool UntypedMultiDoubleValues::HasValue(StringView key) const { + return mValues.find(key) != mValues.end(); +} + +void UntypedMultiDoubleValues::SetValue(const std::string& key, double val) { + if (mMetricEventPtr) { + SetValueNoCopy(mMetricEventPtr->GetSourceBuffer()->CopyString(key), val); + } +} + +void UntypedMultiDoubleValues::SetValue(StringView key, double val) { + if (mMetricEventPtr) { + SetValueNoCopy(mMetricEventPtr->GetSourceBuffer()->CopyString(key), val); + } +} + +void UntypedMultiDoubleValues::SetValueNoCopy(const StringBuffer& key, double val) { + SetValueNoCopy(StringView(key.data, key.size), val); +} + +void UntypedMultiDoubleValues::SetValueNoCopy(StringView key, double val) { + mValues[key] = val; +} + +void UntypedMultiDoubleValues::DelValue(StringView key) { + mValues.erase(key); +} + +std::map::const_iterator UntypedMultiDoubleValues::ValusBegin() const { + return mValues.begin(); +} + +std::map::const_iterator UntypedMultiDoubleValues::ValusEnd() const { + return mValues.end(); +} + +size_t UntypedMultiDoubleValues::ValusSize() const { + return mValues.size(); +} + +size_t UntypedMultiDoubleValues::DataSize() const { + size_t totalSize = sizeof(UntypedMultiDoubleValues); + for (const auto& pair : mValues) { + totalSize += pair.first.size() + sizeof(pair.second); + } + return totalSize; +} + size_t DataSize(const MetricValue& value) { return visit( [](auto&& arg) { @@ -42,29 +98,20 @@ void UntypedSingleValue::FromJson(const Json::Value& value) { mValue = value.asFloat(); } -Json::Value MetricValueToJson(const MetricValue& value) { +Json::Value UntypedMultiDoubleValues::ToJson() const { Json::Value res; - visit( - [&](auto&& arg) { - using T = decay_t; - if constexpr (is_same_v) { - res["type"] = "untyped_single_value"; - res["detail"] = get(value).ToJson(); - } else if constexpr (is_same_v) { - res["type"] = "unknown"; - } - }, - value); + for (auto metric : mValues) { + res[metric.first.to_string()] = metric.second; + } return res; } -MetricValue JsonToMetricValue(const string& type, const Json::Value& detail) { - if (type == "untyped_single_value") { - UntypedSingleValue v; - v.FromJson(detail); - return v; - } else { - return MetricValue(); +void UntypedMultiDoubleValues::FromJson(const Json::Value& value) { + mValues.clear(); + for (Json::Value::const_iterator itr = value.begin(); itr != value.end(); ++itr) { + if (itr->asDouble()) { + SetValue(itr.key().asString(), itr->asDouble()); + } } } #endif diff --git a/core/models/MetricValue.h b/core/models/MetricValue.h index 558f96d601..b3caef108c 100644 --- a/core/models/MetricValue.h +++ b/core/models/MetricValue.h @@ -16,6 +16,7 @@ #pragma once +#include #include #ifdef APSARA_UNIT_TEST_MAIN @@ -24,6 +25,10 @@ #include #endif +#include "common/memory/SourceBuffer.h" +#include "models/PipelineEvent.h" +#include "models/StringView.h" + namespace logtail { struct UntypedSingleValue { @@ -37,13 +42,37 @@ struct UntypedSingleValue { #endif }; -using MetricValue = std::variant; +struct UntypedMultiDoubleValues { + std::map mValues; + PipelineEvent* mMetricEventPtr; -size_t DataSize(const MetricValue& value); + UntypedMultiDoubleValues(PipelineEvent* ptr) : mMetricEventPtr(ptr) {} + UntypedMultiDoubleValues(std::map values, PipelineEvent* ptr) + : mValues(values), mMetricEventPtr(ptr) {} + + bool GetValue(StringView key, double& val) const; + bool HasValue(StringView key) const; + void SetValue(const std::string& key, double val); + void SetValue(StringView key, double val); + void SetValueNoCopy(const StringBuffer& key, double val); + void SetValueNoCopy(StringView key, double val); + void DelValue(StringView key); + + std::map::const_iterator ValusBegin() const; + std::map::const_iterator ValusEnd() const; + size_t ValusSize() const; + + size_t DataSize() const; + void ResetPipelineEvent(PipelineEvent* ptr) { mMetricEventPtr = ptr; } #ifdef APSARA_UNIT_TEST_MAIN -Json::Value MetricValueToJson(const MetricValue& value); -MetricValue JsonToMetricValue(const std::string& type, const Json::Value& detail); + Json::Value ToJson() const; + void FromJson(const Json::Value& value); #endif +}; + +using MetricValue = std::variant; + +size_t DataSize(const MetricValue& value); } // namespace logtail diff --git a/core/runner/FlusherRunner.cpp b/core/runner/FlusherRunner.cpp index 1f9f808371..42a1b452fa 100644 --- a/core/runner/FlusherRunner.cpp +++ b/core/runner/FlusherRunner.cpp @@ -186,13 +186,13 @@ void FlusherRunner::Dispatch(SenderQueueItem* item) { if (!BOOL_FLAG(enable_full_drain_mode) && Application::GetInstance()->IsExiting() && item->mFlusher->Name() == "flusher_sls") { DiskBufferWriter::GetInstance()->PushToDiskBuffer(item, 3); - SenderQueueManager::GetInstance()->RemoveItem(item->mFlusher->GetQueueKey(), item); + SenderQueueManager::GetInstance()->RemoveItem(item->mQueueKey, item); } else { PushToHttpSink(item); } break; default: - SenderQueueManager::GetInstance()->RemoveItem(item->mFlusher->GetQueueKey(), item); + SenderQueueManager::GetInstance()->RemoveItem(item->mQueueKey, item); break; } } diff --git a/core/unittest/models/MetricEventUnittest.cpp b/core/unittest/models/MetricEventUnittest.cpp index 55556be363..ada8af3239 100644 --- a/core/unittest/models/MetricEventUnittest.cpp +++ b/core/unittest/models/MetricEventUnittest.cpp @@ -25,13 +25,18 @@ namespace logtail { class MetricEventUnittest : public ::testing::Test { public: void TestName(); - void TestValue(); + void TestUntypedSingleValue(); + void TestUntypedMultiDoubleValues(); void TestTag(); - void TestSize(); + void TestUntypedSingleValueSize(); + void TestUntypedMultiDoubleValuesSize(); void TestReset(); - void TestToJson(); - void TestFromJson(); + void TestUntypedSingleValueToJson(); + void TestUntypedMultiDoubleValuesToJson(); + void TestUntypedSingleValueFromJson(); + void TestUntypedMultiDoubleValuesFromJson(); void TestTagsIterator(); + void TestCopy(); protected: void SetUp() override { @@ -51,7 +56,7 @@ void MetricEventUnittest::TestName() { APSARA_TEST_EQUAL("test", mMetricEvent->GetName().to_string()); } -void MetricEventUnittest::TestValue() { +void MetricEventUnittest::TestUntypedSingleValue() { mMetricEvent->SetValue(UntypedSingleValue{10.0}); APSARA_TEST_TRUE(mMetricEvent->Is()); APSARA_TEST_EQUAL(10.0, mMetricEvent->GetValue()->mValue); @@ -61,6 +66,33 @@ void MetricEventUnittest::TestValue() { APSARA_TEST_EQUAL(100.0, mMetricEvent->GetValue()->mValue); } +void MetricEventUnittest::TestUntypedMultiDoubleValues() { + UntypedMultiDoubleValues v({{"test-1", 10.0}, {"test-2", 2.0}}, nullptr); + mMetricEvent->SetValue(v); + APSARA_TEST_TRUE(mMetricEvent->Is()); + double val; + APSARA_TEST_EQUAL(true, mMetricEvent->GetValue()->GetValue("test-1", val)); + APSARA_TEST_EQUAL(10.0, val); + APSARA_TEST_EQUAL(true, mMetricEvent->GetValue()->GetValue("test-2", val)); + APSARA_TEST_EQUAL(2.0, val); + + map metrics({{"test-3", 15.0}, {"test-4", 24.0}}); + mMetricEvent->SetValue(metrics); + APSARA_TEST_TRUE(mMetricEvent->Is()); + APSARA_TEST_EQUAL(true, mMetricEvent->GetValue()->GetValue("test-3", val)); + APSARA_TEST_EQUAL(15.0, val); + APSARA_TEST_EQUAL(true, mMetricEvent->GetValue()->GetValue("test-4", val)); + APSARA_TEST_EQUAL(24.0, val); + + mMetricEvent->MutableValue()->SetValue(string("test-1"), 6.0); + APSARA_TEST_EQUAL(true, mMetricEvent->GetValue()->GetValue("test-1", val)); + APSARA_TEST_EQUAL(6.0, val); + + mMetricEvent->MutableValue()->DelValue("test-4"); + APSARA_TEST_EQUAL(false, mMetricEvent->GetValue()->GetValue("test-4", val)); + APSARA_TEST_EQUAL(6.0, val); +} + void MetricEventUnittest::TestTag() { { string key = "key1"; @@ -106,7 +138,7 @@ void MetricEventUnittest::TestTag() { } } -void MetricEventUnittest::TestSize() { +void MetricEventUnittest::TestUntypedSingleValueSize() { size_t basicSize = sizeof(time_t) + sizeof(long) + sizeof(UntypedSingleValue) + sizeof(map); mMetricEvent->SetName("test"); basicSize += 4; @@ -126,6 +158,39 @@ void MetricEventUnittest::TestSize() { APSARA_TEST_EQUAL(basicSize, mMetricEvent->DataSize()); } +void MetricEventUnittest::TestUntypedMultiDoubleValuesSize() { + mMetricEvent->SetName("test"); + mMetricEvent->SetValue(map{}); + size_t basicSize = sizeof(time_t) + sizeof(long) + sizeof(UntypedMultiDoubleValues) + sizeof(map); + basicSize += 4; + + // add tag, and key not existed + mMetricEvent->SetTag(string("key1"), string("a")); + APSARA_TEST_EQUAL(basicSize + 5U, mMetricEvent->DataSize()); + + // add tag, and key existed + mMetricEvent->SetTag(string("key1"), string("bb")); + APSARA_TEST_EQUAL(basicSize + 6U, mMetricEvent->DataSize()); + + // delete tag + mMetricEvent->DelTag(string("key1")); + APSARA_TEST_EQUAL(basicSize, mMetricEvent->DataSize()); + + // add multi values, and key not existed + mMetricEvent->MutableValue()->SetValue(string("test-1"), 5.0); + basicSize += 14; + APSARA_TEST_EQUAL(basicSize, mMetricEvent->DataSize()); + + // add multi values, and key existed + mMetricEvent->MutableValue()->SetValue(string("test-1"), 99.0); + APSARA_TEST_EQUAL(basicSize, mMetricEvent->DataSize()); + + // delete multi values + mMetricEvent->MutableValue()->DelValue("test-1"); + basicSize -= 14; + APSARA_TEST_EQUAL(basicSize, mMetricEvent->DataSize()); +} + void MetricEventUnittest::TestReset() { mMetricEvent->SetTimestamp(12345678901); mMetricEvent->SetName("test"); @@ -139,7 +204,7 @@ void MetricEventUnittest::TestReset() { APSARA_TEST_EQUAL(mMetricEvent->TagsEnd(), mMetricEvent->TagsBegin()); } -void MetricEventUnittest::TestToJson() { +void MetricEventUnittest::TestUntypedSingleValueToJson() { mMetricEvent->SetTimestamp(12345678901, 0); mMetricEvent->SetName("test"); mMetricEvent->SetValue(UntypedSingleValue{10.0}); @@ -166,7 +231,37 @@ void MetricEventUnittest::TestToJson() { APSARA_TEST_TRUE(eventJson == res); } -void MetricEventUnittest::TestFromJson() { +void MetricEventUnittest::TestUntypedMultiDoubleValuesToJson() { + mMetricEvent->SetTimestamp(12345678901, 0); + mMetricEvent->SetName("test"); + mMetricEvent->SetValue(map{{"test-1", 10.0}, {"test-2", 2.0}}); + mMetricEvent->SetTag(string("key1"), string("value1")); + Json::Value res = mMetricEvent->ToJson(); + + Json::Value eventJson; + string eventStr = R"({ + "name": "test", + "tags": { + "key1": "value1" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "type" : 2, + "value": { + "type": "untyped_multi_double_values", + "detail": { + "test-1": 10.0, + "test-2": 2.0 + } + } + })"; + string errorMsg; + ParseJsonTable(eventStr, eventJson, errorMsg); + + APSARA_TEST_TRUE(eventJson == res); +} + +void MetricEventUnittest::TestUntypedSingleValueFromJson() { Json::Value eventJson; string eventStr = R"({ "name": "test", @@ -192,6 +287,39 @@ void MetricEventUnittest::TestFromJson() { APSARA_TEST_EQUAL("value1", mMetricEvent->GetTag("key1").to_string()); } +void MetricEventUnittest::TestUntypedMultiDoubleValuesFromJson() { + Json::Value eventJson; + string eventStr = R"({ + "name": "test", + "tags": { + "key1": "value1" + }, + "timestamp" : 12345678901, + "timestampNanosecond" : 0, + "value": { + "type": "untyped_multi_double_values", + "detail": { + "test-1": 10.0, + "test-2": 2.0 + } + } + })"; + string errorMsg; + ParseJsonTable(eventStr, eventJson, errorMsg); + mMetricEvent->FromJson(eventJson); + double val; + + APSARA_TEST_EQUAL(12345678901, mMetricEvent->GetTimestamp()); + APSARA_TEST_EQUAL(0L, mMetricEvent->GetTimestampNanosecond().value()); + APSARA_TEST_EQUAL("test", mMetricEvent->GetName()); + APSARA_TEST_TRUE(mMetricEvent->Is()); + APSARA_TEST_EQUAL(true, mMetricEvent->GetValue()->GetValue("test-1", val)); + APSARA_TEST_EQUAL(10.0, val); + APSARA_TEST_EQUAL(true, mMetricEvent->GetValue()->GetValue("test-2", val)); + APSARA_TEST_EQUAL(2.0, val); + APSARA_TEST_EQUAL("value1", mMetricEvent->GetTag("key1").to_string()); +} + void MetricEventUnittest::TestTagsIterator() { string key1 = "key1"; string value1 = "value1"; @@ -215,14 +343,36 @@ void MetricEventUnittest::TestTagsIterator() { APSARA_TEST_EQUAL((size_t)3, mMetricEvent->TagsSize()); } +void MetricEventUnittest::TestCopy() { + MetricEvent* oldMetricEvent = mEventGroup->AddMetricEvent(); + oldMetricEvent->SetValue(map{{"test-1", 10.0}, {"test-2", 2.0}}); + APSARA_TEST_EQUAL(1, mEventGroup->GetEvents().size()); + + PipelineEventGroup newGroup = mEventGroup->Copy(); + MetricEvent newMetricEvent = newGroup.GetEvents().at(0).Cast(); + double val; + + APSARA_TEST_TRUE(newMetricEvent.Is()); + APSARA_TEST_EQUAL(true, newMetricEvent.GetValue()->GetValue("test-1", val)); + APSARA_TEST_EQUAL(10.0, val); + APSARA_TEST_EQUAL(true, newMetricEvent.GetValue()->GetValue("test-2", val)); + APSARA_TEST_EQUAL(2.0, val); + APSARA_TEST_NOT_EQUAL(newMetricEvent.GetValue()->mMetricEventPtr, oldMetricEvent->GetValue()->mMetricEventPtr); +} + UNIT_TEST_CASE(MetricEventUnittest, TestName) -UNIT_TEST_CASE(MetricEventUnittest, TestValue) +UNIT_TEST_CASE(MetricEventUnittest, TestUntypedSingleValue) +UNIT_TEST_CASE(MetricEventUnittest, TestUntypedMultiDoubleValues) UNIT_TEST_CASE(MetricEventUnittest, TestTag) -UNIT_TEST_CASE(MetricEventUnittest, TestSize) +UNIT_TEST_CASE(MetricEventUnittest, TestUntypedSingleValueSize) +UNIT_TEST_CASE(MetricEventUnittest, TestUntypedMultiDoubleValuesSize) UNIT_TEST_CASE(MetricEventUnittest, TestReset) -UNIT_TEST_CASE(MetricEventUnittest, TestToJson) -UNIT_TEST_CASE(MetricEventUnittest, TestFromJson) +UNIT_TEST_CASE(MetricEventUnittest, TestUntypedSingleValueToJson) +UNIT_TEST_CASE(MetricEventUnittest, TestUntypedMultiDoubleValuesToJson) +UNIT_TEST_CASE(MetricEventUnittest, TestUntypedSingleValueFromJson) +UNIT_TEST_CASE(MetricEventUnittest, TestUntypedMultiDoubleValuesFromJson) UNIT_TEST_CASE(MetricEventUnittest, TestTagsIterator) +UNIT_TEST_CASE(MetricEventUnittest, TestCopy) } // namespace logtail diff --git a/core/unittest/models/MetricValueUnittest.cpp b/core/unittest/models/MetricValueUnittest.cpp index f55a987962..8163760d84 100644 --- a/core/unittest/models/MetricValueUnittest.cpp +++ b/core/unittest/models/MetricValueUnittest.cpp @@ -19,62 +19,78 @@ using namespace std; namespace logtail { -class MetricValueUnittest : public ::testing::Test { +class UntypedSingleValueUnittest : public ::testing::Test { public: void TestToJson(); void TestFromJson(); }; -void MetricValueUnittest::TestToJson() { - MetricValue value = UntypedSingleValue{10.0}; - Json::Value res = MetricValueToJson(value); +void UntypedSingleValueUnittest::TestToJson() { + UntypedSingleValue value{10.0}; + Json::Value res = value.ToJson(); - Json::Value valueJson; - string valueStr = R"({ - "type": "untyped_single_value", - "detail": 10.0 - })"; - string errorMsg; - ParseJsonTable(valueStr, valueJson, errorMsg); + Json::Value valueJson = Json::Value(10.0); APSARA_TEST_TRUE(valueJson == res); } -void MetricValueUnittest::TestFromJson() { - Json::Value detail(10.0); - MetricValue value = JsonToMetricValue("untyped_single_value", detail); +void UntypedSingleValueUnittest::TestFromJson() { + UntypedSingleValue value; + value.FromJson(Json::Value(10.0)); - APSARA_TEST_TRUE(std::holds_alternative(value)); - APSARA_TEST_EQUAL(10.0, std::get(value).mValue); + APSARA_TEST_EQUAL(10.0, value.mValue); } -UNIT_TEST_CASE(MetricValueUnittest, TestToJson) -UNIT_TEST_CASE(MetricValueUnittest, TestFromJson) +UNIT_TEST_CASE(UntypedSingleValueUnittest, TestToJson) +UNIT_TEST_CASE(UntypedSingleValueUnittest, TestFromJson) -class UntypedSingleValueUnittest : public ::testing::Test { +class UntypedMultiDoubleValuesUnittest : public ::testing::Test { public: void TestToJson(); void TestFromJson(); + +protected: + void SetUp() override { + mSourceBuffer.reset(new SourceBuffer); + mEventGroup.reset(new PipelineEventGroup(mSourceBuffer)); + mMetricEvent = mEventGroup->CreateMetricEvent(); + } + +private: + shared_ptr mSourceBuffer; + unique_ptr mEventGroup; + unique_ptr mMetricEvent; }; -void UntypedSingleValueUnittest::TestToJson() { - UntypedSingleValue value{10.0}; +void UntypedMultiDoubleValuesUnittest::TestToJson() { + UntypedMultiDoubleValues value(mMetricEvent.get()); + value.SetValue(string("test-1"), 10.0); + value.SetValue(string("test-2"), 2.0); Json::Value res = value.ToJson(); - Json::Value valueJson = Json::Value(10.0); + Json::Value valueJson; + valueJson["test-1"] = 10.0; + valueJson["test-2"] = 2.0; APSARA_TEST_TRUE(valueJson == res); } -void UntypedSingleValueUnittest::TestFromJson() { - UntypedSingleValue value; - value.FromJson(Json::Value(10.0)); - - APSARA_TEST_EQUAL(10.0, value.mValue); +void UntypedMultiDoubleValuesUnittest::TestFromJson() { + UntypedMultiDoubleValues value(mMetricEvent.get()); + Json::Value valueJson; + valueJson["test-1"] = 10.0; + valueJson["test-2"] = 2.0; + value.FromJson(valueJson); + double val; + + APSARA_TEST_EQUAL(true, value.GetValue("test-1", val)); + APSARA_TEST_EQUAL(10.0, val); + APSARA_TEST_EQUAL(true, value.GetValue("test-2", val)); + APSARA_TEST_EQUAL(2.0, val); } -UNIT_TEST_CASE(UntypedSingleValueUnittest, TestToJson) -UNIT_TEST_CASE(UntypedSingleValueUnittest, TestFromJson) +UNIT_TEST_CASE(UntypedMultiDoubleValuesUnittest, TestToJson) +UNIT_TEST_CASE(UntypedMultiDoubleValuesUnittest, TestFromJson) } // namespace logtail