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 (c) Nick Khrapov, 2021. All right reserved.
7 
8 **/
9 #pragma once
10 
11 #include <qx/containers/flags.h>
14 #include <qx/memory/sbo_poly.h>
15 #include <qx/patterns/singleton.h>
16 
17 #include <ranges>
18 #include <shared_mutex>
19 
20 /**
21  @page logger_readme logger
22  @include README.md
23 */
24 
25 /**
26  @brief Log with category
27  @param category - category to be used to manage output
28  @param eVerbosity - message verbosity
29  @param ... - user message and its format args. the format string should be without QXT
30 **/
31 #define QX_LOG_C(category, eVerbosity, ...) _QX_LOG_C(constexpr, category, eVerbosity, ##__VA_ARGS__)
32 
33 /**
34  @brief Log message
35  @param eVerbosity - message verbosity
36  @param ... - user message and its format args. the format string should be without QXT
37 **/
38 #define QX_LOG(eVerbosity, ...) QX_LOG_C(QX_GET_FILE_CATEGORY(), eVerbosity, ##__VA_ARGS__)
39 
40 /**
41  @brief Log with category and with non compile time category check
42  @param category - category to be used to manage output
43  @param eVerbosity - message verbosity
44  @param ... - user message and its format args. the format string should be without QXT
45 **/
46 #define QX_LOG_REF(category, eVerbosity, ...) _QX_LOG_C(, category, eVerbosity, ##__VA_ARGS__)
47 
48 namespace qx
49 {
50 
51 /**
52 
53  @class logger
54  @brief Logger class
55  @author Khrapov
56  @date 10.01.2020
57 
58 **/
59 class logger
60 {
61 public:
62  using logger_sbo = sbo_poly<
64 #if QX_CLANG || QX_APPLE_CLANG || QX_GNU
65  1024
66 #else
67  512
68 #endif
69  >;
70 
72 
74  {
75  verbosity eRuntimeVerbosity = verbosity::detailed;
76 
77  // You can:
78  // 1. Modify `message` inplace and return this object, in this case you can ignore `stringPool`
79  // 2. Aquire a new string pool item, format its string and return this object.
80  // In this case you must release the `message` object using `stringPool`.
81  // This helps to avoid additional allocations.
82  //
83  // When using this feature, keep in mind, that this function can be invoked from different threads.
84  //
85  // Also, try to keep the capture list as short as possible - ideally no more than two pointers.
86  // This improves cache locality.
87  using format_user_message_func =
88  std::function<logger_string_pool::item(logger_string_pool::item message, logger_string_pool& stringPool)>;
89  format_user_message_func FormatUserMessage;
90  };
91  struct category_name_hash : std::hash<string_view>
92  {
93  using is_transparent = void;
94  };
95 
96  using category_data_map = std::unordered_map<string, category_data, category_name_hash, std::equal_to<>>;
97 
98  enum class message_necessity_type
99  {
100  not_required = 0,
101  default_verbosity = 1 << 0,
102  category_verbosity = 1 << 1,
103  one_of_streams_requires = 1 << 2
104  };
105 
106 public:
107  logger() noexcept;
108  virtual ~logger() noexcept;
109 
110  /**
111  @brief Add an output stream to the logger
112  @tparam stream_t - stream type, derived from base_logger_stream
113  @param stream - stream object
114  **/
115  template<sbo_poly_assignable_c<base_logger_stream> stream_t>
116  void add_stream(stream_t stream) noexcept;
117 
118  /**
119  @brief Get the first occurrence of a stream of the given type
120  @warning This must be protected with get_streams_mutex() shared lock
121  @tparam stream_t - stream type to search for
122  @retval - stream pointer or nullptr if no stream found
123  **/
124  template<sbo_poly_assignable_c<base_logger_stream> stream_t>
125  stream_t* get_stream() noexcept;
126 
127  /**
128  @brief Get all the streams of the given type
129  @warning This must be protected with get_streams_mutex() shared lock
130  @tparam stream_t - stream type to search for
131  @retval - streams view
132  **/
133  template<sbo_poly_assignable_c<base_logger_stream> stream_t>
134  auto get_streams() noexcept;
135 
136  /**
137  @brief Get streams mutex
138  @retval - streams mutex
139  **/
140  std::shared_mutex& get_streams_mutex() noexcept;
141 
142  /**
143  @brief Remove all the streams of the given type
144  @tparam stream_t - stream type to search for
145  @retval - number of streams removed
146  **/
147  template<sbo_poly_assignable_c<base_logger_stream> stream_t>
148  size_t remove_streams() noexcept;
149 
150  /**
151  @brief Add custom rules for category
152  @param category - category to register
153  @param data - category data
154  **/
155  void register_category(const category& category, category_data data) noexcept;
156 
157  /**
158  @brief Add custom rules for category
159  @param sCategoryName - category name owned by the logger (category existence is not checked)
160  @param data - category data
161  **/
162  void register_category(string sCategoryName, category_data data) noexcept;
163 
164  /**
165  @brief Set category formatter
166  @param category - category to set formatter for
167  @param FormatUserMessage - category formatter
168  **/
170  const category& category,
171  category_data::format_user_message_func FormatUserMessage) noexcept;
172 
173  /**
174  @brief Set category formatter
175  @param svCategoryName - category to set formatter for
176  @param FormatUserMessage - category formatter
177  **/
179  string_view svCategoryName,
180  category_data::format_user_message_func FormatUserMessage) noexcept;
181 
182  /**
183  @brief Compile and set the default pattern for log messages.
184  @warning Not thread safe on purpose due to optimization reasons.
185  Make sure you set it only once before any log line.
186  @param sPattern - formatting pattern, see qx::logger_specifiers
187  @retval - compilation result. If != ok the pattern is unchanged.
188  **/
189  compile_pattern_result set_default_pattern(string sPattern) noexcept;
190 
191  /**
192  @brief Main log function: log to all streams. For macro and internal usage.
193  @warning All input args must be ready for async work (i.e. be stable)
194  @param category - code category
195  @param eVerbosity - message verbosity
196  @param threadId - thread where the log is called
197  @param messageTime - message creation time
198  @param svFile - file name string
199  @param svFunction - function name string
200  @param nLine - code line number
201  @param message - user message string
202  **/
203  virtual void log_macro(
204  const category& category,
205  verbosity eVerbosity,
206  std::thread::id threadId,
207  std::chrono::system_clock::time_point messageTime,
208  string_view svFile,
209  string_view svFunction,
210  int nLine,
211  logger_string_pool::item message);
212 
213  /**
214  @brief Flush all streams
215  **/
216  virtual void flush();
217 
218  /**
219  @brief Reset logger and clear all streams
220  **/
221  virtual void reset() noexcept;
222 
223  /**
224  @brief Check if this message will go somewhere
225  @details Typically you don't want to use it.
226  It may be useful with async logging to avoid unnecessary formatting and queueing.
227  @param category - code category
228  @param eVerbosity - message verbosity
229  @param threadId - thread where the log is called
230  @param messageTime - message creation time
231  @param svFile - file name string
232  @param svFunction - function name string
233  @param nLine - code line number
234  @retval - get message necessity type
235  **/
236  flags<message_necessity_type> get_message_necessity_type(
237  const category& category,
238  verbosity eVerbosity,
239  std::thread::id threadId,
240  std::chrono::system_clock::time_point messageTime,
241  string_view svFile,
242  string_view svFunction,
243  int nLine) const noexcept;
244 
245  // only for internal usage in macros
246  logger_string_pool* _get_string_pool() noexcept;
247 
248 private:
249  QX_PERF_SHARED_MUTEX(m_StreamsMutex);
250  std::vector<logger_sbo> m_Streams;
251 
252  QX_PERF_SHARED_MUTEX(m_RegisteredCategoriesMutex);
253  category_data_map m_RegisteredCategories;
254 
255  logger_string_pool m_StringsPool;
256 
257  string m_sDefaultPattern;
258 };
259 
260 QX_FLAGS_ENUM_CLASS(logger::message_necessity_type);
261 
262 /**
263 
264  @class logger_singleton
265  @brief Default logger instance
266  @author Khrapov
267  @date 19.08.2021
268 
269 **/
271 {
272 public:
273  logger& get_logger() noexcept;
274 
275 private:
276  logger m_Logger;
277 };
278 
279 inline logger& logger_singleton::get_logger() noexcept
280 {
281  return m_Logger;
282 }
283 
284 // Change this variable to override the logger instance used in QX_LOG macros
285 inline logger* g_pGlobalLogger = nullptr;
286 
287 /**
288  @brief Get the logger instance used in QX_LOG macros
289  @retval - logger instance
290 **/
291 inline logger& get_logger() noexcept
292 {
293  return g_pGlobalLogger ? *g_pGlobalLogger : logger_singleton::get_instance().get_logger();
294 }
295 
296 } // namespace qx
297 
298 #ifndef _QX_LOG_C
299 // __FUNCTION__ isn't a char array on linux, so we need to convert it
300  #define _QX_LOG_C(verbosityCheckKeyword, category, eVerbosity, ...) \
301  do \
302  { \
303  verbosityCheckKeyword const auto& _category = category; \
304  if verbosityCheckKeyword (eVerbosity >= _category.get_verbosity()) \
305  { \
306  qx::logger& _logger = qx::get_logger(); \
307  _logger.log_macro( \
308  _category, \
309  eVerbosity, \
310  std::this_thread::get_id(), \
311  std::chrono::system_clock::now(), \
312  QX_SHORT_FILE, \
313  qx::convert_string_literal<qx::char_type, __FUNCTION__>(), \
314  QX_LINE, \
315  _QX_MACRO_USER_MESSAGE(_logger._get_string_pool(), __VA_ARGS__)); \
316  } \
317  } while (false)
318 #endif
319 
320 #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:62
Wrapper for enumerations to be used as a list of flags.
Definition: flags.h:64
Default logger instance.
Definition: logger.h:271
Logger class.
Definition: logger.h:60
void set_category_formatter(const category &category, category_data::format_user_message_func FormatUserMessage) noexcept
Set category formatter.
Definition: logger.inl:83
size_t remove_streams() noexcept
Remove all the streams of the given type.
Definition: logger.inl:66
auto get_streams() noexcept
Get all the streams of the given type.
Definition: logger.inl:50
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:238
void add_stream(stream_t stream) noexcept
Add an output stream to the logger.
Definition: logger.inl:36
void register_category(const category &category, category_data data) noexcept
Add custom rules for category.
Definition: logger.inl:72
std::shared_mutex & get_streams_mutex() noexcept
Get streams mutex.
Definition: logger.inl:60
stream_t * get_stream() noexcept
Get the first occurrence of a stream of the given type.
Definition: logger.inl:43
virtual void log_macro(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)
Main log function: log to all streams. For macro and internal usage.
Definition: logger.inl:111
virtual void reset() noexcept
Reset logger and clear all streams.
Definition: logger.inl:213
virtual void flush()
Flush all streams.
Definition: logger.inl:206
compile_pattern_result set_default_pattern(string sPattern) noexcept
Compile and set the default pattern for log messages.
Definition: logger.inl:101
Fixed-size atomic string pool.
Definition: string_pool.h:35
Small Buffer Object for polymorphic classes.
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:24
logger & get_logger() noexcept
Get the logger instance used in QX_LOG macros.
Definition: logger.h:291