diff --git a/.gitignore b/.gitignore index 050c3c8..3ad428e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ build +webserv.log diff --git a/webserv/log/FileChannel.cpp b/webserv/log/FileChannel.cpp new file mode 100644 index 0000000..631c7b2 --- /dev/null +++ b/webserv/log/FileChannel.cpp @@ -0,0 +1,55 @@ +#include "webserv/log/LogLevel.hpp" +#include + +#include +#include + + +FileChannel::FileChannel(const std::string &filename) + : filename_(filename), + fileStream_(filename, std::ios::trunc) +{ + if (!fileStream_.is_open()) + { + std::cerr << "Failed to open log file: " << filename << '\n'; + } +} + +FileChannel::~FileChannel() +{ + if (fileStream_.is_open()) + { + fileStream_.close(); + } +} + +void FileChannel::log(LogLevel &logLevel, const std::string &message, + const std::map &context) +{ + if (!fileStream_.is_open()) + { + std::cerr << "Log file is not open: " << filename_ << '\n'; + return; + } + + // Get the current time + auto now = std::chrono::system_clock::now(); + auto now_c = std::chrono::system_clock::to_time_t(now); + std::tm *tm = std::localtime(&now_c); + + // Format the log message + fileStream_ << "[" << std::put_time(tm, "%Y-%m-%d %H:%M:%S") << "] " + << "[" << logLevelToString(logLevel) << "] " << message << '\n'; + + // Log the context if it exists + if (!context.empty()) + { + fileStream_ << "Context:" << '\n'; + for (const auto &[key, value] : context) + { + fileStream_ << " " << key << ": " << value << '\n'; + } + } + fileStream_ << std::flush; +} + diff --git a/webserv/log/FileChannel.hpp b/webserv/log/FileChannel.hpp new file mode 100644 index 0000000..63b0140 --- /dev/null +++ b/webserv/log/FileChannel.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +#include +#include +#include + +class FileChannel : public Channel +{ + public: + FileChannel(const std::string &filename); + + FileChannel(const FileChannel &other) = delete; + FileChannel(const FileChannel &&other) = delete; + FileChannel &operator=(const FileChannel &other) = delete; + FileChannel &&operator=(const FileChannel &&other) = delete; + + ~FileChannel(); + void log(LogLevel &logLevel, const std::string &message, + const std::map &context = {}) override; + + private: + std::string filename_; + std::ofstream fileStream_; +}; \ No newline at end of file diff --git a/webserv/log/Log.cpp b/webserv/log/Log.cpp index 2343edc..a0a075e 100644 --- a/webserv/log/Log.cpp +++ b/webserv/log/Log.cpp @@ -1,16 +1,37 @@ -#include "webserv/log/StdoutChannel.hpp" -#include +#include #include +#include +#include +#include +#include Log::Log() { + // get start time + start_time_ = std::chrono::steady_clock::now(); channels_.insert({"stdout", std::unique_ptr(new StdoutChannel())}); } +void Log::setFile(const std::string &filename) +{ + Log &log = getInstance(); + if (log.channels_.contains("file")) + { + log.channels_.erase("file"); + } + try + { + log.channels_.insert({"file", std::unique_ptr(new FileChannel(filename))}); + } + catch (const std::exception &e) + { + std::cerr << "Failed to set log file: " << e.what() << '\n'; + } +} + Log &Log::getInstance() { static Log instance; - return instance; } @@ -24,15 +45,16 @@ void Log::log(LogLevel level, const std::string &message, const std::string &cha } } void Log::log(LogLevel level, const std::string &message, const std::string &file, int line, - const std::string &function, const std::string &channel, const std::map &context) + const std::string &function, const std::string &channel, + const std::map &context) { auto it = channels_.find(channel); if (it != channels_.end()) { -std::string extendedMessage; + std::string extendedMessage; if (!file.empty()) { - extendedMessage += std::filesystem::path(file).filename().string(); + extendedMessage += std::filesystem::path(file).filename().string(); } if (line != -1) { @@ -47,6 +69,14 @@ std::string extendedMessage; } } +int Log::getElapsedTime() +{ + Log &log = Log::getInstance(); + auto now = std::chrono::steady_clock::now(); + auto elapsed = std::chrono::duration_cast(now - log.start_time_).count(); + return static_cast(elapsed); +} + void Log::static_log(LogLevel level, const std::string &message, const std::string &file, int line, const std::string &function, const std::string &channel, const std::map &context) diff --git a/webserv/log/Log.hpp b/webserv/log/Log.hpp index 8a1d324..e6dcaba 100644 --- a/webserv/log/Log.hpp +++ b/webserv/log/Log.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -7,8 +8,7 @@ #include #include -#define LOG(level, message) \ - Log::static_log((level), (message), __FILE__, __LINE__, __FUNCTION__, "stdout", {}) +#define LOG(level, message) Log::static_log((level), (message), __FILE__, __LINE__, __FUNCTION__, "file", {}) #define LOG_TRACE(message) LOG(LogLevel::LOGLVL_TRACE, message) #define LOG_INFO(message) LOG(LogLevel::LOGLVL_INFO, message) @@ -27,6 +27,8 @@ class Log Log &operator=(const Log &other) = delete; Log &&operator=(const Log &&other) = delete; + static void setFile(const std::string &filename); + void log(LogLevel level, const std::string &message, const std::string &channel = "stdout", const std::map &context = {}); @@ -34,10 +36,12 @@ class Log const std::string &function = "", const std::string &channel = "stdout", const std::map &context = {}); + static int getElapsedTime(); + static void static_log(LogLevel level, const std::string &message, const std::string &file = "", int line = -1, const std::string &function = "", const std::string &channel = "stdout", const std::map &context = {}); - + static void trace(const std::string &message, const std::map &context = {}); static void debug(const std::string &message, const std::map &context = {}); static void info(const std::string &message, const std::map &context = {}); @@ -47,8 +51,10 @@ class Log private: Log(); + ~Log() = default; static Log &getInstance(); + std::chrono::steady_clock::time_point start_time_; std::unordered_map> channels_; }; \ No newline at end of file diff --git a/webserv/log/StdoutChannel.cpp b/webserv/log/StdoutChannel.cpp index 19dc3fb..fb634ed 100644 --- a/webserv/log/StdoutChannel.cpp +++ b/webserv/log/StdoutChannel.cpp @@ -1,10 +1,12 @@ #include #include #include +#include void StdoutChannel::log(LogLevel &logLevel, const std::string &message, const std::map &context) { + std::cout << "[" << std::setw(3) << std::setfill('0') << Log::getElapsedTime() << "] "; std::string prefix = "[" + logLevelToColoredString(logLevel) + "] "; std::cout << prefix; std::cout << message; diff --git a/webserv/main.cpp b/webserv/main.cpp index ee0294f..a828ecd 100644 --- a/webserv/main.cpp +++ b/webserv/main.cpp @@ -15,16 +15,9 @@ int main(int argc, char **argv) std::cerr << "Usage: " << argv[0] << " \n"; // NOLINT return 1; } + Log::setFile("webserv.log"); ConfigManager::getInstance().init(argv[1]); // NOLINT Server server(ConfigManager::getInstance()); - LOG_TRACE("Trace message example."); - LOG_INFO("Info message example."); - LOG_DEBUG("Debug message example."); - LOG_WARN("Warning message example."); - LOG_ERROR("Error message example."); - LOG_FATAL("Fatal message example."); - - Log::info("test log message: server starting...", {{"port", "8080"}, {"mode", "production"}}); server.start(); return 0;