diff options
Diffstat (limited to 'gnuradio-runtime')
-rw-r--r-- | gnuradio-runtime/CMakeLists.txt | 8 | ||||
-rw-r--r-- | gnuradio-runtime/include/gnuradio/logger.h | 832 | ||||
-rw-r--r-- | gnuradio-runtime/include/gnuradio/thrift_application_base.h | 5 | ||||
-rw-r--r-- | gnuradio-runtime/lib/CMakeLists.txt | 2 | ||||
-rw-r--r-- | gnuradio-runtime/lib/basic_block.cc | 9 | ||||
-rw-r--r-- | gnuradio-runtime/lib/block.cc | 4 | ||||
-rw-r--r-- | gnuradio-runtime/lib/logger.cc | 444 | ||||
-rw-r--r-- | gnuradio-runtime/lib/pmt/CMakeLists.txt | 2 | ||||
-rw-r--r-- | gnuradio-runtime/lib/qa_logger.cc | 19 | ||||
-rw-r--r-- | gnuradio-runtime/lib/tpb_thread_body.cc | 24 | ||||
-rw-r--r-- | gnuradio-runtime/python/gnuradio/gr/__init__.py | 13 | ||||
-rw-r--r-- | gnuradio-runtime/python/gnuradio/gr/bindings/docstrings/logger_pydoc_template.h | 128 | ||||
-rw-r--r-- | gnuradio-runtime/python/gnuradio/gr/bindings/logger_python.cc | 234 | ||||
-rw-r--r-- | gnuradio-runtime/python/gnuradio/gr/bindings/thrift_application_base_python.cc | 2 |
14 files changed, 416 insertions, 1310 deletions
diff --git a/gnuradio-runtime/CMakeLists.txt b/gnuradio-runtime/CMakeLists.txt index 83705cd83c..a81a5c1427 100644 --- a/gnuradio-runtime/CMakeLists.txt +++ b/gnuradio-runtime/CMakeLists.txt @@ -20,6 +20,11 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/ConfigChecks.cmake) GR_CHECK_LINUX_SCHED_AVAIL() ######################################################################## +# Find spdlog +######################################################################## +find_package(spdlog CONFIG) + +######################################################################## # Register component ######################################################################## include(GrComponent) @@ -27,7 +32,7 @@ GR_REGISTER_COMPONENT("gnuradio-runtime" ENABLE_GNURADIO_RUNTIME Boost_FOUND PYTHONINTERP_FOUND MPLIB_FOUND - LOG4CPP_FOUND + spdlog_FOUND pybind11_FOUND ) @@ -70,6 +75,7 @@ if(ENABLE_COMMON_PCH) PUBLIC $<$<COMPILE_LANGUAGE:CXX>:$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/gnuradio/top_block.h>> ) + target_link_libraries(common-precompiled-headers PUBLIC spdlog::spdlog) endif() ######################################################################## diff --git a/gnuradio-runtime/include/gnuradio/logger.h b/gnuradio-runtime/include/gnuradio/logger.h index dee74b8f5a..68dbf0ad33 100644 --- a/gnuradio-runtime/include/gnuradio/logger.h +++ b/gnuradio-runtime/include/gnuradio/logger.h @@ -1,6 +1,7 @@ /* -*- c++ -*- */ /* * Copyright 2012-2013 Free Software Foundation, Inc. + * Copyright 2021 Marcus Müller * * This file is part of GNU Radio * @@ -13,27 +14,60 @@ /*! * \ingroup logging - * \brief GNU Radio logging wrapper for log4cpp library (C++ port of log4j) + * \brief GNU Radio logging wrapper * */ -#ifdef _MSC_VER -typedef int mode_t; -#else -#include <sys/types.h> -#endif - // Since this file is included in *all* gr::blocks, please make sure this list of includes // keeps as short as possible; if anything is needed only by the implementation in // buffer.cc, then only include it there #include <gnuradio/api.h> -#include <log4cpp/Category.hh> -#include <boost/thread.hpp> -#include <cassert> +#include <fmt/core.h> +#include <fmt/format.h> +#include <spdlog/common.h> #include <memory> +#ifndef SPDLOG_FMT_EXTERNAL +#define SPDLOG_FMT_EXTERNAL +#endif +#include <spdlog/spdlog.h> + +#include <spdlog/sinks/dist_sink.h> + +#include <boost/format.hpp> namespace gr { +using log_level = spdlog::level::level_enum; + +class GR_RUNTIME_API logging +{ +public: + logging(logging const&) = delete; // delete copy ctor, this is a singleton class + void operator=(logging const&) = delete; // can't assign to singleton class + static logging& singleton(); //! \brief get the singleton + + //! \brief get the default logging level + inline log_level default_level() const { return _default_level; } + + //! \brief get the debug logging level + inline log_level debug_level() const { return _debug_level; } + spdlog::sink_ptr default_backend() const; + //! \brief adds a logging sink + void add_default_sink(const spdlog::sink_ptr& sink); + //! \brief adds a debugging sink + void add_debug_sink(const spdlog::sink_ptr& sink); + //! \brief add a default-constructed console sink to the default logger + void add_default_console_sink(); + //! \brief add a default-constructed console sink to the debugging logger + void add_debug_console_sink(); + + static constexpr const char* default_pattern = "%n :%l: %v"; + +private: + logging(); + const log_level _default_level, _debug_level; + std::shared_ptr<spdlog::sinks::dist_sink_mt> _default_backend, _debug_backend; +}; /*! * \brief GR_LOG macros @@ -51,564 +85,6 @@ namespace gr { * LOG_FATAL * LOG_EMERG */ -typedef log4cpp::Category* logger_ptr; - -} /* namespace gr */ - -/* Macros for Programmatic Configuration */ -#define GR_LOG_DECLARE_LOGPTR(logger) gr::logger_ptr logger - -#define GR_LOG_ASSIGN_LOGPTR(logger, name) logger = gr::logger_get_logger(name) - -#define GR_LOG_ASSIGN_CONFIGURED_LOGPTR(logger, name) \ - logger = gr::logger_get_configured_logger(name) - -#define GR_CONFIG_LOGGER(config) gr::logger_config::load_config(config) - -#define GR_CONFIG_AND_WATCH_LOGGER(config, period) \ - gr::logger_config::load_config(config, period) - -#define GR_LOG_GETLOGGER(logger, name) gr::logger_ptr logger = gr::logger_get_logger(name) - -#define GR_LOG_GET_CONFIGURED_LOGGER(logger, name) \ - gr::logger_ptr logger = gr::logger_get_configured_logger(name) - -#define GR_SET_LEVEL(name, level) \ - { \ - gr::logger_ptr logger = gr::logger_get_logger(name); \ - gr::logger_set_level(logger, level); \ - } - -#define GR_LOG_SET_LEVEL(logger, level) gr::logger_set_level(logger, level) - -#define GR_GET_LEVEL(name, level) \ - { \ - gr::logger_ptr logger = gr::logger_get_logger(name); \ - gr::logger_get_level(logger, level); \ - } - -#define GR_LOG_GET_LEVEL(logger, level) gr::logger_get_level(logger, level) - -#define GR_ADD_CONSOLE_APPENDER(name, target, pattern) \ - { \ - gr::logger_ptr logger = gr::logger_get_logger(name); \ - gr::logger_add_console_appender(logger, target, pattern); \ - } - -#define GR_LOG_ADD_CONSOLE_APPENDER(logger, target, pattern) \ - { \ - gr::logger_add_console_appender(logger, target, pattern); \ - } - -#define GR_SET_CONSOLE_APPENDER(name, target, pattern) \ - { \ - gr::logger_ptr logger = gr::logger_get_logger(name); \ - gr::logger_set_console_appender(logger, target, pattern); \ - } - -#define GR_LOG_SET_CONSOLE_APPENDER(logger, target, pattern) \ - { \ - gr::logger_set_console_appender(logger, target, pattern); \ - } - -#define GR_ADD_FILE_APPENDER(name, filename, append, pattern) \ - { \ - gr::logger_ptr logger = gr::logger_get_logger(name); \ - gr::logger_add_file_appender(logger, filename, append, pattern); \ - } - -#define GR_LOG_ADD_FILE_APPENDER(logger, filename, append, pattern) \ - { \ - gr::logger_add_file_appender(logger, filename, append, pattern); \ - } - -#define GR_SET_FILE_APPENDER(name, filename, append, pattern) \ - { \ - gr::logger_ptr logger = gr::logger_get_logger(name); \ - gr::logger_set_file_appender(logger, filename, append, pattern); \ - } - -#define GR_LOG_SET_FILE_APPENDER(logger, filename, append, pattern) \ - { \ - gr::logger_set_file_appender(logger, filename, append, pattern); \ - } - -#define GR_ADD_ROLLINGFILE_APPENDER( \ - name, filename, filesize, bkup_index, append, mode, pattern) \ - { \ - gr::logger_ptr logger = gr::logger_get_logger(name); \ - gr::logger_add_rollingfile_appender( \ - logger, filename, filesize, bkup_index, append, mode, pattern); \ - } - -#define GR_LOG_ADD_ROLLINGFILE_APPENDER( \ - logger, filename, filesize, bkup_index, append, mode, pattern) \ - { \ - gr::logger_add_rollingfile_appender( \ - logger, filename, filesize, bkup_index, append, mode, pattern); \ - } - -#define GR_GET_LOGGER_NAMES(names) \ - { \ - names = gr::logger_get_logger_names(); \ - } - -#define GR_RESET_CONFIGURATION() gr::logger_config::reset_config(); - -/* Logger name referenced macros */ -#define GR_DEBUG(name, msg) \ - { \ - gr::logger_ptr logger = gr::logger_get_logger(name); \ - *logger << log4cpp::Priority::DEBUG << (msg) << log4cpp::eol; \ - } - -#define GR_INFO(name, msg) \ - { \ - gr::logger_ptr logger = gr::logger_get_logger(name); \ - *logger << log4cpp::Priority::INFO << (msg) << log4cpp::eol; \ - } - -#define GR_NOTICE(name, msg) \ - { \ - gr::logger_ptr logger = gr::logger_get_logger(name); \ - *logger << log4cpp::Priority::NOTICE << (msg); \ - } - -#define GR_WARN(name, msg) \ - { \ - gr::logger_ptr logger = gr::logger_get_logger(name); \ - *logger << log4cpp::Priority::WARN << (msg) << log4cpp::eol; \ - } - -#define GR_ERROR(name, msg) \ - { \ - gr::logger_ptr logger = gr::logger_get_logger(name); \ - *logger << log4cpp::Priority::ERROR << (msg) << log4cpp::eol; \ - } - -#define GR_CRIT(name, msg) \ - { \ - gr::logger_ptr logger = gr::logger_get_logger(name); \ - *logger << log4cpp::Priority::CRIT << (msg) << log4cpp::eol; \ - } - -#define GR_ALERT(name, msg) \ - { \ - gr::logger_ptr logger = gr::logger_get_logger(name); \ - *logger << log4cpp::Priority::ALERT << (msg) << log4cpp::eol; \ - } - -#define GR_FATAL(name, msg) \ - { \ - gr::logger_ptr logger = gr::logger_get_logger(name); \ - *logger << log4cpp::Priority::FATAL << (msg) << log4cpp::eol; \ - } - -#define GR_EMERG(name, msg) \ - { \ - gr::logger_ptr logger = gr::logger_get_logger(name); \ - *logger << log4cpp::Priority::EMERG << (msg) << log4cpp::eol; \ - } - -#define GR_ERRORIF(name, cond, msg) \ - { \ - if ((cond)) { \ - gr::logger_ptr logger = gr::logger_get_logger(name); \ - *logger << log4cpp::Priority::ERROR << (msg) << log4cpp::eol; \ - } \ - } - -#define GR_ASSERT(name, cond, msg) \ - { \ - if (!(cond)) { \ - gr::logger_ptr logger = gr::logger_get_logger(name); \ - *logger << log4cpp::Priority::EMERG << (msg) << log4cpp::eol; \ - } \ - assert(0); \ - } - -/* LoggerPtr Referenced Macros */ -#define GR_LOG_DEBUG(logger, msg) \ - { \ - *logger << log4cpp::Priority::DEBUG << (msg) << log4cpp::eol; \ - } - -#define GR_LOG_INFO(logger, msg) \ - { \ - *logger << log4cpp::Priority::INFO << (msg) << log4cpp::eol; \ - } - -#define GR_LOG_NOTICE(logger, msg) \ - { \ - *logger << log4cpp::Priority::NOTICE << (msg) << log4cpp::eol; \ - } - -#define GR_LOG_WARN(logger, msg) \ - { \ - *logger << log4cpp::Priority::WARN << (msg) << log4cpp::eol; \ - } - -#define GR_LOG_ERROR(logger, msg) \ - { \ - *logger << log4cpp::Priority::ERROR << (msg) << log4cpp::eol; \ - } - -#define GR_LOG_CRIT(logger, msg) \ - { \ - *logger << log4cpp::Priority::CRIT << (msg) << log4cpp::eol; \ - } - -#define GR_LOG_ALERT(logger, msg) \ - { \ - *logger << log4cpp::Priority::ALERT << (msg) << log4cpp::eol; \ - } - -#define GR_LOG_FATAL(logger, msg) \ - { \ - *logger << log4cpp::Priority::FATAL << (msg) << log4cpp::eol; \ - } - -#define GR_LOG_EMERG(logger, msg) \ - { \ - *logger << log4cpp::Priority::EMERG << (msg) << log4cpp::eol; \ - } - -#define GR_LOG_ERRORIF(logger, cond, msg) \ - { \ - if ((cond)) { \ - *logger << log4cpp::Priority::ERROR << (msg) << log4cpp::eol; \ - } \ - } - -#define GR_LOG_ASSERT(logger, cond, msg) \ - { \ - if (!(cond)) { \ - *logger << log4cpp::Priority::EMERG << (msg) << log4cpp::eol; \ - assert(0); \ - } \ - } - -namespace gr { - -/*! - * \brief Class to control configuration of logger. - * This is a singleton that can launch a thread to watch a config file for changes - * \ingroup logging - */ -class GR_RUNTIME_API logger_config -{ -private: - /*! \brief filename of logger config file */ - std::string filename; - /*! \brief Period (seconds) over which watcher thread checks config file for changes - */ - unsigned int watch_period; - /*! \brief watch thread for config file changes */ - std::unique_ptr<boost::thread> watch_thread; - - /*! \brief Watcher thread method - * /param filename Name of configuration file - * /param watch_period Seconds between checks for changes in config file - */ - static void watch_file(std::string filename, unsigned int watch_period); - - static bool logger_configured; - - logger_config() /*: - rpc_get_filename("logger_config", "filename", &logger_config::get_filename4rpc, - pmt::mp(""), pmt::mp(""), pmt::mp(""), - "", "filename", RPC_PRIVLVL_MIN, - DISPTIME | DISPOPTSTRIP), - rpc_get_watchperiod("logger_config", "watch_period", - &logger_config::get_watchperiod4rpc, pmt::mp(0), pmt::mp(32768), pmt::mp(0), - "", "watch_period", RPC_PRIVLVL_MIN, - DISPTIME | DISPOPTSTRIP), - rpc_get_config("logger_config", "config", &logger_config::get_config4rpc, - pmt::mp(""), pmt::mp(""), pmt::mp(""), - "", "filename", RPC_PRIVLVL_MIN, - DISPTIME | DISPOPTSTRIP), - rpc_set_config("logger_config","config", &logger_config::set_config4rpc, - pmt::mp(""), pmt::mp(""), pmt::mp(""), - "", "filename", RPC_PRIVLVL_MIN, - DISPTIME | DISPOPTSTRIP) - */ - { - } //!< Constructor - - /* - rpcbasic_register_get<logger_config,std::string> rpc_get_filename; - rpcbasic_register_get<logger_config,int> rpc_get_watchperiod; - rpcbasic_register_get<logger_config,std::string> rpc_get_config; - rpcbasic_register_set<logger_config,std::string> rpc_set_config; - */ - - logger_config(logger_config const&); //!< Copy constructor - void operator=(logger_config const&); //!< Assignment Operator - - std::string get_filename4rpc() { return filename; } - int get_watchperiod4rpc() { return watch_period; }; - - std::string get_config4rpc() { return filename; } - - void set_config4rpc(std::string set) { printf("Set string was:%s\n", set.c_str()); } - - /*! \brief Instance getter for singleton. Only used by class. */ - static logger_config& get_instance(void); - -public: - /*! \brief destructor stops watch thread before exits */ - ~logger_config() { stop_watch(); } - /*! \brief Getter for config filename */ - static std::string get_filename(); - /*! \brief Getter for watch period */ - static unsigned int get_watch_period(); - /*! \brief Method to load configuration - * /param filename Name of configuration file - * /param watch_period Seconds between checks for changes in config file - */ - static void load_config(std::string filename, unsigned int watch_period = 0); - /*! \brief Method to stop watcher thread */ - static void stop_watch(); - /*! \brief method to reset logger configuration */ - static void reset_config(void); -}; - -/*! - * \brief Retrieve a pointer to a logger by name - * - * Retrieves a logger pointer - * \p name. - * - * \param name Name of the logger for which a pointer is requested - */ -GR_RUNTIME_API logger_ptr logger_get_logger(std::string name); - -/*! - * \brief Retrieve a pointer to a fully configured logger by name - * - * Retrieves a logger pointer. - * This method differs from logger_get_logger in that it configures the logger to - * reflect the current gnuradio configuration, including log level and log output file. - * - * \p name. - * - * \param name Name of the logger for which a pointer is requested - */ -GR_RUNTIME_API logger_ptr logger_get_configured_logger(const std::string& name); - -/*! - * \brief Load logger's configuration file. - * - * Initialize the GNU Radio logger by loading the configuration file - * \p config_filename. - * - * \param config_filename The configuration file. Set to "" for the - * basic logger that outputs to the console. - */ -GR_RUNTIME_API bool logger_load_config(const std::string& config_filename = ""); - -/*! - * \brief Reset logger's configuration file. - * - * Remove all appenders from loggers - */ -GR_RUNTIME_API void logger_reset_config(void); - -/*! - * \brief Set the logger's output level. - * - * Sets the level of the logger. This takes a string that is - * translated to the standard levels and can be (case insensitive): - * - * \li off , notset - * \li debug - * \li info - * \li notice - * \li warn - * \li error - * \li crit - * \li alert - * \li fatal - * \li emerg - * - * \param logger the logger to set the level of. - * \param level string to set the level to. - */ -GR_RUNTIME_API void logger_set_level(logger_ptr logger, const std::string& level); - -/*! - * \brief Set the logger's output level. - * - * Sets the level of the logger. This takes the actual Log4cpp::Priority - * data type, which can be: - * - * \li log4cpp::Priority::NOTSET - * \li log4cpp::Priority::DEBUG - * \li log4cpp::Priority::INFO - * \li log4cpp::Priority::NOTICE - * \li log4cpp::Priority::WARN - * \li log4cpp::Priority::ERROR - * \li log4cpp::Priority::CRIT - * \li log4cpp::Priority::ALERT - * \li log4cpp::Priority::FATAL - * \li log4cpp::Priority::EMERG - * - * \param logger the logger to set the level of. - * \param level new logger level of type Log4cpp::Priority - */ -GR_RUNTIME_API void logger_set_level(logger_ptr logger, log4cpp::Priority::Value level); - -/*! - * \brief Get the logger's output level. - * - * Gets the level of the logger. This returns a string that - * corresponds to the standard levels and can be (case insensitive): - * - * \li notset - * \li debug - * \li info - * \li notice - * \li warn - * \li error - * \li crit - * \li alert - * \li fatal - * \li emerg - * - * \param logger the logger to get the level of. - * \param level string to get the level into. - */ -GR_RUNTIME_API void logger_get_level(logger_ptr logger, std::string& level); - -/*! - * \brief Get the logger's output level. - * - * Gets the level of the logger. This returns the actual Log4cpp::Level - * data type, which can be: - * - * \li log4cpp::Priority::NOTSET - * \li log4cpp::Priority::DEBUG - * \li log4cpp::Priority::INFO - * \li log4cpp::Priority::NOTICE - * \li log4cpp::Priority::WARN - * \li log4cpp::Priority::ERROR - * \li log4cpp::Priority::CRIT - * \li log4cpp::Priority::ALERT - * \li log4cpp::Priority::FATAL - * \li log4cpp::Priority::EMERG - * - * \param logger the logger to get the level of. - * \param level of the logger. - */ -GR_RUNTIME_API void logger_get_level(logger_ptr logger, log4cpp::Priority::Value& level); - -/*! - * \brief Add console appender to a given logger - * - * Add console appender to a given logger - * - * \param logger Logger to which appender will be added - * \param appender Name of appender to add to logger - */ -GR_RUNTIME_API void logger_add_appender(logger_ptr logger, std::string appender); - -/*! - * \brief Sets a console appender to a given logger. Deletes any - * existing appenders and adds a new one. To add an additional - * appender, use logger_add_appender. - * - * \param logger Logger to which appender will be added - * \param appender Name of appender to add to logger - */ -GR_RUNTIME_API void logger_set_appender(logger_ptr logger, std::string appender); - -/*! - * \brief Add console appender to a given logger - * - * Add console appender to a given logger - * - * \param logger Logger to which appender will be added - * \param target Std target to write 'cout' or 'cerr' (default is cout) - * \param pattern Formatting pattern for log messages - */ -GR_RUNTIME_API void -logger_add_console_appender(logger_ptr logger, std::string target, std::string pattern); - -/*! - * \brief Sets a new console appender to a given logger after - * removing all others. Use logger_add_console_appender to add - * another. - * - * \param logger Logger to which appender will be added - * \param target Std target to write 'cout' or 'cerr' (default is cout) - * \param pattern Formatting pattern for log messages - */ -GR_RUNTIME_API void -logger_set_console_appender(logger_ptr logger, std::string target, std::string pattern); - -/*! - * \brief Add file appender to a given logger - * - * Add file appender to a given logger - * - * \param logger Logger to which appender will be added - * \param filename File to which log will be written - * \param append Overwrite or append to log file - * \param pattern Formatting pattern for log messages - */ -GR_RUNTIME_API void logger_add_file_appender(logger_ptr logger, - std::string filename, - bool append, - std::string pattern); - -/*! - * \brief Set a file appender to a given logger. To add another file - * appender, use logger_add_file_appender. - * - * \param logger Logger to which appender will be added - * \param filename File to which log will be written - * \param append Overwrite or append to log file - * \param pattern Formatting pattern for log messages - */ -GR_RUNTIME_API void logger_set_file_appender(logger_ptr logger, - std::string filename, - bool append, - std::string pattern); - -/*! - * \brief Add rolling file appender to a given logger - * - * Add rolling file appender to a given logger - * - * \param logger Logger to which appender will be added - * \param filename File to which log will be written - * \param filesize Sizez of files to write - * \param bkup_index Number of files to write - * \param append Overwrite or append to log file - * \param mode Permissions to set on log file - * \param pattern Formatting pattern for log messages - */ -GR_RUNTIME_API void logger_add_rollingfile_appender(logger_ptr logger, - std::string filename, - size_t filesize, - int bkup_index, - bool append, - mode_t mode, - std::string pattern); - -/*! - * \brief Add rolling file appender to a given logger - * - * Add rolling file appender to a given logger - * - * \return vector of string names of loggers - */ -GR_RUNTIME_API std::vector<std::string> logger_get_logger_names(void); - -} /* namespace gr */ - -// If Logger disable do nothing -namespace gr { /********************* Start Classes and Methods for Python ******************/ /*! @@ -621,126 +97,108 @@ namespace gr { class GR_RUNTIME_API logger { private: - /*! \brief logger pointer to logger associated wiith this wrapper class */ - GR_LOG_DECLARE_LOGPTR(d_logger); + /*! \brief pointer to logger associated with this wrapper class */ + std::string _name; + using underlying_logger_ptr = std::shared_ptr<spdlog::logger>; public: /*! * \brief constructor Provide name of logger to associate with this class * \param logger_name Name of logger associated with class */ - logger(std::string logger_name) { GR_LOG_ASSIGN_LOGPTR(d_logger, logger_name); }; + logger(const std::string& logger_name); /*! \brief Destructor */ - ~logger() { ; } + // FIXME implement or = default + ~logger() = default; + + underlying_logger_ptr d_logger; // Wrappers for logging macros /*! \brief inline function, wrapper to set the logger level */ - void set_level(std::string level) { GR_LOG_SET_LEVEL(d_logger, level); } + void set_level(const std::string& level); + void set_level(const log_level level); /*! \brief inline function, wrapper to get the logger level */ - void get_level(std::string& level) { GR_LOG_GET_LEVEL(d_logger, level); } - - /*! \brief inline function, wrapper for LOG4CPP_DEBUG for DEBUG message */ - void debug(std::string msg) { GR_LOG_DEBUG(d_logger, msg); }; - - /*! \brief inline function, wrapper for LOG4CPP_INFO for INFO message */ - void info(std::string msg) { GR_LOG_INFO(d_logger, msg); } - - /*! \brief inline function, wrapper for NOTICE message */ - void notice(std::string msg) { GR_LOG_NOTICE(d_logger, msg); } + void get_level(std::string& level) const; + const std::string get_string_level() const; + log_level get_level() const; - /*! \brief inline function, wrapper for LOG4CPP_WARN for WARN message */ - void warn(std::string msg) { GR_LOG_WARN(d_logger, msg); } + const std::string& name() const; + void set_name(const std::string& name); - /*! \brief inline function, wrapper for LOG4CPP_ERROR for ERROR message */ - void error(std::string msg) { GR_LOG_ERROR(d_logger, msg); } - - /*! \brief inline function, wrapper for NOTICE message */ - void crit(std::string msg) { GR_LOG_CRIT(d_logger, msg); } - - /*! \brief inline function, wrapper for ALERT message */ - void alert(std::string msg) { GR_LOG_ALERT(d_logger, msg); } + /*! \brief inline function, wrapper for TRACE message */ + template <typename... Args> + inline void trace(const spdlog::string_view_t& msg, const Args&... args) + { + d_logger->trace(msg, args...); + } - /*! \brief inline function, wrapper for FATAL message */ - void fatal(std::string msg) { GR_LOG_FATAL(d_logger, msg); } + /*! \brief inline function, wrapper for DEBUG message */ + template <typename... Args> + inline void debug(const spdlog::string_view_t& msg, const Args&... args) + { + d_logger->debug(msg, args...); + } - /*! \brief inline function, wrapper for EMERG message */ - void emerg(std::string msg) { GR_LOG_EMERG(d_logger, msg); } + /*! \brief inline function, wrapper for INFO message */ + template <typename... Args> + inline void info(const spdlog::string_view_t& msg, const Args&... args) + { + d_logger->info(msg, args...); + } - /*! \brief inline function, wrapper for LOG4CPP_ASSERT for conditional ERROR message - */ - void errorIF(bool cond, std::string msg) { GR_LOG_ERRORIF(d_logger, cond, msg); } + /*! \brief inline function, wrapper for INFO message, DEPRECATED */ + template <typename... Args> + inline void notice(const spdlog::string_view_t& msg, const Args&... args) + { + d_logger->info(msg, args...); + } - /*! \brief inline function, wrapper for LOG4CPP_ASSERT for conditional ERROR message - */ - void log_assert(bool cond, std::string msg) { GR_LOG_ASSERT(d_logger, cond, msg); } + /*! \brief inline function, wrapper for WARN message */ + template <typename... Args> + inline void warn(const spdlog::string_view_t& msg, const Args&... args) + { + d_logger->warn(msg, args...); + } - /*! \brief inline function, Method to add console appender to logger */ - void add_console_appender(std::string target, std::string pattern) + /*! \brief inline function, wrapper for ERROR message */ + template <typename... Args> + inline void error(const spdlog::string_view_t& msg, const Args&... args) { - GR_LOG_ADD_CONSOLE_APPENDER(d_logger, target, pattern); + d_logger->error(msg, args...); } - /*! \brief inline function, Method to set a console appender to logger */ - void set_console_appender(std::string target, std::string pattern) + /*! \brief inline function, wrapper for CRITICAL message */ + template <typename... Args> + inline void crit(const spdlog::string_view_t& msg, const Args&... args) { - GR_LOG_SET_CONSOLE_APPENDER(d_logger, target, pattern); + d_logger->critical(msg, args...); } - /*! \brief inline function, Method to add file appender to logger */ - void add_file_appender(std::string filename, bool append, std::string pattern) + /*! \brief inline function, wrapper for CRITICAL message, DEPRECATED */ + template <typename... Args> + inline void alert(const spdlog::string_view_t& msg, const Args&... args) { - GR_LOG_ADD_FILE_APPENDER(d_logger, filename, append, pattern); + d_logger->critical(msg, args...); } - /*! \brief inline function, Method to set a file appender to logger */ - void set_file_appender(std::string filename, bool append, std::string pattern) + /*! \brief inline function, wrapper for CRITICAL message, DEPRECATED */ + template <typename... Args> + inline void fatal(const spdlog::string_view_t& msg, const Args&... args) { - GR_LOG_SET_FILE_APPENDER(d_logger, filename, append, pattern); + d_logger->critical(msg, args...); } - /*! \brief inline function, Method to add rolling file appender to logger */ - void add_rollingfile_appender(std::string filename, - size_t filesize, - int bkup_index, - bool append, - mode_t mode, - std::string pattern) + /*! \brief inline function, wrapper for CRITICAL message, DEPRECATED */ + template <typename... Args> + inline void emerg(const spdlog::string_view_t& msg, const Args&... args) { - GR_LOG_ADD_ROLLINGFILE_APPENDER( - d_logger, filename, filesize, bkup_index, append, mode, pattern); + d_logger->critical(msg, args...); } }; +using logger_ptr = std::shared_ptr<logger>; -} /* namespace gr */ - -/**************** Start Configuration Class and Methods for Python ************/ -/*! - * \brief Function to call configuration macro from python. - * Note: Configuration is only updated if filename or watch_period has changed. - * \param config_filename Name of configuration file - * \param watch_period Seconds to wait between checking for changes in conf file. - * Watch_period defaults to 0 in which case the file is not watched for changes - */ -GR_RUNTIME_API void gr_logger_config(const std::string config_filename, - unsigned int watch_period = 0); - -/*! - * \brief Function to return logger names to python - * \return Vector of name strings - * - */ -GR_RUNTIME_API std::vector<std::string> gr_logger_get_logger_names(void); - -/*! - * \brief Function to reset logger configuration from python - * - */ -GR_RUNTIME_API void gr_logger_reset_config(void); - - -namespace gr { /*! * Function to use the GR prefs files to get and setup the two * default loggers defined there. The loggers are unique to the @@ -750,11 +208,73 @@ namespace gr { * automatically in gr::block. */ GR_RUNTIME_API bool -configure_default_loggers(gr::logger_ptr& l, gr::logger_ptr& d, const std::string name); - -GR_RUNTIME_API bool update_logger_alias(const std::string& name, - const std::string& alias); +configure_default_loggers(gr::logger_ptr& l, gr::logger_ptr& d, const std::string& name); } /* namespace gr */ +// global logging shorthands + +#define GR_LOG_TRACE(log, msg) \ + { \ + log->d_logger->trace(msg); \ + } + +#define GR_LOG_DEBUG(log, msg) \ + { \ + log->d_logger->debug(msg); \ + } + +#define GR_LOG_INFO(log, msg) \ + { \ + log->d_logger->info(msg); \ + } + +#define GR_LOG_NOTICE(log, msg) \ + { \ + log->d_logger->info(msg); \ + } + + +#define GR_LOG_WARN(log, msg) \ + { \ + log->d_logger->warn(msg); \ + } + +#define GR_LOG_ERROR(log, msg) \ + { \ + log->d_logger->error(msg); \ + } + +#define GR_LOG_CRIT(log, msg) \ + { \ + log->d_logger->critical(msg); \ + } + +#define GR_LOG_ALERT(log, msg) \ + { \ + log->d_logger->critical(msg); \ + } + +#define GR_LOG_FATAL(log, msg) \ + { \ + log->d_logger->critical(msg); \ + } + +#define GR_LOG_EMERG(log, msg) \ + { \ + log->d_logger->critical(msg); \ + } + +// Helper class to allow passing of boost::format to fmt +template <> +struct fmt::formatter<boost::format> : formatter<string_view> { + // parse is inherited from formatter<string_view>. + template <typename FormatContext> + auto format(const boost::format& bfmt, FormatContext& ctx) + -> decltype(formatter<string_view>::format(bfmt.str(), ctx)) + { + return formatter<string_view>::format(bfmt.str(), ctx); + } +}; + #endif /* INCLUDED_GR_LOGGER_H */ diff --git a/gnuradio-runtime/include/gnuradio/thrift_application_base.h b/gnuradio-runtime/include/gnuradio/thrift_application_base.h index c8bb8ed3f5..996c769c8c 100644 --- a/gnuradio-runtime/include/gnuradio/thrift_application_base.h +++ b/gnuradio-runtime/include/gnuradio/thrift_application_base.h @@ -214,9 +214,8 @@ void thrift_application_base<TserverBase, TserverClass>::start_application() } if (!app_started) { - GR_WARN("thrift_application_base", - "thrift_application_base::start_application(), " - "timeout waiting to port number might have failed?"); + gr::logger log("thrift_application_base"); + log.warn("start_application(): timeout waiting"); } p_impl->d_application_initialized = true; diff --git a/gnuradio-runtime/lib/CMakeLists.txt b/gnuradio-runtime/lib/CMakeLists.txt index 0afa89e09f..2d8a81ab10 100644 --- a/gnuradio-runtime/lib/CMakeLists.txt +++ b/gnuradio-runtime/lib/CMakeLists.txt @@ -130,7 +130,7 @@ target_link_libraries(gnuradio-runtime PUBLIC Boost::system Boost::regex Boost::thread - Log4Cpp::log4cpp + spdlog::spdlog MPLib::mplib # INTERFACE/PRIVATE split so users of the library can choose how to link to Python # (importantly, extension modules can avoid linking against Python and resolve diff --git a/gnuradio-runtime/lib/basic_block.cc b/gnuradio-runtime/lib/basic_block.cc index 988b399572..08ae079172 100644 --- a/gnuradio-runtime/lib/basic_block.cc +++ b/gnuradio-runtime/lib/basic_block.cc @@ -8,6 +8,7 @@ * */ +#include <memory> #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -36,9 +37,12 @@ basic_block::basic_block(const std::string& name, d_symbol_name(global_block_registry.register_symbolic_name(this)), d_color(WHITE), d_rpc_set(false), + d_logger(std::make_shared<gr::logger>(name)), + d_debug_logger(std::make_shared<logger_ptr::element_type>(name + " (debug)")), d_message_subscribers(pmt::make_dict()) { - configure_default_loggers(d_logger, d_debug_logger, d_symbol_name); + d_logger->set_level(logging::singleton().default_level()); + d_debug_logger->set_level(logging::singleton().debug_level()); s_ncurrently_allocated++; } @@ -62,7 +66,8 @@ void basic_block::set_block_alias(std::string name) // set the block's alias d_symbol_alias = name; - update_logger_alias(symbol_name(), d_symbol_alias); + d_logger->set_name(name); + d_debug_logger->set_name(name + " (debug)"); } // ** Message passing interface ** diff --git a/gnuradio-runtime/lib/block.cc b/gnuradio-runtime/lib/block.cc index f2970cd667..d4a1e1c6c1 100644 --- a/gnuradio-runtime/lib/block.cc +++ b/gnuradio-runtime/lib/block.cc @@ -762,12 +762,12 @@ void block::system_handler(pmt::pmt_t msg) } } -void block::set_log_level(std::string level) { logger_set_level(d_logger, level); } +void block::set_log_level(std::string level) { d_logger->set_level(level); } std::string block::log_level() { std::string level; - logger_get_level(d_logger, level); + d_logger->get_level(level); return level; } diff --git a/gnuradio-runtime/lib/logger.cc b/gnuradio-runtime/lib/logger.cc index 4721571119..64219d6a8e 100644 --- a/gnuradio-runtime/lib/logger.cc +++ b/gnuradio-runtime/lib/logger.cc @@ -1,6 +1,7 @@ /* -*- c++ -*- */ /* * Copyright 2012 Free Software Foundation, Inc. + * Copyright 2021 Marcus Müller * * This file is part of GNU Radio * @@ -8,410 +9,143 @@ * */ -/******************************************************************************* - * Author: Mark Plett - * Description: - * The gr_log module wraps the log4cpp library for logging in gnuradio. - *******************************************************************************/ - +#include <spdlog/common.h> #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <gnuradio/logger.h> - #include <gnuradio/prefs.h> -#include <log4cpp/FileAppender.hh> -#include <log4cpp/OstreamAppender.hh> -#include <log4cpp/PatternLayout.hh> -#include <log4cpp/PropertyConfigurator.hh> -#include <log4cpp/RollingFileAppender.hh> -#include <boost/format.hpp> -#include <boost/thread.hpp> +#include <spdlog/async.h> +#include <spdlog/sinks/basic_file_sink.h> +#include <spdlog/sinks/dist_sink.h> +#include <spdlog/sinks/stdout_color_sinks.h> +#include <spdlog/spdlog.h> -#include <algorithm> -#include <filesystem> -#include <iostream> #include <memory> -#include <stdexcept> namespace gr { - -bool logger_config::logger_configured(false); - -/************************ BEGIN LOG4CPP HELPERS ***********************/ -/* Logger config class. This is a singleton that controls how - * log4cpp is configured If watch_period>0 a thread is started to - * watch the config file for changes. - */ - -// Getters of logger_config -logger_config& logger_config::get_instance(void) -{ - static logger_config instance; - return instance; -} - -std::string logger_config::get_filename() -{ - logger_config& in = get_instance(); - return in.filename; -} - -unsigned int logger_config::get_watch_period() -{ - logger_config& in = get_instance(); - return in.watch_period; -} - -// Method to watch config file for changes -void logger_config::watch_file(std::string filename, unsigned int watch_period) -{ - auto last_write = std::filesystem::last_write_time(filename); - while (true) { - try { - auto current_time = std::filesystem::last_write_time(filename); - if (current_time > last_write) { - // std::cout<<"GNURadio Reloading logger - // configuration:"<<filename<<std::endl; - last_write = current_time; - // Should we wipe out all old configuration or just add the - // new? Just adding... logger_reset_config(); - logger_configured = logger_load_config(filename); - } - boost::this_thread::sleep( - boost::posix_time::time_duration(0, 0, watch_period, 0)); - } catch (const boost::thread_interrupted&) { - std::cerr << "GNURadio leaving logger config file watch." << std::endl; - break; - } +logging::logging() + : _default_level(spdlog::level::from_str( + prefs::singleton()->get_string("LOG", "log_level", "off"))), + _debug_level(spdlog::level::from_str( + prefs::singleton()->get_string("LOG", "debug_level", "off"))), + _default_backend(std::make_shared<spdlog::sinks::dist_sink_mt>()), + _debug_backend(std::make_shared<spdlog::sinks::dist_sink_mt>()) +{ + _default_backend->set_level(_default_level); + _debug_backend->set_level(_debug_level); + + + auto debug_console_sink = std::make_shared<spdlog::sinks::stderr_color_sink_st>(); + _debug_backend->add_sink(debug_console_sink); + + const auto pref = prefs::singleton(); + const auto default_file = pref->get_string("LOG", "log_file", ""); + + if (default_file == "stderr") { + auto console_sink = std::make_shared<spdlog::sinks::stderr_color_sink_st>(); + _default_backend->add_sink(console_sink); + } else if ((!default_file.empty()) && default_file != "stdout") { + auto file_sink = + std::make_shared<spdlog::sinks::basic_file_sink_st>(default_file, true); + _default_backend->add_sink(file_sink); } -} - -// Method to load the configuration. It only loads if the filename -// or watch has changed -void logger_config::load_config(std::string filename, unsigned int watch_period) -{ - logger_config& instance = get_instance(); - // Only reconfigure if filename or watch has changed - if (!logger_configured) { - instance.filename = filename; - instance.watch_period = watch_period; - // Stop any file watching thread - if (instance.watch_thread) - stop_watch(); - // Load configuration - // std::cout<<"GNURadio Loading logger - // configuration:"<<instance.filename<<std::endl; - logger_configured = logger_load_config(instance.filename); - // Start watch if required - if (instance.watch_period > 0) { - instance.watch_thread = std::make_unique<boost::thread>( - watch_file, instance.filename, instance.watch_period); - } - } -} - -// Method to stop the watcher thread -void logger_config::stop_watch() -{ - logger_config& instance = get_instance(); - if (instance.watch_thread) { - instance.watch_thread->interrupt(); - instance.watch_thread->join(); - instance.watch_thread.reset(); - } -} - -// Method to reset logger configuration -void logger_config::reset_config(void) -{ - logger_config& instance = get_instance(); - stop_watch(); - std::vector<log4cpp::Category*>* loggers = log4cpp::Category::getCurrentCategories(); - std::vector<log4cpp::Category*>::iterator logger = loggers->begin(); - // We can't destroy categories but we can neuter them by removing all appenders. - for (; logger != loggers->end(); logger++) { - (*logger)->removeAllAppenders(); - } - instance.filename = std::string(""); - instance.watch_period = 0; - logger_configured = false; -} - -/***************** Functions to call log4cpp methods *************************/ - -logger_ptr logger_get_logger(std::string name) -{ - if (log4cpp::Category::exists(name)) { - logger_ptr logger = &log4cpp::Category::getInstance(name); - return logger; - } else { - logger_ptr logger = &log4cpp::Category::getInstance(name); - logger->setPriority(log4cpp::Priority::NOTSET); - return logger; - } -} - -logger_ptr logger_get_configured_logger(const std::string& name) -{ - if (log4cpp::Category::exists(name)) - return &log4cpp::Category::getInstance(name); - - prefs* p = prefs::singleton(); - std::string config_file = p->get_string("LOG", "log_config", ""); - std::string log_level = p->get_string("LOG", "log_level", "off"); - std::string log_file = p->get_string("LOG", "log_file", ""); - GR_LOG_GETLOGGER(LOG, "gr_log." + name); - GR_LOG_SET_LEVEL(LOG, log_level); - - if (!log_file.empty()) { - if (log_file == "stdout") { - GR_LOG_SET_CONSOLE_APPENDER(LOG, "stdout", "gr::log :%p: %c{1} - %m%n"); - } else if (log_file == "stderr") { - GR_LOG_SET_CONSOLE_APPENDER(LOG, "stderr", "gr::log :%p: %c{1} - %m%n"); - } else { - GR_LOG_SET_FILE_APPENDER(LOG, log_file, true, "%r :%p: %c{1} - %m%n"); - } + if (default_file == "stdout") { + auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_st>(); + _default_backend->add_sink(console_sink); } - return LOG; } -bool logger_load_config(const std::string& config_filename) -{ - if (!config_filename.empty()) { - try { - log4cpp::PropertyConfigurator::configure(config_filename); - return true; - } catch (log4cpp::ConfigureFailure& e) { - std::cerr << "Logger config failed :" << e.what() << std::endl; - } - } - return false; -} -void logger_set_level(logger_ptr logger, const std::string& level) +logging& logging::singleton() { - std::string nocase = level; - std::transform(level.begin(), level.end(), nocase.begin(), ::tolower); - - if (nocase == "off" || nocase == "notset") - logger_set_level(logger, log4cpp::Priority::NOTSET); - else if (nocase == "all" || nocase == "debug") - logger_set_level(logger, log4cpp::Priority::DEBUG); - else if (nocase == "info") - logger_set_level(logger, log4cpp::Priority::INFO); - else if (nocase == "notice") - logger_set_level(logger, log4cpp::Priority::NOTICE); - else if (nocase == "warn") - logger_set_level(logger, log4cpp::Priority::WARN); - else if (nocase == "error") - logger_set_level(logger, log4cpp::Priority::ERROR); - else if (nocase == "crit") - logger_set_level(logger, log4cpp::Priority::CRIT); - else if (nocase == "alert") - logger_set_level(logger, log4cpp::Priority::ALERT); - else if (nocase == "fatal") - logger_set_level(logger, log4cpp::Priority::FATAL); - else if (nocase == "emerg") - logger_set_level(logger, log4cpp::Priority::EMERG); - else - throw std::runtime_error("logger_set_level: Bad level type."); + static logging the_only_one; + return the_only_one; } -void logger_set_level(logger_ptr logger, log4cpp::Priority::Value level) +spdlog::sink_ptr logging::default_backend() const { return _default_backend; } +void logging::add_default_sink(const spdlog::sink_ptr& sink) { - logger->setPriority(level); -} - -void logger_get_level(logger_ptr logger, std::string& level) + _default_backend->add_sink(sink); +}; +void logging::add_debug_sink(const spdlog::sink_ptr& sink) { - log4cpp::Priority::Value levelPtr = logger->getPriority(); - if (levelPtr == log4cpp::Priority::NOTSET) - level = "notset"; - if (levelPtr == log4cpp::Priority::DEBUG) - level = "debug"; - if (levelPtr == log4cpp::Priority::INFO) - level = "info"; - if (levelPtr == log4cpp::Priority::NOTICE) - level = "notice"; - if (levelPtr == log4cpp::Priority::WARN) - level = "warn"; - if (levelPtr == log4cpp::Priority::ERROR) - level = "error"; - if (levelPtr == log4cpp::Priority::CRIT) - level = "crit"; - if (levelPtr == log4cpp::Priority::ALERT) - level = "alert"; - if (levelPtr == log4cpp::Priority::FATAL) - level = "fatal"; - if (levelPtr == log4cpp::Priority::EMERG) - level = "emerg"; -} + _debug_backend->add_sink(sink); +}; -void logger_get_level(logger_ptr logger, log4cpp::Priority::Value level) +void logging::add_default_console_sink() { - level = logger->getPriority(); + add_default_sink(std::make_shared<spdlog::sinks::stdout_color_sink_st>()); } -void logger_add_console_appender(logger_ptr logger, - std::string target, - std::string pattern) +void logging::add_debug_console_sink() { - log4cpp::PatternLayout* layout = new log4cpp::PatternLayout(); - log4cpp::Appender* app; - if (target == "stdout") - app = new log4cpp::OstreamAppender("ConsoleAppender::", &std::cout); - else - app = new log4cpp::OstreamAppender("ConsoleAppender::", &std::cerr); - - layout->setConversionPattern(pattern); - app->setLayout(layout); - logger->setAppender(app); + add_debug_sink(std::make_shared<spdlog::sinks::stdout_color_sink_st>()); } -void logger_set_console_appender(logger_ptr logger, - std::string target, - std::string pattern) +logger::logger(const std::string& logger_name) + : _name(logger_name), + d_logger( + std::make_shared<spdlog::logger>(_name, logging::singleton().default_backend())) { - logger->removeAllAppenders(); - logger_add_console_appender(logger, target, pattern); + d_logger->set_level(logging::singleton().default_level()); + // gr::log :%p: %c{1} - %m%n + // :level: block alias - message + d_logger->set_pattern(logging::default_pattern); } -void logger_add_file_appender(logger_ptr logger, - std::string filename, - bool append, - std::string pattern) -{ - log4cpp::PatternLayout* layout = new log4cpp::PatternLayout(); - log4cpp::Appender* app = - new log4cpp::FileAppender("FileAppender::" + filename, filename, append); - layout->setConversionPattern(pattern); - app->setLayout(layout); - logger->setAppender(app); -} +log_level logger::get_level() const { return d_logger->level(); } -void logger_set_file_appender(logger_ptr logger, - std::string filename, - bool append, - std::string pattern) +const std::string logger::get_string_level() const { - logger->removeAllAppenders(); - logger_add_file_appender(logger, filename, append, pattern); + const auto view = spdlog::level::to_string_view(d_logger->level()); + return std::string(view.begin(), view.end()); } -void logger_add_rollingfile_appender(logger_ptr logger, - std::string filename, - size_t filesize, - int bkup_index, - bool append, - mode_t mode, - std::string pattern) -{ - log4cpp::PatternLayout* layout = new log4cpp::PatternLayout(); - log4cpp::Appender* app = new log4cpp::RollingFileAppender( - "RollFileAppender::" + filename, filename, filesize, bkup_index, append, mode); - layout->setConversionPattern(pattern); - app->setLayout(layout); - logger->setAppender(app); -} +void logger::get_level(std::string& level) const { level = get_string_level(); } +void logger::set_level(const log_level level) { d_logger->set_level(level); } -std::vector<std::string> logger_get_logger_names(void) +void logger::set_level(const std::string& level) { - std::vector<std::string> names; - std::vector<log4cpp::Category*>* loggers = log4cpp::Category::getCurrentCategories(); - std::vector<log4cpp::Category*>::iterator logger = loggers->begin(); - - for (; logger != loggers->end(); logger++) { - names.push_back((*logger)->getName()); + auto parsed_level = spdlog::level::from_str(level); + if (parsed_level == spdlog::level::off) { + if (level == "notice") { + parsed_level = spdlog::level::info; + } else if (level == "warn") { + // older versions of spdlog don't convert this themselves + parsed_level = spdlog::level::warn; + } else if (level == "crit" || level == "alert" || level == "fatal" || + level == "emerg") { + parsed_level = spdlog::level::critical; + } } - return names; + d_logger->set_level(parsed_level); } -} /* namespace gr */ - -/****** Start Methods to provide Python the capabilities of the macros ********/ -void gr_logger_config(const std::string config_filename, unsigned int watch_period) +const std::string& logger::name() const { return _name; } +void logger::set_name(const std::string& name) { - GR_CONFIG_AND_WATCH_LOGGER(config_filename, watch_period); + _name = name; + d_logger->set_pattern(logging::default_pattern); } -std::vector<std::string> gr_logger_get_logger_names(void) -{ - std::vector<std::string> names; - GR_GET_LOGGER_NAMES(names); - return names; -} - -void gr_logger_reset_config(void) { GR_RESET_CONFIGURATION(); } - -// Remaining capability provided by gr::logger class in gnuradio/logger.h - -namespace gr { - bool configure_default_loggers(gr::logger_ptr& l, gr::logger_ptr& d, - const std::string name) + const std::string& name) { - prefs* p = prefs::singleton(); - std::string config_file = p->get_string("LOG", "log_config", ""); - std::string log_level = p->get_string("LOG", "log_level", "off"); - std::string log_file = p->get_string("LOG", "log_file", ""); - std::string debug_level = p->get_string("LOG", "debug_level", "off"); - std::string debug_file = p->get_string("LOG", "debug_file", ""); - - GR_CONFIG_LOGGER(config_file); - - GR_LOG_GETLOGGER(LOG, "gr_log." + name); - GR_LOG_SET_LEVEL(LOG, log_level); - - if (!log_file.empty()) { - if (log_file == "stdout") { - GR_LOG_SET_CONSOLE_APPENDER(LOG, "stdout", "gr::log :%p: %c{1} - %m%n"); - } else if (log_file == "stderr") { - GR_LOG_SET_CONSOLE_APPENDER(LOG, "stderr", "gr::log :%p: %c{1} - %m%n"); - } else { - GR_LOG_SET_FILE_APPENDER(LOG, log_file, true, "%r :%p: %c{1} - %m%n"); - } + if (l == nullptr) { + l = std::make_shared<gr::logger_ptr::element_type>(name); } - l = LOG; - - GR_LOG_GETLOGGER(DLOG, "gr_log_debug." + name); - GR_LOG_SET_LEVEL(DLOG, debug_level); - if (!debug_file.empty()) { - if (debug_file == "stdout") { - GR_LOG_SET_CONSOLE_APPENDER(DLOG, "stdout", "gr::debug :%p: %c{1} - %m%n"); - } else if (debug_file == "stderr") { - GR_LOG_SET_CONSOLE_APPENDER(DLOG, "stderr", "gr::debug :%p: %c{1} - %m%n"); - } else { - GR_LOG_SET_FILE_APPENDER(DLOG, debug_file, true, "%r :%p: %c{1} - %m%n"); - } - } - d = DLOG; - return true; -} - -bool update_logger_alias(const std::string& name, const std::string& alias) -{ - prefs* p = prefs::singleton(); - std::string log_file = p->get_string("LOG", "log_file", ""); - std::string debug_file = p->get_string("LOG", "debug_file", ""); - - GR_LOG_GETLOGGER(LOG, "gr_log." + name); - if (!log_file.empty()) { - if (log_file == "stdout") { - boost::format str("gr::log :%%p: %1% - %%m%%n"); - GR_LOG_SET_CONSOLE_APPENDER(LOG, "stdout", boost::str(str % alias)); - } else if (log_file == "stderr") { - boost::format str("gr::log :%%p: %1% - %%m%%n"); - GR_LOG_SET_CONSOLE_APPENDER(LOG, "stderr", boost::str(str % alias)); - } else { - boost::format str("%%r :%%p: %1% - %%m%%n"); - GR_LOG_SET_FILE_APPENDER(LOG, log_file, true, boost::str(str % alias)); - } + if (d == nullptr) { + d = std::make_shared<gr::logger_ptr::element_type>(name); } + l->set_level(logging::singleton().default_level()); + d->set_level(logging::singleton().debug_level()); + l->set_name(name); + d->set_name(name); return true; } diff --git a/gnuradio-runtime/lib/pmt/CMakeLists.txt b/gnuradio-runtime/lib/pmt/CMakeLists.txt index a45869d218..76d7ddeca5 100644 --- a/gnuradio-runtime/lib/pmt/CMakeLists.txt +++ b/gnuradio-runtime/lib/pmt/CMakeLists.txt @@ -16,7 +16,7 @@ add_library(gnuradio-pmt target_link_libraries(gnuradio-pmt Boost::boost Boost::thread - Log4Cpp::log4cpp + spdlog::spdlog Volk::volk ) diff --git a/gnuradio-runtime/lib/qa_logger.cc b/gnuradio-runtime/lib/qa_logger.cc index 597e21e77a..99a412dc7c 100644 --- a/gnuradio-runtime/lib/qa_logger.cc +++ b/gnuradio-runtime/lib/qa_logger.cc @@ -19,21 +19,18 @@ #include <gnuradio/logger.h> #include <boost/test/unit_test.hpp> +#include <memory> BOOST_AUTO_TEST_CASE(t1) { -#ifdef ENABLE_GR_LOG // This doesn't really test anything, more just // making sure nothing's gone horribly wrong. - - GR_LOG_GETLOGGER(LOG, "main"); - GR_ADD_CONSOLE_APPENDER("main", "cout", "%d{%H:%M:%S} : %m%n"); - GR_LOG_NOTICE(LOG, "test from c++ NOTICE"); - GR_LOG_DEBUG(LOG, "test from c++ DEBUG"); - GR_LOG_INFO(LOG, "test from c++ INFO"); - GR_LOG_WARN(LOG, "test from c++ WARN"); - GR_LOG_ERROR(LOG, "test from c++ ERROR"); - GR_LOG_FATAL(LOG, "test from c++ FATAL"); + auto log = std::make_shared<gr::logger>("main"); + GR_LOG_NOTICE(log, "test from c++ NOTICE"); + GR_LOG_DEBUG(log, "test from c++ DEBUG"); + GR_LOG_INFO(log, "test from c++ INFO"); + GR_LOG_WARN(log, "test from c++ WARN"); + GR_LOG_ERROR(log, "test from c++ ERROR"); + GR_LOG_FATAL(log, "test from c++ FATAL"); BOOST_CHECK(true); -#endif } diff --git a/gnuradio-runtime/lib/tpb_thread_body.cc b/gnuradio-runtime/lib/tpb_thread_body.cc index d6e7637952..abf722dec2 100644 --- a/gnuradio-runtime/lib/tpb_thread_body.cc +++ b/gnuradio-runtime/lib/tpb_thread_body.cc @@ -49,23 +49,9 @@ tpb_thread_body::tpb_thread_body(block_sptr block, prefs* p = prefs::singleton(); size_t max_nmsgs = static_cast<size_t>(p->get_long("DEFAULT", "max_messages", 100)); -// Setup the logger for the scheduler -#undef LOG - std::string config_file = p->get_string("LOG", "log_config", ""); - std::string log_level = p->get_string("LOG", "log_level", "off"); - std::string log_file = p->get_string("LOG", "log_file", ""); - GR_LOG_GETLOGGER(LOG, "gr_log.tpb_thread_body"); - GR_LOG_SET_LEVEL(LOG, log_level); - GR_CONFIG_LOGGER(config_file); - if (!log_file.empty()) { - if (log_file == "stdout") { - GR_LOG_SET_CONSOLE_APPENDER(LOG, "stdout", "gr::log :%p: %c{1} - %m%n"); - } else if (log_file == "stderr") { - GR_LOG_SET_CONSOLE_APPENDER(LOG, "stderr", "gr::log :%p: %c{1} - %m%n"); - } else { - GR_LOG_SET_FILE_APPENDER(LOG, log_file, true, "%r :%p: %c{1} - %m%n"); - } - } + // Set up logging + auto logger = gr::logger("tpb_thread_body"); + // Set thread affinity if it was set before fg was started. if (!block->processor_affinity().empty()) { @@ -99,8 +85,8 @@ tpb_thread_body::tpb_thread_body(block_sptr block, // If we don't have a handler but are building up messages, // prune the queue from the front to keep memory in check. if (block->nmsgs(i.first) > max_nmsgs) { - GR_LOG_WARN( - LOG, "asynchronous message buffer overflowing, dropping message"); + logger.warn( + "asynchronous message buffer overflowing, dropping message"); msg = block->delete_head_nowait(i.first); } } diff --git a/gnuradio-runtime/python/gnuradio/gr/__init__.py b/gnuradio-runtime/python/gnuradio/gr/__init__.py index 5103bf97a7..0715a641ea 100644 --- a/gnuradio-runtime/python/gnuradio/gr/__init__.py +++ b/gnuradio-runtime/python/gnuradio/gr/__init__.py @@ -1,5 +1,6 @@ # # Copyright 2003-2012, 2018 Free Software Foundation, Inc. +# Copyright 2021 Marcus Müller # # This file is part of GNU Radio # @@ -39,19 +40,7 @@ from .gateway import basic_block, sync_block, decim_block, interp_block, py_io_s log = gr.logger("log") log.set_level(prefs().get_string("LOG", "log_level", "notset")) -log_file = gr.prefs().get_string("LOG", "log_file", ""); -if(log_file == "stderr" or log_file == "stdout"): - log.add_console_appender(log_file, "gr::log %d :%p: %m%n") -else: - log.add_file_appender(log_file, True, "%r :%p: %c{1} - %m%n") - log_debug = gr.logger("log_debug") -log_debug.set_level(prefs().get_string("LOG", "debug_level", "notset")) -log_debug_file = gr.prefs().get_string("LOG", "debug_file", ""); -if(log_debug_file == "stderr" or log_file == "stdout"): - log_debug.add_console_appender(log_debug_file, "gr::log %d :%p: %m%n") -else: - log_debug.add_file_appender(log_debug_file, True, "%r :%p: %c{1} - %m%n") io_signaturev = io_signature.makev io_signature3 = io_signature.make3 diff --git a/gnuradio-runtime/python/gnuradio/gr/bindings/docstrings/logger_pydoc_template.h b/gnuradio-runtime/python/gnuradio/gr/bindings/docstrings/logger_pydoc_template.h index 84d2e5e9cf..a52071098f 100644 --- a/gnuradio-runtime/python/gnuradio/gr/bindings/docstrings/logger_pydoc_template.h +++ b/gnuradio-runtime/python/gnuradio/gr/bindings/docstrings/logger_pydoc_template.h @@ -16,132 +16,26 @@ static const char* __doc_gr_logger = R"doc()doc"; - - -static const char* __doc_gr_logger_logger_0 = R"doc()doc"; - - -static const char* __doc_gr_logger_logger_1 = R"doc()doc"; - - +static const char* __doc_gr_logger_logger = R"doc()doc"; static const char* __doc_gr_logger_set_level = R"doc()doc"; - - static const char* __doc_gr_logger_get_level = R"doc()doc"; - - +static const char* __doc_gr_logger_get_string_level = R"doc()doc"; +static const char* __doc_gr_logger_trace = R"doc()doc"; static const char* __doc_gr_logger_debug = R"doc()doc"; - - static const char* __doc_gr_logger_info = R"doc()doc"; - - static const char* __doc_gr_logger_notice = R"doc()doc"; - - static const char* __doc_gr_logger_warn = R"doc()doc"; - - static const char* __doc_gr_logger_error = R"doc()doc"; - - static const char* __doc_gr_logger_crit = R"doc()doc"; - - static const char* __doc_gr_logger_alert = R"doc()doc"; - - static const char* __doc_gr_logger_fatal = R"doc()doc"; - - static const char* __doc_gr_logger_emerg = R"doc()doc"; - -static const char* __doc_gr_logger_errorIF = R"doc()doc"; - - -static const char* __doc_gr_logger_log_assert = R"doc()doc"; - - -static const char* __doc_gr_logger_add_console_appender = R"doc()doc"; - - -static const char* __doc_gr_logger_set_console_appender = R"doc()doc"; - - -static const char* __doc_gr_logger_add_file_appender = R"doc()doc"; - - -static const char* __doc_gr_logger_set_file_appender = R"doc()doc"; - - -static const char* __doc_gr_logger_add_rollingfile_appender = R"doc()doc"; - - -static const char* __doc_gr_logger_config = R"doc()doc"; - - -static const char* __doc_gr_logger_config_get_filename = R"doc()doc"; - - -static const char* __doc_gr_logger_config_get_watch_period = R"doc()doc"; - - -static const char* __doc_gr_logger_config_load_config = R"doc()doc"; - - -static const char* __doc_gr_logger_config_stop_watch = R"doc()doc"; - - -static const char* __doc_gr_logger_config_reset_config = R"doc()doc"; - - -static const char* __doc_gr_configure_default_loggers = R"doc()doc"; - - -static const char* __doc_gr_update_logger_alias = R"doc()doc"; - - -static const char* __doc_gr_logger_get_logger = R"doc()doc"; - - -static const char* __doc_gr_logger_load_config = R"doc()doc"; - - -static const char* __doc_gr_logger_reset_config = R"doc()doc"; - - -static const char* __doc_gr_logger_set_level_0 = R"doc()doc"; - - -static const char* __doc_gr_logger_set_level_1 = R"doc()doc"; - - -static const char* __doc_gr_logger_get_level_0 = R"doc()doc"; - - -static const char* __doc_gr_logger_get_level_1 = R"doc()doc"; - - -static const char* __doc_gr_logger_add_appender = R"doc()doc"; - - -static const char* __doc_gr_logger_set_appender = R"doc()doc"; - - -// static const char *__doc_gr_logger_add_console_appender = R"doc()doc"; - - -// static const char *__doc_gr_logger_set_console_appender = R"doc()doc"; - - -// static const char *__doc_gr_logger_add_file_appender = R"doc()doc"; - - -// static const char *__doc_gr_logger_set_file_appender = R"doc()doc"; - - -// static const char *__doc_gr_logger_add_rollingfile_appender = R"doc()doc"; - - -static const char* __doc_gr_logger_get_logger_names = R"doc()doc"; +static const char* __doc_gr_logging_singleton = R"doc()doc"; +static const char* __doc_gr_logging_default_level = R"doc()doc"; +static const char* __doc_gr_logging_debug_level = R"doc()doc"; +static const char* __doc_gr_logging_add_default_sink = R"doc()doc"; +static const char* __doc_gr_logging_add_debug_sink = R"doc()doc"; +static const char* __doc_gr_logging_add_default_console_sink = R"doc()doc"; +static const char* __doc_gr_logging_add_debug_console_sink = R"doc()doc"; +static const char* __doc_gr_logging_default_pattern = R"doc()doc"; diff --git a/gnuradio-runtime/python/gnuradio/gr/bindings/logger_python.cc b/gnuradio-runtime/python/gnuradio/gr/bindings/logger_python.cc index 0245cf53dd..f82cfaaebf 100644 --- a/gnuradio-runtime/python/gnuradio/gr/bindings/logger_python.cc +++ b/gnuradio-runtime/python/gnuradio/gr/bindings/logger_python.cc @@ -1,5 +1,6 @@ /* * Copyright 2020 Free Software Foundation, Inc. + * Copyright 2021 Marcus Müller * * This file is part of GNU Radio * @@ -14,152 +15,127 @@ /* BINDTOOL_GEN_AUTOMATIC(0) */ /* BINDTOOL_USE_PYGCCXML(0) */ /* BINDTOOL_HEADER_FILE(logger.h) */ -/* BINDTOOL_HEADER_FILE_HASH(6a082fd5563025225373d3f117d2134c) */ +/* BINDTOOL_HEADER_FILE_HASH(7f3cbb9463e52829b27c58d9dd41b422) */ /***********************************************************************************/ #include <pybind11/complex.h> #include <pybind11/pybind11.h> #include <pybind11/stl.h> +#include <spdlog/common.h> namespace py = pybind11; #include <gnuradio/logger.h> +#include <logger_pydoc.h> void bind_logger(py::module& m) { - using logger = gr::logger; - using logger_config = gr::logger_config; + py::enum_<spdlog::level::level_enum>(m, "level_enum") + // Values directly from spdlog/common.h + .value("trace", spdlog::level::trace) + .value("debug", spdlog::level::debug) + .value("info", spdlog::level::info) + .value("warn", spdlog::level::warn) + .value("err", spdlog::level::err) + .value("critical", spdlog::level::critical) + .value("off", spdlog::level::off); + using logger = gr::logger; - py::class_<logger, std::shared_ptr<logger>>(m, "logger") + py::class_<logger, std::shared_ptr<logger>>(m, "logger", D(logger)) - .def(py::init<std::string>(), py::arg("logger_name")) + .def(py::init<std::string>(), py::arg("logger_name"), D(logger, logger)) .def(py::init<gr::logger const&>(), py::arg("arg0")) - .def("set_level", &logger::set_level, py::arg("level")) - .def("get_level", &logger::get_level, py::arg("level")) - .def("debug", &logger::debug, py::arg("msg")) - .def("info", &logger::info, py::arg("msg")) - .def("notice", &logger::notice, py::arg("msg")) - .def("warn", &logger::warn, py::arg("msg")) - .def("error", &logger::error, py::arg("msg")) - .def("crit", &logger::crit, py::arg("msg")) - .def("alert", &logger::alert, py::arg("msg")) - .def("fatal", &logger::fatal, py::arg("msg")) - .def("emerg", &logger::emerg, py::arg("msg")) - .def("errorIF", &logger::errorIF, py::arg("cond"), py::arg("msg")) - .def("log_assert", &logger::log_assert, py::arg("cond"), py::arg("msg")) - .def("add_console_appender", - &logger::add_console_appender, - py::arg("target"), - py::arg("pattern")) - .def("set_console_appender", - &logger::set_console_appender, - py::arg("target"), - py::arg("pattern")) - .def("add_file_appender", - &logger::add_file_appender, - py::arg("filename"), - py::arg("append"), - py::arg("pattern")) - .def("set_file_appender", - &logger::set_file_appender, - py::arg("filename"), - py::arg("append"), - py::arg("pattern")) - .def("add_rollingfile_appender", - &logger::add_rollingfile_appender, - py::arg("filename"), - py::arg("filesize"), - py::arg("bkup_index"), - py::arg("append"), - py::arg("mode"), - py::arg("pattern")); - - - py::class_<logger_config, std::shared_ptr<logger_config>>(m, "logger_config") - - - .def_static("get_filename", &logger_config::get_filename) - .def_static("get_watch_period", &logger_config::get_watch_period) - .def_static("load_config", - &logger_config::load_config, - py::arg("filename"), - py::arg("watch_period") = 0) - .def_static("stop_watch", &logger_config::stop_watch) - .def_static("reset_config", &logger_config::reset_config); + .def("set_level", + py::overload_cast<const std::string&>(&logger::set_level), + py::arg("level"), + D(logger, set_level)) + .def("set_level", + py::overload_cast<const gr::log_level>(&logger::set_level), + py::arg("level"), + D(logger, set_level)) + .def("get_level", + py::overload_cast<std::string&>(&logger::get_level, py::const_), + py::arg("level"), + D(logger, get_level)) + .def("get_string_level", &logger::get_string_level, D(logger, get_string_level)) + .def( + "trace", + [](logger& log, const std::string& msg) { log.trace(msg); }, + py::arg("msg"), + D(logger, trace)) + .def( + "debug", + [](logger& log, const std::string& msg) { log.debug(msg); }, + py::arg("msg"), + D(logger, debug)) + .def( + "info", + [](logger& log, const std::string& msg) { log.info(msg); }, + py::arg("msg"), + D(logger, info)) + .def( + "notice", + [](logger& log, const std::string& msg) { log.notice(msg); }, + py::arg("msg"), + D(logger, notice)) + .def( + "warn", + [](logger& log, const std::string& msg) { log.warn(msg); }, + py::arg("msg"), + D(logger, warn)) + .def( + "error", + [](logger& log, const std::string& msg) { log.error(msg); }, + py::arg("msg"), + D(logger, error)) + .def( + "crit", + [](logger& log, const std::string& msg) { log.crit(msg); }, + py::arg("msg"), + D(logger, crit)) + .def( + "alert", + [](logger& log, const std::string& msg) { log.alert(msg); }, + py::arg("msg"), + D(logger, alert)) + .def( + "fatal", + [](logger& log, const std::string& msg) { log.fatal(msg); }, + py::arg("msg"), + D(logger, fatal)) + .def( + "emerg", + [](logger& log, const std::string& msg) { log.emerg(msg); }, + py::arg("msg"), + D(logger, emerg)); + using logging = gr::logging; - // m.def("configure_default_loggers",&gr::configure_default_loggers, - // py::arg("l"), - // py::arg("d"), - // py::arg("name") - // ); - m.def("update_logger_alias", - &gr::update_logger_alias, - py::arg("name"), - py::arg("alias")); - m.def("logger_get_logger", &gr::logger_get_logger, py::arg("name")); - m.def("logger_get_configured_logger", - &gr::logger_get_configured_logger, - py::arg("name")); - m.def("logger_load_config", &gr::logger_load_config, py::arg("config_filename") = ""); - m.def("gr_logger_reset_config", &gr_logger_reset_config); - m.def("logger_set_level", - (void (*)(gr::logger_ptr, std::string const&)) & gr::logger_set_level, - py::arg("logger"), - py::arg("level")); - m.def("logger_set_level", - (void (*)(gr::logger_ptr, log4cpp::Priority::Value)) & gr::logger_set_level, - py::arg("logger"), - py::arg("level")); - m.def("logger_get_level", - (void (*)(gr::logger_ptr, std::string&)) & gr::logger_get_level, - py::arg("logger"), - py::arg("level")); - // m.def("logger_get_level",(void (*)(gr::logger_ptr, log4cpp::Priority::Value - // &))&gr::logger_get_level, - // py::arg("logger"), - // py::arg("level") - // ); - // m.def("logger_add_appender",&gr::logger_add_appender, - // py::arg("logger"), - // py::arg("appender") - // ); // Not Implemented - // m.def("logger_set_appender",&gr::logger_set_appender, - // py::arg("logger"), - // py::arg("appender") - // ); // Not Implemented - m.def("logger_add_console_appender", - &gr::logger_add_console_appender, - py::arg("logger"), - py::arg("target"), - py::arg("pattern")); - m.def("logger_set_console_appender", - &gr::logger_set_console_appender, - py::arg("logger"), - py::arg("target"), - py::arg("pattern")); - m.def("logger_add_file_appender", - &gr::logger_add_file_appender, - py::arg("logger"), - py::arg("filename"), - py::arg("append"), - py::arg("pattern")); - m.def("logger_set_file_appender", - &gr::logger_set_file_appender, - py::arg("logger"), - py::arg("filename"), - py::arg("append"), - py::arg("pattern")); - m.def("logger_add_rollingfile_appender", - &gr::logger_add_rollingfile_appender, - py::arg("logger"), - py::arg("filename"), - py::arg("filesize"), - py::arg("bkup_index"), - py::arg("append"), - py::arg("mode"), - py::arg("pattern")); - m.def("logger_get_logger_names", &gr::logger_get_logger_names); + py::class_<logging, std::unique_ptr<logging, py::nodelete>>(m, "logging") + .def(py::init([]() { + return std::unique_ptr<logging, py::nodelete>(&logging::singleton()); + }), + D(logging, singleton)) + .def("default_level", &logging::default_level, D(logging, default_level)) + .def("debug_level", &logging::debug_level, D(logging, debug_level)) + .def("add_default_sink", + &logging::add_default_sink, + py::arg("sink"), + D(logging, add_default_sink)) + .def("add_debug_sink", + &logging::add_debug_sink, + py::arg("sink"), + D(logging, add_debug_sink)) + .def("add_default_console_sink", + &logging::add_default_console_sink, + D(logging, add_default_console_sink)) + .def("add_debug_console_sink", + &logging::add_debug_console_sink, + D(logging, add_debug_console_sink)) + .def_property_readonly_static( + "default_pattern", + [](py::object) { return logging::default_pattern; }, + D(logging, default_pattern)); } diff --git a/gnuradio-runtime/python/gnuradio/gr/bindings/thrift_application_base_python.cc b/gnuradio-runtime/python/gnuradio/gr/bindings/thrift_application_base_python.cc index 58042b8f61..b1ba2d2246 100644 --- a/gnuradio-runtime/python/gnuradio/gr/bindings/thrift_application_base_python.cc +++ b/gnuradio-runtime/python/gnuradio/gr/bindings/thrift_application_base_python.cc @@ -14,7 +14,7 @@ /* BINDTOOL_GEN_AUTOMATIC(0) */ /* BINDTOOL_USE_PYGCCXML(0) */ /* BINDTOOL_HEADER_FILE(thrift_application_base.h) */ -/* BINDTOOL_HEADER_FILE_HASH(194037fee916fc62be225080aa9229fc) */ +/* BINDTOOL_HEADER_FILE_HASH(6d4665cd29822e3b0f6248bf71aad6f8) */ /***********************************************************************************/ #include <pybind11/complex.h> |