qxLib
logger.inl
Go to the documentation of this file.
1 /**
2 
3  @file logger.inl
4  @author Khrapov
5  @date 17.06.2019
6  @copyright (c) Nick Khrapov, 2021. All right reserved.
7 
8 **/
9 
10 namespace qx
11 {
12 
13 namespace details
14 {
15 
16 template<sbo_poly_assignable_c<base_logger_stream> stream_t>
17 static constexpr auto k_LoggerStreamFilter = [](const logger::logger_sbo& stream)
18 {
19  return stream->is<stream_t>();
20 };
21 
22 } // namespace details
23 
24 inline logger::logger() noexcept
25 {
26  logger::reset();
27  add_stream(fwrite_logger_stream());
28 }
29 
30 inline logger::~logger() noexcept
31 {
32  logger::flush();
33 }
34 
35 template<sbo_poly_assignable_c<base_logger_stream> stream_t>
36 inline void logger::add_stream(stream_t stream) noexcept
37 {
38  std::unique_lock _(m_StreamsMutex);
39  m_Streams.emplace_back(std::move(stream));
40 }
41 
42 template<sbo_poly_assignable_c<base_logger_stream> stream_t>
43 inline stream_t* logger::get_stream() noexcept
44 {
45  auto it = std::ranges::find_if(m_Streams, details::k_LoggerStreamFilter<stream_t>);
46  return it != m_Streams.end() ? static_cast<stream_t*>(&it->get()) : nullptr;
47 }
48 
49 template<sbo_poly_assignable_c<base_logger_stream> stream_t>
50 inline auto logger::get_streams() noexcept
51 {
52  return m_Streams | std::views::filter(details::k_LoggerStreamFilter<stream_t>)
53  | std::views::transform(
54  [](logger_sbo& stream)
55  {
56  return static_cast<stream_t*>(&stream.get());
57  });
58 }
59 
60 inline std::shared_mutex& logger::get_streams_mutex() noexcept
61 {
62  return m_StreamsMutex;
63 }
64 
65 template<sbo_poly_assignable_c<base_logger_stream> stream_t>
66 inline size_t logger::remove_streams() noexcept
67 {
68  std::unique_lock _(m_StreamsMutex);
69  return std::erase_if(m_Streams, details::k_LoggerStreamFilter<stream_t>);
70 }
71 
72 inline void logger::register_category(const category& category, category_data data) noexcept
73 {
74  register_category(string(category.get_name()), std::move(data));
75 }
76 
77 inline void logger::register_category(string sCategoryName, category_data data) noexcept
78 {
79  std::unique_lock _(m_RegisteredCategoriesMutex);
80  m_RegisteredCategories.try_emplace(std::move(sCategoryName), std::move(data));
81 }
82 
84  const category& category,
85  category_data::format_user_message_func FormatUserMessage) noexcept
86 {
87  set_category_formatter(category.get_name(), std::move(FormatUserMessage));
88 }
89 
91  string_view svCategoryName,
92  category_data::format_user_message_func FormatUserMessage) noexcept
93 {
94  std::unique_lock _(m_RegisteredCategoriesMutex);
95  if (auto it = m_RegisteredCategories.find(svCategoryName); it != m_RegisteredCategories.end())
96  {
97  it->second.FormatUserMessage = std::move(FormatUserMessage);
98  }
99 }
100 
101 inline compile_pattern_result logger::set_default_pattern(string sPattern) noexcept
102 {
103  const compile_pattern_result eResult = compile_pattern(sPattern);
104 
105  if (eResult == compile_pattern_result::ok)
106  m_sDefaultPattern = std::move(sPattern);
107 
108  return eResult;
109 }
110 
111 inline void logger::log_macro(
112  const category& category,
113  verbosity eVerbosity,
114  std::thread::id threadId,
115  std::chrono::system_clock::time_point messageTime,
116  string_view svFile,
117  string_view svFunction,
118  int nLine,
119  logger_string_pool::item message)
120 {
121  const flags<message_necessity_type> eMessageNecessity =
122  get_message_necessity_type(category, eVerbosity, threadId, messageTime, svFile, svFunction, nLine);
123 
124  {
125  std::shared_lock _(m_RegisteredCategoriesMutex);
126  if (auto itRegisteredCategory = m_RegisteredCategories.find(category.get_name());
127  itRegisteredCategory != m_RegisteredCategories.end())
128  {
129  const category_data& data = itRegisteredCategory->second;
130  if (data.FormatUserMessage)
131  {
132  message = data.FormatUserMessage(std::move(message), m_StringsPool);
133  }
134  }
135  }
136 
137  if (eMessageNecessity != message_necessity_type::not_required)
138  {
139  // assume most streams will not have their own pattern and pre-format with the default pattern
140  logger_string_pool::item finalLogMessage = m_StringsPool.acquire();
142  finalLogMessage.sValue,
143  m_sDefaultPattern,
144  category,
145  eVerbosity,
146  threadId,
147  messageTime,
148  svFile,
149  svFunction,
150  nLine,
151  message.sValue);
152 
153  {
154  std::shared_lock _(m_StreamsMutex);
155  for (auto& stream : m_Streams)
156  {
157  if (eMessageNecessity != message_necessity_type::one_of_streams_requires
158  || stream->log_unconditionally_required(
159  category,
160  eVerbosity,
161  threadId,
162  messageTime,
163  svFile,
164  svFunction,
165  nLine))
166  {
167  std::optional<logger_string_pool::item> optStreamLogMessage;
168  if (const std::optional<string_view> optPattern = stream->get_pattern())
169  {
170  optStreamLogMessage = m_StringsPool.acquire();
172  optStreamLogMessage->sValue,
173  *optPattern,
174  category,
175  eVerbosity,
176  threadId,
177  messageTime,
178  svFile,
179  svFunction,
180  nLine,
181  message.sValue);
182  }
183 
184  stream->log(
185  category,
186  eVerbosity,
187  threadId,
188  messageTime,
189  svFile,
190  svFunction,
191  nLine,
192  optStreamLogMessage ? optStreamLogMessage->sValue : finalLogMessage.sValue);
193 
194  if (optStreamLogMessage)
195  m_StringsPool.release(std::move(*optStreamLogMessage));
196  }
197  }
198  }
199 
200  m_StringsPool.release(std::move(finalLogMessage));
201  }
202 
203  m_StringsPool.release(std::move(message));
204 }
205 
206 inline void logger::flush()
207 {
208  std::shared_lock _(m_StreamsMutex);
209  for (auto& stream : m_Streams)
210  stream->flush();
211 }
212 
213 inline void logger::reset() noexcept
214 {
215  flush();
216 
217  {
218  std::unique_lock _(m_StreamsMutex);
219  m_Streams.clear();
220  }
221 
222  {
223  std::unique_lock _(m_RegisteredCategoriesMutex);
224  m_RegisteredCategories.clear();
225  }
226 
227  /*
228  Default pattern for the logger. Some examples:
229 
230  > [08.01.2026_23:51:41] Time? Is it really that time again?
231  > [08.01.2026_23:51:41][CatCore] Time? Is it really that time again?
232  >[W][08.01.2026_23:51:41] Time? Is it really that time again?
233  >[W][08.01.2026_23:51:41][CatCore] Time? Is it really that time again?
234  */
235  set_default_pattern(QXT("{verbosity:l}[{time:%d.%m.%Y_%H:%M:%S}]{category:l} {message}\n"));
236 }
237 
239  const category& category,
240  verbosity eVerbosity,
241  std::thread::id threadId,
242  std::chrono::system_clock::time_point messageTime,
243  string_view svFile,
244  string_view svFunction,
245  int nLine) const noexcept
246 {
247  flags<message_necessity_type> eMessageNecessity;
248  {
249  std::shared_lock _(m_StreamsMutex);
250  const bool bSomeStreamRequires = std::ranges::any_of(
251  m_Streams,
252  [&category, eVerbosity, threadId, messageTime, svFile, svFunction, nLine](const auto& stream)
253  {
254  return stream->log_unconditionally_required(
255  category,
256  eVerbosity,
257  threadId,
258  messageTime,
259  svFile,
260  svFunction,
261  nLine);
262  });
263  if (bSomeStreamRequires)
264  eMessageNecessity |= message_necessity_type::one_of_streams_requires;
265  }
266 
267  {
268  std::shared_lock _(m_RegisteredCategoriesMutex);
269  if (auto itRegisteredCategory = m_RegisteredCategories.find(category.get_name());
270  itRegisteredCategory != m_RegisteredCategories.end())
271  {
272  const category_data& data = itRegisteredCategory->second;
273  if (eVerbosity >= data.eRuntimeVerbosity)
274  eMessageNecessity |= message_necessity_type::category_verbosity;
275  }
276  }
277 
278  if (eVerbosity >= verbosity::log)
279  eMessageNecessity |= message_necessity_type::default_verbosity;
280 
281  return eMessageNecessity;
282 }
283 
284 inline logger::logger_string_pool* logger::_get_string_pool() noexcept
285 {
286  return &m_StringsPool;
287 }
288 
289 } // namespace qx
A category is a class that identifies a particular piece of code. This code can be located in differe...
Definition: category.h:62
constexpr string_view get_name() const noexcept
Get category name.
Definition: category.inl:26
Wrapper for enumerations to be used as a list of flags.
Definition: flags.h:64
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
item acquire()
Acquire a string from the pool.
Definition: string_pool.inl:33
void release(item item)
Return a string back to the pool.
Definition: string_pool.inl:58
Small Buffer Object for polymorphic classes.
void format_log_specifiers(string &sStorage, string_view svPattern, const category &category, verbosity eVerbosity, std::thread::id threadId, std::chrono::system_clock::time_point messageTime, string_view svFile, string_view svFunction, int nLine, string_view svMessage)
Format sStorage using qx::logger_specifiers.
compile_pattern_result compile_pattern(basic_string< char_t, traits_t > &sPattern) noexcept
Compile an fmt pattern with named arguments into replacement fields.