#include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace psemek::log { namespace { std::atomic max_thread_name_length = 3; std::mutex thread_names_mutex; util::hash_map> thread_names; std::mutex sinks_mutex; std::vector> sinks; struct signal_data { int signal; std::string message; }; signal_data const signals[] = { {SIGINT, "Aborting due to SIGINT" }, {SIGILL, "Aborting due to SIGILL" }, {SIGABRT, "Aborting due to SIGABRT"}, {SIGFPE, "Aborting due to SIGFPE" }, {SIGSEGV, "Aborting due to SIGSEGV"}, {SIGTERM, "Aborting due to SIGTERM"}, }; struct signal_handler { signal_handler() { for (auto const & p : signals) std::signal(p.signal, &handler); } private: static void handler(int signal) { for (auto const & p : signals) { if (p.signal == signal) { log::error() << p.message; log::error() << util::stacktrace(); break; } } { std::lock_guard lock{sinks_mutex}; for (auto & sink : sinks) sink->flush(); sinks.clear(); } std::signal(signal, SIG_DFL); std::raise(signal); } } signal_handler; void put_message(level l, std::string const & str, std::string const & thread_name) { message msg { clock::now(), thread_name, l, str }; std::lock_guard lock{sinks_mutex}; for (auto const & sink : sinks) sink->put_message(msg); } static ::tm safe_localtime(std::time_t const & time) { static std::mutex mutex; std::lock_guard lock{mutex}; return *std::localtime(&time); } struct default_sink_impl : sink { default_sink_impl(std::unique_ptr stream, level min, level max) : stream_(std::move(stream)) , min_(min) , max_(max) {} void put_message(message const & msg) override { if (msg.level < min_ || msg.level > max_) return; auto const time = clock::to_time_t(msg.time); auto const tm = safe_localtime(time); auto const millis = std::chrono::duration_cast(msg.time.time_since_epoch()).count() % 1000; 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(' ') << msg.thread_name << ']' << '[' << std::setw(5) << msg.level << ']' << ' ' << msg.message << '\n'; auto const str = os.str(); stream_->write(str.data(), str.size()); } void flush() override { stream_->flush(); } private: std::unique_ptr stream_; level min_; level max_; }; } void register_thread(std::string name) { #ifdef _PTHREAD_H #if (defined _GNU_SOURCE) #if __APPLE__ ::pthread_setname_np(name.data()); #else ::pthread_setname_np(::pthread_self(), name.data()); #endif #endif #endif auto id = std::this_thread::get_id(); { std::lock_guard lock{thread_names_mutex}; auto it = thread_names.find(id); if (it != thread_names.end()) throw util::exception("Thread \"" + name + "\" already registered!"); thread_names[id] = std::make_unique(name); } { std::size_t current_max = max_thread_name_length.load(); while(current_max < name.size() && !max_thread_name_length.compare_exchange_weak(current_max, name.size())) {} } put_message(level::info, "Thread \"" + name + "\" registered"); } void unregister_thread(std::thread::id id) { std::string name; { std::lock_guard lock{thread_names_mutex}; auto it = thread_names.find(id); if (it == thread_names.end()) { std::ostringstream os; os << "Thread " << id << " not found!"; throw util::exception(os.str()); } name = std::move(*(it->second)); thread_names.erase(it); } if (id == std::this_thread::get_id()) put_message(level::info, "Thread \"" + name + "\" unregistered", name); else put_message(level::info, "Thread \"" + name + "\" unregistered"); } std::unique_ptr default_sink(std::unique_ptr stream, level min, level max) { return std::make_unique(std::move(stream), min, max); } sink * add_sink(std::unique_ptr s) { auto ptr = s.get(); std::lock_guard lock{sinks_mutex}; sinks.push_back(std::move(s)); return ptr; } std::unique_ptr remove_sink(sink * stream) { std::lock_guard lock{sinks_mutex}; for (std::size_t i = 0; i < sinks.size(); ++i) { if (sinks[i].get() == stream) { auto result = std::move(sinks[i]); sinks.erase(sinks.begin() + i); return result; } } return nullptr; } void put_message(level l, std::string const & message) { static std::string unknown_thread_name = "???"; auto const id = std::this_thread::get_id(); std::string const * thread_name = nullptr; { std::lock_guard lock{thread_names_mutex}; auto const it = thread_names.find(id); if (it == thread_names.end()) thread_name = &unknown_thread_name; else thread_name = it->second.get(); } put_message(l, message, *thread_name); } }