feat(logger): create more modular logger

This commit is contained in:
illyum 2024-09-20 04:53:33 -06:00
parent 1015569263
commit 51de852ab1
4 changed files with 56 additions and 29 deletions

View File

@ -9,5 +9,6 @@
#include <cstdarg> #include <cstdarg>
#include <iomanip> #include <iomanip>
#include <utility> #include <utility>
#include <fstream>
#include <sstream> #include <sstream>
#include <iostream> #include <iostream>

View File

@ -17,42 +17,65 @@ namespace ICEngine {
FATAL FATAL
}; };
class Logger { class LogSink {
private:
Logger(const char *name, std::ostream *stream) : name(name) {
if (stream) {
streams.push_back(std::unique_ptr<std::ostream>(stream));
}
}
friend class Log;
public: public:
virtual ~LogSink() = default;
virtual void Log(const std::string &message) = 0;
};
class ConsoleSink : public LogSink {
public:
void Log(const std::string &message) override {
std::cout << message << std::endl;
}
};
class FileSink : public LogSink {
public:
explicit FileSink(const std::string &filename) {
fileStream.open(filename);
}
void Log(const std::string &message) override {
if (fileStream.is_open()) {
fileStream << message << std::endl;
}
}
private:
std::ofstream fileStream;
};
class Logger {
public:
explicit Logger(const char *name) : name(name) {
}
void SetLevel(LogLevel level) { void SetLevel(LogLevel level) {
this->level = level; this->level = level;
} }
void AddStream(std::unique_ptr<std::ostream> stream) { void AddSink(std::shared_ptr<LogSink> sink) {
this->streams.push_back(std::move(stream)); sinks.push_back(std::move(sink));
} }
template<typename... Args> template<typename... Args>
void Log(LogLevel level, const char *file, int line, const char *func, const std::string &formatStr, void Log(LogLevel level, const char *file, int line, const char *func, const std::string &formatStr,
Args &&... args) { Args &&... args) {
// TODO: Create a crash handler and manage fatal messages separately (creates crashdump)
// TODO: Allow user to create their own class that inherits Fatal Handler so they can implement it themselves
if (level < this->level) return; if (level < this->level) return;
std::string formattedMessage = format(formatStr, std::forward<Args>(args)...);
std::string logMessage = formatMessage(level, file, line, func, formattedMessage);
for (const auto &stream: streams) { std::string logMessage = formatMessage(level, file, line, func,
(*stream) << logMessage << std::endl; format(formatStr, std::forward<Args>(args)...));
for (const auto &sink: sinks) {
sink->Log(logMessage);
} }
} }
private: private:
const char *name; const char *name;
std::vector<std::unique_ptr<std::ostream> > streams; std::vector<std::shared_ptr<LogSink> > sinks;
LogLevel level = DEBUG; LogLevel level = DEBUG;
static const char *levelToString(LogLevel level) { static const char *levelToString(LogLevel level) {
@ -130,17 +153,16 @@ namespace ICEngine {
} }
GetCoreLogger(); GetCoreLogger();
GetAppLogger(); GetAppLogger();
isInitialized = true; isInitialized = true;
} }
static std::shared_ptr<Logger> &GetCoreLogger() { static std::shared_ptr<Logger> &GetCoreLogger() {
static std::shared_ptr<Logger> CoreLogger(new Logger("CORE", &std::cout)); static std::shared_ptr<Logger> CoreLogger(new Logger("CORE"));
return CoreLogger; return CoreLogger;
} }
static std::shared_ptr<Logger> &GetAppLogger() { static std::shared_ptr<Logger> &GetAppLogger() {
static std::shared_ptr<Logger> AppLogger(new Logger("APP", &std::cout)); static std::shared_ptr<Logger> AppLogger(new Logger("APP"));
return AppLogger; return AppLogger;
} }

View File

@ -8,11 +8,20 @@
void ShowLogger(); void ShowLogger();
int main() { int main() {
try {
ShowLogger(); ShowLogger();
ICEngine::StartApplication(); ICEngine::StartApplication();
} catch (const std::exception& e) {
CORE_LOG_FATAL("Unhandled exception: {}", e.what());
return -1;
} catch (...) {
CORE_LOG_FATAL("Unknown fatal error occurred.");
return -1;
}
return 0; return 0;
} }
void ShowLogger() { void ShowLogger() {
// The logger needs to be initialized before you can use it. // The logger needs to be initialized before you can use it.
// This will be done by the engine, NEVER the user. // This will be done by the engine, NEVER the user.
@ -39,11 +48,8 @@ void ShowLogger() {
// You need to create a unique pointer, because the logger will // You need to create a unique pointer, because the logger will
// take ownership of the streams and clean them up when the app // take ownership of the streams and clean them up when the app
// is complete // is complete
auto coreFileStream = std::make_unique<std::ofstream>("core_logs.txt"); ICEngine::Log::GetCoreLogger()->AddSink(std::make_shared<ICEngine::FileSink>("core_logs.txt"));
auto appFileStream = std::make_unique<std::ofstream>("app_logs.txt"); ICEngine::Log::GetAppLogger()->AddSink(std::make_shared<ICEngine::FileSink>("app_logs.txt"));
ICEngine::Log::GetCoreLogger()->AddStream(std::move(coreFileStream));
ICEngine::Log::GetAppLogger()->AddStream(std::move(appFileStream));
// You can log multiple different types with formatting // You can log multiple different types with formatting
int age = 30; int age = 30;

View File

@ -5,7 +5,5 @@
#include <ICEngine.hpp> #include <ICEngine.hpp>
int main() { int main() {
Logger logger;
logger.logVersion();
return 0;
} }