/** * @copyright (c) 2015-2021 Ing. Buero Rothfuss * Riedlinger Str. 8 * 70327 Stuttgart * Germany * http://www.rothfuss-web.de * * @author <a href="mailto:armin@rothfuss-web.de">Armin Rothfuss</a> * * Project logging lib * * @brief C++ logger * * @license MIT license. See accompanying file LICENSE. */ #pragma once // -------------------------------------------------------------------------- // // Common includes // #include <vector> #include <atomic> #include <functional> #include <thread> #if defined USE_MINGW && __MINGW_GCC_VERSION < 100000 #include <mingw/mingw.thread.h> #endif // -------------------------------------------------------------------------- // // Library includes // #include <logging/message_queue.h> #include <logging/formatter.h> #ifdef WIN32 #pragma warning (disable: 4251) #endif /** * Provides an API for stream logging to multiple sinks. */ namespace logging { /** * Sink description with target ostream, level to log and log record formatter */ struct LOGGING_EXPORT sink { sink (std::ostream* stream, level lvl, const record_formatter& formatter); std::ostream* m_stream; level m_level; record_formatter m_formatter; }; /** * Logging core. Manage sinks and thread safe logging. */ class LOGGING_EXPORT core { public: core (); ~core (); /// start the logging core void start (); /// finish the logging core void finish (); /// flush cashed entries to the sinks void flush (); /// add a log entry with current time point to the cache void log (level lvl, std::string&& message); /// add a log entry with specific time point to the cache void log (level lvl, std::chrono::system_clock::time_point time_point, std::string&& message); /// add a sink with a formatter void add_sink (std::ostream* stream, level lvl, const record_formatter& formatter); /// remove a sink void remove_sink (std::ostream* stream); /// remove all sinks void remove_all_sinks (); /// get a standard formatter static record_formatter get_standard_formatter (); /// get a standard formatter without time point prefix static record_formatter get_no_time_formatter (); /// get a simplified console formatter static record_formatter get_console_formatter (); /// get the singleton core instance. static core& instance (); /// helper to rename files with a number and a given maximum number static void rename_file_with_max_count (const std::string& name, int maxnum); /// helper to build a temporary file name static std::string build_temp_log_file_name (const std::string& name); /// Thread name. Thread specific variable to hold the name of the thread. static void set_thread_name (const char* name); core (const core&) = delete; void operator= (const core&) = delete; protected: friend class record; private: static void logging_sink_call (core* core); void log_to_sinks (record&& entry); volatile bool m_is_active; std::atomic_uint m_line_id{}; std::mutex m_mutex; typedef std::vector<sink> sink_list; sink_list m_sinks; message_queue m_messages; std::thread m_sink_thread; }; } // namespace logging #include <logging/core.inl> /** * Since statics are not shared over DLL boundaries, we have * to handle this in a special manner. * If you build logging as static library, we have to decide in * which module the core of logging resists. */ #if !defined(LOGGING_BUILT_AS_STATIC_LIB) namespace logging { LOGGING_EXPORT core& get_logging_core(); } // namespace logging #endif /** * Macro to define the login core singleton. * * Since statics are not shared over DLL boundaries, implement this somewhere * where you can ensure, that it will only be instantiated once. * Usualy this will be in the main module, where your gui_main resist. * * * in C++11 statics are "magic statics" and their initialization _is_ thread safe. * @see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm * * Unfortunately Visual Studio 2013 doesn't support them yet. * @see https://msdn.microsoft.com/library/hh567368(v=vs.130).aspx */ #if (_MSC_VER >= 1900) || !defined(_MSC_VER) #define DEFINE_LOGGING_CORE(EXP) \ namespace logging {\ EXP logging::core& get_logging_core() {\ static logging::core s_logging_core; \ return s_logging_core; \ }\ } // namespace logging #else // _MSC_VER < 1900 /** * Macro to define the login core singleton with the "double-check, single-lock" pattern. * No longer needed for modern C++11 compilers, except Visual Studio 2013. */ #define DEFINE_LOGGING_CORE(EXP) \ namespace logging {\ static std::atomic<core*> s_logging_core;\ static std::mutex s_logging_core_mutex;\ EXP core& get_logging_core() {\ core* temp_core = s_logging_core.load();\ if (nullptr == temp_core) {\ std::lock_guard<std::mutex> lock(s_logging_core_mutex);\ temp_core = s_logging_core.load();\ if (nullptr == temp_core) {\ temp_core = new core();\ s_logging_core.store(temp_core);\ }\ }\ return *temp_core;\ }\ } // namespace logging #endif