diff --git a/lib/base/configobject.cpp b/lib/base/configobject.cpp index 4317771d1f8..337d4dedae9 100644 --- a/lib/base/configobject.cpp +++ b/lib/base/configobject.cpp @@ -419,6 +419,30 @@ void ConfigObject::OnAllConfigLoaded() if (!zoneName.IsEmpty()) m_Zone = ctype->GetObject(zoneName); + + std::vector toDo {this}; + + do { + auto current (toDo.back()); + + toDo.pop_back(); + + if (m_AllParentsAffectingLogging.Data.emplace(current.get()).second) { + current->GetParentsAffectingLogging(toDo); + } + } while (!toDo.empty()); + + m_AllParentsAffectingLogging.Frozen.store(true); +} + +const std::set& ConfigObject::GetAllParentsAffectingLogging() const +{ + if (m_AllParentsAffectingLogging.Frozen.load(std::memory_order_relaxed)) { + return m_AllParentsAffectingLogging.Data; + } + + static const std::set fallback; + return fallback; } void ConfigObject::CreateChildObjects(const Type::Ptr& childType) diff --git a/lib/base/configobject.hpp b/lib/base/configobject.hpp index 5596363703c..bc68a0a3b92 100644 --- a/lib/base/configobject.hpp +++ b/lib/base/configobject.hpp @@ -4,11 +4,13 @@ #define CONFIGOBJECT_H #include "base/i2-base.hpp" +#include "base/atomic.hpp" #include "base/configobject-ti.hpp" #include "base/object.hpp" #include "base/type.hpp" #include "base/dictionary.hpp" #include +#include namespace icinga { @@ -60,6 +62,7 @@ class ConfigObject : public ObjectImpl virtual void OnStateLoaded(); Dictionary::Ptr GetSourceLocation() const override; + const std::set& GetAllParentsAffectingLogging() const; template static intrusive_ptr GetObject(const String& name) @@ -82,6 +85,11 @@ class ConfigObject : public ObjectImpl private: ConfigObject::Ptr m_Zone; + struct { + std::set Data; + Atomic Frozen {false}; + } m_AllParentsAffectingLogging; + static void RestoreObject(const String& message, int attributeTypes); }; diff --git a/lib/base/configobject.ti b/lib/base/configobject.ti index ea67dfa7ba8..177a6f3c00b 100644 --- a/lib/base/configobject.ti +++ b/lib/base/configobject.ti @@ -66,7 +66,7 @@ abstract class ConfigObject : ConfigObjectBase < ConfigType return shortName; }}} }; - [config, no_user_modify] name(Zone) zone (ZoneName); + [config, no_user_modify, parent_affecting_logging] name(Zone) zone (ZoneName); [config, no_user_modify] String package; [config, get_protected, no_user_modify] Array::Ptr templates; [config, no_storage, no_user_modify] Dictionary::Ptr source_location { diff --git a/lib/base/logger.cpp b/lib/base/logger.cpp index 38a2c6721b4..9cfa010e1e4 100644 --- a/lib/base/logger.cpp +++ b/lib/base/logger.cpp @@ -14,7 +14,9 @@ #endif /* _WIN32 */ #include #include +#include #include +#include using namespace icinga; @@ -62,6 +64,19 @@ void Logger::Start(bool runtimeCreated) UpdateMinLogSeverity(); } +void Logger::SetObjectFilter(const Dictionary::Ptr& value, bool suppress_events, const Value& cookie) +{ + ObjectImpl::SetObjectFilter(value, suppress_events, cookie); + UpdateCheckObjectFilterCache(); +} + +void Logger::OnAllConfigLoaded() +{ + ObjectImpl::OnAllConfigLoaded(); + m_CalledOnAllConfigLoaded.store(true); + UpdateCheckObjectFilterCache(); +} + void Logger::Stop(bool runtimeRemoved) { { @@ -74,6 +89,47 @@ void Logger::Stop(bool runtimeRemoved) ObjectImpl::Stop(runtimeRemoved); } +void Logger::ValidateObjectFilter(const Lazy& lvalue, const ValidationUtils& utils) +{ + ObjectImpl::ValidateObjectFilter(lvalue, utils); + + auto filter (lvalue()); + + if (filter) { + ObjectLock lock (filter); + + for (auto& kv : filter) { + auto type (Type::GetByName(kv.first)); + + if (!type) { + BOOST_THROW_EXCEPTION( + ValidationError(this, {"object_filter"}, "No such type: '" + kv.first + "'") + ); + } + + if (!dynamic_cast(type.get())) { + BOOST_THROW_EXCEPTION( + ValidationError(this, {"object_filter"}, "Not a config object type: '" + kv.first + "'") + ); + } + + Array::Ptr objects = kv.second; + + if (objects) { + ObjectLock lock (objects); + + for (auto& object : objects) { + if (object.GetType() != ValueString) { + BOOST_THROW_EXCEPTION( + ValidationError(this, {"object_filter", kv.first}, "Must be an array of strings.") + ); + } + } + } + } + } +} + std::set Logger::GetLoggers() { std::unique_lock lock(m_Mutex); @@ -243,17 +299,61 @@ void Logger::UpdateMinLogSeverity() m_MinLogSeverity.store(result); } -Log::Log(LogSeverity severity, String facility, const String& message) - : Log(severity, std::move(facility)) +void Logger::UpdateCheckObjectFilterCache() { - if (!m_IsNoOp) { - m_Buffer << message; + if (!m_CalledOnAllConfigLoaded.load()) { + return; + } + + auto filter (GetObjectFilter()); + + if (!filter) { + ObjectLock lock (this); + m_ObjectFilterCache.clear(); + return; + } + + std::vector allObjects; + + { + ObjectLock lock (filter); + + for (auto& kv : filter) { + auto type (Type::GetByName(kv.first)); + auto ctype (dynamic_cast(type.get())); + Array::Ptr objects = kv.second; + + if (ctype && objects) { + ObjectLock lock (objects); + + for (String name : objects) { + auto object (ctype->GetObject(name)); + + if (object) { + allObjects.emplace_back(object.get()); + } else { + Log(LogWarning, GetReflectionType()->GetName()) + << "Missing " << kv.first << " '" << name << "' in name filter of '" << GetName() << "'."; + } + } + } + } } + + std::sort(allObjects.begin(), allObjects.end()); + + ObjectLock lock (this); + + m_ObjectFilterCache.swap(allObjects); } -Log::Log(LogSeverity severity, String facility) +Log::Log(LogSeverity severity, String facility, const String& message) : m_Severity(severity), m_Facility(std::move(facility)), m_IsNoOp(severity < Logger::GetMinLogSeverity()) -{ } +{ + if (!m_IsNoOp && !message.IsEmpty()) { + m_Buffer << message; + } +} /** * Writes the message to the application's log. @@ -289,11 +389,33 @@ Log::~Log() for (const Logger::Ptr& logger : Logger::GetLoggers()) { ObjectLock llock(logger); - if (!logger->IsActive()) + if (!logger->IsActive()) { + continue; + } + + if (entry.Severity < logger->GetMinSeverity()) { continue; + } + + auto filter (logger->GetObjectFilter()); + + if (logger->GetObjectFilter()) { + if (!m_Involved) { + continue; + } + + auto& allowed (logger->GetObjectFilterCache()); + auto& indirect (m_Involved->GetAllParentsAffectingLogging()); + std::vector intersection; + + std::set_intersection(allowed.begin(), allowed.end(), indirect.begin(), indirect.end(), std::back_inserter(intersection)); + + if (intersection.empty()) { + continue; + } + } - if (entry.Severity >= logger->GetMinSeverity()) - logger->ProcessLogEntry(entry); + logger->ProcessLogEntry(entry); #ifdef I2_DEBUG /* I2_DEBUG */ /* Always flush, don't depend on the timer. Enable this for development sprints on Linux/macOS only. Windows crashes. */ diff --git a/lib/base/logger.hpp b/lib/base/logger.hpp index 10e0872ae68..027817164cc 100644 --- a/lib/base/logger.hpp +++ b/lib/base/logger.hpp @@ -5,9 +5,11 @@ #include "base/atomic.hpp" #include "base/i2-base.hpp" +#include "base/configobject.hpp" #include "base/logger-ti.hpp" #include #include +#include namespace icinga { @@ -88,14 +90,24 @@ class Logger : public ObjectImpl void SetSeverity(const String& value, bool suppress_events = false, const Value& cookie = Empty) override; void ValidateSeverity(const Lazy& lvalue, const ValidationUtils& utils) final; + void SetObjectFilter(const Dictionary::Ptr& value, bool suppress_events = false, const Value& cookie = Empty) override; + void OnAllConfigLoaded() override; + + inline const std::vector& GetObjectFilterCache() const + { + return m_ObjectFilterCache; + } protected: void Start(bool runtimeCreated) override; void Stop(bool runtimeRemoved) override; + void ValidateObjectFilter(const Lazy& lvalue, const ValidationUtils& utils) override; private: static void UpdateMinLogSeverity(); + void UpdateCheckObjectFilterCache(); + static std::mutex m_Mutex; static std::set m_Loggers; static bool m_ConsoleLogEnabled; @@ -104,6 +116,9 @@ class Logger : public ObjectImpl static LogSeverity m_ConsoleLogSeverity; static std::mutex m_UpdateMinLogSeverityMutex; static Atomic m_MinLogSeverity; + + Atomic m_CalledOnAllConfigLoaded {false}; + std::vector m_ObjectFilterCache; }; class Log @@ -113,9 +128,7 @@ class Log Log(const Log& other) = delete; Log& operator=(const Log& rhs) = delete; - Log(LogSeverity severity, String facility, const String& message); - Log(LogSeverity severity, String facility); - + Log(LogSeverity severity, String facility, const String& message = String()); ~Log(); template @@ -130,6 +143,7 @@ class Log private: LogSeverity m_Severity; String m_Facility; + ConfigObject::Ptr m_Involved; std::ostringstream m_Buffer; bool m_IsNoOp; }; diff --git a/lib/base/logger.ti b/lib/base/logger.ti index 44226cee41a..7a788d3c8c4 100644 --- a/lib/base/logger.ti +++ b/lib/base/logger.ti @@ -12,6 +12,13 @@ abstract class Logger : ConfigObject [config, virtual] String severity { default {{{ return "information"; }}} }; + [config, set_virtual] Dictionary::Ptr object_filter; +}; + +validator Logger { + Dictionary object_filter { + Array "*"; + }; }; } diff --git a/lib/icinga/comment.ti b/lib/icinga/comment.ti index b8ad6f7f252..74c2a24cef1 100644 --- a/lib/icinga/comment.ti +++ b/lib/icinga/comment.ti @@ -34,12 +34,12 @@ class Comment : ConfigObject < CommentNameComposer load_after Host; load_after Service; - [config, no_user_modify, protected, required, navigation(host)] name(Host) host_name { + [config, no_user_modify, protected, required, navigation(host), parent_affecting_logging] name(Host) host_name { navigate {{{ return Host::GetByName(GetHostName()); }}} }; - [config, no_user_modify, protected, navigation(service)] String service_name { + [config, no_user_modify, protected, navigation(service), parent_affecting_logging] String service_name { track {{{ if (!oldValue.IsEmpty()) { Service::Ptr service = Service::GetByNamePair(GetHostName(), oldValue); diff --git a/lib/icinga/dependency.ti b/lib/icinga/dependency.ti index 41de7ba23cf..7cba87d5b9e 100644 --- a/lib/icinga/dependency.ti +++ b/lib/icinga/dependency.ti @@ -23,13 +23,13 @@ class Dependency : CustomVarObject < DependencyNameComposer load_after Host; load_after Service; - [config, no_user_modify, required, navigation(child_host)] name(Host) child_host_name { + [config, no_user_modify, required, navigation(child_host), parent_affecting_logging] name(Host) child_host_name { navigate {{{ return Host::GetByName(GetChildHostName()); }}} }; - [config, no_user_modify, navigation(child_service)] String child_service_name { + [config, no_user_modify, navigation(child_service), parent_affecting_logging] String child_service_name { track {{{ if (!oldValue.IsEmpty()) { Service::Ptr service = Service::GetByNamePair(GetChildHostName(), oldValue); @@ -50,13 +50,13 @@ class Dependency : CustomVarObject < DependencyNameComposer }}} }; - [config, no_user_modify, required, navigation(parent_host)] name(Host) parent_host_name { + [config, no_user_modify, required, navigation(parent_host), parent_affecting_logging] name(Host) parent_host_name { navigate {{{ return Host::GetByName(GetParentHostName()); }}} }; - [config, no_user_modify, navigation(parent_service)] String parent_service_name { + [config, no_user_modify, navigation(parent_service), parent_affecting_logging] String parent_service_name { track {{{ if (!oldValue.IsEmpty()) { Service::Ptr service = Service::GetByNamePair(GetParentHostName(), oldValue); diff --git a/lib/icinga/downtime.cpp b/lib/icinga/downtime.cpp index 2178953f3d1..e8a619c85fc 100644 --- a/lib/icinga/downtime.cpp +++ b/lib/icinga/downtime.cpp @@ -161,6 +161,17 @@ void Downtime::Stop(bool runtimeRemoved) ObjectImpl::Stop(runtimeRemoved); } +void Downtime::GetParentsAffectingLogging(std::vector& output) const +{ + ObjectImpl::GetParentsAffectingLogging(output); + + auto object (ConfigObject::GetObject(GetConfigOwner())); + + if (object) { + output.emplace_back(std::move(object)); + } +} + void Downtime::Pause() { if (m_CleanupTimer) { diff --git a/lib/icinga/downtime.hpp b/lib/icinga/downtime.hpp index 15aa0af5d3d..fc2bbf2ba30 100644 --- a/lib/icinga/downtime.hpp +++ b/lib/icinga/downtime.hpp @@ -71,6 +71,7 @@ class Downtime final : public ObjectImpl protected: void Start(bool runtimeCreated) override; void Stop(bool runtimeRemoved) override; + void GetParentsAffectingLogging(std::vector& output) const override; void Pause() override; void Resume() override; diff --git a/lib/icinga/downtime.ti b/lib/icinga/downtime.ti index 21e97313eae..1e8690ab122 100644 --- a/lib/icinga/downtime.ti +++ b/lib/icinga/downtime.ti @@ -25,12 +25,12 @@ class Downtime : ConfigObject < DowntimeNameComposer load_after Host; load_after Service; - [config, no_user_modify, required, navigation(host)] name(Host) host_name { + [config, no_user_modify, required, navigation(host), parent_affecting_logging] name(Host) host_name { navigate {{{ return Host::GetByName(GetHostName()); }}} }; - [config, no_user_modify, navigation(service)] String service_name { + [config, no_user_modify, navigation(service), parent_affecting_logging] String service_name { track {{{ if (!oldValue.IsEmpty()) { Service::Ptr service = Service::GetByNamePair(GetHostName(), oldValue); @@ -72,7 +72,7 @@ class Downtime : ConfigObject < DowntimeNameComposer [no_storage] bool was_cancelled { get {{{ return GetRemoveTime() > 0; }}} }; - [config] String config_owner; + [config, no_user_modify] String config_owner; [config] String config_owner_hash; [config] String authoritative_zone; diff --git a/lib/icinga/host.ti b/lib/icinga/host.ti index f6624e30764..f674220fdd3 100644 --- a/lib/icinga/host.ti +++ b/lib/icinga/host.ti @@ -15,7 +15,7 @@ class Host : Checkable load_after Endpoint; load_after Zone; - [config, no_user_modify, required, signal_with_old_value] array(name(HostGroup)) groups { + [config, no_user_modify, required, signal_with_old_value, parent_affecting_logging] array(name(HostGroup)) groups { default {{{ return new Array(); }}} }; diff --git a/lib/icinga/hostgroup.ti b/lib/icinga/hostgroup.ti index b679344aabd..600240e8500 100644 --- a/lib/icinga/hostgroup.ti +++ b/lib/icinga/hostgroup.ti @@ -19,7 +19,7 @@ class HostGroup : CustomVarObject }}} }; - [config, no_user_modify] array(name(HostGroup)) groups; + [config, no_user_modify, parent_affecting_logging] array(name(HostGroup)) groups; [config] String notes; [config] String notes_url; [config] String action_url; diff --git a/lib/icinga/notification.ti b/lib/icinga/notification.ti index 48536075748..40bafd34f4f 100644 --- a/lib/icinga/notification.ti +++ b/lib/icinga/notification.ti @@ -43,12 +43,12 @@ class Notification : CustomVarObject < NotificationNameComposer [no_user_view, no_user_modify] int type_filter_real (TypeFilter); [config] array(Value) states; [no_user_view, no_user_modify] int state_filter_real (StateFilter); - [config, no_user_modify, protected, required, navigation(host)] name(Host) host_name { + [config, no_user_modify, protected, required, navigation(host), parent_affecting_logging] name(Host) host_name { navigate {{{ return Host::GetByName(GetHostName()); }}} }; - [config, protected, no_user_modify, navigation(service)] String service_name { + [config, protected, no_user_modify, navigation(service), parent_affecting_logging] String service_name { track {{{ if (!oldValue.IsEmpty()) { Service::Ptr service = Service::GetByNamePair(GetHostName(), oldValue); diff --git a/lib/icinga/scheduleddowntime.ti b/lib/icinga/scheduleddowntime.ti index 1653f27e74d..e70992d2d03 100644 --- a/lib/icinga/scheduleddowntime.ti +++ b/lib/icinga/scheduleddowntime.ti @@ -26,12 +26,12 @@ class ScheduledDowntime : CustomVarObject < ScheduledDowntimeNameComposer load_after Host; load_after Service; - [config, protected, no_user_modify, required, navigation(host)] name(Host) host_name { + [config, protected, no_user_modify, required, navigation(host), parent_affecting_logging] name(Host) host_name { navigate {{{ return Host::GetByName(GetHostName()); }}} }; - [config, protected, no_user_modify, navigation(service)] String service_name { + [config, protected, no_user_modify, navigation(service), parent_affecting_logging] String service_name { track {{{ if (!oldValue.IsEmpty()) { Service::Ptr service = Service::GetByNamePair(GetHostName(), oldValue); diff --git a/lib/icinga/service.ti b/lib/icinga/service.ti index 12c2d8c66c9..ea9989e0cde 100644 --- a/lib/icinga/service.ti +++ b/lib/icinga/service.ti @@ -27,7 +27,7 @@ class Service : Checkable < ServiceNameComposer load_after Host; load_after Zone; - [config, no_user_modify, required, signal_with_old_value] array(name(ServiceGroup)) groups { + [config, no_user_modify, required, signal_with_old_value, parent_affecting_logging] array(name(ServiceGroup)) groups { default {{{ return new Array(); }}} }; @@ -40,7 +40,7 @@ class Service : Checkable < ServiceNameComposer return displayName; }}} }; - [config, no_user_modify, required] name(Host) host_name; + [config, no_user_modify, required, parent_affecting_logging] name(Host) host_name; [no_storage, navigation] Host::Ptr host { get; navigate {{{ diff --git a/lib/icinga/servicegroup.ti b/lib/icinga/servicegroup.ti index 7daf9d419b3..edc1d35b700 100644 --- a/lib/icinga/servicegroup.ti +++ b/lib/icinga/servicegroup.ti @@ -19,7 +19,7 @@ class ServiceGroup : CustomVarObject }}} }; - [config, no_user_modify] array(name(ServiceGroup)) groups; + [config, no_user_modify, parent_affecting_logging] array(name(ServiceGroup)) groups; [config] String notes; [config] String notes_url; [config] String action_url; diff --git a/lib/icinga/user.ti b/lib/icinga/user.ti index 8b8c43a14d4..a89765bc7b0 100644 --- a/lib/icinga/user.ti +++ b/lib/icinga/user.ti @@ -20,7 +20,7 @@ class User : CustomVarObject return displayName; }}} }; - [config, no_user_modify, required, signal_with_old_value] array(name(UserGroup)) groups { + [config, no_user_modify, required, signal_with_old_value, parent_affecting_logging] array(name(UserGroup)) groups { default {{{ return new Array(); }}} }; [config, navigation] name(TimePeriod) period (PeriodRaw) { diff --git a/lib/icinga/usergroup.ti b/lib/icinga/usergroup.ti index e955c5e5ed1..8605cd74e4f 100644 --- a/lib/icinga/usergroup.ti +++ b/lib/icinga/usergroup.ti @@ -19,7 +19,7 @@ class UserGroup : CustomVarObject }}} }; - [config, no_user_modify] array(name(UserGroup)) groups; + [config, no_user_modify, parent_affecting_logging] array(name(UserGroup)) groups; }; } diff --git a/lib/remote/endpoint.cpp b/lib/remote/endpoint.cpp index e534fc17840..714606ccad7 100644 --- a/lib/remote/endpoint.cpp +++ b/lib/remote/endpoint.cpp @@ -26,6 +26,12 @@ void Endpoint::OnAllConfigLoaded() "' does not belong to a zone.", GetDebugInfo())); } +void Endpoint::GetParentsAffectingLogging(std::vector& output) const +{ + ObjectImpl::GetParentsAffectingLogging(output); + output.emplace_back(GetZone()); +} + void Endpoint::SetCachedZone(const Zone::Ptr& zone) { if (m_Zone) diff --git a/lib/remote/endpoint.hpp b/lib/remote/endpoint.hpp index d641c2c6b8f..2e121d2fb99 100644 --- a/lib/remote/endpoint.hpp +++ b/lib/remote/endpoint.hpp @@ -51,6 +51,7 @@ class Endpoint final : public ObjectImpl protected: void OnAllConfigLoaded() override; + void GetParentsAffectingLogging(std::vector& output) const override; private: mutable std::mutex m_ClientsLock; diff --git a/lib/remote/zone.ti b/lib/remote/zone.ti index 25f6a642dc2..e06d92d3bdc 100644 --- a/lib/remote/zone.ti +++ b/lib/remote/zone.ti @@ -15,7 +15,7 @@ class Zone : ConfigObject }}} }; - [config] array(name(Endpoint)) endpoints (EndpointsRaw); + [config, no_user_modify] array(name(Endpoint)) endpoints (EndpointsRaw); [config] bool global; [no_user_modify, no_storage] array(Value) all_parents { get; diff --git a/tools/mkclass/class_lexer.ll b/tools/mkclass/class_lexer.ll index 217ca492cb0..06a1291fc2b 100644 --- a/tools/mkclass/class_lexer.ll +++ b/tools/mkclass/class_lexer.ll @@ -135,6 +135,7 @@ deprecated { yylval->num = FADeprecated; return T_FIELD_ATTRIBUTE; } get_virtual { yylval->num = FAGetVirtual; return T_FIELD_ATTRIBUTE; } set_virtual { yylval->num = FASetVirtual; return T_FIELD_ATTRIBUTE; } signal_with_old_value { yylval->num = FASignalWithOldValue; return T_FIELD_ATTRIBUTE; } +parent_affecting_logging { yylval->num = FAParentAffectingLogging; return T_FIELD_ATTRIBUTE; } virtual { yylval->num = FAGetVirtual | FASetVirtual; return T_FIELD_ATTRIBUTE; } navigation { return T_NAVIGATION; } validator { return T_VALIDATOR; } diff --git a/tools/mkclass/classcompiler.cpp b/tools/mkclass/classcompiler.cpp index 3a49576b87d..3c019cedce8 100644 --- a/tools/mkclass/classcompiler.cpp +++ b/tools/mkclass/classcompiler.cpp @@ -1078,6 +1078,87 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) << field.GetFriendlyName() << "ChangedWithOldValue;" << std::endl << std::endl; } } + + /* override ConfigObject#GetParentsAffectingLogging() */ + + bool overrideGetParentsAffectingLogging = klass.Name == "ConfigObject"; + + if (!overrideGetParentsAffectingLogging) { + for (auto& field : klass.Fields) { + if (field.Attributes & FAParentAffectingLogging && (field.Type.IsName || field.Attributes & FANavigation)) { + overrideGetParentsAffectingLogging = true; + break; + } + } + } + + if (overrideGetParentsAffectingLogging) { + m_Header << "protected:" << std::endl << "\t"; + + if (klass.Name == "ConfigObject") { + m_Header << "virtual void GetParentsAffectingLogging(std::vector>& output) const;"; + } else { + m_Header << "void GetParentsAffectingLogging(std::vector& output) const override;"; + } + + m_Header << std::endl; + + m_Impl << "void ObjectImpl<" << klass.Name + << ">::GetParentsAffectingLogging(std::vector& output) const" << std::endl + << "{" << std::endl; + + std::set types; + + for (auto& field : klass.Fields) { + if (field.Attributes & FAParentAffectingLogging && field.Type.IsName && types.emplace(field.Type.TypeName).second) { + m_Impl << "\t" << "static const auto type" << field.Type.TypeName + << " (Type::GetByName(\"" << field.Type.TypeName << "\"));" << std::endl + << "\t" << "static const auto configType" << field.Type.TypeName + << " (dynamic_cast(type" << field.Type.TypeName << ".get()));" << std::endl; + } + } + + if (klass.Name != "ConfigObject") { + m_Impl << std::endl << "\t" << klass.Parent << "::GetParentsAffectingLogging(output);" << std::endl; + } + + for (auto& field : klass.Fields) { + if (field.Attributes & FAParentAffectingLogging && (field.Type.IsName || field.Attributes & FANavigation)) { + m_Impl << std::endl << "\t"; + + if (field.Type.IsName) { + if (field.Type.ArrayRank) { + m_Impl << "auto names" << field.GetFriendlyName() + << " (Get" << field.GetFriendlyName() << "());" << std::endl << std::endl + << "\t" << "if (names" << field.GetFriendlyName() << ") {" << std::endl + << "\t\t" << "ObjectLock lock (names" << field.GetFriendlyName() << ");" << std::endl << std::endl + << "\t\t" << "for (auto& name : names" << field.GetFriendlyName() << ") {" << std::endl + << "\t\t\t" << "auto object (configType" << field.Type.TypeName << "->GetObject(name));" << std::endl << std::endl + << "\t\t\t" << "if (object) {" << std::endl + << "\t\t\t\t" << "output.emplace_back(std::move(object));" << std::endl + << "\t\t\t" << "}" << std::endl + << "\t\t" << "}"; + } else { + m_Impl << "auto object" << field.GetFriendlyName() + << " (configType" << field.Type.TypeName << "->GetObject(Get" + << field.GetFriendlyName() << "()));" << std::endl << std::endl + << "\t" << "if (object" << field.GetFriendlyName() << ") {" << std::endl + << "\t\t" << "output.emplace_back(std::move(object" << field.GetFriendlyName() << "));"; + } + } else { + m_Impl << "auto object" << field.GetFriendlyName() + << " (static_pointer_cast(Navigate" + << field.GetFriendlyName() << "()));" << std::endl << std::endl + << "\t" << "if (object" << field.GetFriendlyName() << ") {" << std::endl + << "\t\t" << "output.emplace_back(std::move(object" << field.GetFriendlyName() << "));"; + } + + m_Impl << std::endl << "\t" << "}" << std::endl; + } + } + + m_Impl << "}" << std::endl << std::endl; + } } if (klass.Name == "ConfigObject") diff --git a/tools/mkclass/classcompiler.hpp b/tools/mkclass/classcompiler.hpp index 0bd789dd313..8befea0b137 100644 --- a/tools/mkclass/classcompiler.hpp +++ b/tools/mkclass/classcompiler.hpp @@ -62,6 +62,7 @@ enum FieldAttribute FASetVirtual = 16384, FAActivationPriority = 32768, FASignalWithOldValue = 65536, + FAParentAffectingLogging = 131072, }; struct FieldType