Implement file logging functionality with FileChannel class and update Log class to support file output
This commit is contained in:
parent
6262cda801
commit
94cd396ac1
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,3 +5,4 @@ build
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
webserv.log
|
||||||
|
|||||||
55
webserv/log/FileChannel.cpp
Normal file
55
webserv/log/FileChannel.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#include "webserv/log/LogLevel.hpp"
|
||||||
|
#include <webserv/log/FileChannel.hpp>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
|
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<std::string, std::string> &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;
|
||||||
|
}
|
||||||
|
|
||||||
27
webserv/log/FileChannel.hpp
Normal file
27
webserv/log/FileChannel.hpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <webserv/log/Channel.hpp>
|
||||||
|
#include <webserv/log/LogLevel.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
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<std::string, std::string> &context = {}) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string filename_;
|
||||||
|
std::ofstream fileStream_;
|
||||||
|
};
|
||||||
@ -1,16 +1,37 @@
|
|||||||
#include "webserv/log/StdoutChannel.hpp"
|
#include <chrono>
|
||||||
#include <webserv/log/Log.hpp>
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <iostream>
|
||||||
|
#include <webserv/log/FileChannel.hpp>
|
||||||
|
#include <webserv/log/Log.hpp>
|
||||||
|
#include <webserv/log/StdoutChannel.hpp>
|
||||||
|
|
||||||
Log::Log()
|
Log::Log()
|
||||||
{
|
{
|
||||||
|
// get start time
|
||||||
|
start_time_ = std::chrono::steady_clock::now();
|
||||||
channels_.insert({"stdout", std::unique_ptr<Channel>(new StdoutChannel())});
|
channels_.insert({"stdout", std::unique_ptr<Channel>(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<Channel>(new FileChannel(filename))});
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to set log file: " << e.what() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Log &Log::getInstance()
|
Log &Log::getInstance()
|
||||||
{
|
{
|
||||||
static Log instance;
|
static Log instance;
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,7 +45,8 @@ 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,
|
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<std::string, std::string> &context)
|
const std::string &function, const std::string &channel,
|
||||||
|
const std::map<std::string, std::string> &context)
|
||||||
{
|
{
|
||||||
auto it = channels_.find(channel);
|
auto it = channels_.find(channel);
|
||||||
if (it != channels_.end())
|
if (it != channels_.end())
|
||||||
@ -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<std::chrono::seconds>(now - log.start_time_).count();
|
||||||
|
return static_cast<int>(elapsed);
|
||||||
|
}
|
||||||
|
|
||||||
void Log::static_log(LogLevel level, const std::string &message, const std::string &file, int line,
|
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::string &function, const std::string &channel,
|
||||||
const std::map<std::string, std::string> &context)
|
const std::map<std::string, std::string> &context)
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -7,8 +8,7 @@
|
|||||||
#include <webserv/log/Channel.hpp>
|
#include <webserv/log/Channel.hpp>
|
||||||
#include <webserv/log/LogLevel.hpp>
|
#include <webserv/log/LogLevel.hpp>
|
||||||
|
|
||||||
#define LOG(level, message) \
|
#define LOG(level, message) Log::static_log((level), (message), __FILE__, __LINE__, __FUNCTION__, "file", {})
|
||||||
Log::static_log((level), (message), __FILE__, __LINE__, __FUNCTION__, "stdout", {})
|
|
||||||
|
|
||||||
#define LOG_TRACE(message) LOG(LogLevel::LOGLVL_TRACE, message)
|
#define LOG_TRACE(message) LOG(LogLevel::LOGLVL_TRACE, message)
|
||||||
#define LOG_INFO(message) LOG(LogLevel::LOGLVL_INFO, 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;
|
||||||
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",
|
void log(LogLevel level, const std::string &message, const std::string &channel = "stdout",
|
||||||
const std::map<std::string, std::string> &context = {});
|
const std::map<std::string, std::string> &context = {});
|
||||||
|
|
||||||
@ -34,6 +36,8 @@ class Log
|
|||||||
const std::string &function = "", const std::string &channel = "stdout",
|
const std::string &function = "", const std::string &channel = "stdout",
|
||||||
const std::map<std::string, std::string> &context = {});
|
const std::map<std::string, std::string> &context = {});
|
||||||
|
|
||||||
|
static int getElapsedTime();
|
||||||
|
|
||||||
static void static_log(LogLevel level, const std::string &message, const std::string &file = "", int line = -1,
|
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::string &function = "", const std::string &channel = "stdout",
|
||||||
const std::map<std::string, std::string> &context = {});
|
const std::map<std::string, std::string> &context = {});
|
||||||
@ -47,8 +51,10 @@ class Log
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Log();
|
Log();
|
||||||
|
|
||||||
~Log() = default;
|
~Log() = default;
|
||||||
static Log &getInstance();
|
static Log &getInstance();
|
||||||
|
|
||||||
|
std::chrono::steady_clock::time_point start_time_;
|
||||||
std::unordered_map<std::string, std::unique_ptr<Channel>> channels_;
|
std::unordered_map<std::string, std::unique_ptr<Channel>> channels_;
|
||||||
};
|
};
|
||||||
@ -1,10 +1,12 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <webserv/log/StdoutChannel.hpp>
|
#include <webserv/log/StdoutChannel.hpp>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
void StdoutChannel::log(LogLevel &logLevel, const std::string &message,
|
void StdoutChannel::log(LogLevel &logLevel, const std::string &message,
|
||||||
const std::map<std::string, std::string> &context)
|
const std::map<std::string, std::string> &context)
|
||||||
{
|
{
|
||||||
|
std::cout << "[" << std::setw(3) << std::setfill('0') << Log::getElapsedTime() << "] ";
|
||||||
std::string prefix = "[" + logLevelToColoredString(logLevel) + "] ";
|
std::string prefix = "[" + logLevelToColoredString(logLevel) + "] ";
|
||||||
std::cout << prefix;
|
std::cout << prefix;
|
||||||
std::cout << message;
|
std::cout << message;
|
||||||
|
|||||||
@ -15,16 +15,9 @@ int main(int argc, char **argv)
|
|||||||
std::cerr << "Usage: " << argv[0] << " <config_file_path>\n"; // NOLINT
|
std::cerr << "Usage: " << argv[0] << " <config_file_path>\n"; // NOLINT
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
Log::setFile("webserv.log");
|
||||||
ConfigManager::getInstance().init(argv[1]); // NOLINT
|
ConfigManager::getInstance().init(argv[1]); // NOLINT
|
||||||
Server server(ConfigManager::getInstance());
|
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();
|
server.start();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user