// // Logger.cpp // DanmakuRPG // // Created by Sam Jaffe on 7/18/15. // Copyright (c) 2015 Sam Jaffe. All rights reserved. // #ifdef _WIN32 #include #endif #include #include #include #include #include #include "logger.hpp" #include "properties.hpp" namespace logging { #define X(l) \ case logging::L##l: \ return #l; const char* level_header(log_level l) { assert(l != logging::LNONE); switch (l) { X(FATAL) X(CRITICAL) X(ERROR) X(WARNING) X(INFO) X(DEBUG) X(TRACE) X(NONE) } } #undef X std::string timestamp() { time_t round = std::time(NULL); struct tm time = *std::localtime(&round); char buf[32]; snprintf(buf, sizeof(buf), "%0.4d-%0.2d-%0.2d %0.2d:%0.2d:%0.2d.%0.3d ", 1900 + time.tm_year, time.tm_mon + 1, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec, static_cast(round % 1000)); return buf; } } namespace logging { class multiple_bindings : public std::logic_error { using std::logic_error::logic_error; }; // extern logger_impl& _logger_impl_shared_instance(); extern _binding _default_logger_impl; static _binding _impl_binding = nullptr; bool bind_logger_impl(_binding impl) { assert(_impl_binding == nullptr); if (_impl_binding != nullptr) { throw multiple_bindings{"Only one logger implementation may be bound"}; } _impl_binding = impl; return true; } logger_impl& _get_shared_instance() { if (_impl_binding == nullptr) { return _default_logger_impl(); } else { return _impl_binding(); } } } namespace logging { logger& logger::instance() { static logger instance; return instance; } logger& logger::instance(const char* name) { static std::map> instances; std::map>::iterator it; if ((it = instances.find(name)) != instances.end()) { return *(it->second); } return *(instances.emplace(std::make_pair(name, std::unique_ptr(new logger(name)))).first->second); } logger::logger(const char* name) : min_level_(LDEBUG) , logger_name_(name) , impl_(_get_shared_instance()) { } logger::~logger() { flush(); } std::string logger::get_header(log_level level) { std::string ts = timestamp(); char data[64]; snprintf(data, sizeof(data), "[%-5s] %s %s - ", level_header(level), logger_name_, ts.c_str()); return data; } void logger::vlognf(log_level level, size_t num_bytes, char const* fmt, va_list args) { if (level < min_level_) return; const std::unique_ptr data(new char[num_bytes]); int n = vsnprintf(data.get(), num_bytes, fmt, args); if (n >= 0) { data[num_bytes-1] = '\0'; log(level, get_header(level) + data.get()); } } void logger::log(log_level level, std::string const& msg) { impl_.write(level, msg); } void logger::flush() { impl_.flush(); } }