added logging module and a sandbox project
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
{}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user