#include #include #include #include #include #include #include #include #include namespace psemek::log { static int max_thread_name_length = 5; void set_max_thread_name_length(int length) { max_thread_name_length = length; } static std::unordered_map thread_names; void register_thread(std::string name) { register_thread(std::this_thread::get_id(), std::move(name)); } void register_thread(std::thread::id id, std::string name) { auto it = thread_names.find(id); if (it != thread_names.end()) throw std::runtime_error("Thread \"" + name + "\" already registered!"); if (name.size() > max_thread_name_length) throw std::runtime_error("Thread \"" + name + "\" name is too long"); thread_names[id] = name; put_message(level::info, "Thread \"" + name + "\" registered"); } static ::tm safe_localtime(std::time_t const & time) { static std::mutex mutex; std::lock_guard lock{mutex}; return *std::localtime(&time); } void put_message(level l, std::string const & message) { static std::string unknown_thread_name = "???"; using clock = std::chrono::system_clock; auto const time = clock::to_time_t(clock::now()); auto const tm = safe_localtime(time); auto const millis = std::chrono::duration_cast(clock::now().time_since_epoch()).count() % 1000; auto const id = std::this_thread::get_id(); auto const it = thread_names.find(id); auto const & thread_name = (it == thread_names.end()) ? unknown_thread_name : it->second; std::ostringstream os; os << '[' << std::put_time(&tm, "%Y %b %d %H:%M:%S.") << std::setw(3) << std::setfill('0') << millis << ']' << '[' << std::setw(max_thread_name_length) << std::setfill(' ') << thread_name << ']' << '[' << std::setw(7) << l << ']' << ' ' << message; static std::mutex cout_mutex; std::lock_guard lock{cout_mutex}; std::cout << os.str() << std::endl; } }