#include "matador/utils/logger.hpp" #include "matador/utils/os.hpp" #include "matador/utils/string.hpp" #include #include #include #include #include #include #include namespace matador::utils { std::size_t acquire_thread_index(std::thread::id id); std::map 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" } }; logger::logger(const std::string &path, std::string source) : source_(std::move(source)) { 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 result; if (utils::split(filename, '.', result) != 2) { throw std::logic_error("split path must consists of two elements"); } // get current path auto pwd = os::get_current_dir(); // make path os::mkpath(path_); // change into path os::chdir(path_); // create file stream = os::fopen(filename, "a"); if (stream == nullptr) { os::chdir(pwd); throw std::logic_error("error opening file"); } os::chdir(pwd); } logger::logger(FILE *file, std::string source) : stream(file) , source_(std::move(source)) {} logger::logger(const logger &x) : path_(x.path_) , stream(x.stream) , source_(x.source_) {} logger &logger::operator=(const logger &x) { if (this == &x) { return *this; } path_ = x.path_; stream = x.stream; source_ = x.source_; return *this; } logger::logger(logger &&x) noexcept : path_(std::move(x.path_)) , stream(x.stream) , source_(std::move(x.source_)) { x.stream = nullptr; } logger &logger::operator=(logger &&x) noexcept { path_ = std::move(x.path_); std::swap(stream, x.stream); source_ = std::move(x.source_); return *this; } void logger::log(log_level lvl, const char *message) const { using namespace std::chrono; auto timestamp = system_clock::now(); auto coarse = system_clock::to_time_t(timestamp); auto fine = time_point_cast(timestamp); char timestamp_buffer[sizeof "9999-12-31 23:59:59.999"]; std::snprintf(timestamp_buffer + std::strftime(timestamp_buffer, sizeof timestamp_buffer - 3, "%F %T.", std::localtime(&coarse)), 4, "%03ld", fine.time_since_epoch().count() % 1000); char buffer[1024]; #ifdef _MSC_VER int ret = sprintf_s(buffer, 1024, "%s [Thread %zu] [%-7s] [%s]: %s\n", timestamp_buffer, 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_buffer, acquire_thread_index(std::this_thread::get_id()), level_strings[lvl].c_str(), source_.c_str(), message); #endif std::lock_guard l(mutex_); write(buffer, ret); } void logger::write(const char *message, size_t size) const { fwrite(message, sizeof(char), size, stream); fflush(stream); } void logger::close() { if (stream) { fclose(stream); stream = nullptr; } } std::size_t acquire_thread_index(std::thread::id id) { static std::size_t next_index = 0; static std::mutex my_mutex; static std::map ids; std::lock_guard lock(my_mutex); if(ids.find(id) == ids.end()) { ids[id] = next_index++; } return ids[id]; } }