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 logger_stream_filter = [](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::logger_stream_filter<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::logger_stream_filter<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::logger_stream_filter<stream_t>);
70 }
71 
72 inline void logger::register_category(const category& category, category_data data) noexcept
73 {
74  register_category(category.get_name(), std::move(data));
75 }
76 
77 inline void logger::register_category(string_view svCategoryName, category_data data) noexcept
78 {
79  std::unique_lock _(m_RegisteredCategoriesMutex);
80  m_RegisteredCategories.emplace(svCategoryName, std::move(data));
81 }
82 
83 inline compile_pattern_result logger::set_default_pattern(string sPattern) noexcept
84 {
85  const compile_pattern_result eResult = compile_pattern(sPattern);
86 
87  if (eResult == compile_pattern_result::ok)
88  m_sDefaultPattern = std::move(sPattern);
89 
90  return eResult;
91 }
92 
93 inline void logger::log_macro(
94  const category& category,
95  verbosity eVerbosity,
96  std::thread::id threadId,
97  std::chrono::system_clock::time_point messageTime,
98  string_view svFile,
99  string_view svFunction,
100  int nLine,
101  logger_string_pool::item message)
102 {
103  const flags<message_necessity_type> eMessageNecessity =
104  get_message_necessity_type(category, eVerbosity, threadId, messageTime, svFile, svFunction, nLine);
105 
106  {
107  std::shared_lock _(m_RegisteredCategoriesMutex);
108  if (auto itRegisteredCategory = m_RegisteredCategories.find(category.get_name());
109  itRegisteredCategory != m_RegisteredCategories.end())
110  {
111  const category_data& data = itRegisteredCategory->second;
112  if (data.FormatUserMessage)
113  {
114  message = data.FormatUserMessage(std::move(message), m_StringsPool);
115  }
116  }
117  }
118 
119  if (eMessageNecessity != message_necessity_type::not_required)
120  {
121  // assume most streams will not have their own pattern and pre-format with the default pattern
122  logger_string_pool::item finalLogMessage = m_StringsPool.acquire();
124  finalLogMessage.sValue,
125  m_sDefaultPattern,
126  category,
127  eVerbosity,
128  threadId,
129  messageTime,
130  svFile,
131  svFunction,
132  nLine,
133  message.sValue);
134 
135  {
136  std::shared_lock _(m_StreamsMutex);
137  for (auto& stream : m_Streams)
138  {
139  if (eMessageNecessity != message_necessity_type::one_of_streams_requires
140  || stream->log_unconditionally_required(
141  category,
142  eVerbosity,
143  threadId,
144  messageTime,
145  svFile,
146  svFunction,
147  nLine))
148  {
149  std::optional<logger_string_pool::item> optStreamLogMessage;
150  if (const std::optional<string_view> optPattern = stream->get_pattern())
151  {
152  optStreamLogMessage = m_StringsPool.acquire();
154  optStreamLogMessage->sValue,
155  *optPattern,
156  category,
157  eVerbosity,
158  threadId,
159  messageTime,
160  svFile,
161  svFunction,
162  nLine,
163  message.sValue);
164  }
165 
166  stream->log(
167  category,
168  eVerbosity,
169  threadId,
170  messageTime,
171  svFile,
172  svFunction,
173  nLine,
174  optStreamLogMessage ? optStreamLogMessage->sValue : finalLogMessage.sValue);
175 
176  if (optStreamLogMessage)
177  m_StringsPool.release(std::move(*optStreamLogMessage));
178  }
179  }
180  }
181 
182  m_StringsPool.release(std::move(finalLogMessage));
183  }
184 
185  m_StringsPool.release(std::move(message));
186 }
187 
188 inline void logger::flush()
189 {
190  std::shared_lock _(m_StreamsMutex);
191  for (auto& stream : m_Streams)
192  stream->flush();
193 }
194 
195 inline void logger::reset() noexcept
196 {
197  flush();
198 
199  {
200  std::unique_lock _(m_StreamsMutex);
201  m_Streams.clear();
202  }
203 
204  {
205  std::unique_lock _(m_RegisteredCategoriesMutex);
206  m_RegisteredCategories.clear();
207  }
208 
209  /*
210  Default pattern for the logger. Some examples:
211 
212  > [08.01.2026_23:51:41] Time? Is it really that time again?
213  > [08.01.2026_23:51:41][CatCore] Time? Is it really that time again?
214  >[W][08.01.2026_23:51:41] Time? Is it really that time again?
215  >[W][08.01.2026_23:51:41][CatCore] Time? Is it really that time again?
216  */
217  set_default_pattern(QXT("{v:l}[{t:%d.%m.%Y_%H:%M:%S}]{c:l} {m}\n"));
218 }
219 
221  const category& category,
222  verbosity eVerbosity,
223  std::thread::id threadId,
224  std::chrono::system_clock::time_point messageTime,
225  string_view svFile,
226  string_view svFunction,
227  int nLine) const noexcept
228 {
229  flags<message_necessity_type> eMessageNecessity;
230  {
231  std::shared_lock _(m_StreamsMutex);
232  const bool bSomeStreamRequires = std::ranges::any_of(
233  m_Streams,
234  [&category, eVerbosity, threadId, messageTime, svFile, svFunction, nLine](const auto& stream)
235  {
236  return stream->log_unconditionally_required(
237  category,
238  eVerbosity,
239  threadId,
240  messageTime,
241  svFile,
242  svFunction,
243  nLine);
244  });
245  if (bSomeStreamRequires)
246  eMessageNecessity |= message_necessity_type::one_of_streams_requires;
247  }
248 
249  {
250  std::shared_lock _(m_RegisteredCategoriesMutex);
251  if (auto itRegisteredCategory = m_RegisteredCategories.find(category.get_name());
252  itRegisteredCategory != m_RegisteredCategories.end())
253  {
254  const category_data& data = itRegisteredCategory->second;
255  if (eVerbosity >= data.eRuntimeVerbosity)
256  eMessageNecessity |= message_necessity_type::category_verbosity;
257  }
258  }
259 
260  if (eVerbosity >= verbosity::log)
261  eMessageNecessity |= message_necessity_type::default_verbosity;
262 
263  return eMessageNecessity;
264 }
265 
266 inline logger::logger_string_pool* logger::_get_string_pool() noexcept
267 {
268  return &m_StringsPool;
269 }
270 
271 } // 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:62
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
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.