Refactor logging system: replace LogLevel with Log::Level across multiple files for improved consistency and clarity

This commit is contained in:
whaffman 2025-09-22 12:33:18 +02:00
parent 358b48e407
commit 6803524989
12 changed files with 113 additions and 143 deletions

View File

@ -1,4 +1,3 @@
#include "webserv/log/LogLevel.hpp"
#include "webserv/socket/Socket.hpp"
#include <webserv/client/Client.hpp>

4
webserv/log/Channel.cpp Normal file
View File

@ -0,0 +1,4 @@
#include <webserv/log/Channel.hpp>
Channel::Channel(Log::Level logLevel) : logLevel_(logLevel) {}

View File

@ -1,7 +1,7 @@
#pragma once
#include <webserv/log/Log.hpp>
#include <webserv/log/LogLevel.hpp>
#include <map>
#include <string>
@ -10,7 +10,7 @@ class Channel
{
public:
Channel() = default;
Channel(LogLevel logLevel);
Channel(Log::Level logLevel);
virtual ~Channel() = default;
Channel(const Channel &other) = delete;
@ -18,9 +18,9 @@ class Channel
Channel &operator=(const Channel &other) = delete;
Channel &&operator=(const Channel &&other) = delete;
virtual void log(LogLevel &logLevel, const std::string &message,
virtual void log(Log::Level &logLevel, const std::string &message,
const std::map<std::string, std::string> &context = {}) = 0;
protected:
LogLevel logLevel_{LogLevel::LOGLVL_TRACE};
Log::Level logLevel_{Log::Level::Trace};
};

View File

@ -1,3 +0,0 @@
#include <webserv/log/Channel.hpp>
Channel::Channel(LogLevel logLevel) : logLevel_(logLevel) {}

View File

@ -1,12 +1,13 @@
#include "webserv/log/LogLevel.hpp"
#include <webserv/log/FileChannel.hpp>
#include <webserv/log/Log.hpp>
#include <chrono>
#include <ios>
#include <iostream>
FileChannel::FileChannel(const std::string &filename, std::ios_base::openmode mode, LogLevel logLevel)
FileChannel::FileChannel(const std::string &filename, std::ios_base::openmode mode, Log::Level logLevel)
: Channel(logLevel), filename_(filename), fileStream_(filename, mode)
{
if (!fileStream_.is_open())
@ -23,7 +24,7 @@ FileChannel::~FileChannel()
}
}
void FileChannel::log(LogLevel &logLevel, const std::string &message, const std::map<std::string, std::string> &context)
void FileChannel::log(Log::Level &logLevel, const std::string &message, const std::map<std::string, std::string> &context)
{
if (logLevel < logLevel_)
{
@ -42,7 +43,7 @@ void FileChannel::log(LogLevel &logLevel, const std::string &message, const std:
// Format the log message
fileStream_ << "[" << std::put_time(tm, "%Y-%m-%d %H:%M:%S") << "] "
<< "[" << logLevelToString(logLevel) << "] " << message << '\n';
<< "[" << Log::logLevelToString(logLevel) << "] " << message << '\n';
// Log the context if it exists
if (!context.empty())

View File

@ -1,7 +1,7 @@
#pragma once
#include <webserv/log/Channel.hpp>
#include <webserv/log/LogLevel.hpp>
#include <fstream>
#include <map>
@ -10,7 +10,7 @@
class FileChannel : public Channel
{
public:
FileChannel(const std::string &filename, std::ios_base::openmode mode, LogLevel logLevel = LogLevel::LOGLVL_TRACE);
FileChannel(const std::string &filename, std::ios_base::openmode mode, Log::Level logLevel = Log::Level::Trace);
FileChannel(const FileChannel &other) = delete;
FileChannel(const FileChannel &&other) = delete;
@ -18,7 +18,7 @@ class FileChannel : public Channel
FileChannel &&operator=(const FileChannel &&other) = delete;
~FileChannel();
void log(LogLevel &logLevel, const std::string &message,
void log(Log::Level &logLevel, const std::string &message,
const std::map<std::string, std::string> &context = {}) override;
private:

View File

@ -13,7 +13,7 @@ Log::Log()
start_time_ = std::chrono::steady_clock::now();
}
void Log::setStdoutChannel(LogLevel logLevel)
void Log::setStdoutChannel(Log::Level logLevel)
{
Log &log = getInstance();
if (log.channels_.contains("stdout"))
@ -30,7 +30,7 @@ void Log::setStdoutChannel(LogLevel logLevel)
}
}
void Log::setFileChannel(const std::string &filename, std::ios_base::openmode mode, LogLevel logLevel)
void Log::setFileChannel(const std::string &filename, std::ios_base::openmode mode, Log::Level logLevel)
{
Log &log = getInstance();
if (log.channels_.contains("file"))
@ -53,7 +53,7 @@ Log &Log::getInstance()
return instance;
}
void Log::log(LogLevel level, const std::string &message, const std::map<std::string, std::string> &context,
void Log::log(Log::Level level, const std::string &message, const std::map<std::string, std::string> &context,
const std::source_location &location)
{
for (auto &it : channels_)
@ -81,35 +81,76 @@ int Log::getElapsedTime()
void Log::trace(const std::string &message, const std::map<std::string, std::string> &context,
const std::source_location &location)
{
getInstance().log(LogLevel::LOGLVL_TRACE, message, context, location);
getInstance().log(Log::Level::Trace, message, context, location);
}
void Log::debug(const std::string &message, const std::map<std::string, std::string> &context,
const std::source_location &location)
{
getInstance().log(LogLevel::LOGLVL_DEBUG, message, context, location);
getInstance().log(Log::Level::Debug, message, context, location);
}
void Log::info(const std::string &message, const std::map<std::string, std::string> &context,
const std::source_location &location)
{
getInstance().log(LogLevel::LOGLVL_INFO, message, context, location);
getInstance().log(Log::Level::Info, message, context, location);
}
void Log::warning(const std::string &message, const std::map<std::string, std::string> &context,
const std::source_location &location)
{
getInstance().log(LogLevel::LOGLVL_WARN, message, context, location);
getInstance().log(Log::Level::Warn, message, context, location);
}
void Log::error(const std::string &message, const std::map<std::string, std::string> &context,
const std::source_location &location)
{
getInstance().log(LogLevel::LOGLVL_ERROR, message, context, location);
getInstance().log(Log::Level::Error, message, context, location);
}
void Log::fatal(const std::string &message, const std::map<std::string, std::string> &context,
const std::source_location &location)
{
getInstance().log(LogLevel::LOGLVL_FATAL, message, context, location);
getInstance().log(Log::Level::Fatal, message, context, location);
}
std::string Log::logLevelToString(Log::Level level)
{
for (const auto &mapping : LOG_LEVEL_MAP)
{
if (mapping.level == level)
{
return std::string(mapping.name);
}
}
return "UNKNOWN";
}
const char *Log::logLevelToColor(Log::Level level)
{
for (const auto &mapping : LOG_LEVEL_MAP)
{
if (mapping.level == level)
{
return mapping.color;
}
}
return "\033[0m"; // Default to reset
}
std::string Log::logLevelToColoredString(Log::Level level)
{
return std::string(Log::logLevelToColor(level)) + Log::logLevelToString(level) + "\033[0m";
}
Log::Level Log::stringToLogLevel(const std::string &level)
{
for (const auto &mapping : LOG_LEVEL_MAP)
{
if (mapping.name == level)
{
return mapping.level;
}
}
return Log::Level::Info; // Default fallback
}

View File

@ -1,45 +1,42 @@
#pragma once
#include <webserv/log/Channel.hpp>
#include <webserv/log/LogLevel.hpp>
// #include <webserv/log/Channel.hpp>
#include <array>
#include <chrono>
#include <map>
#include <memory>
#include <source_location>
#include <string>
#include <string_view>
#include <unordered_map>
/*
TODO ACTUALLY WE USE C++20 SO WE CAN USE std::source_location TO AUTOMATICALLY CAPTURE FILE, LINE, FUNCTION
#include <source_location>
void log(LogLevel level, const std::string& message,
std::source_location location = std::source_location::current()) {
std::cout << "File: " << location.file_name() << std::endl;
std::cout << "Line: " << location.line() << std::endl;
std::cout << "Function: " << location.function_name() << std::endl;
}
No macros needed!!!!
*/
class Channel; // Forward declaration
class Log
{
public:
enum class Level : uint8_t
{
Trace = 0,
Debug = 1,
Info = 2,
Warn = 3,
Error = 4,
Fatal = 5
};
Log(const Log &other) = delete;
Log(const Log &&other) = delete;
Log &operator=(const Log &other) = delete;
Log &&operator=(const Log &&other) = delete;
void log(LogLevel level, const std::string &message, const std::map<std::string, std::string> &context,
void log(Level level, const std::string &message, const std::map<std::string, std::string> &context,
const std::source_location &location);
static void setFileChannel(const std::string &filename, std::ios_base::openmode mode = std::ios_base::app,
LogLevel logLevel = LogLevel::LOGLVL_TRACE);
static void setStdoutChannel(LogLevel logLevel = LogLevel::LOGLVL_TRACE);
Level logLevel = Level::Trace);
static void setStdoutChannel(Level logLevel = Level::Trace);
static int getElapsedTime();
@ -55,6 +52,10 @@ class Log
const std::source_location &location = std::source_location::current());
static void fatal(const std::string &message, const std::map<std::string, std::string> &context = {},
const std::source_location &location = std::source_location::current());
static std::string logLevelToString(Level level);
static const char *logLevelToColor(Level level);
static std::string logLevelToColoredString(Level level);
static Log::Level stringToLogLevel(const std::string &level);
private:
Log();
@ -64,4 +65,21 @@ class Log
std::chrono::steady_clock::time_point start_time_;
std::unordered_map<std::string, std::unique_ptr<Channel>> channels_;
struct LogLevelMapping
{
Log::Level level;
std::string_view name;
const char *color;
};
constexpr static std::array<LogLevelMapping, 6> LOG_LEVEL_MAP = {
{{.level = Log::Level::Trace, .name = "TRACE", .color = "\033[36m"},
{.level = Log::Level::Debug, .name = "DEBUG", .color = "\033[90m"},
{.level = Log::Level::Info, .name = "INFO", .color = "\033[37m"},
{.level = Log::Level::Warn, .name = "WARN", .color = "\033[33m"},
{.level = Log::Level::Error, .name = "ERROR", .color = "\033[31m"},
{.level = Log::Level::Fatal, .name = "FATAL", .color = "\033[1;31m"}}};
};

View File

@ -1,89 +0,0 @@
#pragma once
#include <array>
#include <cstdint>
#include <string>
#include <string_view>
// TODO LOG_LEVEL_MAP should not be in the global namespace, but i do think this is the nice way? lets refactor this to
// the log class or use a seperate namespace or put everything in a log namespace except for the class, then everything
// has Log::
enum class LogLevel : uint8_t
{
LOGLVL_TRACE = 0,
LOGLVL_DEBUG = 1,
LOGLVL_INFO = 2,
LOGLVL_WARN = 3,
LOGLVL_ERROR = 4,
LOGLVL_FATAL = 5
};
// ANSI color codes
namespace LogColors
{
constexpr const char *RESET = "\033[0m";
constexpr const char *TRACE_COLOR = "\033[36m"; // Cyan
constexpr const char *DEBUG_COLOR = "\033[90m"; // Bright black (gray)
constexpr const char *INFO_COLOR = "\033[37m"; // White
constexpr const char *WARN_COLOR = "\033[33m"; // Yellow
constexpr const char *ERROR_COLOR = "\033[31m"; // Red
constexpr const char *FATAL_COLOR = "\033[1;31m"; // Bold red
} // namespace LogColors
// Constexpr bidirectional mapping
struct LogLevelMapping
{
LogLevel level;
std::string_view name;
const char *color;
};
constexpr std::array<LogLevelMapping, 6> LOG_LEVEL_MAP = {
{{.level = LogLevel::LOGLVL_TRACE, .name = "TRACE", .color = LogColors::TRACE_COLOR},
{.level = LogLevel::LOGLVL_DEBUG, .name = "DEBUG", .color = LogColors::DEBUG_COLOR},
{.level = LogLevel::LOGLVL_INFO, .name = "INFO", .color = LogColors::INFO_COLOR},
{.level = LogLevel::LOGLVL_WARN, .name = "WARN", .color = LogColors::WARN_COLOR},
{.level = LogLevel::LOGLVL_ERROR, .name = "ERROR", .color = LogColors::ERROR_COLOR},
{.level = LogLevel::LOGLVL_FATAL, .name = "FATAL", .color = LogColors::FATAL_COLOR}}};
inline std::string logLevelToString(LogLevel level)
{
for (const auto &mapping : LOG_LEVEL_MAP)
{
if (mapping.level == level)
{
return std::string(mapping.name);
}
}
return "UNKNOWN";
}
inline const char *logLevelToColor(LogLevel level)
{
for (const auto &mapping : LOG_LEVEL_MAP)
{
if (mapping.level == level)
{
return mapping.color;
}
}
return LogColors::RESET;
}
inline std::string logLevelToColoredString(LogLevel level)
{
return std::string(logLevelToColor(level)) + logLevelToString(level) + LogColors::RESET;
}
inline LogLevel stringToLogLevel(const std::string &level)
{
for (const auto &mapping : LOG_LEVEL_MAP)
{
if (mapping.name == level)
{
return mapping.level;
}
}
return LogLevel::LOGLVL_INFO; // Default fallback
}

View File

@ -2,17 +2,17 @@
#include <webserv/log/Channel.hpp>
#include <webserv/log/Log.hpp>
#include <webserv/log/LogLevel.hpp>
#include <iomanip>
#include <iostream>
#include <map>
StdoutChannel::StdoutChannel(LogLevel logLevel) : Channel(logLevel) {}
StdoutChannel::StdoutChannel(Log::Level logLevel) : Channel(logLevel) {}
StdoutChannel::~StdoutChannel() {}
void StdoutChannel::log(LogLevel &logLevel, const std::string &message,
void StdoutChannel::log(Log::Level &logLevel, const std::string &message,
const std::map<std::string, std::string> &context)
{
if (logLevel < logLevel_)
@ -20,7 +20,7 @@ void StdoutChannel::log(LogLevel &logLevel, const std::string &message,
return;
}
std::cout << "[" << std::setw(3) << std::setfill('0') << Log::getElapsedTime() << "] ";
std::string prefix = "[" + logLevelToColoredString(logLevel) + "] ";
std::string prefix = "[" + Log::logLevelToColoredString(logLevel) + "] ";
std::cout << prefix;
std::cout << message;
if (!context.empty())

View File

@ -5,7 +5,7 @@
class StdoutChannel : public Channel
{
public:
StdoutChannel(LogLevel logLevel = LogLevel::LOGLVL_TRACE);
StdoutChannel(Log::Level logLevel = Log::Level::Trace);
StdoutChannel(const StdoutChannel &other) = delete;
StdoutChannel(const StdoutChannel &&other) = delete;
@ -14,6 +14,6 @@ class StdoutChannel : public Channel
~StdoutChannel();
void log(LogLevel &logLevel, const std::string &message,
void log(Log::Level &logLevel, const std::string &message,
const std::map<std::string, std::string> &context = {}) override;
};

View File

@ -2,7 +2,6 @@
#include <webserv/config/LocationConfig.hpp>
#include <webserv/config/ServerConfig.hpp>
#include <webserv/log/Log.hpp>
#include <webserv/log/LogLevel.hpp>
#include <webserv/server/Server.hpp>
#include <iostream>
@ -16,8 +15,8 @@ int main(int argc, char **argv)
std::cerr << "Usage: " << argv[0] << " <config_file_path>\n"; // NOLINT
return 1;
}
Log::setFileChannel("webserv.log", std::ios_base::app, LogLevel::LOGLVL_TRACE);
Log::setStdoutChannel(LogLevel::LOGLVL_INFO);
Log::setFileChannel("webserv.log", std::ios_base::app, Log::Level::Trace);
Log::setStdoutChannel(Log::Level::Info);
Log::info("\n======================\nStarting webserv...\n======================\n");
ConfigManager::getInstance().init(argv[1]); // NOLINT
Server server(ConfigManager::getInstance());