added logging module and a sandbox project

This commit is contained in:
Sascha Kühl
2025-07-03 16:13:58 +02:00
parent c4a00f19fd
commit 20d6a77275
29 changed files with 2287 additions and 25 deletions
+17
View File
@@ -1,4 +1,12 @@
add_library(matador-core STATIC
../../include/matador/logger/basic_file_sink.hpp
../../include/matador/logger/file_sink.hpp
../../include/matador/logger/log_domain.hpp
../../include/matador/logger/log_level.hpp
../../include/matador/logger/log_manager.hpp
../../include/matador/logger/log_sink.hpp
../../include/matador/logger/logger.hpp
../../include/matador/logger/rotating_file_sink.hpp
../../include/matador/net/event_type.hpp
../../include/matador/net/handler.hpp
../../include/matador/net/os.hpp
@@ -36,6 +44,7 @@ add_library(matador-core STATIC
../../include/matador/utils/errors.hpp
../../include/matador/utils/export.hpp
../../include/matador/utils/fetch_type.hpp
../../include/matador/utils/file.hpp
../../include/matador/utils/field_attributes.hpp
../../include/matador/utils/foreign_attributes.hpp
../../include/matador/utils/identifier.hpp
@@ -52,6 +61,13 @@ add_library(matador-core STATIC
../../include/matador/utils/uuid.hpp
../../include/matador/utils/value.hpp
../../include/matador/utils/version.hpp
logger/basic_file_sink.cpp
logger/file_sink.cpp
logger/log_domain.cpp
logger/log_level.cpp
logger/log_manager.cpp
logger/logger.cpp
logger/rotating_file_sink.cpp
object/attribute_definition_generator.cpp
object/attribute_definition.cpp
object/basic_object_info.cpp
@@ -66,6 +82,7 @@ add_library(matador-core STATIC
utils/error.cpp
utils/errors.cpp
utils/field_attributes.cpp
utils/file.cpp
utils/foreign_attributes.cpp
utils/identifier.cpp
utils/leader_follower_thread_pool.cpp
+23
View File
@@ -0,0 +1,23 @@
#include "matador/logger/basic_file_sink.hpp"
namespace matador::logger {
basic_file_sink::basic_file_sink(FILE *f)
: stream(f)
{}
void basic_file_sink::write(const char *message, const size_t size)
{
fwrite(message, sizeof(char), size, stream);
fflush(stream);
}
void basic_file_sink::close()
{
if (stream) {
fclose(stream);
stream = nullptr;
}
}
}
+68
View File
@@ -0,0 +1,68 @@
#include "matador/logger/file_sink.hpp"
#include "matador/utils/os.hpp"
#include "matador/utils/string.hpp"
#include <vector>
namespace matador::logger {
file_sink::file_sink(const std::string &path)
: path_(path)
{
std::string filename(path);
// find last dir delimiter
const char *last = strrchr(path.c_str(), os::DIR_SEPARATOR);
if (last != nullptr) {
path_.assign(path.data(), last-path.data());
} else {
path_.clear();
}
if (last != nullptr) {
filename = (last + 1);
}
// extract base path and extension
if (std::vector<std::string> result; utils::split(filename, '.', result) != 2) {
throw std::logic_error("split path must consists of two elements");
}
// get the current path
const auto pwd = os::get_current_dir();
// make the path
os::mkpath(path_);
// change into the path
os::chdir(path_);
// create the file
stream = os::fopen(filename, "a");
if (stream == nullptr) {
os::chdir(pwd);
throw std::logic_error("error opening file");
}
os::chdir(pwd);
}
file_sink::file_sink(const char *path)
: file_sink(std::string(path))
{}
file_sink::~file_sink()
{
if (stream) {
fclose(stream);
stream = nullptr;
}
}
std::string file_sink::path() const
{
return path_;
}
stdout_sink::stdout_sink()
: basic_file_sink(stdout)
{}
stderr_sink::stderr_sink()
: basic_file_sink(stderr)
{}
}
+123
View File
@@ -0,0 +1,123 @@
#include "matador/logger/log_domain.hpp"
#include <chrono>
#include <thread>
namespace matador::logger {
namespace details {
std::size_t acquire_thread_index(const std::thread::id id) {
static std::size_t next_index = 0;
static std::mutex thread_mutex;
static std::map<std::thread::id, std::size_t> ids;
std::lock_guard lock(thread_mutex);
if(ids.find(id) == ids.end()) {
ids[id] = next_index++;
}
return ids[id];
}
char* gettimestamp(char* const buffer, const size_t size) {
using namespace std::chrono;
const auto now = system_clock::now();
const auto now_ms = time_point_cast<milliseconds>(now);
std::time_t t = system_clock::to_time_t(now);
const auto ms = duration_cast<milliseconds>(now_ms.time_since_epoch()) % 1000;
std::tm tm{};
#ifdef _WIN32
localtime_s(&tm, &t);
#else
localtime_r(&t, &tm);
#endif
char buf[32];
std::strftime(buf, 32, "%Y-%m-%d %H:%M:%S", &tm);
std::snprintf(buffer, size, "%s.%03ld", buf, static_cast<long>(ms.count()));
return buffer;
}
}
std::map<log_level, std::string> log_domain::level_strings = { /* NOLINT */
{ log_level::LVL_DEBUG, "DEBUG" },
{ log_level::LVL_INFO, "INFO" },
{ log_level::LVL_WARN, "WARN" },
{ log_level::LVL_ERROR, "ERROR" },
{ log_level::LVL_TRACE, "TRACE" }
};
log_domain::log_domain(std::string name, const log_level_range log_range)
: name_(std::move(name))
, log_level_range_(log_range)
{}
std::string log_domain::name() const
{
return name_;
}
void log_domain::max_log_level(log_level max_level)
{
log_level_range_.max_level = max_level;
}
log_level log_domain::max_log_level() const
{
return log_level_range_.max_level;
}
void log_domain::min_log_level(log_level min_level)
{
log_level_range_.min_level = min_level;
}
log_level log_domain::min_log_level() const
{
return log_level_range_.min_level;
}
void log_domain::add_sink(sink_ptr sink)
{
sinks.push_back(std::move(sink));
}
void log_domain::log(log_level lvl, const std::string &source, const char *message)
{
if (lvl < log_level_range_.max_level || lvl > log_level_range_.min_level) {
return;
}
char timestamp[80];
get_time_stamp(timestamp);
char buffer[1024];
#ifdef _MSC_VER
int ret = sprintf_s(buffer, 1024, "%s [Thread %zu] [%-7s] [%s]: %s\n", timestamp, details::acquire_thread_index(std::this_thread::get_id()), level_strings[lvl].c_str(), source.c_str(), message);
#else
int ret = sprintf(buffer, "%s [Thread %lu] [%-7s] [%s]: %s\n", timestamp, acquire_thread_index(std::this_thread::get_id()), level_strings[lvl].c_str(), source.c_str(), message);
#endif
std::lock_guard<std::mutex> l(mutex_);
for (auto &sink : sinks) {
sink->write(buffer, ret);
}
}
void log_domain::clear()
{
sinks.clear();
}
void log_domain::get_time_stamp(char* const timestamp_buffer)
{
std::lock_guard l(mutex_);
details::gettimestamp(timestamp_buffer, 80);
}
}
+38
View File
@@ -0,0 +1,38 @@
#include "matador/logger/log_level.hpp"
#include <iostream>
namespace matador::logger {
std::ostream& operator<<(std::ostream &os, const log_level lvl)
{
switch (lvl) {
case log_level::LVL_ERROR:
os << "ERROR";
break;
case log_level::LVL_FATAL:
os << "FATAL";
break;
case log_level::LVL_DEBUG:
os << "DEBUG";
break;
case log_level::LVL_INFO:
os << "INFO";
break;
case log_level::LVL_TRACE:
os << "TRACE";
break;
case log_level::LVL_WARN:
os << "WARN";
break;
case log_level::LVL_ALL:
os << "ALL";
break;
default:
os << "UNKNOWN";
break;
}
return os;
}
}
+170
View File
@@ -0,0 +1,170 @@
#include "matador/logger/log_manager.hpp"
namespace matador::logger {
log_level_range log_manager::default_log_level_range_ = {};
logger log_manager::create_logger(std::string source) const {
return logger(std::move(source), default_log_domain_);
}
logger log_manager::create_logger(std::string source, const std::string &domain_name) {
return logger(std::move(source), acquire_domain(domain_name));
}
void log_manager::add_sink(sink_ptr sink) const {
default_log_domain_->add_sink(std::move(sink));
}
void log_manager::add_sink(sink_ptr sink, const std::string &domain_name)
{
const auto log_domain = acquire_domain(domain_name);
log_domain->add_sink(std::move(sink));
}
void log_manager::clear_all_sinks() const {
default_log_domain_->clear();
}
void log_manager::clear_all_sinks(const std::string &domain_name)
{
if (const auto it = log_domain_map_.find(domain_name); it != log_domain_map_.end()) {
it->second->clear();
}
}
void log_manager::clear()
{
log_domain_map_.clear();
default_log_domain_->clear();
}
void log_manager::max_default_log_level(log_level max_level)
{
default_log_level_range_.max_level = max_level;
}
log_level log_manager::max_default_log_level()
{
return default_log_level_range_.max_level;
}
void log_manager::min_default_log_level(log_level min_level)
{
default_log_level_range_.min_level = min_level;
}
log_level log_manager::min_default_log_level()
{
return default_log_level_range_.min_level;
}
std::shared_ptr<log_domain> log_manager::acquire_domain(const std::string &name)
{
if (name == "default") {
return default_log_domain_;
}
auto it = log_domain_map_.find(name);
if (it == log_domain_map_.end()) {
it = log_domain_map_.insert(std::make_pair(name, std::make_shared<log_domain>(name, default_log_level_range_))).first;
}
return it->second;
}
std::shared_ptr<log_domain> log_manager::find_domain(const std::string &name)
{
if (name == "default") {
return default_log_domain_;
}
auto it = log_domain_map_.find(name);
if (it != log_domain_map_.end()) {
return it->second;
}
return std::shared_ptr<log_domain>();
}
void log_manager::log_default(const log_level lvl, const std::string &source, const char *message) const {
default_log_domain_->log(lvl, source, message);
}
std::shared_ptr<file_sink> create_file_sink(const std::string &logfile)
{
return std::make_shared<file_sink>(logfile);
}
std::shared_ptr<stderr_sink> create_stderr_sink()
{
return std::make_shared<stderr_sink>();
}
std::shared_ptr<stdout_sink> create_stdout_sink()
{
return std::make_shared<stdout_sink>();
}
std::shared_ptr<rotating_file_sink> create_rotating_file_sink(const std::string &logfile, size_t max_size, size_t file_count)
{
return std::make_shared<rotating_file_sink>(logfile, max_size, file_count);
}
void default_min_log_level(const log_level min_lvl)
{
log_manager::min_default_log_level(min_lvl);
}
void default_max_log_level(const log_level max_lvl)
{
log_manager::max_default_log_level(max_lvl);
}
void domain_min_log_level(const std::string &name, const log_level min_lvl)
{
if (const auto domain = log_manager::instance().find_domain(name)) {
domain->min_log_level(min_lvl);
}
}
void domain_max_log_level(const std::string &name, const log_level max_lvl)
{
if (const auto domain = log_manager::instance().find_domain(name)) {
domain->min_log_level(max_lvl);
}
}
void add_log_sink(sink_ptr sink)
{
log_manager::instance().add_sink(std::move(sink));
}
void add_log_sink(sink_ptr sink, const std::string &domain)
{
log_manager::instance().add_sink(std::move(sink), domain);
}
void clear_all_log_sinks()
{
log_manager::instance().clear_all_sinks();
}
void clear_all_log_sinks(const std::string &domain)
{
log_manager::instance().clear_all_sinks(domain);
}
logger create_logger(std::string source)
{
return log_manager::instance().create_logger(std::move(source));
}
logger create_logger(std::string source, const std::string &domain)
{
return log_manager::instance().create_logger(std::move(source), domain);
}
void log_default(const log_level lvl, const std::string &source, const char *message)
{
log_manager::instance().log_default(lvl, source, message);
}
}
+34
View File
@@ -0,0 +1,34 @@
#include "matador/logger/logger.hpp"
namespace matador::logger {
logger::logger(std::string source, std::shared_ptr<log_domain> log_domain)
: source_(std::move(source))
, logger_domain_(std::move(log_domain))
{}
logger::logger(logger&& l) noexcept
: source_(std::move(l.source_))
, logger_domain_(std::move(l.logger_domain_))
{}
logger& logger::operator=(logger&& l) noexcept {
source_ = std::move(l.source_);
logger_domain_ = std::move(l.logger_domain_);
return *this;
}
const std::string& logger::source() const
{
return source_;
}
std::string logger::domain() const
{
return logger_domain_->name();
}
void logger::log(const log_level lvl, const char *what) const {
logger_domain_->log(lvl, source_, what);
}
}
+110
View File
@@ -0,0 +1,110 @@
#include "matador/logger/rotating_file_sink.hpp"
#include "matador/utils/string.hpp"
#include "matador/utils/os.hpp"
#include <iostream>
#include <cstring>
namespace matador::logger {
rotating_file_sink::rotating_file_sink(const std::string& path, size_t max_size, size_t file_count)
: max_size_(max_size)
, file_count_(file_count)
{
// split path and file
prepare(path);
current_size_ = logfile_.size();
}
void rotating_file_sink::write(const char *message, size_t size)
{
current_size_ += size;
if (current_size_ > max_size_) {
current_size_ = size;
rotate();
}
fwrite(message, sizeof(char), size, logfile_.stream());
fflush(logfile_.stream());
}
void rotating_file_sink::close()
{
logfile_.close();
}
std::string rotating_file_sink::calculate_filename(size_t fileno)
{
char buffer[1024];
#ifdef _WIN32
sprintf_s(buffer, 1024, "%s.%zu.%s", base_path_.c_str(), fileno, extension_.c_str());
#else
sprintf(buffer, "%s.%zu.%s", base_path_.c_str(), fileno, extension_.c_str());
#endif
return buffer;
}
/*
* Rotate log files:
* log_path.log -> log_path.01.log
* log_path.01.log -> log_path.02.log
* log_path.02.log -> log_path.03.log
* ...
* log_path.[n-1].log -> log_path.[n].log
* delete log_path.[n].log
* create new log_path.log
*/
void rotating_file_sink::rotate()
{
std::string filename;
for (size_t i = file_count_; i != 0; --i) {
filename = calculate_filename(i);
if (!matador::os::exists(filename.c_str())) {
continue;
}
if (i == file_count_) {
matador::os::remove(filename.c_str());
} else {
matador::os::rename(filename.c_str(), calculate_filename(i+1).c_str());
}
}
std::string path = logfile_.path();
logfile_.close();
matador::os::rename(path.c_str(), filename.c_str());
logfile_.open(path, "a");
}
void rotating_file_sink::prepare(const std::string &path)
{
std::string filename(path);
// find last dir delimiter
const char *last = strrchr(path.c_str(), os::DIR_SEPARATOR);
if (last != nullptr) {
path_.assign(path.data(), last-path.data());
} else {
path_.clear();
}
if (last != nullptr) {
filename = (last + 1);
}
// extract base path and extension
std::vector<std::string> result;
if (utils::split(filename, '.', result) != 2) {
throw std::logic_error("split path must consists of two elements");
}
base_path_.assign(result[0]);
extension_.assign(result[1]);
// get the current path
const auto pwd = os::get_current_dir();
// make the path
os::mkpath(path_);
// change into the path
os::chdir(path_);
// create the file
logfile_.open(filename, "a");
// change back to the origin path
os::chdir(pwd);
}
}
+2 -1
View File
@@ -9,7 +9,8 @@ utils::error make_error(const error_code ec, const std::string &msg) {
schema::schema(std::string name)
: name_(std::move(name))
, root_(schema_node::make_null_node(*this)) {
, root_(schema_node::make_null_node(*this))
, log_(logger::create_logger("schema")) {
root_->first_child_ = std::shared_ptr<schema_node>(new schema_node(*this));
root_->last_child_ = std::shared_ptr<schema_node>(new schema_node(*this));
root_->first_child_->next_sibling_ = root_->last_child_;
+83
View File
@@ -0,0 +1,83 @@
#include "matador/utils/file.hpp"
#include "matador/utils/os.hpp"
#include <stdexcept>
namespace matador {
file::file(const char *path, const char *mode)
: file(std::string(path), mode)
{}
file::file(const std::string &path, const char *mode)
{
open(path, mode);
}
file::~file()
{
close();
}
void file::open(const char *path, const char *mode)
{
open(std::string(path), mode);
}
void file::open(const std::string &path, const char *mode)
{
close();
path_ = path;
stream_ = os::fopen(path, mode);
}
void file::close()
{
if (stream_ != nullptr) {
os::fclose(stream_);
stream_ = nullptr;
}
}
size_t file::size() const
{
return os::file_size(stream_);
}
std::string file::path() const
{
return path_;
}
FILE *file::stream() const
{
return stream_;
}
bool file::is_open() const
{
return stream_ != nullptr;
}
std::string read_as_text(const file &f)
{
if (!f.is_open()) {
throw std::logic_error("file is not open");
}
// get file size:
fseek (f.stream() , 0 , SEEK_END);
const size_t size = ftell(f.stream());
rewind (f.stream());
std::string result(size, '\0');
if (const auto ret = fread((result.data()), 1, size, f.stream()); ret == 0 && ferror(f.stream())) {
char error_buffer[1024];
os::strerror(errno, error_buffer, 1024);
throw std::logic_error(error_buffer);
}
return result;
}
}