// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima). // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef _FASTDDS_LOG_LOG_HPP_ #define _FASTDDS_LOG_LOG_HPP_ #include #include #include #include #include #include /** * eProsima log layer. Logging categories and verbosities can be specified dynamically at runtime. However, even on a category * not covered by the current verbosity level, there is some overhead on calling a log macro. For maximum performance, you can * opt out of logging any particular level by defining the following symbols: * * * define LOG_NO_ERROR * * define LOG_NO_WARNING * * define LOG_NO_INFO * * Additionally. the lowest level (Info) is disabled by default on release branches. */ // Logging API: //! Logs an info message. Disable it through Log::SetVerbosity, define LOG_NO_INFO, or being in a release branch #define logInfo(cat, msg) logInfo_(cat, msg) //! Logs a warning. Disable reporting through Log::SetVerbosity or define LOG_NO_WARNING #define logWarning(cat, msg) logWarning_(cat, msg) //! Logs an error. Disable reporting through define LOG_NO_ERROR #define logError(cat, msg) logError_(cat, msg) namespace eprosima { namespace fastdds { namespace dds { class LogConsumer; /** * Logging utilities. * Logging is accessed through the three macros above, and configuration on the log output * can be achieved through static methods on the class. Logging at various levels can be * disabled dynamically (through the Verbosity level) or statically (through the LOG_NO_[VERB] * macros) for maximum performance. * @ingroup COMMON_MODULE */ class Log { public: /** * Types of log entry. * * Error: Maximum priority. Can only be disabled statically through LOG_NO_ERROR. * * Warning: Medium priority. Can be disabled statically and dynamically. * * Info: Low priority. Useful for debugging. Disabled by default on release branches. */ enum Kind { Error, Warning, Info, }; /** * Registers an user defined consumer to route log output. * There is a default stdout consumer active as default. * @param consumer r-value to a consumer unique_ptr. It will be invalidated after the call. */ RTPS_DllAPI static void RegisterConsumer( std::unique_ptr&& consumer); //! Removes all registered consumers, including the default stdout. RTPS_DllAPI static void ClearConsumers(); //! Enables the reporting of filenames in log entries. Disabled by default. RTPS_DllAPI static void ReportFilenames( bool); //! Enables the reporting of function names in log entries. Enabled by default when supported. RTPS_DllAPI static void ReportFunctions( bool); //! Sets the verbosity level, allowing for messages equal or under that priority to be logged. RTPS_DllAPI static void SetVerbosity( Log::Kind); //! Returns the current verbosity level. RTPS_DllAPI static Log::Kind GetVerbosity(); //! Sets a filter that will pattern-match against log categories, dropping any unmatched categories. RTPS_DllAPI static void SetCategoryFilter( const std::regex&); //! Sets a filter that will pattern-match against filenames, dropping any unmatched categories. RTPS_DllAPI static void SetFilenameFilter( const std::regex&); //! Sets a filter that will pattern-match against the provided error string, dropping any unmatched categories. RTPS_DllAPI static void SetErrorStringFilter( const std::regex&); //! Returns the logging engine to configuration defaults. RTPS_DllAPI static void Reset(); //! Waits until no more log info is availabel RTPS_DllAPI static void Flush(); //! Stops the logging thread. It will re-launch on the next call to a successful log macro. RTPS_DllAPI static void KillThread(); // Note: In VS2013, if you're linking this class statically, you will have to call KillThread before leaving // main, due to an unsolved MSVC bug. struct Context { const char* filename; int line; const char* function; const char* category; }; struct Entry { std::string message; Log::Context context; Log::Kind kind; std::string timestamp; }; /** * Not recommended to call this method directly! Use the following macros: * * logInfo(cat, msg); * * logWarning(cat, msg); * * logError(cat, msg); */ RTPS_DllAPI static void QueueLog( const std::string& message, const Log::Context&, Log::Kind); private: struct Resources { fastrtps::DBQueue logs; std::vector > consumers; std::unique_ptr logging_thread; // Condition variable segment. std::condition_variable cv; std::mutex cv_mutex; bool logging; bool work; int current_loop; // Context configuration. std::mutex config_mutex; bool filenames; bool functions; std::unique_ptr category_filter; std::unique_ptr filename_filter; std::unique_ptr error_string_filter; std::atomic verbosity; Resources(); ~Resources(); }; static struct Resources resources_; // Applies transformations to the entries compliant with the options selected (such as // erasure of certain context information, or filtering by category. Returns false // if the log entry is blacklisted. static bool preprocess( Entry&); static void run(); static void get_timestamp( std::string&); }; /** * Consumes a log entry to output it somewhere. */ class LogConsumer { public: virtual ~LogConsumer() { } virtual void Consume( const Log::Entry&) = 0; protected: void print_timestamp( std::ostream& stream, const Log::Entry&, bool color) const; void print_header( std::ostream& stream, const Log::Entry&, bool color) const; void print_context( std::ostream& stream, const Log::Entry&, bool color) const; void print_message( std::ostream& stream, const Log::Entry&, bool color) const; void print_new_line( std::ostream& stream, bool color) const; }; #if defined(WIN32) #define __func__ __FUNCTION__ #endif #ifndef LOG_NO_ERROR #define logError_(cat, msg) \ { \ using namespace eprosima::fastdds::dds; \ std::stringstream ss; \ ss << msg; \ Log::QueueLog(ss.str(), Log::Context{__FILE__, __LINE__, __func__, #cat}, Log::Kind::Error); \ } #elif (defined(__INTERNALDEBUG) || defined(_INTERNALDEBUG)) #define logError_(cat, msg) \ { \ auto tmp_lambda = [&]() \ { \ std::stringstream ss; \ ss << msg; \ }; \ (void)tmp_lambda; \ } #else #define logError_(cat, msg) #endif #ifndef LOG_NO_WARNING #define logWarning_(cat, msg) \ { \ using namespace eprosima::fastdds::dds; \ if (Log::GetVerbosity() >= Log::Kind::Warning) \ { \ std::stringstream ss; \ ss << msg; \ Log::QueueLog(ss.str(), Log::Context{__FILE__, __LINE__, __func__, #cat}, Log::Kind::Warning); \ } \ } #elif (defined(__INTERNALDEBUG) || defined(_INTERNALDEBUG)) #define logWarning_(cat, msg) \ { \ auto tmp_lambda = [&]() \ { \ std::stringstream ss; \ ss << msg; \ }; \ (void)tmp_lambda; \ } #else #define logWarning_(cat, msg) #endif #if (defined(__INTERNALDEBUG) || defined(_INTERNALDEBUG)) && (defined(_DEBUG) || defined(__DEBUG)) && \ (!defined(LOG_NO_INFO)) #define logInfo_(cat, msg) \ { \ using namespace eprosima::fastdds::dds; \ if (Log::GetVerbosity() >= Log::Kind::Info) \ { \ std::stringstream ss; \ ss << msg; \ Log::QueueLog(ss.str(), Log::Context{__FILE__, __LINE__, __func__, #cat}, Log::Kind::Info); \ } \ } #elif (defined(__INTERNALDEBUG) || defined(_INTERNALDEBUG)) #define logInfo_(cat, msg) \ { \ auto tmp_lambda = [&]() \ { \ std::stringstream ss; \ ss << msg; \ }; \ (void)tmp_lambda; \ } #else #define logInfo_(cat, msg) #endif } // namespace dds } // namespace fastdds } // namespace eprosima #endif