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  std::function<logger_string_pool::item(logger_string_pool::item message, logger_string_pool& stringPool)>
88  FormatUserMessage;
89  };
90  using category_data_map = std::unordered_map<string_view, category_data>;
91 
92  enum class message_necessity_type
93  {
94  not_required = 0,
95  default_verbosity = 1 << 0,
96  category_verbosity = 1 << 1,
97  one_of_streams_requires = 1 << 2
98  };
99 
100 public:
101  logger() noexcept;
102  virtual ~logger() noexcept;
103 
104  /**
105  @brief Add an output stream to the logger
106  @tparam stream_t - stream type, derived from base_logger_stream
107  @param stream - stream object
108  **/
109  template<sbo_poly_assignable_c<base_logger_stream> stream_t>
110  void add_stream(stream_t stream) noexcept;
111 
112  /**
113  @brief Get the first occurrence of a stream of the given type
114  @warning This must be protected with get_streams_mutex() shared lock
115  @tparam stream_t - stream type to search for
116  @retval - stream pointer or nullptr if no stream found
117  **/
118  template<sbo_poly_assignable_c<base_logger_stream> stream_t>
119  stream_t* get_stream() noexcept;
120 
121  /**
122  @brief Get all the streams of the given type
123  @warning This must be protected with get_streams_mutex() shared lock
124  @tparam stream_t - stream type to search for
125  @retval - streams view
126  **/
127  template<sbo_poly_assignable_c<base_logger_stream> stream_t>
128  auto get_streams() noexcept;
129 
130  /**
131  @brief Get streams mutex
132  @retval - streams mutex
133  **/
134  std::shared_mutex& get_streams_mutex() noexcept;
135 
136  /**
137  @brief Remove all the streams of the given type
138  @tparam stream_t - stream type to search for
139  @retval - number of streams removed
140  **/
141  template<sbo_poly_assignable_c<base_logger_stream> stream_t>
142  size_t remove_streams() noexcept;
143 
144  /**
145  @brief Add custom rules for category
146  @param category - category to register
147  @param data - category data
148  **/
149  void register_category(const category& category, category_data data) noexcept;
150 
151  /**
152  @brief Add custom rules for category
153  @param svCategoryName - category name, must stay valid while the logger is alive (category existence is not checked)
154  @param data - category data
155  **/
156  void register_category(string_view svCategoryName, category_data data) noexcept;
157 
158  /**
159  @brief Compile and set the default pattern for log messages.
160  @warning Not thread safe on purpose due to optimization reasons.
161  Make sure you set it only once before any log line.
162  @param sPattern - formatting pattern, see qx::logger_specifiers
163  @retval - compilation result. If != ok the pattern is unchanged.
164  **/
165  compile_pattern_result set_default_pattern(string sPattern) noexcept;
166 
167  /**
168  @brief Main log function: log to all streams. For macro and internal usage.
169  @warning All input args must be ready for async work (i.e. be stable)
170  @param category - code category
171  @param eVerbosity - message verbosity
172  @param threadId - thread where the log is called
173  @param messageTime - message creation time
174  @param svFile - file name string
175  @param svFunction - function name string
176  @param nLine - code line number
177  @param message - user message string
178  **/
179  virtual void log_macro(
180  const category& category,
181  verbosity eVerbosity,
182  std::thread::id threadId,
183  std::chrono::system_clock::time_point messageTime,
184  string_view svFile,
185  string_view svFunction,
186  int nLine,
187  logger_string_pool::item message);
188 
189  /**
190  @brief Flush all streams
191  **/
192  virtual void flush();
193 
194  /**
195  @brief Reset logger and clear all streams
196  **/
197  virtual void reset() noexcept;
198 
199  /**
200  @brief Check if this message will go somewhere
201  @details Typically you don't want to use it.
202  It may be useful with async logging to avoid unnecessary formatting and queueing.
203  @param category - code category
204  @param eVerbosity - message verbosity
205  @param threadId - thread where the log is called
206  @param messageTime - message creation time
207  @param svFile - file name string
208  @param svFunction - function name string
209  @param nLine - code line number
210  @retval - get message necessity type
211  **/
212  flags<message_necessity_type> get_message_necessity_type(
213  const category& category,
214  verbosity eVerbosity,
215  std::thread::id threadId,
216  std::chrono::system_clock::time_point messageTime,
217  string_view svFile,
218  string_view svFunction,
219  int nLine) const noexcept;
220 
221  // only for internal usage in macros
222  logger_string_pool* _get_string_pool() noexcept;
223 
224 private:
225  QX_PERF_SHARED_MUTEX(m_StreamsMutex);
226  std::vector<logger_sbo> m_Streams;
227 
228  QX_PERF_SHARED_MUTEX(m_RegisteredCategoriesMutex);
229  category_data_map m_RegisteredCategories;
230 
231  logger_string_pool m_StringsPool;
232 
233  string m_sDefaultPattern;
234 };
235 
236 QX_FLAGS_ENUM_CLASS(logger::message_necessity_type);
237 
238 /**
239 
240  @class logger_singleton
241  @brief Default logger instance
242  @author Khrapov
243  @date 19.08.2021
244 
245 **/
247 {
248 public:
249  logger& get_logger() noexcept
250  {
251  return m_Logger;
252  }
253 
254 private:
255  logger m_Logger;
256 };
257 
258 // Change this variable to override the logger instance used in QX_LOG macros
259 inline logger* g_pGlobalLogger = nullptr;
260 
261 /**
262  @brief Get the logger instance used in QX_LOG macros
263  @retval - logger instance
264 **/
265 inline logger& get_logger() noexcept
266 {
267  return g_pGlobalLogger ? *g_pGlobalLogger : logger_singleton::get_instance().get_logger();
268 }
269 
270 } // namespace qx
271 
272 #ifndef _QX_LOG_C
273 // __FUNCTION__ isn't a char array on linux, so we need to convert it
274  #define _QX_LOG_C(verbosityCheckKeyword, category, eVerbosity, ...) \
275  do \
276  { \
277  verbosityCheckKeyword const auto& _category = category; \
278  if verbosityCheckKeyword (eVerbosity >= _category.get_verbosity()) \
279  { \
280  qx::logger& _logger = qx::get_logger(); \
281  _logger.log_macro( \
282  _category, \
283  eVerbosity, \
284  std::this_thread::get_id(), \
285  std::chrono::system_clock::now(), \
286  QX_SHORT_FILE, \
287  qx::convert_string_literal<qx::char_type, __FUNCTION__>(), \
288  QX_LINE, \
289  _QX_MACRO_USER_MESSAGE(_logger._get_string_pool(), __VA_ARGS__)); \
290  } \
291  } while (false)
292 #endif
293 
294 #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:62
Default logger instance.
Definition: logger.h:247
Logger class.
Definition: logger.h:60
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:220
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:93
virtual void reset() noexcept
Reset logger and clear all streams.
Definition: logger.inl:195
virtual void flush()
Flush all streams.
Definition: logger.inl:188
compile_pattern_result set_default_pattern(string sPattern) noexcept
Compile and set the default pattern for log messages.
Definition: logger.inl:83
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:22
logger & get_logger() noexcept
Get the logger instance used in QX_LOG macros.
Definition: logger.h:265