/* * Copyright (C) 2014 Cloudius Systems, Ltd. */ #ifndef LOG_HH_ #define LOG_HH_ #include "core/sstring.hh" #include #include #include #include #include #include namespace logging { enum class log_level { error, warn, info, debug, trace, }; } // Must exist logging namespace, or ADL gets confused in logger::stringer std::ostream& operator<<(std::ostream& out, logging::log_level level); std::istream& operator>>(std::istream& in, logging::log_level& level); // Boost doesn't auto-deduce the existence of the streaming operators for some reason namespace boost { template <> logging::log_level lexical_cast(const std::string& source); } namespace logging { class logger; class registry; class logger { sstring _name; std::atomic _level = { log_level::warn }; private: struct stringer { // no need for virtual dtor, since not dynamically destroyed virtual void append(std::ostream& os) = 0; }; template struct stringer_for final : stringer { explicit stringer_for(const Arg& arg) : arg(arg) {} const Arg& arg; virtual void append(std::ostream& os) override { os << arg; } }; template void do_log(log_level level, const char* fmt, Args&&... args); template void do_log_step(log_level level, const char* fmt, stringer** s, size_t n, size_t idx, Arg&& arg, Args&&... args); void do_log_step(log_level level, const char* fmt, stringer** s, size_t n, size_t idx); void really_do_log(log_level level, const char* fmt, stringer** stringers, size_t n); public: explicit logger(sstring name); logger(logger&& x); ~logger(); template void log(log_level level, const char* fmt, Args&&... args) { if (level <= _level.load(std::memory_order_relaxed)) { do_log(level, fmt, std::forward(args)...); } } template void error(const char* fmt, Args&&... args) { log(log_level::error, fmt, std::forward(args)...); } template void warn(const char* fmt, Args&&... args) { log(log_level::warn, fmt, std::forward(args)...); } template void info(const char* fmt, Args&&... args) { log(log_level::info, fmt, std::forward(args)...); } template void debug(const char* fmt, Args&&... args) { log(log_level::debug, fmt, std::forward(args)...); } template void trace(const char* fmt, Args&&... args) { log(log_level::trace, fmt, std::forward(args)...); } const sstring& name() const { return _name; } log_level level() const { return _level.load(std::memory_order_relaxed); } void set_level(log_level level) { _level.store(level, std::memory_order_relaxed); } }; class registry { mutable std::mutex _mutex; std::unordered_map _loggers; public: void set_all_loggers_level(log_level level); log_level get_logger_level(sstring name) const; void set_logger_level(sstring name, log_level level); std::vector get_all_logger_names(); void register_logger(logger* l); void unregister_logger(logger* l); void moved(logger* from, logger* to); }; sstring pretty_type_name(const std::type_info&); registry& logger_registry(); template class logger_for : public logger { public: logger_for() : logger(pretty_type_name(typeid(T))) {} }; inline void logger::do_log_step(log_level level, const char* fmt, stringer** s, size_t n, size_t idx) { really_do_log(level, fmt, s, n); } template inline void logger::do_log_step(log_level level, const char* fmt, stringer** s, size_t n, size_t idx, Arg&& arg, Args&&... args) { stringer_for sarg{arg}; s[idx] = &sarg; do_log_step(level, fmt, s, n, idx + 1, std::forward(args)...); } template void logger::do_log(log_level level, const char* fmt, Args&&... args) { stringer* s[sizeof...(Args)]; do_log_step(level, fmt, s, sizeof...(Args), 0, std::forward(args)...); } } // Pretty-printer for exceptions to be logged, e.g., std::current_exception(). std::ostream& operator<<(std::ostream&, std::exception_ptr); #endif /* LOG_HH_ */