qxLib
logger.h
Go to the documentation of this file.
1 /**
2 
3  @file logger.h
4  @author Khrapov
5  @date 17.06.2019
6  @copyright © Nick Khrapov, 2021. All right reserved.
7 
8 **/
9 #pragma once
10 
11 #include <qx/containers/flags.h>
16 #include <qx/memory/sbo_poly.h>
17 #include <qx/patterns/singleton.h>
18 
19 #include <ranges>
20 #include <shared_mutex>
21 
22 /**
23  @page logger_readme logger
24  @include README.md
25 */
26 
27 /**
28  @brief Log with category
29  @param category - category to be used to manage output
30  @param eVerbosity - message verbosity
31  @param ... - user message and its format args. the format string should be without QXT
32 **/
33 #define QX_LOG_C(category, eVerbosity, ...) _QX_LOG_C(constexpr, category, eVerbosity, ##__VA_ARGS__)
34 
35 /**
36  @brief Log message
37  @param eVerbosity - message verbosity
38  @param ... - user message and its format args. the format string should be without QXT
39 **/
40 #define QX_LOG(eVerbosity, ...) QX_LOG_C(QX_GET_FILE_CATEGORY(), eVerbosity, ##__VA_ARGS__)
41 
42 /**
43  @brief Log with category and with non compile time category check
44  @param category - category to be used to manage output
45  @param eVerbosity - message verbosity
46  @param ... - user message and its format args. the format string should be without QXT
47 **/
48 #define QX_LOG_REF(category, eVerbosity, ...) _QX_LOG_C(, category, eVerbosity, ##__VA_ARGS__)
49 
50 namespace qx
51 {
52 
53 /**
54 
55  @class logger
56  @brief Logger class
57  @author Khrapov
58  @date 10.01.2020
59 
60 **/
61 class logger
62 {
63 public:
64  using logger_sbo = sbo_poly<
66 #if QX_CLANG || QX_APPLE_CLANG || QX_GNU
67  1024
68 #else
69  512
70 #endif
71  >;
72 
73  // For best performance, do not allocate anything in this function and return the modified sMessage object.
74  // See default_formatter for an example.
75  using format_signature = string(
76  const category&,
77  verbosity,
78  std::thread::id,
79  std::chrono::system_clock::time_point,
80  string_view,
81  string_view,
82  int,
83  string);
84  using format_function_pointer = format_signature*;
85  using format_function = std::function<format_signature>;
86 
88  {
89  verbosity eRuntimeVerbosity = verbosity::detailed;
90  format_function formatFunction;
91  };
92 
93  using category_data_map = std::unordered_map<string_view, category_data>;
95 
96  enum class message_necessity_type
97  {
98  not_required = 0,
99  default_verbosity = 1 << 0,
100  category_verbosity = 1 << 1,
101  one_of_streams_requires = 1 << 2
102  };
103 
104 public:
105  logger() noexcept;
106  virtual ~logger() noexcept;
107 
108  /**
109  @brief Add an output stream to the logger
110  @tparam stream_t - stream type, derived from base_logger_stream
111  @param stream - stream object
112  **/
113  template<sbo_poly_assignable_c<base_logger_stream> stream_t>
114  void add_stream(stream_t stream) noexcept;
115 
116  /**
117  @brief Get the first occurrence of a stream of the given type
118  @warning This must be protected with get_streams_mutex() shared lock
119  @tparam stream_t - stream type to search for
120  @retval - stream pointer or nullptr if no stream found
121  **/
122  template<sbo_poly_assignable_c<base_logger_stream> stream_t>
123  stream_t* get_stream() noexcept;
124 
125  /**
126  @brief Get all the streams of the given type
127  @warning This must be protected with get_streams_mutex() shared lock
128  @tparam stream_t - stream type to search for
129  @retval - streams view
130  **/
131  template<sbo_poly_assignable_c<base_logger_stream> stream_t>
132  auto get_streams() noexcept;
133 
134  /**
135  @brief Get streams mutex
136  @retval - streams mutex
137  **/
138  std::shared_mutex& get_streams_mutex() noexcept;
139 
140  /**
141  @brief Remove all the streams of the given type
142  @tparam stream_t - stream type to search for
143  @retval - number of streams removed
144  **/
145  template<sbo_poly_assignable_c<base_logger_stream> stream_t>
146  size_t remove_streams() noexcept;
147 
148  /**
149  @brief Add custom rules for category
150  @param category - category to register
151  @param data - category data
152  **/
153  void register_category(const category& category, category_data data) noexcept;
154 
155  /**
156  @brief Add custom rules for category
157  @param svCategoryName - category name, must stay valid while the logger is alive (category existence is not checked)
158  @param data - category data
159  **/
160  void register_category(string_view svCategoryName, category_data data) noexcept;
161 
162  /**
163  @brief Set a function that will be used as the default formatter in case no formatter found in categories registered
164  @param pFormatter - default formatter
165  **/
166  void set_default_formatter(format_function_pointer pFormatter) noexcept;
167 
168  /**
169  @brief Log to all streams
170  @warning All input args must be ready for async work (i.e. be stable)
171  @param category - code category
172  @param eVerbosity - message verbosity
173  @param threadId - thread where the log is called
174  @param messageTime - message creation time
175  @param svFile - file name string
176  @param svFunction - function name string
177  @param nLine - code line number
178  @param message - user message string
179  **/
180  virtual void log(
181  const category& category,
182  verbosity eVerbosity,
183  std::thread::id threadId,
184  std::chrono::system_clock::time_point messageTime,
185  string_view svFile,
186  string_view svFunction,
187  int nLine,
188  logger_string_pool::item message);
189 
190  /**
191  @brief Flush all streams
192  **/
193  virtual void flush();
194 
195  /**
196  @brief Reset logger and clear all streams
197  **/
198  virtual void reset() noexcept;
199 
200  /**
201  @brief Check if this message will go somewhere
202  @details Typically you don't want to use it.
203  It may be useful with async logging to avoid unnecessary formatting and queueing.
204  @param category - code category
205  @param eVerbosity - message verbosity
206  @param threadId - thread where the log is called
207  @param messageTime - message creation time
208  @param svFile - file name string
209  @param svFunction - function name string
210  @param nLine - code line number
211  @retval - get message necessity type
212  **/
213  flags<message_necessity_type> get_message_necessity_type(
214  const category& category,
215  verbosity eVerbosity,
216  std::thread::id threadId,
217  std::chrono::system_clock::time_point messageTime,
218  string_view svFile,
219  string_view svFunction,
220  int nLine) const noexcept;
221 
222  // only for internal usage in macros
223  logger_string_pool* _get_string_pool() noexcept;
224 
225 private:
226  QX_PERF_SHARED_MUTEX(m_StreamsMutex);
227  std::vector<logger_sbo> m_Streams;
228 
229  QX_PERF_SHARED_MUTEX(m_RegisteredCategoriesMutex);
230  category_data_map m_RegisteredCategories;
231 
232  logger_string_pool m_StringsPool;
233 
234  std::atomic<format_function_pointer> m_DefaultFormatFunction = format_message_qx;
235  static_assert(std::atomic<format_function_pointer>::is_always_lock_free);
236 };
237 
238 QX_FLAGS_ENUM_CLASS(logger::message_necessity_type);
239 
240 /**
241 
242  @class logger_singleton
243  @brief Default logger instance
244  @author Khrapov
245  @date 19.08.2021
246 
247 **/
249 {
250 public:
251  logger& get_logger() noexcept
252  {
253  return m_Logger;
254  }
255 
256 private:
257  logger m_Logger;
258 };
259 
260 // Change this variable to override the logger instance used in QX_LOG macros
261 inline logger* g_pGlobalLogger = nullptr;
262 
263 /**
264  @brief Get the logger instance used in QX_LOG macros
265  @retval - logger instance
266 **/
267 inline logger& get_logger() noexcept
268 {
269  return g_pGlobalLogger ? *g_pGlobalLogger : logger_singleton::get_instance().get_logger();
270 }
271 
272 } // namespace qx
273 
274 #ifndef _QX_LOG_C
275 // __FUNCTION__ isn't a char array on linux, so we need to convert it
276  #define _QX_LOG_C(verbosityCheckKeyword, category, eVerbosity, ...) \
277  do \
278  { \
279  verbosityCheckKeyword const auto& _category = category; \
280  if verbosityCheckKeyword (eVerbosity >= _category.get_verbosity()) \
281  { \
282  qx::logger& _logger = qx::get_logger(); \
283  _logger.log( \
284  _category, \
285  eVerbosity, \
286  std::this_thread::get_id(), \
287  std::chrono::system_clock::now(), \
288  QX_SHORT_FILE, \
289  qx::convert_string_literal<qx::char_type, __FUNCTION__>(), \
290  QX_LINE, \
291  _QX_MACRO_USER_MESSAGE(_logger._get_string_pool(), __VA_ARGS__)); \
292  } \
293  } while (false)
294 #endif
295 
296 #include <qx/logger/logger.inl>
Base class for logger streams.
A category is a class that identifies a particular piece of code. This code can be located in differe...
Definition: category.h:59
Wrapper for enumerations to be used as a list of flags.
Definition: flags.h:62
Default logger instance.
Definition: logger.h:249
Logger class.
Definition: logger.h:62
virtual void log(const category &category, verbosity eVerbosity, std::thread::id threadId, std::chrono::system_clock::time_point messageTime, string_view svFile, string_view svFunction, int nLine, logger_string_pool::item message)
Log to all streams.
Definition: logger.inl:87
size_t remove_streams() noexcept
Remove all the streams of the given type.
Definition: logger.inl:65
auto get_streams() noexcept
Get all the streams of the given type.
Definition: logger.inl:49
flags< message_necessity_type > get_message_necessity_type(const category &category, verbosity eVerbosity, std::thread::id threadId, std::chrono::system_clock::time_point messageTime, string_view svFile, string_view svFunction, int nLine) const noexcept
Check if this message will go somewhere.
Definition: logger.inl:187
void add_stream(stream_t stream) noexcept
Add an output stream to the logger.
Definition: logger.inl:35
void register_category(const category &category, category_data data) noexcept
Add custom rules for category.
Definition: logger.inl:71
std::shared_mutex & get_streams_mutex() noexcept
Get streams mutex.
Definition: logger.inl:59
stream_t * get_stream() noexcept
Get the first occurrence of a stream of the given type.
Definition: logger.inl:42
virtual void reset() noexcept
Reset logger and clear all streams.
Definition: logger.inl:170
void set_default_formatter(format_function_pointer pFormatter) noexcept
Set a function that will be used as the default formatter in case no formatter found in categories re...
Definition: logger.inl:82
virtual void flush()
Flush all streams.
Definition: logger.inl:163
Small Buffer Object for polymorphic classes.
Definition: sbo_poly.h:58
Fixed-size atomic string pool.
Definition: string_pool.h:34
Inherit the necessary singleton class from this.
#define QX_FLAGS_ENUM_CLASS(enumName)
Define to let to use this enum class in different binary operations returning qx::flags.
Definition: flags.h:22
logger & get_logger() noexcept
Get the logger instance used in QX_LOG macros.
Definition: logger.h:267