Repository: alibaba/loongcollector
Pull request #2205: feat: add  metric calculate into host_monitor
Author: login: WRPStephanie
Created: 2025-05-09T03:12:02Z | Merged: 2025-05-19T01:59:10Z

新增计算指标min、max、avg的抽象类，主机监控指标都可以使用这个抽象类进行计算。


Diff:
diff --git a/.github/workflows/static-check.yaml b/.github/workflows/static-check.yaml
index adb6cfaf9c..2cc425859c 100644
--- a/.github/workflows/static-check.yaml
+++ b/.github/workflows/static-check.yaml
@@ -106,7 +106,7 @@ jobs:
         with:
           clang-format-version: '18'
           check-path: 'core'
-          exclude-regex: 'common/xxhash|labels/Relabel\.cpp|ProcessorParseContainerLogNative\.cpp|FlusherSLS\.cpp|_thirdparty|core/common/LRUCache\.h|core/common/queue/.*|core/common/magic_enum\.hpp'
+          exclude-regex: 'common/xxhash|labels/Relabel\.cpp|ProcessorParseContainerLogNative\.cpp|FlusherSLS\.cpp|_thirdparty|core/common/LRUCache\.h|core/common/queue/.*|core/common/magic_enum\.hpp|core/host_monitor/common/FieldEntry\.h'
           include-regex: '.*\.(cpp|h)$'
 
       - name: Go Plugin Lint
diff --git a/core/host_monitor/collector/MetricCalculate.h b/core/host_monitor/collector/MetricCalculate.h
new file mode 100644
index 0000000000..408f2e233d
--- /dev/null
+++ b/core/host_monitor/collector/MetricCalculate.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2025 iLogtail Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <memory>
+
+#include "common/FieldEntry.h"
+
+namespace logtail {
+
+// MetricCalculate 用于计算各个collector采集的指标的最大值、最小值和均值
+// TMetric为各个collector保存指标数据的类，TField为各个collector保存指标数据的字段
+// 计算完成后，各collector打上tag，生成对应的metricEvent
+
+template <typename TMetric, typename TField = double>
+class MetricCalculate {
+public:
+    using FieldMeta = FieldName<TMetric, TField>;
+    void Reset() {
+        mCount = 0;
+        mMax.reset();
+        mMin.reset();
+        mTotal.reset();
+        mLast.reset();
+    }
+
+    void AddValue(const TMetric& v) {
+        mCount++;
+        if (mCount == 1) {
+            // First value - initialize all stats
+            mMax.emplace(v);
+            mMin.emplace(v);
+            mTotal.emplace(v);
+            mLast.emplace(v);
+        } else {
+            // Update existing stats
+            TMetric::enumerate([&](const auto& field) {
+                const TField& value = field.value(v);
+                field.value(*mMax) = std::max(field.value(*mMax), value);
+                field.value(*mMin) = std::min(field.value(*mMin), value);
+                field.value(*mTotal) += value;
+            });
+            mLast.emplace(v);
+        }
+    }
+
+    bool GetMaxValue(TMetric& dst) const {
+        if (!mMax.has_value()) {
+            return false;
+        }
+
+        dst = *mMax;
+        return true;
+    }
+
+    bool GetMinValue(TMetric& dst) const {
+        if (!mMin.has_value()) {
+            return false;
+        }
+
+        dst = *mMin;
+        return true;
+    }
+
+    bool GetAvgValue(TMetric& dst) const {
+        if (!mTotal.has_value()) {
+            return false;
+        }
+
+        dst = *mTotal;
+        if (mCount > 1) {
+            TMetric::enumerate([&](const auto& field) { field.value(dst) /= mCount; });
+        }
+        return true;
+    }
+
+    bool GetLastValue(TMetric& dst) const {
+        if (!mLast.has_value()) {
+            return false;
+        }
+
+        dst = *mLast;
+        return true;
+    }
+
+    std::shared_ptr<TMetric> GetLastValue() const {
+        if (!mLast.has_value()) {
+            return nullptr;
+        }
+
+        return std::make_shared<TMetric>(*mLast);
+    }
+
+    bool Stat(TMetric& max, TMetric& min, TMetric& avg, TMetric* last = nullptr) {
+        bool success = GetMaxValue(max) && GetMinValue(min) && GetAvgValue(avg);
+        if (success && last) {
+            GetLastValue(*last);
+        }
+        return success;
+    }
+
+    size_t Count() const { return mCount; }
+
+private:
+    size_t mCount = 0;
+    std::optional<TMetric> mMax;
+    std::optional<TMetric> mMin;
+    std::optional<TMetric> mTotal;
+    std::optional<TMetric> mLast;
+};
+
+
+} // namespace logtail
diff --git a/core/host_monitor/common/FieldEntry.h b/core/host_monitor/common/FieldEntry.h
new file mode 100644
index 0000000000..6aae942c05
--- /dev/null
+++ b/core/host_monitor/common/FieldEntry.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2025 iLogtail Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <functional>
+#include <string>
+
+
+template <typename TClass, typename TField = double>
+class FieldName {
+public:
+    std::string_view name;
+    TField TClass::*ptr; // 成员指针，直接访问字段
+
+    constexpr FieldName(std::string_view n, TField TClass::*p) : name(n), ptr(p) {}
+
+    // 访问对象中的字段值
+    TField& value(TClass& obj) const { return obj.*ptr; }
+    const TField& value(const TClass& obj) const { return obj.*ptr; }
+};
+
+// 字段定义宏
+#define FIELD_ENTRY(CLASS, FIELD) FieldName<CLASS>(#FIELD, &CLASS::FIELD)
diff --git a/core/unittest/host_monitor/CMakeLists.txt b/core/unittest/host_monitor/CMakeLists.txt
index cadb6f3733..41933a24cc 100644
--- a/core/unittest/host_monitor/CMakeLists.txt
+++ b/core/unittest/host_monitor/CMakeLists.txt
@@ -27,8 +27,12 @@ target_link_libraries(system_information_tools_unittest ${UT_BASE_TARGET})
 add_executable(cpu_collector_unittest CPUCollectorUnittest.cpp)
 target_link_libraries(cpu_collector_unittest ${UT_BASE_TARGET})
 
+add_executable(metric_calculate_unittest MetricCalculateUnittest.cpp)
+target_link_libraries(metric_calculate_unittest ${UT_BASE_TARGET})
+
 include(GoogleTest)
 gtest_discover_tests(process_entity_collector_unittest)
 gtest_discover_tests(host_monitor_input_runner_unittest)
 gtest_discover_tests(system_information_tools_unittest)
 gtest_discover_tests(cpu_collector_unittest)
+gtest_discover_tests(metric_calculate_unittest)
diff --git a/core/unittest/host_monitor/MetricCalculateUnittest.cpp b/core/unittest/host_monitor/MetricCalculateUnittest.cpp
new file mode 100644
index 0000000000..8b2c048f14
--- /dev/null
+++ b/core/unittest/host_monitor/MetricCalculateUnittest.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2025 iLogtail Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CPUCollector.h"
+#include "MetricCalculate.h"
+#include "unittest/Unittest.h"
+
+using namespace std;
+
+namespace logtail {
+struct MockData {
+    double data1;
+    double data2;
+    double data3;
+    // Define the field descriptors
+    static inline const FieldName<MockData> mockDataFields[] = {
+        FIELD_ENTRY(MockData, data1),
+        FIELD_ENTRY(MockData, data2),
+        FIELD_ENTRY(MockData, data3),
+    };
+
+    // Define the enumerate function for your metric type
+    static void enumerate(const std::function<void(const FieldName<MockData, double>&)>& callback) {
+        for (const auto& field : mockDataFields) {
+            callback(field);
+        }
+    }
+};
+
+
+class MetricCalculateUnittest : public testing::Test {
+public:
+    void TestReset();
+    void TestAddValue();
+    void TestGetMaxValue();
+    void TestGetMinValue();
+    void TestGetAvgValue();
+    void TestStat();
+    void TestGetLastValue();
+    void TestCount();
+    void TestCalAfterReset();
+};
+
+void MetricCalculateUnittest::TestReset() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData = {1, 2, 3};
+    metricCalculate.AddValue(mockData);
+    metricCalculate.Reset();
+    int cnt = metricCalculate.Count();
+    APSARA_TEST_EQUAL(cnt, 0);
+}
+
+void MetricCalculateUnittest::TestAddValue() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData = {1, 2, 3};
+    metricCalculate.AddValue(mockData);
+    int cnt = metricCalculate.Count();
+    APSARA_TEST_EQUAL(cnt, 1);
+}
+
+void MetricCalculateUnittest::TestGetMaxValue() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData1 = {1, 6, 8};
+    metricCalculate.AddValue(mockData1);
+    MockData mockData2 = {2, 4, 9};
+    metricCalculate.AddValue(mockData2);
+    MockData mockData3 = {3, 5, 7};
+    metricCalculate.AddValue(mockData3);
+    MockData maxData;
+    metricCalculate.GetMaxValue(maxData);
+    APSARA_TEST_EQUAL(maxData.data1, 3);
+    APSARA_TEST_EQUAL(maxData.data2, 6);
+    APSARA_TEST_EQUAL(maxData.data3, 9);
+}
+
+void MetricCalculateUnittest::TestGetMinValue() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData1 = {1, 6, 8};
+    metricCalculate.AddValue(mockData1);
+    MockData mockData2 = {2, 4, 9};
+    metricCalculate.AddValue(mockData2);
+    MockData mockData3 = {3, 5, 7};
+    metricCalculate.AddValue(mockData3);
+    MockData minData;
+    metricCalculate.GetMinValue(minData);
+    APSARA_TEST_EQUAL(minData.data1, 1);
+    APSARA_TEST_EQUAL(minData.data2, 4);
+    APSARA_TEST_EQUAL(minData.data3, 7);
+}
+
+void MetricCalculateUnittest::TestGetAvgValue() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData1 = {1, 6, 8};
+    metricCalculate.AddValue(mockData1);
+    MockData mockData2 = {2, 4, 9};
+    metricCalculate.AddValue(mockData2);
+    MockData mockData3 = {3, 5, 7};
+    metricCalculate.AddValue(mockData3);
+    MockData avgData;
+    metricCalculate.GetAvgValue(avgData);
+    APSARA_TEST_EQUAL(avgData.data1, 2);
+    APSARA_TEST_EQUAL(avgData.data2, 5);
+    APSARA_TEST_EQUAL(avgData.data3, 8);
+}
+
+void MetricCalculateUnittest::TestStat() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData1 = {1, 6, 8};
+    metricCalculate.AddValue(mockData1);
+    MockData mockData2 = {2, 4, 9};
+    metricCalculate.AddValue(mockData2);
+    MockData mockData3 = {3, 5, 7};
+    metricCalculate.AddValue(mockData3);
+    MockData maxData, minData, avgData;
+    metricCalculate.Stat(maxData, minData, avgData);
+    APSARA_TEST_EQUAL(maxData.data1, 3);
+    APSARA_TEST_EQUAL(maxData.data2, 6);
+    APSARA_TEST_EQUAL(maxData.data3, 9);
+    APSARA_TEST_EQUAL(minData.data1, 1);
+    APSARA_TEST_EQUAL(minData.data2, 4);
+    APSARA_TEST_EQUAL(minData.data3, 7);
+    APSARA_TEST_EQUAL(avgData.data1, 2);
+    APSARA_TEST_EQUAL(avgData.data2, 5);
+    APSARA_TEST_EQUAL(avgData.data3, 8);
+}
+
+void MetricCalculateUnittest::TestGetLastValue() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData1 = {1, 6, 8};
+    metricCalculate.AddValue(mockData1);
+    MockData mockData2 = {2, 4, 9};
+    metricCalculate.AddValue(mockData2);
+    MockData lastData;
+    metricCalculate.GetLastValue(lastData);
+    APSARA_TEST_EQUAL(lastData.data1, 2);
+    APSARA_TEST_EQUAL(lastData.data2, 4);
+    APSARA_TEST_EQUAL(lastData.data3, 9);
+}
+
+void MetricCalculateUnittest::TestCount() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData1 = {1, 6, 8};
+    metricCalculate.AddValue(mockData1);
+    MockData mockData2 = {2, 4, 9};
+    metricCalculate.AddValue(mockData2);
+    MockData mockData3 = {3, 5, 7};
+    metricCalculate.AddValue(mockData3);
+    int cnt = metricCalculate.Count();
+    APSARA_TEST_EQUAL(cnt, 3);
+}
+
+void MetricCalculateUnittest::TestCalAfterReset() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData0 = {1, 1, 1};
+    metricCalculate.AddValue(mockData0);
+    int cnt = metricCalculate.Count();
+    APSARA_TEST_EQUAL(cnt, 1);
+    metricCalculate.Reset();
+    MockData mockData1 = {1, 6, 8};
+    metricCalculate.AddValue(mockData1);
+    MockData mockData2 = {2, 4, 9};
+    metricCalculate.AddValue(mockData2);
+    MockData mockData3 = {3, 5, 7};
+    metricCalculate.AddValue(mockData3);
+    cnt = metricCalculate.Count();
+    APSARA_TEST_EQUAL(cnt, 3);
+    MockData maxData, minData, avgData;
+    metricCalculate.Stat(maxData, minData, avgData);
+    APSARA_TEST_EQUAL(maxData.data1, 3);
+    APSARA_TEST_EQUAL(minData.data2, 4);
+    APSARA_TEST_EQUAL(avgData.data3, 8);
+}
+
+UNIT_TEST_CASE(MetricCalculateUnittest, TestReset);
+UNIT_TEST_CASE(MetricCalculateUnittest, TestAddValue);
+UNIT_TEST_CASE(MetricCalculateUnittest, TestGetMaxValue);
+UNIT_TEST_CASE(MetricCalculateUnittest, TestGetMinValue);
+UNIT_TEST_CASE(MetricCalculateUnittest, TestGetAvgValue);
+UNIT_TEST_CASE(MetricCalculateUnittest, TestStat);
+UNIT_TEST_CASE(MetricCalculateUnittest, TestGetLastValue);
+UNIT_TEST_CASE(MetricCalculateUnittest, TestCount);
+UNIT_TEST_CASE(MetricCalculateUnittest, TestCalAfterReset);
+
+
+} // namespace logtail
+
+UNIT_TEST_MAIN


Reviews:
user: login: Abingcbc
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-05-10T07:24:39Z
user: login: WRPStephanie
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T01:42:52Z
user: login: WRPStephanie
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T05:55:43Z
user: login: WRPStephanie
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T06:04:19Z
user: login: WRPStephanie
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T06:14:05Z
user: login: WRPStephanie
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T06:28:19Z
user: login: Abingcbc
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-13T14:59:15Z
user: login: messixukejia
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-05-14T01:47:14Z
user: login: WRPStephanie
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T02:07:11Z
user: login: WRPStephanie
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T06:46:51Z
user: login: WRPStephanie
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T06:50:07Z
user: login: messixukejia
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-14T07:05:21Z
user: login: messixukejia
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-14T07:05:59Z
user: login: WRPStephanie
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T07:24:02Z
user: login: yyuuttaaoo
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-16T05:34:01Z
user: login: WRPStephanie
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T06:52:11Z
user: login: messixukejia
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-16T08:13:43Z
user: login: yyuuttaaoo
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-16T08:26:05Z

Review comments:
diff_hunk: @@ -0,0 +1,92 @@
+#ifndef LOONGCOLLECTOR_METRIC_CALCULATE_H
path: core/host_monitor/collector/MetricCalculate.h
user: login: Abingcbc
body: apache的copyright要加一下
created_at: 2025-05-09T10:06:34Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,92 @@
+#ifndef LOONGCOLLECTOR_METRIC_CALCULATE_H
+#define LOONGCOLLECTOR_METRIC_CALCULATE_H
+#ifdef max
path: core/host_monitor/collector/MetricCalculate.h
user: login: Abingcbc
body: 这个max、min宏是做什么的
created_at: 2025-05-09T10:07:44Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,92 @@
+#ifndef LOONGCOLLECTOR_METRIC_CALCULATE_H
+#define LOONGCOLLECTOR_METRIC_CALCULATE_H
+#ifdef max
+#undef max
+#endif
+#ifdef min
+#undef min
+#endif
+#include <algorithm>
+#include <memory>
+
+#include "common/FieldEntry.h"
+
+namespace logtail {
+
+template <typename TMetric, typename TField = double>
+class MetricCalculate {
+public:
+    typedef FieldName<TMetric, TField> FieldMeta;
+    void Reset() { mCount = 0; }
+
+    void AddValue(const TMetric& v) {
+        mCount++;
+        if (1 == mCount) {
+            enumerate([&](const FieldMeta& field) {
path: core/host_monitor/collector/MetricCalculate.h
user: login: Abingcbc
body: 这个enumerate没有定义，是之后参考argus在每个Collector里面定义吗？
created_at: 2025-05-10T07:01:03Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,92 @@
+#ifndef LOONGCOLLECTOR_METRIC_CALCULATE_H
path: core/host_monitor/collector/MetricCalculate.h
user: login: Abingcbc
body: 为什么需要这个宏 LOONGCOLLECTOR_METRIC_CALCULATE_H
created_at: 2025-05-10T07:07:05Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,92 @@
+#ifndef LOONGCOLLECTOR_METRIC_CALCULATE_H
+#define LOONGCOLLECTOR_METRIC_CALCULATE_H
+#ifdef max
+#undef max
+#endif
+#ifdef min
+#undef min
+#endif
+#include <algorithm>
+#include <memory>
+
+#include "common/FieldEntry.h"
+
+namespace logtail {
+
+template <typename TMetric, typename TField = double>
path: core/host_monitor/collector/MetricCalculate.h
user: login: Abingcbc
body: 这里是不是可以补充一下注释？TMetric和MetricEvent的关系
created_at: 2025-05-10T07:08:18Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,38 @@
+#ifndef LOONGCOLLECTOR_FIELD_ENTRY_H
+#define LOONGCOLLECTOR_FIELD_ENTRY_H
+#include <functional>
+#include <string>
+
+#define FIELD_NAME_INITIALIZER(NAME, CLASS, F) NAME, [](CLASS& p) { return std::ref(p.F); }
path: core/host_monitor/common/FieldEntry.h
user: login: Abingcbc
body: 这几个宏在argus里调用点也很少，我们有必要再引入这些东西吗？
created_at: 2025-05-10T07:10:36Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,92 @@
+#ifndef LOONGCOLLECTOR_METRIC_CALCULATE_H
+#define LOONGCOLLECTOR_METRIC_CALCULATE_H
+#ifdef max
+#undef max
+#endif
+#ifdef min
+#undef min
+#endif
+#include <algorithm>
+#include <memory>
+
+#include "common/FieldEntry.h"
+
+namespace logtail {
+
+template <typename TMetric, typename TField = double>
+class MetricCalculate {
+public:
+    typedef FieldName<TMetric, TField> FieldMeta;
+    void Reset() { mCount = 0; }
+
+    void AddValue(const TMetric& v) {
+        mCount++;
+        if (1 == mCount) {
+            enumerate([&](const FieldMeta& field) {
+                const TField& metricValue = field.value(v);
+
+                field.value(mMax) = metricValue;
+                field.value(mMin) = metricValue;
+                field.value(mTotal) = metricValue;
+                field.value(mLast) = metricValue;
+            });
+        } else {
+            enumerate([&](const FieldName<TMetric, TField>& field) {
path: core/host_monitor/collector/MetricCalculate.h
user: login: Abingcbc
body: FieldName<TMetric, TField> = FieldMeta
created_at: 2025-05-10T07:23:03Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,38 @@
+#ifndef LOONGCOLLECTOR_FIELD_ENTRY_H
+#define LOONGCOLLECTOR_FIELD_ENTRY_H
+#include <functional>
+#include <string>
+
+#define FIELD_NAME_INITIALIZER(NAME, CLASS, F) NAME, [](CLASS& p) { return std::ref(p.F); }
+#define FIELD_NAME_ENTRY(NAME, CLASS, F) {FIELD_NAME_INITIALIZER(NAME, CLASS, F)}
+// 适用于字段与Name同名的情况
+#define FIELD_ENTRY(CLASS, F) {#F, [](CLASS& p) { return std::ref(p.F); }}
+
+template <typename TClass, typename TFieldType = double>
+class FieldName {
+    std::function<TFieldType&(TClass&)> _fnGet;
+
+public:
+    const std::string name; // 名称
+
+    FieldName(const char* n, std::function<TFieldType&(TClass&)> fnGet) : _fnGet(fnGet), name(n == nullptr ? "" : n) {}
+
+    const TFieldType& value(const TClass& p) const { return _fnGet(const_cast<TClass&>(p)); }
+
+    TFieldType& value(TClass& p) const { return const_cast<TFieldType&>(_fnGet(p)); }
+};
+
+#define METRIC_FIELD_NAME_INITIALIZER(NAME, CLASS, F, BOOL) FIELD_NAME_INITIALIZER(NAME, CLASS, F), BOOL
path: core/host_monitor/common/FieldEntry.h
user: login: Abingcbc
body: 同上，这些宏在argus也只有一两个调用点，是否有必要引入
created_at: 2025-05-10T07:24:12Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,92 @@
+#ifndef LOONGCOLLECTOR_METRIC_CALCULATE_H
+#define LOONGCOLLECTOR_METRIC_CALCULATE_H
+#ifdef max
+#undef max
+#endif
+#ifdef min
+#undef min
+#endif
+#include <algorithm>
+#include <memory>
+
+#include "common/FieldEntry.h"
+
+namespace logtail {
+
+template <typename TMetric, typename TField = double>
+class MetricCalculate {
+public:
+    typedef FieldName<TMetric, TField> FieldMeta;
+    void Reset() { mCount = 0; }
+
+    void AddValue(const TMetric& v) {
+        mCount++;
+        if (1 == mCount) {
+            enumerate([&](const FieldMeta& field) {
path: core/host_monitor/collector/MetricCalculate.h
user: login: WRPStephanie
body: 是的，每个collector自行实现
created_at: 2025-05-12T01:42:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,38 @@
+#ifndef LOONGCOLLECTOR_FIELD_ENTRY_H
+#define LOONGCOLLECTOR_FIELD_ENTRY_H
+#include <functional>
+#include <string>
+
+#define FIELD_NAME_INITIALIZER(NAME, CLASS, F) NAME, [](CLASS& p) { return std::ref(p.F); }
+#define FIELD_NAME_ENTRY(NAME, CLASS, F) {FIELD_NAME_INITIALIZER(NAME, CLASS, F)}
+// 适用于字段与Name同名的情况
+#define FIELD_ENTRY(CLASS, F) {#F, [](CLASS& p) { return std::ref(p.F); }}
+
+template <typename TClass, typename TFieldType = double>
+class FieldName {
+    std::function<TFieldType&(TClass&)> _fnGet;
+
+public:
+    const std::string name; // 名称
+
+    FieldName(const char* n, std::function<TFieldType&(TClass&)> fnGet) : _fnGet(fnGet), name(n == nullptr ? "" : n) {}
+
+    const TFieldType& value(const TClass& p) const { return _fnGet(const_cast<TClass&>(p)); }
+
+    TFieldType& value(TClass& p) const { return const_cast<TFieldType&>(_fnGet(p)); }
+};
+
+#define METRIC_FIELD_NAME_INITIALIZER(NAME, CLASS, F, BOOL) FIELD_NAME_INITIALIZER(NAME, CLASS, F), BOOL
path: core/host_monitor/common/FieldEntry.h
user: login: WRPStephanie
body: 这三个宏仅新增标识指标的布尔字段，调用点较少，可以考虑删去。
created_at: 2025-05-12T05:55:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,92 @@
+#ifndef LOONGCOLLECTOR_METRIC_CALCULATE_H
+#define LOONGCOLLECTOR_METRIC_CALCULATE_H
+#ifdef max
path: core/host_monitor/collector/MetricCalculate.h
user: login: WRPStephanie
body: 防止和其他地方定义的min max的宏冲突。但本段代码中调用时已经写明了std::min和std::max，这几行可以删去

created_at: 2025-05-12T06:04:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,92 @@
+#ifndef LOONGCOLLECTOR_METRIC_CALCULATE_H
path: core/host_monitor/collector/MetricCalculate.h
user: login: WRPStephanie
body: 可以不需要，我稍后删去
created_at: 2025-05-12T06:14:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,38 @@
+#ifndef LOONGCOLLECTOR_FIELD_ENTRY_H
+#define LOONGCOLLECTOR_FIELD_ENTRY_H
+#include <functional>
+#include <string>
+
+#define FIELD_NAME_INITIALIZER(NAME, CLASS, F) NAME, [](CLASS& p) { return std::ref(p.F); }
path: core/host_monitor/common/FieldEntry.h
user: login: WRPStephanie
body: 这几个宏用于初始化类字段与对应名称的绑定关系。FIELD_NAME_INITIALIZER定义一个字段名和获取该字段引用的lambda表达式，FIELD_NAME_ENTRY使用上述宏创建一个结构体或对象条目，FIELD_ENTRY也是创建一个结构体或对象条目，适用于字段名与名称字符串相同的情况。这样可以将类成员变量封装为可反射访问的形式，便于统一处理，感觉还是有必要保留
created_at: 2025-05-12T06:28:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,41 @@
+/**
+ * Copyright 2025 Alibaba Cloud
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LOONGCOLLECTOR_FIELD_ENTRY_H
path: core/host_monitor/common/FieldEntry.h
user: login: Abingcbc
body: 这个宏也可以去掉
created_at: 2025-05-13T14:58:56Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,102 @@
+/**
+ * Copyright 2025 Alibaba Cloud
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
path: core/host_monitor/collector/MetricCalculate.h
user: login: messixukejia
body: 基础功能要有ut
created_at: 2025-05-14T01:47:02Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,41 @@
+/**
+ * Copyright 2025 Alibaba Cloud
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LOONGCOLLECTOR_FIELD_ENTRY_H
path: core/host_monitor/common/FieldEntry.h
user: login: WRPStephanie
body: 好的
created_at: 2025-05-14T02:07:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,102 @@
+/**
+ * Copyright 2025 Alibaba Cloud
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
path: core/host_monitor/collector/MetricCalculate.h
user: login: WRPStephanie
body: > 基础功能要有ut
已补充

created_at: 2025-05-14T06:46:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,92 @@
+#ifndef LOONGCOLLECTOR_METRIC_CALCULATE_H
+#define LOONGCOLLECTOR_METRIC_CALCULATE_H
+#ifdef max
+#undef max
+#endif
+#ifdef min
+#undef min
+#endif
+#include <algorithm>
+#include <memory>
+
+#include "common/FieldEntry.h"
+
+namespace logtail {
+
+template <typename TMetric, typename TField = double>
path: core/host_monitor/collector/MetricCalculate.h
user: login: WRPStephanie
body: > 这里是不是可以补充一下注释？TMetric和MetricEvent的关系

已补充
created_at: 2025-05-14T06:50:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,176 @@
+/*
+ * Copyright 2025 iLogtail Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CPUCollector.h"
+#include "MetricCalculate.h"
+#include "unittest/Unittest.h"
+
+using namespace std;
+
+namespace logtail {
+struct MockData {
+    double data1;
+    double data2;
+    double data3;
+};
+
+const FieldName<MockData> mockDataMeta[] = {
+    FIELD_ENTRY(MockData, data1),
+    FIELD_ENTRY(MockData, data2),
+    FIELD_ENTRY(MockData, data3),
+};
+const size_t mockDataMetaSize = sizeof(mockDataMeta) / sizeof(mockDataMeta[0]);
+const FieldName<MockData>* const mockDataMetaEnd = mockDataMeta + mockDataMetaSize;
+static_assert(mockDataMetaSize == sizeof(MockData) / sizeof(double), "mockDataMeta unexpected");
+void enumerate(const std::function<void(const FieldName<MockData>&)>& callback) {
+    for (auto it = mockDataMeta; it < mockDataMetaEnd; ++it) {
+        callback(*it);
+    }
+}
+
+class MetricCalculateUnittest : public testing::Test {
+public:
+    void TestReset();
+    void TestAddValue();
+    void TestGetMaxValue();
+    void TestGetMinValue();
+    void TestGetAvgValue();
+    void TestStat();
+    void TestGetLastValue();
+    void TestCount();
+};
+
+void MetricCalculateUnittest::TestReset() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData = {1, 2, 3};
+    metricCalculate.AddValue(mockData);
+    metricCalculate.Reset();
+    int cnt = metricCalculate.Count();
+    APSARA_TEST_EQUAL(cnt, 0);
+}
+
+void MetricCalculateUnittest::TestAddValue() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData = {1, 2, 3};
+    metricCalculate.AddValue(mockData);
+    int cnt = metricCalculate.Count();
+    APSARA_TEST_EQUAL(cnt, 1);
+}
+
+void MetricCalculateUnittest::TestGetMaxValue() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData1 = {1, 6, 8};
+    metricCalculate.AddValue(mockData1);
+    MockData mockData2 = {2, 4, 9};
+    metricCalculate.AddValue(mockData2);
+    MockData mockData3 = {3, 5, 7};
+    metricCalculate.AddValue(mockData3);
+    MockData maxData;
+    metricCalculate.GetMaxValue(maxData);
+    APSARA_TEST_EQUAL(maxData.data1, 3);
+    APSARA_TEST_EQUAL(maxData.data2, 6);
+    APSARA_TEST_EQUAL(maxData.data3, 9);
+}
+
+void MetricCalculateUnittest::TestGetMinValue() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData1 = {1, 6, 8};
+    metricCalculate.AddValue(mockData1);
+    MockData mockData2 = {2, 4, 9};
+    metricCalculate.AddValue(mockData2);
+    MockData mockData3 = {3, 5, 7};
+    metricCalculate.AddValue(mockData3);
+    MockData minData;
+    metricCalculate.GetMinValue(minData);
+    APSARA_TEST_EQUAL(minData.data1, 1);
+    APSARA_TEST_EQUAL(minData.data2, 4);
+    APSARA_TEST_EQUAL(minData.data3, 7);
+}
+
+void MetricCalculateUnittest::TestGetAvgValue() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData1 = {1, 6, 8};
+    metricCalculate.AddValue(mockData1);
+    MockData mockData2 = {2, 4, 9};
+    metricCalculate.AddValue(mockData2);
+    MockData mockData3 = {3, 5, 7};
+    metricCalculate.AddValue(mockData3);
+    MockData avgData;
+    metricCalculate.GetAvgValue(avgData);
+    APSARA_TEST_EQUAL(avgData.data1, 2);
+    APSARA_TEST_EQUAL(avgData.data2, 5);
+    APSARA_TEST_EQUAL(avgData.data3, 8);
+}
+
+void MetricCalculateUnittest::TestStat() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData1 = {1, 6, 8};
+    metricCalculate.AddValue(mockData1);
+    MockData mockData2 = {2, 4, 9};
+    metricCalculate.AddValue(mockData2);
+    MockData mockData3 = {3, 5, 7};
+    metricCalculate.AddValue(mockData3);
+    MockData maxData, minData, avgData;
+    metricCalculate.Stat(maxData, minData, avgData);
+    APSARA_TEST_EQUAL(maxData.data1, 3);
+    APSARA_TEST_EQUAL(maxData.data2, 6);
+    APSARA_TEST_EQUAL(maxData.data3, 9);
+    APSARA_TEST_EQUAL(minData.data1, 1);
+    APSARA_TEST_EQUAL(minData.data2, 4);
+    APSARA_TEST_EQUAL(minData.data3, 7);
+    APSARA_TEST_EQUAL(avgData.data1, 2);
+    APSARA_TEST_EQUAL(avgData.data2, 5);
+    APSARA_TEST_EQUAL(avgData.data3, 8);
+}
+
+void MetricCalculateUnittest::TestGetLastValue() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData1 = {1, 6, 8};
+    metricCalculate.AddValue(mockData1);
+    MockData mockData2 = {2, 4, 9};
+    metricCalculate.AddValue(mockData2);
+    MockData lastData;
+    metricCalculate.GetLastValue(lastData);
+    APSARA_TEST_EQUAL(lastData.data1, 2);
+    APSARA_TEST_EQUAL(lastData.data2, 4);
+    APSARA_TEST_EQUAL(lastData.data3, 9);
+}
+
+void MetricCalculateUnittest::TestCount() {
+    MetricCalculate<MockData> metricCalculate;
path: core/unittest/host_monitor/MetricCalculateUnittest.cpp
user: login: messixukejia
body: 清理再计算的场景补充下
created_at: 2025-05-14T07:05:58Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,176 @@
+/*
+ * Copyright 2025 iLogtail Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CPUCollector.h"
+#include "MetricCalculate.h"
+#include "unittest/Unittest.h"
+
+using namespace std;
+
+namespace logtail {
+struct MockData {
+    double data1;
+    double data2;
+    double data3;
+};
+
+const FieldName<MockData> mockDataMeta[] = {
+    FIELD_ENTRY(MockData, data1),
+    FIELD_ENTRY(MockData, data2),
+    FIELD_ENTRY(MockData, data3),
+};
+const size_t mockDataMetaSize = sizeof(mockDataMeta) / sizeof(mockDataMeta[0]);
+const FieldName<MockData>* const mockDataMetaEnd = mockDataMeta + mockDataMetaSize;
+static_assert(mockDataMetaSize == sizeof(MockData) / sizeof(double), "mockDataMeta unexpected");
+void enumerate(const std::function<void(const FieldName<MockData>&)>& callback) {
+    for (auto it = mockDataMeta; it < mockDataMetaEnd; ++it) {
+        callback(*it);
+    }
+}
+
+class MetricCalculateUnittest : public testing::Test {
+public:
+    void TestReset();
+    void TestAddValue();
+    void TestGetMaxValue();
+    void TestGetMinValue();
+    void TestGetAvgValue();
+    void TestStat();
+    void TestGetLastValue();
+    void TestCount();
+};
+
+void MetricCalculateUnittest::TestReset() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData = {1, 2, 3};
+    metricCalculate.AddValue(mockData);
+    metricCalculate.Reset();
+    int cnt = metricCalculate.Count();
+    APSARA_TEST_EQUAL(cnt, 0);
+}
+
+void MetricCalculateUnittest::TestAddValue() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData = {1, 2, 3};
+    metricCalculate.AddValue(mockData);
+    int cnt = metricCalculate.Count();
+    APSARA_TEST_EQUAL(cnt, 1);
+}
+
+void MetricCalculateUnittest::TestGetMaxValue() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData1 = {1, 6, 8};
+    metricCalculate.AddValue(mockData1);
+    MockData mockData2 = {2, 4, 9};
+    metricCalculate.AddValue(mockData2);
+    MockData mockData3 = {3, 5, 7};
+    metricCalculate.AddValue(mockData3);
+    MockData maxData;
+    metricCalculate.GetMaxValue(maxData);
+    APSARA_TEST_EQUAL(maxData.data1, 3);
+    APSARA_TEST_EQUAL(maxData.data2, 6);
+    APSARA_TEST_EQUAL(maxData.data3, 9);
+}
+
+void MetricCalculateUnittest::TestGetMinValue() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData1 = {1, 6, 8};
+    metricCalculate.AddValue(mockData1);
+    MockData mockData2 = {2, 4, 9};
+    metricCalculate.AddValue(mockData2);
+    MockData mockData3 = {3, 5, 7};
+    metricCalculate.AddValue(mockData3);
+    MockData minData;
+    metricCalculate.GetMinValue(minData);
+    APSARA_TEST_EQUAL(minData.data1, 1);
+    APSARA_TEST_EQUAL(minData.data2, 4);
+    APSARA_TEST_EQUAL(minData.data3, 7);
+}
+
+void MetricCalculateUnittest::TestGetAvgValue() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData1 = {1, 6, 8};
+    metricCalculate.AddValue(mockData1);
+    MockData mockData2 = {2, 4, 9};
+    metricCalculate.AddValue(mockData2);
+    MockData mockData3 = {3, 5, 7};
+    metricCalculate.AddValue(mockData3);
+    MockData avgData;
+    metricCalculate.GetAvgValue(avgData);
+    APSARA_TEST_EQUAL(avgData.data1, 2);
+    APSARA_TEST_EQUAL(avgData.data2, 5);
+    APSARA_TEST_EQUAL(avgData.data3, 8);
+}
+
+void MetricCalculateUnittest::TestStat() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData1 = {1, 6, 8};
+    metricCalculate.AddValue(mockData1);
+    MockData mockData2 = {2, 4, 9};
+    metricCalculate.AddValue(mockData2);
+    MockData mockData3 = {3, 5, 7};
+    metricCalculate.AddValue(mockData3);
+    MockData maxData, minData, avgData;
+    metricCalculate.Stat(maxData, minData, avgData);
+    APSARA_TEST_EQUAL(maxData.data1, 3);
+    APSARA_TEST_EQUAL(maxData.data2, 6);
+    APSARA_TEST_EQUAL(maxData.data3, 9);
+    APSARA_TEST_EQUAL(minData.data1, 1);
+    APSARA_TEST_EQUAL(minData.data2, 4);
+    APSARA_TEST_EQUAL(minData.data3, 7);
+    APSARA_TEST_EQUAL(avgData.data1, 2);
+    APSARA_TEST_EQUAL(avgData.data2, 5);
+    APSARA_TEST_EQUAL(avgData.data3, 8);
+}
+
+void MetricCalculateUnittest::TestGetLastValue() {
+    MetricCalculate<MockData> metricCalculate;
+    MockData mockData1 = {1, 6, 8};
+    metricCalculate.AddValue(mockData1);
+    MockData mockData2 = {2, 4, 9};
+    metricCalculate.AddValue(mockData2);
+    MockData lastData;
+    metricCalculate.GetLastValue(lastData);
+    APSARA_TEST_EQUAL(lastData.data1, 2);
+    APSARA_TEST_EQUAL(lastData.data2, 4);
+    APSARA_TEST_EQUAL(lastData.data3, 9);
+}
+
+void MetricCalculateUnittest::TestCount() {
+    MetricCalculate<MockData> metricCalculate;
path: core/unittest/host_monitor/MetricCalculateUnittest.cpp
user: login: WRPStephanie
body: > 清理再计算的场景补充下

已补充
created_at: 2025-05-14T07:24:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,213 @@
+/*
+ * Copyright 2025 iLogtail Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CPUCollector.h"
+#include "MetricCalculate.h"
+#include "unittest/Unittest.h"
+
+using namespace std;
+
+namespace logtail {
+struct MockData {
+    double data1;
+    double data2;
+    double data3;
+    // Define the field descriptors
+    static inline const FieldName<MockData> mockDataFields[] = {
+        FIELD_ENTRY(MockData, data1),
+        FIELD_ENTRY(MockData, data2),
+        FIELD_ENTRY(MockData, data3),
+    };
+
+    // Define the enumerate function for your metric type
+    static void enumerate(const std::function<void(const FieldName<MockData, double>&)>& callback) {
+        for (const auto& field : mockDataFields) {
+            callback(field);
+        }
+    }
+};
+
+const FieldName<MockData> mockDataMeta[] = {
path: core/unittest/host_monitor/MetricCalculateUnittest.cpp
user: login: yyuuttaaoo
body: 这些代码从43到55是重复的吗？用上面定义的呀
created_at: 2025-05-16T05:32:43Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,213 @@
+/*
+ * Copyright 2025 iLogtail Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CPUCollector.h"
+#include "MetricCalculate.h"
+#include "unittest/Unittest.h"
+
+using namespace std;
+
+namespace logtail {
+struct MockData {
+    double data1;
+    double data2;
+    double data3;
+    // Define the field descriptors
+    static inline const FieldName<MockData> mockDataFields[] = {
+        FIELD_ENTRY(MockData, data1),
+        FIELD_ENTRY(MockData, data2),
+        FIELD_ENTRY(MockData, data3),
+    };
+
+    // Define the enumerate function for your metric type
+    static void enumerate(const std::function<void(const FieldName<MockData, double>&)>& callback) {
+        for (const auto& field : mockDataFields) {
+            callback(field);
+        }
+    }
+};
+
+const FieldName<MockData> mockDataMeta[] = {
path: core/unittest/host_monitor/MetricCalculateUnittest.cpp
user: login: WRPStephanie
body: > 这些代码从43到55是重复的吗？用上面定义的呀

已删除
created_at: 2025-05-16T06:52:11Z
author_association: CONTRIBUTOR

PR comments:
user: login: CLAassistant
created_at: 2025-05-09T03:12:08Z
body: [![CLA assistant check](https://cla-assistant.io/pull/badge/signed)](https://cla-assistant.io/alibaba/loongcollector?pullRequest=2205) <br/>All committers have signed the CLA.
author_association: NONE
user: login: yyuuttaaoo
created_at: 2025-05-14T08:39:23Z
body: # 这些代码比较晦涩难懂，我希望能简化一下，建议看完

## Understanding the Current Code

The code appears to be a templated class MetricCalculate that calculates aggregate statistics (max, min, avg) for a set of metrics. The metrics are stored in a class TMetric, and each metric has fields of type TField (which defaults to double).

There's a helper class FieldName that's used to access fields in the TMetric class by name.

### The FieldName Class

The FieldName class is essentially a wrapper that allows accessing fields of a class by name. It has:

*   A string name to identify the field
    
*   A function \_fnGet to get a reference to the field in a class instance
    

### The Macros

There are a few macros to simplify creating FieldName instances:

*   FIELD\_NAME\_INITIALIZER creates a FieldName with a given name and field accessor.
    
*   FIELD\_NAME\_ENTRY wraps FIELD\_NAME\_INITIALIZER in curly braces.
    
*   FIELD\_ENTRY is for when the field name is the same as the name to be used, just using the string version of the field name.
    

### The MetricCalculate Class

This class is used to calculate statistics on a set of metrics. It has:

*   Methods to add values and calculate statistics (max, min, avg, last)
    
*   Internal storage for the current max, min, total, and last values
    
*   A count of how many values have been added
    

### The enumerate Function

There seems to be an enumerate function that's used to iterate over the fields of TMetric. This function is called in AddValue, GetAvgValue, and potentially other places, but it's not defined in the provided code. From the unittest, I can see that enumerate is defined to iterate through an array of FieldName objects for the specific type (MockData in the test).

## What's Wrong with the Current Code?

1.  **Complexity**: The code is using template metaprogramming and function pointers, which makes it harder to understand.
    
2.  **Dependencies**: The enumerate function isn't defined in the main code, making it unclear how the fields are iterated.
    
3.  **Usage**: The macros and the way fields are accessed make the code less intuitive.
    

## C++17Simplifications

Here are some C++17 features that could help simplify this code:

1.  **Structured Bindings**: Can simplify iterating over key-value pairs.
    
2.  **std::optional**: Can be used to better handle the "no value" case instead of boolean returns.
    
3.  **Fold Expressions**: Can simplify operations across a parameter pack, which could be useful for field operations.
    
4.  **std::variant**: Could be used instead of function pointers for type-safe field access.
    
5.  **if constexpr**: Can help with compile-time branching.
    
6.  **auto deduction for non-type template parameters**: Can make templates more flexible.
    
7.  **Inline variables**: Simplify the definition of static constants.
    

# Simplified MetricCalculate with C++17 Features

I'll provide a more modern implementation of the MetricCalculate class using C++17 features that retains the core functionality while making the code more readable.

```cpp
cppCopy#include <algorithm>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>

namespace logtail {

// A field descriptor for accessing members of a metric class
template <typename TClass, typename TField = double>
class FieldName {
public:
    std::string_view name;
    TField TClass::* ptr;  // Member pointer for direct field access

    constexpr FieldName(std::string_view n, TField TClass::* p) 
        : name(n), ptr(p) {}

    // Access field value in an object
    TField& value(TClass& obj) const {
        return obj.*ptr;
    }
    const TField& value(const TClass& obj) const {
        return obj.*ptr;
    }
};

// Simplified macro for field definition
#define FIELD_ENTRY(CLASS, F) FieldName<CLASS>(#F, &CLASS::F)

// MetricCalculate class using std::optional for cleaner null state handling
template <typename TMetric, typename TField = double>
class MetricCalculate {
public:
    using FieldMeta = FieldName<TMetric, TField>;
    void Reset() {
        mCount = 0;
        mMax.reset();
        mMin.reset();
        mTotal.reset();
        mLast.reset();
    }

    void AddValue(const TMetric& v) {
        mCount++;
        if (mCount == 1) {
            // First value - initialize all stats
            mMax.emplace(v);
            mMin.emplace(v);
            mTotal.emplace(v);
            mLast.emplace(v);
        } else {
            // Update existing stats
            enumerate([&](const auto& field) {
                const TField& value = field.value(v);
                field.value(*mMax) = std::max(field.value(*mMax), value);
                field.value(*mMin) = std::min(field.value(*mMin), value);
                field.value(*mTotal) += value;
            });mLast.emplace(v);
        }
    }

    bool GetMaxValue(TMetric& dst) const {
        if (!mMax.has_value()) return false;
        dst = *mMax;
        return true;
    }

    bool GetMinValue(TMetric& dst) const {
        if (!mMin.has_value()) return false;
        dst = *mMin;
        return true;
    }

    bool GetAvgValue(TMetric& dst) const {
        if (!mTotal.has_value()) return false;dst = *mTotal;
        if (mCount > 1) {
            enumerate([&](const auto& field) {
                field.value(dst) /= mCount;
            });}
        return true;
    }

    bool GetLastValue(TMetric& dst) const {
        if (!mLast.has_value()) return false;
        dst = *mLast;
        return true;
    }

    std::shared_ptr<TMetric> GetLastValue() const {
        if (!mLast.has_value()) return nullptr;
        return std::make_shared<TMetric>(*mLast);
    }

    bool Stat(TMetric& max, TMetric& min, TMetric& avg, TMetric* last = nullptr) {
        bool success = GetMaxValue(max) && GetMinValue(min) && GetAvgValue(avg);
        if (success && last) {
            GetLastValue(*last);
        }
        return success;
    }

    size_t Count() const {
        return mCount;
    }

private:
    size_t mCount = 0;
    std::optional<TMetric> mMax;
    std::optional<TMetric> mMin;
    std::optional<TMetric> mTotal;
    std::optional<TMetric> mLast;
};

} // namespace logtail
```

## Example of how to use this class:

```cpp
cppCopy// Define your metric structure
struct CPUMetric {double userTime;
    double systemTime;
    double idleTime;
};

// Define the field descriptors
const FieldName<CPUMetric> cpuMetricFields[] = {
    FIELD_ENTRY(CPUMetric, userTime),
    FIELD_ENTRY(CPUMetric, systemTime),
    FIELD_ENTRY(CPUMetric, idleTime)
};

// Define the enumerate function for your metric type
void enumerate(const std::function<void(const FieldName<CPUMetric>&)>& callback) {
    for (const auto& field : cpuMetricFields) {
        callback(field);}
}

// Usage example
void processCPUMetrics(const std::vector<CPUMetric>& metrics) {
    MetricCalculate<CPUMetric> calculator;
    for (const auto& metric : metrics) {
        calculator.AddValue(metric);
    }
    
    CPUMetric max, min, avg, last;
    if (calculator.Stat(max, min, avg, &last)) {
        // Process the statistics...
    }
}
```

## Key improvements:

1.  Used **member pointers** (TField TClass::\*) instead of function objects for simpler and more direct field access
    
2.  Used **std::optional** for cleaner handling of uninitialized states
    
3.  Used **std::string\_view** for more efficient string handling (C++17)
    
4.  Simplified the macros to be more intuitive
    
5.  Added clearer method semantics with proper parameter names
    

This implementation maintains the same functionality while being more readable and using modern C++17 features.
author_association: COLLABORATOR
user: login: yyuuttaaoo
created_at: 2025-05-14T09:07:44Z
body: # 将字段定义集成到指标类中的实现

理解您的需求了，您希望将字段描述和enumerate函数移到指标结构体内部，这样可以一次定义，多处使用，避免为每个计算器重复定义字段。这是一个更优雅的设计。

下面是一个改进的实现，将字段描述和枚举功能集成到指标类中：

```cpp
cppCopy#include <algorithm>
#include <functional>
#include <memory>
#include <optional>
#include<string>
#include <string_view>
#include <array>

namespace logtail {

// 字段描述类
template <typename TClass, typename TField = double>
class FieldName {
public:
    std::string_view name;
    TField TClass::* ptr;  // 成员指针，直接访问字段

    constexpr FieldName(std::string_view n, TField TClass::* p)
        : name(n), ptr(p) {}

    // 访问对象中的字段值
    TField& value(TClass& obj) const { return obj.*ptr; }
    const TField& value(const TClass& obj) const { return obj.*ptr; }
};

//字段定义宏
#define FIELD_ENTRY(CLASS, FIELD)FieldName<CLASS>(#FIELD, &CLASS::FIELD)

// MetricCalculate类- 使用指标类自身的enumerate函数
template <typename TMetric, typename TField = double>
class MetricCalculate {
public:
    using FieldMeta = FieldName<TMetric, TField>;
    void Reset() {
        mCount = 0;
        mMax.reset();
        mMin.reset();
        mTotal.reset();mLast.reset();}

    void AddValue(const TMetric& v) {
        mCount++;
        if (mCount == 1) {
            // 初始化所有统计
            mMax.emplace(v);
            mMin.emplace(v);mTotal.emplace(v);
            mLast.emplace(v);
        } else {
            // 使用指标类中定义的enumerate函数
            TMetric::enumerate([&](const FieldMeta& field) {
                const TField& metricValue = field.value(v);
                field.value(*mMax) = std::max(field.value(*mMax), metricValue);
                field.value(*mMin) = std::min(field.value(*mMin), metricValue);field.value(*total) += metricValue;
            });
            mLast.emplace(v);
        }
    }

    bool GetMaxValue(TMetric& dst) const {
        if (!mMax.has_value()) return false;dst = *mMax;
        return true;
    }

    bool GetMinValue(TMetric& dst) const {
        if (!mMin.has_value()) return false;
        dst = *mMin;
        return true;
    }

    bool GetAvgValue(TMetric& dst) const {
        if (!mTotal.has_value()) return false;
        dst = *mTotal;
        if (mCount > 1) {
            TMetric::enumerate([&](constFieldMeta& field) {
                field.value(dst) /= mCount;
            });
        }
        return true;
    }

    bool GetLastValue(TMetric& dst) const {
        if (!mLast.has_value()) return false;
        dst = *mLast;
        return true;
    }

    std::shared_ptr<TMetric> GetLastValue() const {
        if (!mLast.has_value()) return nullptr;return std::make_shared<TMetric>(*mLast);
    }

    bool Stat(TMetric& max, TMetric& min, TMetric& avg, TMetric* last = nullptr) {
        bool success = GetMaxValue(max) && GetMinValue(min) && GetAvgValue(avg);
        if (success && last) {
            success = GetLastValue(*last);
        }
        return success;}

    size_t Count() const { return mCount; }

private:
    size_t mCount = 0;
    std::optional<TMetric> mMax;
    std::optional<TMetric> mMin;
    std::optional<TMetric> mTotal;
    std::optional<TMetric> mLast;
};

} // namespace logtail
```

## 指标类定义示例

```cpp
cppCopy// 定义CPU指标结构体
struct CPUMetric {
    double userTime;
    double systemTime;
    double idleTime;

    // 在类内部定义静态字段描述数组
    static inline const std::array fields = {
        FIELD_ENTRY(CPUMetric, userTime),
        FIELD_ENTRY(CPUMetric, systemTime),
        FIELD_ENTRY(CPUMetric, idleTime)
    };

    // 在类内部定义静态enumerate函数static void enumerate(const std::function<void(const FieldName<CPUMetric>&)>& callback) {
        for (const auto& field : fields) {
            callback(field);
        }
    }
};
```

## 使用示例

```cpp
cppCopy// 创建多个计算器，它们共享相同的字段定义
logtail::MetricCalculate<CPUMetric> totalCalculator;
logtail::MetricCalculate<CPUMetric> hourlyCalculator;

// 使用计算器
void processCPUMetrics(const std::vector<CPUMetric>& metrics) {
    totalCalculator.Reset();
    for (const auto& metric : metrics) {
        totalCalculator.AddValue(metric);
    }
    
    CPUMetric max, min, avg;
    if (totalCalculator.Stat(max, min, avg)) {
        // 处理统计结果...
    }
}
```

## 优势

1.  **代码集中** - 字段定义和枚举逻辑直接集成在指标类内部
    
2.  **减少重复** - 字段列表只需要定义一次，所有计算器实例共享
    
3.  **自包含** - 每个指标类包含其全部逻辑，符合面向对象设计
    
4.  **高效** - 使用C++17的inline变量避免多次定义的问题
    
5.  **简洁** - 不需要为每个指标类型单独定义外部函数
    

使用这种方法，您可以轻松创建多个不同的指标计算器，它们都将使用同一个指标类型中定义的字段列表，从而减少代码重复和错误可能。
author_association: COLLABORATOR
user: login: WRPStephanie
created_at: 2025-05-14T09:19:15Z
body: > # 将字段定义集成到指标类中的实现
> 理解您的需求了，您希望将字段描述和enumerate函数移到指标结构体内部，这样可以一次定义，多处使用，避免为每个计算器重复定义字段。这是一个更优雅的设计。
> 
> 下面是一个改进的实现，将字段描述和枚举功能集成到指标类中：
> 
> ```c++
> cppCopy#include <algorithm>
> #include <functional>
> #include <memory>
> #include <optional>
> #include<string>
> #include <string_view>
> #include <array>
> 
> namespace logtail {
> 
> // 字段描述类
> template <typename TClass, typename TField = double>
> class FieldName {
> public:
>     std::string_view name;
>     TField TClass::* ptr;  // 成员指针，直接访问字段
> 
>     constexpr FieldName(std::string_view n, TField TClass::* p)
>         : name(n), ptr(p) {}
> 
>     // 访问对象中的字段值
>     TField& value(TClass& obj) const { return obj.*ptr; }
>     const TField& value(const TClass& obj) const { return obj.*ptr; }
> };
> 
> //字段定义宏
> #define FIELD_ENTRY(CLASS, FIELD)FieldName<CLASS>(#FIELD, &CLASS::FIELD)
> 
> // MetricCalculate类- 使用指标类自身的enumerate函数
> template <typename TMetric, typename TField = double>
> class MetricCalculate {
> public:
>     using FieldMeta = FieldName<TMetric, TField>;
>     void Reset() {
>         mCount = 0;
>         mMax.reset();
>         mMin.reset();
>         mTotal.reset();mLast.reset();}
> 
>     void AddValue(const TMetric& v) {
>         mCount++;
>         if (mCount == 1) {
>             // 初始化所有统计
>             mMax.emplace(v);
>             mMin.emplace(v);mTotal.emplace(v);
>             mLast.emplace(v);
>         } else {
>             // 使用指标类中定义的enumerate函数
>             TMetric::enumerate([&](const FieldMeta& field) {
>                 const TField& metricValue = field.value(v);
>                 field.value(*mMax) = std::max(field.value(*mMax), metricValue);
>                 field.value(*mMin) = std::min(field.value(*mMin), metricValue);field.value(*total) += metricValue;
>             });
>             mLast.emplace(v);
>         }
>     }
> 
>     bool GetMaxValue(TMetric& dst) const {
>         if (!mMax.has_value()) return false;dst = *mMax;
>         return true;
>     }
> 
>     bool GetMinValue(TMetric& dst) const {
>         if (!mMin.has_value()) return false;
>         dst = *mMin;
>         return true;
>     }
> 
>     bool GetAvgValue(TMetric& dst) const {
>         if (!mTotal.has_value()) return false;
>         dst = *mTotal;
>         if (mCount > 1) {
>             TMetric::enumerate([&](constFieldMeta& field) {
>                 field.value(dst) /= mCount;
>             });
>         }
>         return true;
>     }
> 
>     bool GetLastValue(TMetric& dst) const {
>         if (!mLast.has_value()) return false;
>         dst = *mLast;
>         return true;
>     }
> 
>     std::shared_ptr<TMetric> GetLastValue() const {
>         if (!mLast.has_value()) return nullptr;return std::make_shared<TMetric>(*mLast);
>     }
> 
>     bool Stat(TMetric& max, TMetric& min, TMetric& avg, TMetric* last = nullptr) {
>         bool success = GetMaxValue(max) && GetMinValue(min) && GetAvgValue(avg);
>         if (success && last) {
>             success = GetLastValue(*last);
>         }
>         return success;}
> 
>     size_t Count() const { return mCount; }
> 
> private:
>     size_t mCount = 0;
>     std::optional<TMetric> mMax;
>     std::optional<TMetric> mMin;
>     std::optional<TMetric> mTotal;
>     std::optional<TMetric> mLast;
> };
> 
> } // namespace logtail
> ```
> 
> ## 指标类定义示例
> ```c++
> cppCopy// 定义CPU指标结构体
> struct CPUMetric {
>     double userTime;
>     double systemTime;
>     double idleTime;
> 
>     // 在类内部定义静态字段描述数组
>     static inline const std::array fields = {
>         FIELD_ENTRY(CPUMetric, userTime),
>         FIELD_ENTRY(CPUMetric, systemTime),
>         FIELD_ENTRY(CPUMetric, idleTime)
>     };
> 
>     // 在类内部定义静态enumerate函数static void enumerate(const std::function<void(const FieldName<CPUMetric>&)>& callback) {
>         for (const auto& field : fields) {
>             callback(field);
>         }
>     }
> };
> ```
> 
> ## 使用示例
> ```c++
> cppCopy// 创建多个计算器，它们共享相同的字段定义
> logtail::MetricCalculate<CPUMetric> totalCalculator;
> logtail::MetricCalculate<CPUMetric> hourlyCalculator;
> 
> // 使用计算器
> void processCPUMetrics(const std::vector<CPUMetric>& metrics) {
>     totalCalculator.Reset();
>     for (const auto& metric : metrics) {
>         totalCalculator.AddValue(metric);
>     }
>     
>     CPUMetric max, min, avg;
>     if (totalCalculator.Stat(max, min, avg)) {
>         // 处理统计结果...
>     }
> }
> ```
> 
> ## 优势
> 1. **代码集中** - 字段定义和枚举逻辑直接集成在指标类内部
> 2. **减少重复** - 字段列表只需要定义一次，所有计算器实例共享
> 3. **自包含** - 每个指标类包含其全部逻辑，符合面向对象设计
> 4. **高效** - 使用C++17的inline变量避免多次定义的问题
> 5. **简洁** - 不需要为每个指标类型单独定义外部函数
> 
> 使用这种方法，您可以轻松创建多个不同的指标计算器，它们都将使用同一个指标类型中定义的字段列表，从而减少代码重复和错误可能。

好的，我再看下
author_association: CONTRIBUTOR
user: login: WRPStephanie
created_at: 2025-05-14T12:00:26Z
body: 已修改，麻烦再看下 @yyuuttaaoo 
author_association: CONTRIBUTOR
user: login: yyuuttaaoo
created_at: 2025-05-15T08:02:33Z
body: > 已修改，麻烦再看下 @yyuuttaaoo

可以将字段定义集成到指标类中实现吗？就是struct，field_desc和enumerate定一个在一个类里面，内聚，不要拆成3个看似不相关的。
上面代码的例子可以吗？
author_association: COLLABORATOR
user: login: WRPStephanie
created_at: 2025-05-15T09:08:39Z
body: > > 已修改，麻烦再看下 @yyuuttaaoo
> 
> 可以将字段定义集成到指标类中实现吗？就是struct，field_desc和enumerate定一个在一个类里面，内聚，不要拆成3个看似不相关的。 上面代码的例子可以吗？

可以的，已修改
author_association: CONTRIBUTOR