diff --git a/core/plugin/processor/inner/ProcessorPromRelabelMetricNative.cpp b/core/plugin/processor/inner/ProcessorPromRelabelMetricNative.cpp index 84d6aa8be1..b93fb801e5 100644 --- a/core/plugin/processor/inner/ProcessorPromRelabelMetricNative.cpp +++ b/core/plugin/processor/inner/ProcessorPromRelabelMetricNative.cpp @@ -126,6 +126,21 @@ bool ProcessorPromRelabelMetricNative::ProcessEvent(PipelineEventPtr& e, sourceEvent.DelTag(k); } + for (const auto& [k, v] : mScrapeConfigPtr->mExternalLabels) { + if (sourceEvent.HasTag(k)) { + if (!mScrapeConfigPtr->mHonorLabels) { + // metric event labels is secondary + // if confiliction, then rename it exported_ + auto key = prometheus::EXPORTED_PREFIX + k; + auto b = sourceEvent.GetSourceBuffer()->CopyString(key); + sourceEvent.SetTagNoCopy(StringView(b.data, b.size), sourceEvent.GetTag(k)); + sourceEvent.SetTagNoCopy(k, v); + } + } else { + sourceEvent.SetTagNoCopy(k, v); + } + } + // set metricEvent name sourceEvent.SetTagNoCopy(prometheus::NAME, sourceEvent.GetName()); diff --git a/core/prometheus/Constants.h b/core/prometheus/Constants.h index f0be5177ac..e663fdf207 100644 --- a/core/prometheus/Constants.h +++ b/core/prometheus/Constants.h @@ -19,6 +19,7 @@ const char* const SOURCE_LABELS = "source_labels"; const char* const SEPARATOR = "separator"; const char* const TARGET_LABEL = "target_label"; const char* const REGEX = "regex"; +const char* const MATCH_LIST = "match_list"; const char* const REPLACEMENT = "replacement"; const char* const ACTION = "action"; const char* const MODULUS = "modulus"; @@ -78,6 +79,7 @@ const char* const KEY_FILE = "key_file"; const char* const SERVER_NAME = "server_name"; const char* const HOST = "Host"; const char* const INSECURE_SKIP_VERIFY = "insecure_skip_verify"; +const char* const EXTERNAL_LABELS = "external_labels"; // scrape protocols, from https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config // text/plain, application/openmetrics-text will be used diff --git a/core/prometheus/labels/Relabel.cpp b/core/prometheus/labels/Relabel.cpp index 6a2f33b6c4..45d5944a29 100644 --- a/core/prometheus/labels/Relabel.cpp +++ b/core/prometheus/labels/Relabel.cpp @@ -18,13 +18,12 @@ #include +#include +#include +#include #include #include -#include "boost/algorithm/string.hpp" -#include "boost/algorithm/string/join.hpp" -#include "boost/regex.hpp" - #include "common/ParamExtractor.h" #include "common/StringTools.h" #include "logger/Logger.h" @@ -49,7 +48,8 @@ Action StringToAction(const string& action) { STRING_TO_ENUM_CASE(LABELDROP), STRING_TO_ENUM_CASE(LABELKEEP), STRING_TO_ENUM_CASE(LOWERCASE), - STRING_TO_ENUM_CASE(UPPERCASE)}; + STRING_TO_ENUM_CASE(UPPERCASE), + STRING_TO_ENUM_CASE(DROPMETRIC)}; auto it = sActionStrings.find(action); if (it != sActionStrings.end()) { @@ -69,7 +69,9 @@ const std::string& ActionToString(Action action) { ENUM_TO_STRING_CASE(LABELDROP), ENUM_TO_STRING_CASE(LABELKEEP), ENUM_TO_STRING_CASE(LOWERCASE), - ENUM_TO_STRING_CASE(UPPERCASE)}; + ENUM_TO_STRING_CASE(UPPERCASE), + ENUM_TO_STRING_CASE(DROPMETRIC)}; + static string sUndefined = prometheus::UNDEFINED; auto it = sActionStrings.find(action); if (it != sActionStrings.end()) { @@ -107,6 +109,26 @@ bool RelabelConfig::Init(const Json::Value& config) { if (config.isMember(prometheus::ACTION) && config[prometheus::ACTION].isString()) { string actionString = config[prometheus::ACTION].asString(); mAction = StringToAction(actionString); + } else { + LOG_ERROR(sLogger, ("no action specified", "")); + return false; + } + + if (mAction == Action::DROPMETRIC) { + mSourceLabels.emplace_back(prometheus::NAME); + if (config.isMember(prometheus::MATCH_LIST) && config[prometheus::MATCH_LIST].isArray()) { + for (const auto& item : config[prometheus::MATCH_LIST]) { + if (item.isString()) { + mMatchList.insert(item.asString()); + } else { + LOG_ERROR(sLogger, ("invalid match_list item", "")); + return false; + } + } + } else { + LOG_ERROR(sLogger, ("no match_list specified", "")); + return false; + } } if (config.isMember(prometheus::MODULUS) && config[prometheus::MODULUS].isUInt64()) { @@ -219,6 +241,12 @@ bool RelabelConfig::Process(Labels& l, vector& toDelete) const { } break; } + case Action::DROPMETRIC: { + if (mMatchList.find(val) != mMatchList.end()) { + return false; + } + break; + } default: // error LOG_ERROR(sLogger, ("relabel: unknown relabel action type", ActionToString(mAction))); diff --git a/core/prometheus/labels/Relabel.h b/core/prometheus/labels/Relabel.h index 62b222621f..edb06e9828 100644 --- a/core/prometheus/labels/Relabel.h +++ b/core/prometheus/labels/Relabel.h @@ -15,10 +15,10 @@ */ #pragma once -#include +#include -#include "boost/regex.hpp" -#include "json/json.h" +#include +#include #include "prometheus/labels/Labels.h" @@ -36,6 +36,7 @@ enum class Action { LABELKEEP, LOWERCASE, UPPERCASE, + DROPMETRIC, UNDEFINED }; @@ -65,6 +66,8 @@ class RelabelConfig { // Action is the action to be performed for the relabeling. Action mAction; + std::set mMatchList; + private: void CollectLabelsToDelete(const std::string& labelName, std::vector& toDelete) const; }; diff --git a/core/prometheus/schedulers/ScrapeConfig.cpp b/core/prometheus/schedulers/ScrapeConfig.cpp index 58d926900d..33d8a06942 100644 --- a/core/prometheus/schedulers/ScrapeConfig.cpp +++ b/core/prometheus/schedulers/ScrapeConfig.cpp @@ -183,6 +183,13 @@ bool ScrapeConfig::InitStaticConfig(const Json::Value& scrapeConfig) { return false; } } + + if (scrapeConfig.isMember(prometheus::EXTERNAL_LABELS)) { + if (!InitExternalLabels(scrapeConfig[prometheus::EXTERNAL_LABELS])) { + LOG_ERROR(sLogger, ("external labels config error", "")); + return false; + } + } return true; } @@ -397,4 +404,20 @@ bool ScrapeConfig::InitTLSConfig(const Json::Value& tlsConfig) { return true; } +bool ScrapeConfig::InitExternalLabels(const Json::Value& externalLabels) { + if (!externalLabels.isObject()) { + LOG_ERROR(sLogger, ("external_labels config error", "")); + return false; + } + for (auto& key : externalLabels.getMemberNames()) { + if (externalLabels[key].isString()) { + mExternalLabels.emplace_back(key, externalLabels[key].asString()); + } else { + LOG_ERROR(sLogger, ("external_labels config error", "")); + return false; + } + } + return true; +} + } // namespace logtail diff --git a/core/prometheus/schedulers/ScrapeConfig.h b/core/prometheus/schedulers/ScrapeConfig.h index feec495c98..e2be15da26 100644 --- a/core/prometheus/schedulers/ScrapeConfig.h +++ b/core/prometheus/schedulers/ScrapeConfig.h @@ -44,6 +44,8 @@ class ScrapeConfig { std::string mQueryString; + std::vector> mExternalLabels; + ScrapeConfig(); bool Init(const Json::Value& config); bool InitStaticConfig(const Json::Value& config); @@ -54,6 +56,7 @@ class ScrapeConfig { bool InitScrapeProtocols(const Json::Value& scrapeProtocols); void InitEnableCompression(bool enableCompression); bool InitTLSConfig(const Json::Value& tlsConfig); + bool InitExternalLabels(const Json::Value& externalLabels); #ifdef APSARA_UNIT_TEST_MAIN friend class ScrapeConfigUnittest; diff --git a/core/unittest/processor/ProcessorPromRelabelMetricNativeUnittest.cpp b/core/unittest/processor/ProcessorPromRelabelMetricNativeUnittest.cpp index ad659ac16e..06a0668aa8 100644 --- a/core/unittest/processor/ProcessorPromRelabelMetricNativeUnittest.cpp +++ b/core/unittest/processor/ProcessorPromRelabelMetricNativeUnittest.cpp @@ -110,7 +110,11 @@ void ProcessorPromRelabelMetricNativeUnittest::TestProcess() { ], "target_label": "__address__" } - ] + ], + "external_labels": { + "test_key1": "test_value1", + "test_key2": "test_value2" + } } )"; @@ -151,6 +155,10 @@ test_metric8{k1="v1", k3="v2", } 9.9410452992e+10 1715829785083 APSARA_TEST_EQUAL("test_metric6", eventGroup.GetEvents().at(5).Cast().GetName()); APSARA_TEST_EQUAL("test_metric7", eventGroup.GetEvents().at(6).Cast().GetName()); // test_metric8 is dropped by relabel config + + // check external labels + APSARA_TEST_EQUAL("test_value1", eventGroup.GetEvents().at(0).Cast().GetTag("test_key1")); + APSARA_TEST_EQUAL("test_value2", eventGroup.GetEvents().at(0).Cast().GetTag("test_key2")); } void ProcessorPromRelabelMetricNativeUnittest::TestAddAutoMetrics() { diff --git a/core/unittest/prometheus/RelabelUnittest.cpp b/core/unittest/prometheus/RelabelUnittest.cpp index 502237ef51..f6fdd2edfc 100644 --- a/core/unittest/prometheus/RelabelUnittest.cpp +++ b/core/unittest/prometheus/RelabelUnittest.cpp @@ -39,6 +39,7 @@ class RelabelConfigUnittest : public testing::Test { void TestReplace(); void TestKeep(); void TestDrop(); + void TestDropMetric(); void TestDropEqual(); void TestHashMod(); void TestLabelDrop(); @@ -200,6 +201,31 @@ void RelabelConfigUnittest::TestDrop() { APSARA_TEST_FALSE(configList.Process(result, toDelete)); } +void RelabelConfigUnittest::TestDropMetric() { + Json::Value configJson; + string configStr; + string errorMsg; + RelabelConfigList configList; + Labels labels; + labels.Set("__meta_kubernetes_pod_ip", "172.17.0.3"); + labels.Set("__meta_kubernetes_pod_label_app", "node-exporter"); + labels.Set("__name__", "node_cpu_seconds_total"); + // single relabel drop + configStr = R"( + [{ + "action": "dropmetric", + "match_list": ["test_1","node_cpu_seconds_total"] + }] + )"; + + APSARA_TEST_TRUE(ParseJsonTable(configStr, configJson, errorMsg)); + configList = RelabelConfigList(); + APSARA_TEST_TRUE(configList.Init(configJson)); + auto result = labels; + vector toDelete; + APSARA_TEST_FALSE(configList.Process(result, toDelete)); +} + void RelabelConfigUnittest::TestDropEqual() { Json::Value configJson; string configStr; @@ -495,6 +521,7 @@ UNIT_TEST_CASE(ActionConverterUnittest, TestActionToString) UNIT_TEST_CASE(RelabelConfigUnittest, TestRelabelConfig) UNIT_TEST_CASE(RelabelConfigUnittest, TestReplace) UNIT_TEST_CASE(RelabelConfigUnittest, TestDrop) +UNIT_TEST_CASE(RelabelConfigUnittest, TestDropMetric) UNIT_TEST_CASE(RelabelConfigUnittest, TestKeep) UNIT_TEST_CASE(RelabelConfigUnittest, TestHashMod) UNIT_TEST_CASE(RelabelConfigUnittest, TestLabelMap) diff --git a/core/unittest/prometheus/ScrapeConfigUnittest.cpp b/core/unittest/prometheus/ScrapeConfigUnittest.cpp index a3e74009dc..7efa40f17c 100644 --- a/core/unittest/prometheus/ScrapeConfigUnittest.cpp +++ b/core/unittest/prometheus/ScrapeConfigUnittest.cpp @@ -94,6 +94,10 @@ void ScrapeConfigUnittest::TestInit() { ] } ], + "external_labels": { + "test_key1": "test_value1", + "test_key2": "test_value2" + }, "params" : { "__param_query": [ "test_query" @@ -142,6 +146,13 @@ void ScrapeConfigUnittest::TestInit() { APSARA_TEST_EQUAL(scrapeConfig.mRelabelConfigs.mRelabelConfigs.size(), 1UL); APSARA_TEST_EQUAL(scrapeConfig.mParams["__param_query"][0], "test_query"); APSARA_TEST_EQUAL(scrapeConfig.mParams["__param_query_1"][0], "test_query_1"); + + // external labels + APSARA_TEST_EQUAL(scrapeConfig.mExternalLabels.size(), 2UL); + APSARA_TEST_EQUAL(scrapeConfig.mExternalLabels[0].first, "test_key1"); + APSARA_TEST_EQUAL(scrapeConfig.mExternalLabels[0].second, "test_value1"); + APSARA_TEST_EQUAL(scrapeConfig.mExternalLabels[1].first, "test_key2"); + APSARA_TEST_EQUAL(scrapeConfig.mExternalLabels[1].second, "test_value2"); } void ScrapeConfigUnittest::TestAuth() {