#include "drake/common/text_logging.h" #include #include #include #ifdef HAVE_SPDLOG #include #include #endif #include "drake/common/never_destroyed.h" namespace drake { #ifdef HAVE_SPDLOG namespace { // Returns the default logger. NOTE: This function assumes that it is mutexed, // as in the initializer of a static local. std::shared_ptr onetime_create_log() { // Check if anyone has already set up a logger named "console". If so, we // will just return it; if not, we'll create our own default one. std::shared_ptr result(spdlog::get("console")); if (!result) { // We wrap our stderr sink in a dist_sink so that users can atomically swap // out the sinks used by all Drake logging, via dist_sink_mt's APIs. auto wrapper = std::make_shared(); // We use the stderr_sink_mt (instead of stderr_sink_st) so more than one // thread can use this logger and have their messages be staggered by line, // instead of co-mingling their character bytes. wrapper->add_sink(std::make_shared()); result = std::make_shared("console", std::move(wrapper)); result->set_level(spdlog::level::info); } return result; } } // namespace logging::logger* log() { static const never_destroyed> g_logger( onetime_create_log()); return g_logger.access().get(); } logging::sink* logging::get_dist_sink() { // Extract the dist_sink_mt from Drake's logger instance. auto* sink = log()->sinks().empty() ? nullptr : log()->sinks().front().get(); auto* result = dynamic_cast(sink); if (result == nullptr) { throw std::logic_error( "drake::logging::get_sink(): error: the spdlog sink configuration has" "unexpectedly changed."); } return result; } std::string logging::set_log_level(const std::string& level) { spdlog::level::level_enum prev_value = drake::log()->level(); spdlog::level::level_enum value{}; if (level == "trace") { value = spdlog::level::trace; } else if (level == "debug") { value = spdlog::level::debug; } else if (level == "info") { value = spdlog::level::info; } else if (level == "warn") { value = spdlog::level::warn; } else if (level == "err") { value = spdlog::level::err; } else if (level == "critical") { value = spdlog::level::critical; } else if (level == "off") { value = spdlog::level::off; } else if (level == "unchanged") { value = prev_value; } else { throw std::runtime_error(fmt::format("Unknown spdlog level: {}", level)); } drake::log()->set_level(value); switch (prev_value) { case spdlog::level::trace: return "trace"; case spdlog::level::debug: return "debug"; case spdlog::level::info: return "info"; case spdlog::level::warn: return "warn"; case spdlog::level::err: return "err"; case spdlog::level::critical: return "critical"; case spdlog::level::off: return "off"; default: { // N.B. `spdlog::level::level_enum` is not a `enum class`, so the // compiler does not know that it has a closed set of values. For // simplicity in linking, we do not use `DRAKE_UNREACHABLE`. throw std::runtime_error("Should not reach here!"); } } } const char* const logging::kSetLogLevelHelpMessage = "sets the spdlog output threshold; possible values are " "'unchanged', " "'trace', " "'debug', " "'info', " "'warn', " "'err', " "'critical', " "'off'"; void logging::set_log_pattern(const std::string& pattern) { drake::log()->set_pattern(pattern); } const char* const logging::kSetLogPatternHelpMessage = "sets the spdlog pattern for formatting; for more information, see " "https://github.com/gabime/spdlog/wiki/3.-Custom-formatting"; #else // HAVE_SPDLOG logging::logger::logger() {} logging::sink::sink() {} logging::logger* log() { // A do-nothing logger instance. static logging::logger g_logger; return &g_logger; } logging::sink* logging::get_dist_sink() { // An empty sink instance. static logging::sink g_sink; return &g_sink; } std::string logging::set_log_level(const std::string&) { return ""; } const char* const logging::kSetLogLevelHelpMessage = "(Text logging is unavailable.)"; void logging::set_log_pattern(const std::string&) {} const char* const logging::kSetLogPatternHelpMessage = "(Text logging is unavailable.)"; #endif // HAVE_SPDLOG const char* const logging::kSetLogLevelUnchanged = "unchanged"; } // namespace drake