Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: MetricEvent support UntypedMultiValues #1908

Merged
merged 9 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/models/MetricEvent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ bool MetricEvent::FromJson(const Json::Value& root) {
}
SetName(root["name"].asString());
const Json::Value& value = root["value"];
SetValue(JsonToMetricValue(value["type"].asString(), value["detail"]));
SetValue(JsonToMetricValue(value["type"].asString(), value["detail"], this));
if (root.isMember("tags")) {
Json::Value tags = root["tags"];
for (const auto& key : tags.getMemberNames()) {
Expand Down
81 changes: 80 additions & 1 deletion core/models/MetricValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,61 @@ using namespace std;

namespace logtail {

double UntypedMultiFloatValues::GetMultiKeyValue(StringView key) const {
if (mValues.find(key) != mValues.end()) {
return mValues.at(key);
}
return 0;
Takuka0311 marked this conversation as resolved.
Show resolved Hide resolved
}

bool UntypedMultiFloatValues::HasMultiKeyValue(StringView key) const {
return mValues.find(key) != mValues.end();
}

void UntypedMultiFloatValues::SetMultiKeyValue(const std::string& key, double val) const {
if (mMetricEventPtr) {
SetMultiKeyValueNoCopy(mMetricEventPtr->GetSourceBuffer()->CopyString(key), val);
}
}

void UntypedMultiFloatValues::SetMultiKeyValue(StringView key, double val) const {
if (mMetricEventPtr) {
SetMultiKeyValueNoCopy(mMetricEventPtr->GetSourceBuffer()->CopyString(key), val);
}
}

void UntypedMultiFloatValues::SetMultiKeyValueNoCopy(const StringBuffer& key, double val) const {
SetMultiKeyValueNoCopy(StringView(key.data, key.size), val);
}

void UntypedMultiFloatValues::SetMultiKeyValueNoCopy(StringView key, double val) const {
mValues[key] = val;
}

void UntypedMultiFloatValues::DelMultiKeyValue(StringView key) const {
mValues.erase(key);
}

std::map<StringView, double>::const_iterator UntypedMultiFloatValues::MultiKeyValusBegin() const {
return mValues.begin();
}

std::map<StringView, double>::const_iterator UntypedMultiFloatValues::MultiKeyValusEnd() const {
return mValues.end();
}

size_t UntypedMultiFloatValues::MultiKeyValusSize() const {
return mValues.size();
}

size_t UntypedMultiFloatValues::DataSize() const {
size_t totalSize = sizeof(UntypedMultiFloatValues);
for (const auto& pair : mValues) {
totalSize += pair.first.size() + sizeof(pair.second);
}
return totalSize;
}

size_t DataSize(const MetricValue& value) {
return visit(
[](auto&& arg) {
Expand All @@ -42,6 +97,23 @@ void UntypedSingleValue::FromJson(const Json::Value& value) {
mValue = value.asFloat();
}

Json::Value UntypedMultiFloatValues::ToJson() const {
Json::Value res;
for (auto metric : mValues) {
res[metric.first.to_string()] = metric.second;
}
return res;
}

void UntypedMultiFloatValues::FromJson(const Json::Value& value) {
mValues.clear();
for (Json::Value::const_iterator itr = value.begin(); itr != value.end(); ++itr) {
if (itr->asDouble()) {
SetMultiKeyValue(itr.key().asString(), itr->asDouble());
}
}
}

Json::Value MetricValueToJson(const MetricValue& value) {
Takuka0311 marked this conversation as resolved.
Show resolved Hide resolved
Json::Value res;
visit(
Expand All @@ -50,6 +122,9 @@ Json::Value MetricValueToJson(const MetricValue& value) {
if constexpr (is_same_v<T, UntypedSingleValue>) {
res["type"] = "untyped_single_value";
res["detail"] = get<UntypedSingleValue>(value).ToJson();
} else if constexpr (is_same_v<T, UntypedMultiFloatValues>) {
res["type"] = "untyped_multi_values";
res["detail"] = get<UntypedMultiFloatValues>(value).ToJson();
} else if constexpr (is_same_v<T, monostate>) {
res["type"] = "unknown";
}
Expand All @@ -58,11 +133,15 @@ Json::Value MetricValueToJson(const MetricValue& value) {
return res;
}

MetricValue JsonToMetricValue(const string& type, const Json::Value& detail) {
MetricValue JsonToMetricValue(const string& type, const Json::Value& detail, PipelineEvent* mMetricEventPtr) {
Takuka0311 marked this conversation as resolved.
Show resolved Hide resolved
if (type == "untyped_single_value") {
UntypedSingleValue v;
v.FromJson(detail);
return v;
} else if (type == "untyped_multi_values") {
UntypedMultiFloatValues v(mMetricEventPtr);
v.FromJson(detail);
return v;
} else {
return MetricValue();
}
Expand Down
36 changes: 34 additions & 2 deletions core/models/MetricValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#pragma once

#include <map>
#include <variant>

#ifdef APSARA_UNIT_TEST_MAIN
Expand All @@ -24,6 +25,10 @@
#include <string>
#endif

#include "common/memory/SourceBuffer.h"
#include "models/PipelineEvent.h"
#include "models/StringView.h"

namespace logtail {

struct UntypedSingleValue {
Expand All @@ -37,13 +42,40 @@ struct UntypedSingleValue {
#endif
};

using MetricValue = std::variant<std::monostate, UntypedSingleValue>;
struct UntypedMultiFloatValues {
mutable std::map<StringView, double> mValues;
Takuka0311 marked this conversation as resolved.
Show resolved Hide resolved
PipelineEvent* mMetricEventPtr;
Takuka0311 marked this conversation as resolved.
Show resolved Hide resolved

UntypedMultiFloatValues(PipelineEvent* ptr): mMetricEventPtr(ptr) {}
UntypedMultiFloatValues(std::map<StringView, double> values, PipelineEvent* ptr): mValues(values), mMetricEventPtr(ptr) {}
Takuka0311 marked this conversation as resolved.
Show resolved Hide resolved

double GetMultiKeyValue(StringView key) const;
Takuka0311 marked this conversation as resolved.
Show resolved Hide resolved
bool HasMultiKeyValue(StringView key) const;
void SetMultiKeyValue(const std::string& key, double val) const;
void SetMultiKeyValue(StringView key, double val) const;
void SetMultiKeyValueNoCopy(const StringBuffer& key, double val) const;
void SetMultiKeyValueNoCopy(StringView key, double val) const;
void DelMultiKeyValue(StringView key) const;

std::map<StringView, double>::const_iterator MultiKeyValusBegin() const;
std::map<StringView, double>::const_iterator MultiKeyValusEnd() const;
size_t MultiKeyValusSize() const;

size_t DataSize() const;

#ifdef APSARA_UNIT_TEST_MAIN
Json::Value ToJson() const;
void FromJson(const Json::Value& value);
#endif
};

using MetricValue = std::variant<std::monostate, UntypedSingleValue, UntypedMultiFloatValues>;

size_t DataSize(const MetricValue& value);

#ifdef APSARA_UNIT_TEST_MAIN
Json::Value MetricValueToJson(const MetricValue& value);
MetricValue JsonToMetricValue(const std::string& type, const Json::Value& detail);
MetricValue JsonToMetricValue(const std::string& type, const Json::Value& detail, PipelineEvent* mMetricEventPtr);
#endif

} // namespace logtail
146 changes: 134 additions & 12 deletions core/unittest/models/MetricEventUnittest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@ namespace logtail {
class MetricEventUnittest : public ::testing::Test {
public:
void TestName();
void TestValue();
void TestUntypedSingleValue();
void TestUntypedMultiFloatValues();
void TestTag();
void TestSize();
void TestUntypedSingleValueSize();
void TestUntypedMultiFloatValuesSize();
void TestReset();
void TestToJson();
void TestFromJson();
void TestUntypedSingleValueToJson();
void TestUntypedMultiFloatValuesToJson();
void TestUntypedSingleValueFromJson();
void TestUntypedMultiFloatValuesFromJson();
void TestTagsIterator();

protected:
Expand All @@ -51,7 +55,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<UntypedSingleValue>());
APSARA_TEST_EQUAL(10.0, mMetricEvent->GetValue<UntypedSingleValue>()->mValue);
Expand All @@ -61,6 +65,25 @@ void MetricEventUnittest::TestValue() {
APSARA_TEST_EQUAL(100.0, mMetricEvent->GetValue<UntypedSingleValue>()->mValue);
}

void MetricEventUnittest::TestUntypedMultiFloatValues() {
mMetricEvent->SetValue(UntypedMultiFloatValues{{{"test-1", 10.0}, {"test-2", 2.0}}, mMetricEvent.get()});
APSARA_TEST_TRUE(mMetricEvent->Is<UntypedMultiFloatValues>());
APSARA_TEST_EQUAL(10.0, mMetricEvent->GetValue<UntypedMultiFloatValues>()->GetMultiKeyValue("test-1"));
APSARA_TEST_EQUAL(2.0, mMetricEvent->GetValue<UntypedMultiFloatValues>()->GetMultiKeyValue("test-2"));

map<StringView, double> metrics({{"test-3", 15.0}, {"test-4", 24.0}});
mMetricEvent->SetValue<UntypedMultiFloatValues>(metrics, mMetricEvent.get());
APSARA_TEST_TRUE(mMetricEvent->Is<UntypedMultiFloatValues>());
APSARA_TEST_EQUAL(15.0, mMetricEvent->GetValue<UntypedMultiFloatValues>()->GetMultiKeyValue("test-3"));
APSARA_TEST_EQUAL(24.0, mMetricEvent->GetValue<UntypedMultiFloatValues>()->GetMultiKeyValue("test-4"));

mMetricEvent->GetValue<UntypedMultiFloatValues>()->SetMultiKeyValue(string("test-1"), 6.0);
APSARA_TEST_EQUAL(6.0, mMetricEvent->GetValue<UntypedMultiFloatValues>()->GetMultiKeyValue("test-1"));

mMetricEvent->GetValue<UntypedMultiFloatValues>()->DelMultiKeyValue("test-4");
APSARA_TEST_EQUAL(0, mMetricEvent->GetValue<UntypedMultiFloatValues>()->GetMultiKeyValue("test-4"));
}

void MetricEventUnittest::TestTag() {
{
string key = "key1";
Expand Down Expand Up @@ -106,7 +129,7 @@ void MetricEventUnittest::TestTag() {
}
}

void MetricEventUnittest::TestSize() {
void MetricEventUnittest::TestUntypedSingleValueSize() {
size_t basicSize = sizeof(time_t) + sizeof(long) + sizeof(UntypedSingleValue) + sizeof(map<StringView, StringView>);
mMetricEvent->SetName("test");
basicSize += 4;
Expand All @@ -126,6 +149,39 @@ void MetricEventUnittest::TestSize() {
APSARA_TEST_EQUAL(basicSize, mMetricEvent->DataSize());
}

void MetricEventUnittest::TestUntypedMultiFloatValuesSize() {
mMetricEvent->SetName("test");
mMetricEvent->SetValue(UntypedMultiFloatValues{{}, mMetricEvent.get()});
size_t basicSize = sizeof(time_t) + sizeof(long) + sizeof(UntypedMultiFloatValues) + sizeof(map<StringView, StringView>);
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->GetValue<UntypedMultiFloatValues>()->SetMultiKeyValue(string("test-1"), 5.0);
basicSize += 14;
APSARA_TEST_EQUAL(basicSize, mMetricEvent->DataSize());

// add multi values, and key existed
mMetricEvent->GetValue<UntypedMultiFloatValues>()->SetMultiKeyValue(string("test-1"), 99.0);
APSARA_TEST_EQUAL(basicSize, mMetricEvent->DataSize());

// delete multi values
mMetricEvent->GetValue<UntypedMultiFloatValues>()->DelMultiKeyValue("test-1");
basicSize -= 14;
APSARA_TEST_EQUAL(basicSize, mMetricEvent->DataSize());
}

void MetricEventUnittest::TestReset() {
mMetricEvent->SetTimestamp(12345678901);
mMetricEvent->SetName("test");
Expand All @@ -139,7 +195,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});
Expand All @@ -166,7 +222,37 @@ void MetricEventUnittest::TestToJson() {
APSARA_TEST_TRUE(eventJson == res);
}

void MetricEventUnittest::TestFromJson() {
void MetricEventUnittest::TestUntypedMultiFloatValuesToJson() {
mMetricEvent->SetTimestamp(12345678901, 0);
mMetricEvent->SetName("test");
mMetricEvent->SetValue(UntypedMultiFloatValues{{{"test-1", 10.0}, {"test-2", 2.0}}, mMetricEvent.get()});
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_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",
Expand All @@ -192,6 +278,38 @@ void MetricEventUnittest::TestFromJson() {
APSARA_TEST_EQUAL("value1", mMetricEvent->GetTag("key1").to_string());
}

void MetricEventUnittest::TestUntypedMultiFloatValuesFromJson() {
Json::Value eventJson;
string eventStr = R"({
"name": "test",
"tags": {
"key1": "value1"
},
"timestamp" : 12345678901,
"timestampNanosecond" : 0,
"value": {
"type": "untyped_multi_values",
"detail": {
"test-1": 10.0,
"test-2": 2.0
}
}
})";
string errorMsg;
ParseJsonTable(eventStr, eventJson, errorMsg);
mMetricEvent->FromJson(eventJson);

APSARA_TEST_EQUAL(12345678901, mMetricEvent->GetTimestamp());
APSARA_TEST_EQUAL(0L, mMetricEvent->GetTimestampNanosecond().value());
APSARA_TEST_EQUAL("test", mMetricEvent->GetName());
APSARA_TEST_TRUE(mMetricEvent->Is<UntypedMultiFloatValues>());
APSARA_TEST_EQUAL(10.0, mMetricEvent->GetValue<UntypedMultiFloatValues>()->GetMultiKeyValue("test-1"));
APSARA_TEST_EQUAL(2.0, mMetricEvent->GetValue<UntypedMultiFloatValues>()->GetMultiKeyValue("test-2"));
APSARA_TEST_EQUAL(10.0, mMetricEvent->GetValue<UntypedMultiFloatValues>()->GetMultiKeyValue("test-1"));
APSARA_TEST_EQUAL(2.0, mMetricEvent->GetValue<UntypedMultiFloatValues>()->GetMultiKeyValue("test-2"));
APSARA_TEST_EQUAL("value1", mMetricEvent->GetTag("key1").to_string());
}

void MetricEventUnittest::TestTagsIterator() {
string key1 = "key1";
string value1 = "value1";
Expand All @@ -216,12 +334,16 @@ void MetricEventUnittest::TestTagsIterator() {
}

UNIT_TEST_CASE(MetricEventUnittest, TestName)
UNIT_TEST_CASE(MetricEventUnittest, TestValue)
UNIT_TEST_CASE(MetricEventUnittest, TestUntypedSingleValue)
UNIT_TEST_CASE(MetricEventUnittest, TestUntypedMultiFloatValues)
UNIT_TEST_CASE(MetricEventUnittest, TestTag)
UNIT_TEST_CASE(MetricEventUnittest, TestSize)
UNIT_TEST_CASE(MetricEventUnittest, TestUntypedSingleValueSize)
UNIT_TEST_CASE(MetricEventUnittest, TestUntypedMultiFloatValuesSize)
UNIT_TEST_CASE(MetricEventUnittest, TestReset)
UNIT_TEST_CASE(MetricEventUnittest, TestToJson)
UNIT_TEST_CASE(MetricEventUnittest, TestFromJson)
UNIT_TEST_CASE(MetricEventUnittest, TestUntypedSingleValueToJson)
UNIT_TEST_CASE(MetricEventUnittest, TestUntypedMultiFloatValuesToJson)
UNIT_TEST_CASE(MetricEventUnittest, TestUntypedSingleValueFromJson)
UNIT_TEST_CASE(MetricEventUnittest, TestUntypedMultiFloatValuesFromJson)
UNIT_TEST_CASE(MetricEventUnittest, TestTagsIterator)

} // namespace logtail
Expand Down
Loading
Loading