diff --git a/lib/perfdata/elasticsearchwriter.cpp b/lib/perfdata/elasticsearchwriter.cpp index 9fb2aa90f..eaa19da59 100644 --- a/lib/perfdata/elasticsearchwriter.cpp +++ b/lib/perfdata/elasticsearchwriter.cpp @@ -5,6 +5,7 @@ #include "remote/url.hpp" #include "icinga/compatutility.hpp" #include "icinga/service.hpp" +#include "icinga/macroprocessor.hpp" #include "icinga/checkcommand.hpp" #include "base/application.hpp" #include "base/defer.hpp" @@ -131,6 +132,33 @@ void ElasticsearchWriter::Pause() ObjectImpl::Pause(); } +void ElasticsearchWriter::AddTemplateTags(const Dictionary::Ptr& fields, const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) +{ + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + Dictionary::Ptr tmpl = service ? GetServiceTagsTemplate() : GetHostTagsTemplate(); + + if (tmpl) { + MacroProcessor::ResolverList resolvers; + resolvers.emplace_back("host", host); + if (service) { + resolvers.emplace_back("service", service); + } + + ObjectLock olock(tmpl); + for (const Dictionary::Pair& pair : tmpl) { + String missingMacro; + Value value = MacroProcessor::ResolveMacros(pair.second, resolvers, cr, &missingMacro); + + if (missingMacro.IsEmpty()) { + fields->Set(pair.first, value); + } + } + } +} + void ElasticsearchWriter::AddCheckResult(const Dictionary::Ptr& fields, const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) { String prefix = "check_result."; @@ -257,6 +285,8 @@ void ElasticsearchWriter::InternalCheckResultHandler(const Checkable::Ptr& check ts = cr->GetExecutionEnd(); } + AddTemplateTags(fields, checkable, cr); + Enqueue(checkable, "checkresult", fields, ts); } @@ -307,6 +337,8 @@ void ElasticsearchWriter::StateChangeHandlerInternal(const Checkable::Ptr& check ts = cr->GetExecutionEnd(); } + AddTemplateTags(fields, checkable, cr); + Enqueue(checkable, "statechange", fields, ts); } @@ -377,6 +409,8 @@ void ElasticsearchWriter::NotificationSentToAllUsersHandlerInternal(const Notifi ts = cr->GetExecutionEnd(); } + AddTemplateTags(fields, checkable, cr); + Enqueue(checkable, "notification", fields, ts); } @@ -683,3 +717,49 @@ String ElasticsearchWriter::FormatTimestamp(double ts) return Utility::FormatDateTime("%Y-%m-%dT%H:%M:%S", ts) + "." + Convert::ToString(milliSeconds) + Utility::FormatDateTime("%z", ts); } + +void ElasticsearchWriter::ValidateHostTagsTemplate(const Lazy& lvalue, const ValidationUtils& utils) +{ + ObjectImpl::ValidateHostTagsTemplate(lvalue, utils); + + Dictionary::Ptr tags = lvalue(); + if (tags) { + ObjectLock olock(tags); + for (const Dictionary::Pair& pair : tags) { + if (pair.second.IsObjectType()) { + Array::Ptr arrObject = pair.second; + ObjectLock arrLock(arrObject); + for (const Value& arrValue : arrObject) { + if (!MacroProcessor::ValidateMacroString(arrValue)) { + BOOST_THROW_EXCEPTION(ValidationError(this, { "host_tags_template", pair.first }, "Closing $ not found in macro format string '" + arrValue + "'.")); + } + } + } else if (!MacroProcessor::ValidateMacroString(pair.second)) { + BOOST_THROW_EXCEPTION(ValidationError(this, { "host_tags_template", pair.first }, "Closing $ not found in macro format string '" + pair.second + "'.")); + } + } + } +} + +void ElasticsearchWriter::ValidateServiceTagsTemplate(const Lazy& lvalue, const ValidationUtils& utils) +{ + ObjectImpl::ValidateServiceTagsTemplate(lvalue, utils); + + Dictionary::Ptr tags = lvalue(); + if (tags) { + ObjectLock olock(tags); + for (const Dictionary::Pair& pair : tags) { + if (pair.second.IsObjectType()) { + Array::Ptr arrObject = pair.second; + ObjectLock arrLock(arrObject); + for (const Value& arrValue : arrObject) { + if (!MacroProcessor::ValidateMacroString(arrValue)) { + BOOST_THROW_EXCEPTION(ValidationError(this, { "service_tags_template", pair.first }, "Closing $ not found in macro format string '" + arrValue + "'.")); + } + } + } else if (!MacroProcessor::ValidateMacroString(pair.second)) { + BOOST_THROW_EXCEPTION(ValidationError(this, { "service_tags_template", pair.first }, "Closing $ not found in macro format string '" + pair.second + "'.")); + } + } + } +} diff --git a/lib/perfdata/elasticsearchwriter.hpp b/lib/perfdata/elasticsearchwriter.hpp index a988094d8..9ecd0fd76 100644 --- a/lib/perfdata/elasticsearchwriter.hpp +++ b/lib/perfdata/elasticsearchwriter.hpp @@ -23,6 +23,9 @@ public: static String FormatTimestamp(double ts); + void ValidateHostTagsTemplate(const Lazy &lvalue, const ValidationUtils &utils) override; + void ValidateServiceTagsTemplate(const Lazy &lvalue, const ValidationUtils &utils) override; + protected: void OnConfigLoaded() override; void Resume() override; @@ -37,6 +40,7 @@ private: std::mutex m_DataBufferMutex; void AddCheckResult(const Dictionary::Ptr& fields, const Checkable::Ptr& checkable, const CheckResult::Ptr& cr); + void AddTemplateTags(const Dictionary::Ptr& fields, const Checkable::Ptr& checkable, const CheckResult::Ptr& cr); void StateChangeHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, StateType type); void StateChangeHandlerInternal(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, StateType type); diff --git a/lib/perfdata/elasticsearchwriter.ti b/lib/perfdata/elasticsearchwriter.ti index e3b8e27f5..ed9b24fb5 100644 --- a/lib/perfdata/elasticsearchwriter.ti +++ b/lib/perfdata/elasticsearchwriter.ti @@ -29,6 +29,9 @@ class ElasticsearchWriter : ConfigObject [config] bool enable_tls { default {{{ return false; }}} }; + + [config] Dictionary::Ptr host_tags_template; + [config] Dictionary::Ptr service_tags_template; [config] bool insecure_noverify { default {{{ return false; }}} }; @@ -47,4 +50,19 @@ class ElasticsearchWriter : ConfigObject }; }; +validator ElasticsearchWriter { + Dictionary host_tags_template { + String "*"; + Array "*" { + String "*"; + }; + }; + Dictionary service_tags_template { + String "*"; + Array "*" { + String "*"; + }; + }; +}; + }