// // Copyright 2012,2014,2016 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // #include #include #include #include #include #include #include #include #include #include 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 static const std::string GREEN = "\033[32;1m"; // green static const std::string YELLOW = "\033[33;1m"; // yellow 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 static const std::string verbosity_color(const uhd::log::severity_level &level){ switch(level){ case (uhd::log::trace): return PURPLE; case(uhd::log::debug): return BLUE; case(uhd::log::info): return GREEN; case(uhd::log::warning): return YELLOW; case(uhd::log::error): return RED; case(uhd::log::fatal): return BRED; default: return RESET_COLORS; } } /*********************************************************************** * Global resources for the logger **********************************************************************/ class log_resource_type{ public: uhd::log::severity_level level; uhd::log::severity_level file_level; uhd::log::severity_level console_level; log_resource_type(void): level(uhd::log::info), file_level(uhd::log::info), console_level(uhd::log::info){ //file lock pointer must be null _file_lock = NULL; //default to no file logging this->file_logging = false; //set the default log level this->level = uhd::log::off; this->file_level = uhd::log::off; this->console_level = uhd::log::off; //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); #endif #ifdef UHD_LOG_FILE this->log_file_target = BOOST_STRINGIZE(UHD_LOG_FILE); this->file_logging = true; #endif //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); 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); 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); 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_resource_type(void){ if (this->file_logging){ boost::lock_guard lock(_mutex); _file_stream.close(); if (_file_lock != NULL) delete _file_lock; } } void log_to_file(const std::string &log_msg){ if ( this->file_logging ){ boost::lock_guard 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()); } _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::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])) { const uhd::log::severity_level log_level_num = uhd::log::severity_level(std::stoi(log_level_str)); if (log_level_num >= uhd::log::trace and log_level_num <= uhd::log::fatal) { return log_level_num; }else{ return previous_level; } } #define if_loglevel_equal(name) \ else if (log_level_str == #name) return uhd::log::name if_loglevel_equal(trace); if_loglevel_equal(debug); if_loglevel_equal(info); if_loglevel_equal(warning); if_loglevel_equal(error); if_loglevel_equal(fatal); if_loglevel_equal(off); return previous_level; } //file stream and lock: std::ofstream _file_stream; ip::file_lock *_file_lock; boost::mutex _mutex; }; UHD_SINGLETON_FCN(log_resource_type, 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 ) { _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 << "," ; } #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 ; } } } void uhd::_log::log::set_log_level(uhd::log::severity_level level){ log_rs().level = level; } void uhd::_log::log::set_console_level(uhd::log::severity_level level){ log_rs().console_level = level; } void uhd::_log::log::set_file_level(uhd::log::severity_level level){ log_rs().file_level = level; }