diff --git a/L2BotCore/Domain/Enums/ChatChannelEnum.h b/L2BotCore/Domain/Enums/ChatChannelEnum.h index 29beed3..605884f 100644 --- a/L2BotCore/Domain/Enums/ChatChannelEnum.h +++ b/L2BotCore/Domain/Enums/ChatChannelEnum.h @@ -19,6 +19,7 @@ namespace L2Bot::Domain::Enums announcement, partyroomCommander = 15, partyroomAll, - heroVoice + heroVoice, + log = 255 }; } \ No newline at end of file diff --git a/L2BotCore/Domain/Logger/LogChannel.h b/L2BotCore/Domain/Logger/LogChannel.h new file mode 100644 index 0000000..436bf20 --- /dev/null +++ b/L2BotCore/Domain/Logger/LogChannel.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include +#include "LogLevel.h" + +namespace L2Bot::Domain::Logger +{ + class LogChannel + { + public: + LogChannel(const std::vector levels) : m_Levels(levels) {}; + virtual ~LogChannel() = default; + + void SendToChannel(const std::wstring& logEntry) + { + DoSendToChannel(logEntry); + } + + const bool IsAppropriateLevel(const LogLevel level) const + { + return m_Levels.size() == 0 || std::find(m_Levels.begin(), m_Levels.end(), level) != m_Levels.end(); + } + + protected: + virtual void DoSendToChannel(const std::wstring& logEntry) = 0; + + const std::wstring GetCurrentDateTime() const + { + time_t rawTime = std::time(nullptr); + struct tm timeinfo; + localtime_s(&timeinfo, &rawTime); + std::wstringstream oss; + oss << "[" << std::put_time(&timeinfo, L"%Y-%m-%d %H.%M.%S") << "]"; + return oss.str(); + } + + private: + const std::vector m_Levels; + }; +} \ No newline at end of file diff --git a/L2BotCore/Domain/Logger/LogLevel.h b/L2BotCore/Domain/Logger/LogLevel.h new file mode 100644 index 0000000..109e073 --- /dev/null +++ b/L2BotCore/Domain/Logger/LogLevel.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace L2Bot::Domain::Logger +{ + enum class LogLevel : uint8_t + { + error, + warning, + info, + app + }; +} \ No newline at end of file diff --git a/L2BotCore/Domain/Logger/Logger.h b/L2BotCore/Domain/Logger/Logger.h new file mode 100644 index 0000000..ac3bf90 --- /dev/null +++ b/L2BotCore/Domain/Logger/Logger.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#include +#include "LogChannel.h" +#include "LogLevel.h" + +namespace L2Bot::Domain::Logger +{ + class Logger + { + public: + Logger(std::vector> channels) : m_Channels(std::move(channels)) {}; + virtual ~Logger() = default; + + template + void Error(const std::wformat_string format, Args... args) const + { + Log(LogLevel::error, format, args...); + } + template + void Warning(const std::wformat_string format, Args... args) const + { + Log(LogLevel::warning, format, args...); + } + template + void Info(const std::wformat_string format, Args... args) const + { + Log(LogLevel::info, format, args...); + } + template + void App(const std::wformat_string format, Args... args) const + { + Log(LogLevel::app, format, args...); + } + + private: + template + void Log(LogLevel level, const std::wformat_string format, Args... args) const + { + std::wstring prefix = L""; + if (level == LogLevel::error) { + prefix = L"[Error]: "; + } + else if (level == LogLevel::warning) { + prefix = L"[Warning]: "; + } + else if (level == LogLevel::info) { + prefix = L"[Info]: "; + } + else if (level == LogLevel::app) { + prefix = L"[App]: "; + } + + for (const auto& channel : m_Channels) { + if (channel->IsAppropriateLevel(level)) { + channel->SendToChannel(prefix + std::vformat(format.get(), std::make_wformat_args(args...))); + } + } + } + + private: + const std::vector> m_Channels; + }; +} diff --git a/L2BotCore/Domain/Services/ServiceLocator.h b/L2BotCore/Domain/Services/ServiceLocator.h index 149d8b7..6bbeb9d 100644 --- a/L2BotCore/Domain/Services/ServiceLocator.h +++ b/L2BotCore/Domain/Services/ServiceLocator.h @@ -2,6 +2,7 @@ #include #include "../Events/EventDispatcher.h" +#include "../Logger/Logger.h" namespace L2Bot::Domain::Services { @@ -21,6 +22,15 @@ namespace L2Bot::Domain::Services { m_EventDispatcher = std::move(dispatcher); } + + const std::unique_ptr& GetLogger() + { + return m_Logger; + } + void SetLogger(std::unique_ptr logger) + { + m_Logger = std::move(logger); + } private: ServiceLocator() = default; virtual ~ServiceLocator() = default; @@ -29,5 +39,6 @@ namespace L2Bot::Domain::Services ServiceLocator& operator=(const ServiceLocator&) = delete; private: std::unique_ptr m_EventDispatcher; + std::unique_ptr m_Logger; }; } \ No newline at end of file diff --git a/L2BotCore/L2BotCore.vcxproj b/L2BotCore/L2BotCore.vcxproj index a939de2..c3da13a 100644 --- a/L2BotCore/L2BotCore.vcxproj +++ b/L2BotCore/L2BotCore.vcxproj @@ -194,6 +194,9 @@ + + + diff --git a/L2BotCore/L2BotCore.vcxproj.filters b/L2BotCore/L2BotCore.vcxproj.filters index a2971ed..ea47703 100644 --- a/L2BotCore/L2BotCore.vcxproj.filters +++ b/L2BotCore/L2BotCore.vcxproj.filters @@ -225,6 +225,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + diff --git a/L2BotDll/L2BotDll.vcxproj b/L2BotDll/L2BotDll.vcxproj index bab5615..ab7da6c 100644 --- a/L2BotDll/L2BotDll.vcxproj +++ b/L2BotDll/L2BotDll.vcxproj @@ -165,6 +165,9 @@ + + + diff --git a/L2BotDll/L2BotDll.vcxproj.filters b/L2BotDll/L2BotDll.vcxproj.filters index aa19f89..cb8ae46 100644 --- a/L2BotDll/L2BotDll.vcxproj.filters +++ b/L2BotDll/L2BotDll.vcxproj.filters @@ -147,6 +147,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + diff --git a/L2BotDll/Logger/ChatLogChannel.h b/L2BotDll/Logger/ChatLogChannel.h new file mode 100644 index 0000000..df4e743 --- /dev/null +++ b/L2BotDll/Logger/ChatLogChannel.h @@ -0,0 +1,39 @@ +#pragma once + +#include "Domain/Enums/ChatChannelEnum.h" +#include "Domain/Events/ChatMessageCreatedEvent.h" +#include "Domain/Logger/LogChannel.h" +#include "Domain/Services/ServiceLocator.h" + +using namespace L2Bot::Domain; + +class ChatLogChannel : public Logger::LogChannel +{ +public: + ChatLogChannel(const Enums::ChatChannelEnum chatChannel, const std::vector levels) : + Logger::LogChannel(levels), + m_ChatChannel(chatChannel) + { + }; + virtual ~ChatLogChannel() = default; + +protected: + void DoSendToChannel(const std::wstring& logEntry) override + { + Services::ServiceLocator::GetInstance().GetEventDispatcher()->Dispatch( + Events::ChatMessageCreatedEvent + { + DTO::ChatMessageData + { + 0, + static_cast(m_ChatChannel), + L"", + GetCurrentDateTime() + logEntry + } + } + ); + } + +private: + const Enums::ChatChannelEnum m_ChatChannel; +}; \ No newline at end of file diff --git a/L2BotDll/Logger/FileLogChannel.h b/L2BotDll/Logger/FileLogChannel.h new file mode 100644 index 0000000..18e0492 --- /dev/null +++ b/L2BotDll/Logger/FileLogChannel.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include "Domain/Logger/LogChannel.h" + +using namespace L2Bot::Domain; + +class FileLogChannel : public Logger::LogChannel +{ +public: + FileLogChannel(const std::wstring& path, const std::vector levels) : + m_FileStream(path.c_str(), std::wofstream::app), + Logger::LogChannel(levels) + { + }; + virtual ~FileLogChannel() + { + m_FileStream.close(); + } + +protected: + void DoSendToChannel(const std::wstring& logEntry) override + { + m_FileStream << GetCurrentDateTime() << logEntry << std::endl; + } + +private: + std::wofstream m_FileStream; +}; \ No newline at end of file diff --git a/L2BotDll/Logger/OutputDebugLogChannel.h b/L2BotDll/Logger/OutputDebugLogChannel.h new file mode 100644 index 0000000..27003cb --- /dev/null +++ b/L2BotDll/Logger/OutputDebugLogChannel.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "Domain/Logger/LogChannel.h" + +using namespace L2Bot::Domain; + +class OutputDebugLogChannel : public Logger::LogChannel +{ +public: + OutputDebugLogChannel(const std::vector levels) : Logger::LogChannel(levels) {}; + virtual ~OutputDebugLogChannel() = default; + +protected: + void DoSendToChannel(const std::wstring& logEntry) override + { + OutputDebugStringW((GetCurrentDateTime() + logEntry).c_str()); + } +}; \ No newline at end of file