#pragma once #include "dtcore/inc/dtstring.h" #include "dtcore/inc/dtdatetime.h" #pragma warning(push) #include #pragma warning(pop) namespace dt { class LoggerImpl; enum class LogLevel { Trace, Debug, Info, Success, Warning, Error, Always, Off }; class LogContext { public: DT_CORE_DLL_ENTRY LogContext() = default; DT_CORE_DLL_ENTRY LogContext(const String& key, const String& value, bool visible); DT_CORE_DLL_ENTRY const String& GetKey() const { return m_Key; } DT_CORE_DLL_ENTRY const String& GetValue() const { return m_Value; } DT_CORE_DLL_ENTRY bool IsVisible() const { return m_Visible; } private: String m_Key; String m_Value; bool m_Visible = false; }; class LogContexts { public: DT_CORE_DLL_ENTRY LogContexts(); DT_CORE_DLL_ENTRY LogContexts(const Vector& contexts, const LogContexts* parentContexts = nullptr); DT_CORE_DLL_ENTRY LogContexts(const LogContexts& rhs); DT_CORE_DLL_ENTRY LogContexts& operator=(const LogContexts& rhs); DT_CORE_DLL_ENTRY bool operator==(const LogContexts& rhs) const { return m_Snapshot == rhs.m_Snapshot; } DT_CORE_DLL_ENTRY bool TryGetValue(const String& key, String* value) const; typedef Vector::ConstIterator ConstIterator; DT_CORE_DLL_ENTRY ConstIterator Begin() const { return m_Snapshot->Begin(); } DT_CORE_DLL_ENTRY ConstIterator End() const { return m_Snapshot->End(); } DT_CORE_DLL_ENTRY ConstIterator begin() const { return m_Snapshot->Begin(); } DT_CORE_DLL_ENTRY ConstIterator end() const { return m_Snapshot->End(); } DT_CORE_DLL_ENTRY const LogContext& Back() const { return m_Snapshot->Back(); } DT_CORE_DLL_ENTRY const LogContext& operator[](SizeT index) const { return (*m_Snapshot)[index]; } DT_CORE_DLL_ENTRY SizeT Size() const { return m_Snapshot->Size(); } DT_CORE_DLL_ENTRY bool Empty() const { return m_Snapshot->Empty(); } DT_CORE_DLL_ENTRY bool HasAnyVisibleContext() const; private: std::shared_ptr> m_Snapshot; }; DT_CORE_DLL_ENTRY String& operator<<(String& buf, const LogContexts& contexts); class LogChannel { public: DT_CORE_DLL_ENTRY LogChannel(const char* name); DT_CORE_DLL_ENTRY const char* GetName() const { return m_Name; } private: const char* m_Name; }; class LogStatement { public: DT_CORE_DLL_ENTRY LogStatement(const DateTime& date, const LogChannel& channel, LogLevel level, const LogContexts& contexts, const String& message, const char* file, int line); DT_CORE_DLL_ENTRY LogStatement(const LogStatement& rhs); DT_CORE_DLL_ENTRY DateTime GetDate() const { return m_Date; } DT_CORE_DLL_ENTRY const LogChannel& GetChannel() const { return m_Channel; } DT_CORE_DLL_ENTRY LogLevel GetLevel() const { return m_Level; } DT_CORE_DLL_ENTRY const char* GetFile() const { return m_File; } DT_CORE_DLL_ENTRY int GetLine() const { return m_Line; } DT_CORE_DLL_ENTRY const LogContexts& GetContexts() const { return m_Contexts; } DT_CORE_DLL_ENTRY const String& GetLogMessage() const { return m_Message; } private: LogStatement& operator=(const LogStatement&) = delete; const DateTime m_Date; const LogChannel& m_Channel; const LogLevel m_Level; const LogContexts m_Contexts; const String m_Message; const char* m_File; const int m_Line; }; class LogAppender { public: DT_CORE_DLL_ENTRY virtual ~LogAppender() {} DT_CORE_DLL_ENTRY virtual void Write(LogStatement& msg) = 0; DT_CORE_DLL_ENTRY virtual void Flush() = 0; }; class Logger { public: DT_CORE_DLL_ENTRY Logger(); DT_CORE_DLL_ENTRY ~Logger(); DT_CORE_DLL_ENTRY static Logger& Get(); DT_CORE_DLL_ENTRY void SetLevel(LogLevel lvl); DT_CORE_DLL_ENTRY LogLevel GetLevel() const; DT_CORE_DLL_ENTRY static const char* GetLevelString(LogLevel lvl); DT_CORE_DLL_ENTRY static LogLevel GetLevelFromString(const String& level); DT_CORE_DLL_ENTRY void AddAppender(LogAppender& appender); DT_CORE_DLL_ENTRY void RemoveAppender(LogAppender& appender); DT_CORE_DLL_ENTRY void Write(const LogChannel& channel, LogLevel lvl, const String& msg, const char* file, int line); DT_CORE_DLL_ENTRY void Flush(); DT_CORE_DLL_ENTRY LogContexts GetThreadContexts() const; DT_CORE_DLL_ENTRY LogContexts GetGlobalContexts() const; public: // internal DT_CORE_DLL_ENTRY void InternalPushThreadContexts(const LogContexts& contexts); DT_CORE_DLL_ENTRY void InternalPopThreadContexts(const LogContexts& contexts); DT_CORE_DLL_ENTRY void InternalPushGlobalContexts(const LogContexts& contexts); DT_CORE_DLL_ENTRY void InternalPopGlobalContexts(const LogContexts& contexts); private: friend LoggerImpl; LoggerImpl* m_Impl; }; #define DT_LOG_IF(level) \ if (level >= dt::Logger::Get().GetLevel()) #define DT_LOG(level, channel, expr) \ DT_LOG_IF(level) \ { \ dt::String logText; \ logText << expr; \ dt::Logger::Get().Write(channel, level, logText, __FILE__, __LINE__); \ } #define DT_LOG_TRACE(channel, expr) DT_LOG(dt::LogLevel::Trace, channel, expr) #define DT_LOG_DEBUG(channel, expr) DT_LOG(dt::LogLevel::Debug, channel, expr) #define DT_LOG_INFO(channel, expr) DT_LOG(dt::LogLevel::Info, channel, expr) #define DT_LOG_SUCCESS(channel, expr) DT_LOG(dt::LogLevel::Success, channel, expr) #define DT_LOG_WARNING(channel, expr) DT_LOG(dt::LogLevel::Warning, channel, expr) #define DT_LOG_ERROR(channel, expr) DT_LOG(dt::LogLevel::Error, channel, expr) #define DT_LOG_ALWAYS(channel, expr) DT_LOG(dt::LogLevel::Always, channel, expr) #define DT_LOG_IF_TRACE() DT_LOG_IF(dt::LogLevel::Trace) #define DT_LOG_IF_DEBUG() DT_LOG_IF(dt::LogLevel::Debug) #define DT_LOG_IF_INFO() DT_LOG_IF(dt::LogLevel::Info) #define DT_LOG_IF_SUCCESS() DT_LOG_IF(dt::LogLevel::Success) #define DT_LOG_IF_WARNING() DT_LOG_IF(dt::LogLevel::Warning) #define DT_LOG_IF_ERROR() DT_LOG_IF(dt::LogLevel::Error) #define DT_LOG_IF_ALWAYS() DT_LOG_IF(dt::LogLevel::Always) class LoggerAutoThreadContexts { public: DT_CORE_DLL_ENTRY LoggerAutoThreadContexts(const LogContexts& contexts); DT_CORE_DLL_ENTRY ~LoggerAutoThreadContexts(); private: LogContexts m_Contexts; }; #define DT_LOG_AUTO_THREAD_CONTEXTS(contexts) \ dt::LoggerAutoThreadContexts DT_UNIQUE_IDENTIFIER(logCtx)(contexts); class LoggerAutoGlobalContexts { public: DT_CORE_DLL_ENTRY LoggerAutoGlobalContexts(const LogContexts& contexts); DT_CORE_DLL_ENTRY ~LoggerAutoGlobalContexts(); private: LogContexts m_Contexts; }; #define DT_LOG_AUTO_GLOBAL_CONTEXTS(contexts) \ dt::LoggerAutoGlobalContexts DT_UNIQUE_IDENTIFIER(logCtx)(contexts); static LogChannel LogChannelGeneral("General"); static LogChannel LogChannelDatabase("Database"); static LogChannel LogChannelRunner("Runner"); static LogChannel LogChannelServiceAgency("Agency"); static LogChannel LogChannelServiceLog("Log"); static LogChannel LogChannelServiceTest("Test"); static LogChannel LogChannelServiceArtefact("Artefact"); static LogChannel LogChannelServiceConnection("Connection"); static LogChannel LogChannelServiceDetection("Detection"); static LogChannel LogChannelServiceTestManagementTool("TestManagementTool"); static LogChannel LogChannelService("Service"); static LogChannel LogChannelMessage("Message"); static LogChannel LogChannelProfiling("Profiling"); static LogChannel LogChannelStateMachine("State"); static LogChannel LogChannelToken("Token"); static LogChannel LogChannelScript("Script"); static LogChannel LogChannelBloomberg("Bloomberg"); }