diff options
Diffstat (limited to 'host')
-rw-r--r-- | host/include/uhd/utils/log.hpp | 118 | ||||
-rw-r--r-- | host/lib/utils/log.cpp | 325 |
2 files changed, 266 insertions, 177 deletions
diff --git a/host/include/uhd/utils/log.hpp b/host/include/uhd/utils/log.hpp index 9cc400f7c..62cf20ff2 100644 --- a/host/include/uhd/utils/log.hpp +++ b/host/include/uhd/utils/log.hpp @@ -99,10 +99,11 @@ namespace uhd { namespace log { - /** logging severity levels + /*! Logging severity levels + * * Either numeric value or string can be used to define loglevel in CMake and environment variables */ - enum severity_level { + enum UHD_API severity_level { trace = 0, /**< displays every available log message */ debug = 1, /**< displays most log messages necessary for debugging internals */ info = 2, /**< informational messages about setup and what is going on*/ @@ -112,6 +113,81 @@ namespace uhd { off = 6, /**< logging is turned off */ }; + /*! Logging info structure + * + * Information needed to create a log entry is fully contained in the + * logging_info structure. + */ + struct UHD_API logging_info { + logging_info(){}; + logging_info( + const boost::posix_time::ptime &time_, + const uhd::log::severity_level &verbosity_, + const std::string &file_, + const size_t &line_, + const std::string &component_, + const boost::thread::id &thread_id_ + ) : time(time_), + verbosity(verbosity_), + file(file_), + line(line_), + component(component_), + thread_id(thread_id_) + { /* nop */ } + + boost::posix_time::ptime time; + uhd::log::severity_level verbosity; + std::string file; + unsigned int line; + std::string component; + boost::thread::id thread_id; + std::string message; + }; + + /*! Logging function type + * + * Every logging_backend has to define a function with this signature. + * Can be added to the logging core. + */ + using log_fn_t = std::function<void(const uhd::log::logging_info&)>; + + + /*! Set the global log level + * + * The global log level gets applied before the specific log level. + * So, if the global log level is 'info', no logger can can print + * messages at level 'debug' or below. + */ + UHD_API void set_log_level(uhd::log::severity_level level); + + /*! Set the log level for the console logger (if defined). + * + * Short-hand for `set_logger_level("console", level);` + */ + UHD_API void set_console_level(uhd::log::severity_level level); + + /*! Set the log level for the file logger (if defined) + * + * Short-hand for `set_logger_level("file", level);` + */ + UHD_API void set_file_level(uhd::log::severity_level level); + + /*! Set the log level for any specific logger. + * + * \param logger Name of the logger + * \param level New log level for this logger. + * + * \throws uhd::key_error if \p logger was not defined + */ + UHD_API void set_logger_level(const std::string &logger, uhd::log::severity_level level); + + /*! Add logging backend to the log system + * + * \param key Identifies the logging backend in the logging core + * \param logger_fn function which actually logs messages to this backend + */ + UHD_API void add_logger(const std::string &key, log_fn_t logger_fn); + // The operator is used when putting the severity level to log template <typename CharT, typename TraitsT> inline std::basic_ostream<CharT, TraitsT>& @@ -130,7 +206,6 @@ namespace uhd { return strm; } - } } @@ -215,9 +290,7 @@ namespace uhd { #define UHD_HEX(var) \ UHD_LOGGER_DEBUG("DEBUG") << #var << " = 0x" << std::hex << std::setfill('0') << std::setw(8) << var << std::dec; - - -namespace uhd{ namespace _log{ +namespace uhd{ namespace _log { //! Internal logging object (called by UHD_LOG macros) class UHD_API log { @@ -227,15 +300,11 @@ namespace uhd{ namespace _log{ const std::string &file, const unsigned int line, const std::string &component, - const boost::thread::id id + const boost::thread::id thread_id ); ~log(void); - static void set_log_level(uhd::log::severity_level level); - static void set_console_level(uhd::log::severity_level level); - static void set_file_level(uhd::log::severity_level level); - // Macro for overloading insertion operators to avoid costly // conversion of types if not logging. #define INSERTION_OVERLOAD(x) log& operator<< (x) \ @@ -256,31 +325,12 @@ namespace uhd{ namespace _log{ INSERTION_OVERLOAD(std::ios_base& (*val)(std::ios_base&)) private: + uhd::log::logging_info _log_info; std::ostringstream _ss; - std::ostringstream _file; - std::ostringstream _console; - bool _log_it; - bool _log_file; - bool _log_console; + const bool _log_it; }; - } //namespace uhd::_log - namespace log{ - inline void - set_console_level(severity_level level){ - ::uhd::_log::log::set_console_level(level); - } - - inline void - set_log_level(severity_level level){ - ::uhd::_log::log::set_log_level(level); - } - - inline void - set_file_level(severity_level level){ - ::uhd::_log::log::set_file_level(level); - } - } -} +} //namespace uhd::_log +} /* namespace uhd */ #endif /* INCLUDED_UHD_UTILS_LOG_HPP */ diff --git a/host/lib/utils/log.cpp b/host/lib/utils/log.cpp index 42219e925..1d87a0b3d 100644 --- a/host/lib/utils/log.cpp +++ b/host/lib/utils/log.cpp @@ -18,17 +18,17 @@ #include <uhd/utils/log.hpp> #include <uhd/utils/static.hpp> #include <uhd/utils/paths.hpp> -#include <boost/filesystem.hpp> -#include <boost/thread/mutex.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/transport/bounded_buffer.hpp> #include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/make_shared.hpp> #include <boost/thread/locks.hpp> -#include <boost/interprocess/sync/file_lock.hpp> +#include <boost/thread/mutex.hpp> #include <fstream> #include <cctype> +#include <atomic> -namespace fs = boost::filesystem; namespace pt = boost::posix_time; -namespace ip = boost::interprocess; static const std::string PURPLE = "\033[35;1m"; // purple static const std::string BLUE = "\033[34;1m"; // blue @@ -38,7 +38,9 @@ static const std::string RED = "\033[31;0m"; // red static const std::string BRED = "\033[31;1m"; // bright red static const std::string RESET_COLORS = "\033[39;0m"; // reset colors - +/*********************************************************************** + * Helpers + **********************************************************************/ static const std::string verbosity_color(const uhd::log::severity_level &level){ switch(level){ case (uhd::log::trace): @@ -58,86 +60,178 @@ static const std::string verbosity_color(const uhd::log::severity_level &level){ } } +//! get the relative file path from the host directory +inline std::string path_to_filename(std::string path) +{ + return path.substr(path.find_last_of("/\\") + 1); +} /*********************************************************************** - * Global resources for the logger + * Logger backends **********************************************************************/ -class log_resource_type{ + + +void console_log( + const uhd::log::logging_info &log_info +) { + std::clog +#ifdef UHD_LOG_CONSOLE_COLOR + << verbosity_color(log_info.verbosity) +#endif +#ifdef UHD_LOG_CONSOLE_TIME + << "[" << pt::to_simple_string(log_info.time) << "] " +#endif +#ifdef UHD_LOG_CONSOLE_THREAD + << "[0x" << log_info.thread_id << "] " +#endif +#ifdef UHD_LOG_CONSOLE_SRC + << "[" << path_to_filename(log_info.file) << ":" << log_info.line << "] " +#endif + << "[" << log_info.verbosity << "] " + << "[" << log_info.component << "] " +#ifdef UHD_LOG_CONSOLE_COLOR + << RESET_COLORS +#endif + << log_info.message + << std::endl + ; +} + +class file_logger_backend +{ public: - uhd::log::severity_level level; - uhd::log::severity_level file_level; - uhd::log::severity_level console_level; + file_logger_backend(const std::string &file_path) + { + _file_stream.exceptions(std::ofstream::failbit | std::ofstream::badbit); + if (!file_path.empty()){ + try { + _file_stream.open(file_path.c_str(), std::fstream::out | std::fstream::app); + } catch (const std::ofstream::failure& fail){ + std::cerr << "Error opening log file: " << fail.what() << std::endl; + } + } + + } - log_resource_type(void): level(uhd::log::info), file_level(uhd::log::info), console_level(uhd::log::info){ + void log(const uhd::log::logging_info &log_info) + { + if (_file_stream.is_open()){ + _file_stream + << pt::to_simple_string(log_info.time) << "," + << "0x" << log_info.thread_id << "," + << path_to_filename(log_info.file) << ":" << log_info.line << "," + << log_info.verbosity << "," + << log_info.component << "," + << log_info.message + << std::endl; + ; + } + } - //file lock pointer must be null - _file_lock = NULL; - //default to no file logging - this->file_logging = false; + ~file_logger_backend() + { + if (_file_stream.is_open()){ + _file_stream.close(); + } + } - //set the default log level - this->level = uhd::log::off; - this->file_level = uhd::log::off; - this->console_level = uhd::log::off; +private: + std::ofstream _file_stream; +}; + +/*********************************************************************** + * Global resources for the logger + **********************************************************************/ + +#define UHD_CONSOLE_LOGGER_KEY "console" +#define UHD_FILE_LOGGER_KEY "file" + +class log_resource { +public: + uhd::log::severity_level global_level; + std::map<std::string, uhd::log::severity_level> logger_level; + + log_resource(void): + global_level(uhd::log::off), + _exit(false), + _log_queue(10) + { //allow override from macro definition #ifdef UHD_LOG_MIN_LEVEL - this->level = _get_log_level(BOOST_STRINGIZE(UHD_LOG_MIN_LEVEL), this->level); -#endif -#if defined(UHD_LOG_FILE_LEVEL) && defined(UHD_LOG_FILE_PATH) - this->file_level = _get_log_level(BOOST_STRINGIZE(UHD_LOG_FILE_LEVEL), this->file_level); -#endif -#ifdef UHD_LOG_CONSOLE_LEVEL - this->console_level = _get_log_level(BOOST_STRINGIZE(UHD_LOG_CONSOLE_LEVEL), this->console_level); + this->global_level = _get_log_level(BOOST_STRINGIZE(UHD_LOG_MIN_LEVEL), this->global_level); #endif -#ifdef UHD_LOG_FILE - this->log_file_target = BOOST_STRINGIZE(UHD_LOG_FILE); - this->file_logging = true; -#endif - - //allow override from environment variables + //allow override from environment variables const char * log_level_env = std::getenv("UHD_LOG_LEVEL"); - if (log_level_env != NULL && log_level_env[0] != '\0') this->level = _get_log_level(log_level_env, this->level); + if (log_level_env != NULL && log_level_env[0] != '\0') this->global_level = _get_log_level(log_level_env, this->global_level); - const char * log_file_level_env = std::getenv("UHD_LOG_FILE_LEVEL"); - if (log_file_level_env != NULL && log_file_level_env[0] != '\0') this->file_level = _get_log_level(log_file_level_env, this->file_level); +// console logging support +#ifndef UHD_LOG_CONSOLE_DISABLE + uhd::log::severity_level console_level = uhd::log::trace; +#ifdef UHD_LOG_CONSOLE_LEVEL + console_level = _get_log_level(BOOST_STRINGIZE(UHD_LOG_CONSOLE_LEVEL), console_level); +#endif const char * log_console_level_env = std::getenv("UHD_LOG_CONSOLE_LEVEL"); - if (log_console_level_env != NULL && log_console_level_env[0] != '\0') this->console_level = _get_log_level(log_console_level_env, this->console_level); + if (log_console_level_env != NULL && log_console_level_env[0] != '\0') console_level = _get_log_level(log_console_level_env, console_level); + logger_level[UHD_CONSOLE_LOGGER_KEY] = console_level; + _loggers[UHD_CONSOLE_LOGGER_KEY] = &console_log; +#endif + uhd::log::severity_level file_level = uhd::log::trace; + std::string log_file_target; +#if defined(UHD_LOG_FILE_LEVEL) && defined(UHD_LOG_FILE_PATH) + file_level = _get_log_level(BOOST_STRINGIZE(UHD_LOG_FILE_LEVEL), file_level); + log_file_target = BOOST_STRINGIZE(UHD_LOG_FILE); +#endif + const char * log_file_level_env = std::getenv("UHD_LOG_FILE_LEVEL"); + if (log_file_level_env != NULL && log_file_level_env[0] != '\0'){ + file_level = _get_log_level(log_file_level_env, file_level); + } const char* log_file_env = std::getenv("UHD_LOG_FILE"); if ((log_file_env != NULL) && (log_file_env[0] != '\0')) { - this->log_file_target = log_file_env; - this->file_logging = true; + log_file_target = std::string(log_file_env); } + if (!log_file_target.empty()){ + logger_level[UHD_FILE_LOGGER_KEY] = file_level; + auto F = boost::make_shared<file_logger_backend>(log_file_target); + _loggers[UHD_FILE_LOGGER_KEY] = [F](const uhd::log::logging_info& log_info){F->log(log_info);}; + } + _pop_task = uhd::task::make([this](){this->pop_task();}); + } - ~log_resource_type(void){ - if (this->file_logging){ - boost::lock_guard<boost::mutex> lock(_mutex); - _file_stream.close(); - if (_file_lock != NULL) delete _file_lock; - } + ~log_resource(void){ + _exit = true; + _pop_task.reset(); } - void log_to_file(const std::string &log_msg){ - if ( this->file_logging ){ - boost::lock_guard<boost::mutex> lock(_mutex); - if (_file_lock == NULL){ - _file_stream.open(this->log_file_target.c_str(), std::fstream::out | std::fstream::app); - _file_lock = new ip::file_lock(this->log_file_target.c_str()); + void push(const uhd::log::logging_info& log_info) + { + + _log_queue.push_with_haste(log_info); + } + + void pop_task() + { + while (!_exit) { + uhd::log::logging_info log_info; + if (_log_queue.pop_with_timed_wait(log_info, 1)){ + for (const auto &logger : _loggers) { + auto level = logger_level.find(logger.first); + if(level != logger_level.end() && log_info.verbosity < level->second){ + UHD_VAR(level->second) + continue; + } + logger.second(log_info); + } } - _file_lock->lock(); - _file_stream << log_msg << std::flush; - _file_lock->unlock(); } } private: - //! set the log level from a string that is either a digit or an enum name - bool file_logging; - std::string log_file_target; + uhd::task::sptr _pop_task; uhd::log::severity_level _get_log_level(const std::string &log_level_str, const uhd::log::severity_level &previous_level){ if (std::isdigit(log_level_str[0])) { @@ -147,6 +241,7 @@ private: log_level_num <= uhd::log::fatal) { return log_level_num; }else{ + UHD_LOGGER_ERROR("LOG") << "Failed to set log level to: " << log_level_str; return previous_level; } } @@ -163,118 +258,62 @@ private: return previous_level; } - //file stream and lock: - std::ofstream _file_stream; - ip::file_lock *_file_lock; - boost::mutex _mutex; + std::atomic<bool> _exit; + std::map<std::string, uhd::log::log_fn_t> _loggers; + uhd::transport::bounded_buffer<uhd::log::logging_info> _log_queue; // Init auf size 10 oder so }; -UHD_SINGLETON_FCN(log_resource_type, log_rs); +UHD_SINGLETON_FCN(log_resource, log_rs); /*********************************************************************** * The logger object implementation **********************************************************************/ -//! get the relative file path from the host directory - -inline std::string path_to_filename(std::string path) -{ - return path.substr(path.find_last_of("/\\") + 1); -} - uhd::_log::log::log( const uhd::log::severity_level verbosity, const std::string &file, const unsigned int line, const std::string &component, - const boost::thread::id id - ) + const boost::thread::id thread_id + ) : + _log_it(verbosity >= log_rs().global_level) { - _log_it = (verbosity >= log_rs().level); - _log_file =(verbosity >= log_rs().file_level); - _log_console = (verbosity >= log_rs().console_level); - if (_log_it) - { - if (_log_file){ - const std::string time = pt::to_simple_string(pt::microsec_clock::local_time()); - _file - << time << "," - << "0x" << id << "," - << path_to_filename(file) << ":" << line << "," - << verbosity << "," - << component << "," - ; + if (_log_it){ + this->_log_info = uhd::log::logging_info( + pt::microsec_clock::local_time(), + verbosity, + file, + line, + component, + thread_id); } -#ifndef UHD_LOG_CONSOLE_DISABLE - if (_log_console){ -#ifdef UHD_LOG_CONSOLE_TIME - const std::string time = pt::to_simple_string(pt::microsec_clock::local_time()); -#endif - _console -#ifdef UHD_LOG_CONSOLE_COLOR - << verbosity_color(verbosity) -#endif -#ifdef UHD_LOG_CONSOLE_TIME - << "[" << time << "] " -#endif -#ifdef UHD_LOG_CONSOLE_THREAD - << "[0x" << id << "] " -#endif -#ifdef UHD_LOG_CONSOLE_SRC - << "[" << path_to_filename(file) << ":" << line << "] " -#endif - << "[" << verbosity << "] " - << "[" << component << "] " -#ifdef UHD_LOG_CONSOLE_COLOR - << RESET_COLORS -#endif - ; - } -#endif - } } uhd::_log::log::~log(void) { - if (not _log_it) - return; -#ifndef UHD_LOG_CONSOLE_DISABLE - if ( _log_console ){ - std::clog << _console.str() << _ss.str() << std::endl; - } -#endif - if ( _log_file){ - _file << _ss.str() << std::endl; - try{ - log_rs().log_to_file(_file.str()); - } - catch(const std::exception &e){ - /*! - * Critical behavior below. - * The following steps must happen in order to avoid a lock-up condition. - * This is because the message facility will call into the logging facility. - * Therefore we must disable the logger (level = never) before messaging. - */ - log_rs().level = uhd::log::off; - std::cerr - << "Logging failed: " << e.what() << std::endl - << "Logging has been disabled for this process" << std::endl - ; - } + if (_log_it) { + this->_log_info.message = _ss.str(); + log_rs().push(this->_log_info); } } void -uhd::_log::log::set_log_level(uhd::log::severity_level level){ - log_rs().level = level; +uhd::log::set_log_level(uhd::log::severity_level level){ + log_rs().global_level = level; +} + +void +uhd::log::set_logger_level(const std::string &key, uhd::log::severity_level level){ + log_rs().logger_level[key] = level; } void -uhd::_log::log::set_console_level(uhd::log::severity_level level){ - log_rs().console_level = level; +uhd::log::set_console_level(uhd::log::severity_level level){ + set_logger_level(UHD_CONSOLE_LOGGER_KEY, level); } void -uhd::_log::log::set_file_level(uhd::log::severity_level level){ - log_rs().file_level = level; +uhd::log::set_file_level(uhd::log::severity_level level){ + set_logger_level(UHD_FILE_LOGGER_KEY, level); } + |