quill
LoggerManager.h
1 
7 #pragma once
8 
9 #include "quill/core/Attributes.h"
10 #include "quill/core/Common.h"
11 #include "quill/core/LogLevel.h"
12 #include "quill/core/LoggerBase.h"
13 #include "quill/core/PatternFormatterOptions.h"
14 #include "quill/core/Spinlock.h"
15 
16 #include <algorithm>
17 #include <atomic>
18 #include <cstdlib>
19 #include <initializer_list>
20 #include <memory>
21 #include <string>
22 #include <string_view>
23 #include <vector>
24 
25 QUILL_BEGIN_NAMESPACE
26 
28 class Sink;
29 class UserClockSource;
30 
31 namespace detail
32 {
34 {
35 public:
36  LoggerManager(LoggerManager const&) = delete;
37  LoggerManager& operator=(LoggerManager const&) = delete;
38 
39  /***/
40  QUILL_EXPORT static LoggerManager& instance() noexcept
41  {
42  static LoggerManager instance;
43  return instance;
44  }
45 
46  /***/
47  QUILL_NODISCARD LoggerBase* get_logger(std::string const& logger_name) const
48  {
49  LockGuard const lock{_spinlock};
50  LoggerBase* logger = _find_logger(logger_name);
51  return logger && logger->is_valid_logger() ? logger : nullptr;
52  }
53 
54  /***/
55  QUILL_NODISCARD std::vector<LoggerBase*> get_all_loggers() const
56  {
57  LockGuard const lock{_spinlock};
58 
59  std::vector<LoggerBase*> loggers;
60 
61  for (auto const& elem : _loggers)
62  {
63  // we can not add invalidated loggers as they can be removed at any time
64  if (elem->is_valid_logger())
65  {
66  loggers.push_back(elem.get());
67  }
68  }
69 
70  return loggers;
71  }
72 
73  /***/
74  QUILL_NODISCARD LoggerBase* get_valid_logger(std::string_view exclude_logger_substr = {}) const
75  {
76  // Retrieves any valid logger without the need for constructing a vector
77  LockGuard const lock{_spinlock};
78 
79  for (auto const& elem : _loggers)
80  {
81  // we can not add invalidated loggers as they can be removed at any time
82  if (elem->is_valid_logger())
83  {
84  // Return the logger only if it does not match the exclude_logger_substr
85  if (exclude_logger_substr.empty() ||
86  elem->get_logger_name().find(exclude_logger_substr) == std::string::npos)
87  {
88  // Return this logger if it's valid and not excluded
89  return elem.get();
90  }
91  }
92  }
93 
94  return nullptr;
95  }
96 
97  /***/
98  QUILL_NODISCARD LoggerBase* get_valid_logger(std::vector<std::string> const& exclude_logger_substrs) const
99  {
100  LockGuard const lock{_spinlock};
101  for (auto const& elem : _loggers)
102  {
103  if (elem->is_valid_logger())
104  {
105  bool excluded = false;
106  for (auto const& exclude_substr : exclude_logger_substrs)
107  {
108  if (!exclude_substr.empty() && elem->get_logger_name().find(exclude_substr) != std::string::npos)
109  {
110  excluded = true;
111  break;
112  }
113  }
114 
115  if (!excluded)
116  {
117  return elem.get();
118  }
119  }
120  }
121  return nullptr;
122  }
123 
124  /***/
125  QUILL_NODISCARD size_t get_number_of_loggers() const noexcept
126  {
127  LockGuard const lock{_spinlock};
128  return _loggers.size();
129  }
130 
134  template <typename TCallback>
135  void for_each_logger(TCallback cb) const
136  {
137  LockGuard const lock{_spinlock};
138 
139  for (auto const& elem : _loggers)
140  {
141  // Here we do not check for valid_logger() like in get_all_loggers() because this
142  // function is only called by the backend
143  if (cb(elem.get()))
144  {
145  // When the callback returns true stop the loop early
146  break;
147  }
148  }
149  }
150 
151  /***/
152  template <typename TLogger>
153  LoggerBase* create_or_get_logger(std::string const& logger_name, std::vector<std::shared_ptr<Sink>> sinks,
154  PatternFormatterOptions const& pattern_formatter_options,
155  ClockSourceType clock_source, UserClockSource* user_clock)
156  {
157  LockGuard const lock{_spinlock};
158 
159  LoggerBase* logger_ptr = _find_logger(logger_name);
160 
161  if (!logger_ptr)
162  {
163  // If logger pointer is null, create a new logger instance.
164  std::unique_ptr<LoggerBase> new_logger{
165  new TLogger{logger_name, static_cast<std::vector<std::shared_ptr<Sink>>&&>(sinks),
166  pattern_formatter_options, clock_source, user_clock}};
167 
168  _insert_logger(static_cast<std::unique_ptr<LoggerBase>&&>(new_logger));
169 
170  // Although we could directly return .get() from the new_logger here,
171  // we retain this portion of code for additional safety in case of potential re-lookup of
172  // the logger. This section is not performance-critical.
173  logger_ptr = _find_logger(logger_name);
174 
175  if (logger_ptr && _env_log_level)
176  {
177  logger_ptr->set_log_level(*_env_log_level);
178  }
179  }
180 
181  QUILL_ASSERT(logger_ptr, "logger_ptr is nullptr in LoggerManager::get_logger()");
182  QUILL_ASSERT(logger_ptr->is_valid_logger(),
183  "logger is not valid in LoggerManager::get_logger()");
184  return logger_ptr;
185  }
186 
187  /***/
188  template <typename TLogger>
189  LoggerBase* create_or_get_logger(std::string const& logger_name, LoggerBase* source_logger)
190  {
191  if (!source_logger)
192  {
193  return get_logger(logger_name);
194  }
195 
196  return create_or_get_logger<TLogger>(logger_name, source_logger->_sinks, source_logger->_pattern_formatter_options,
197  source_logger->_clock_source, source_logger->_user_clock);
198  }
199 
200  /***/
201  void remove_logger(LoggerBase* logger)
202  {
203  logger->mark_invalid();
204  _has_invalidated_loggers.store(true, std::memory_order_release);
205  }
206 
207  /***/
208  template <typename TCheckQueuesEmpty>
209  void cleanup_invalidated_loggers(TCheckQueuesEmpty check_queues_empty, std::vector<std::string>& removed_loggers)
210  {
211  if (_has_invalidated_loggers.load(std::memory_order_acquire))
212  {
213  _has_invalidated_loggers.store(false, std::memory_order_release);
214 
215  LockGuard const lock{_spinlock};
216  for (auto it = _loggers.begin(); it != _loggers.end();)
217  {
218  if (!it->get()->is_valid_logger())
219  {
220  // invalid logger, check if the logger has any pending records in the queue
221  if (!check_queues_empty())
222  {
223  // we have pending records in the queue, we can not remove the logger yet
224  ++it;
225  _has_invalidated_loggers.store(true, std::memory_order_release);
226  }
227  else
228  {
229  removed_loggers.push_back(it->get()->get_logger_name());
230  it = _loggers.erase(it);
231  }
232  }
233  else
234  {
235  ++it;
236  }
237  }
238  }
239  }
240 
241  /***/
242  QUILL_NODISCARD bool has_invalidated_loggers() const noexcept
243  {
244  return _has_invalidated_loggers.load(std::memory_order_acquire);
245  }
246 
247  QUILL_ATTRIBUTE_COLD void parse_log_level_from_env()
248  {
249  constexpr char const* field = "QUILL_LOG_LEVEL";
250 
251  std::string log_level;
252 
253 #if defined(_MSC_VER)
254  size_t len = 0;
255  char buf[128];
256  bool const ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
257  if (ok)
258  {
259  log_level = buf;
260  }
261 #else // revert to getenv
262  char* buf = ::getenv(field);
263  if (buf)
264  {
265  log_level = buf;
266  }
267 #endif
268 
269  if (!log_level.empty())
270  {
271  _env_log_level = std::make_unique<LogLevel>(loglevel_from_string(log_level));
272  }
273  }
274 
275 private:
276  LoggerManager() { parse_log_level_from_env(); }
277 
278  ~LoggerManager() = default;
279 
280  /***/
281  void _insert_logger(std::unique_ptr<LoggerBase> logger)
282  {
283  auto search_it = std::lower_bound(_loggers.begin(), _loggers.end(), logger->get_logger_name(),
284  [](std::unique_ptr<LoggerBase> const& a, std::string const& b)
285  { return a->get_logger_name() < b; });
286 
287  _loggers.insert(search_it, static_cast<std::unique_ptr<LoggerBase>&&>(logger));
288  }
289 
290  /***/
291  QUILL_NODISCARD LoggerBase* _find_logger(std::string const& target) const noexcept
292  {
293  auto search_it = std::lower_bound(_loggers.begin(), _loggers.end(), target,
294  [](std::unique_ptr<LoggerBase> const& a, std::string const& b)
295  { return a->get_logger_name() < b; });
296 
297  return (search_it != std::end(_loggers) && search_it->get()->get_logger_name() == target)
298  ? search_it->get()
299  : nullptr;
300  }
301 
302 private:
303  std::vector<std::unique_ptr<LoggerBase>> _loggers;
304  std::unique_ptr<LogLevel> _env_log_level;
305  mutable Spinlock _spinlock;
306  std::atomic<bool> _has_invalidated_loggers{false};
307 };
308 } // namespace detail
309 
310 QUILL_END_NAMESPACE
Base class for sinks.
Definition: Sink.h:40
QUILL_NODISCARD bool is_valid_logger() const noexcept
Checks if the logger is valid.
Definition: LoggerBase.h:110
void for_each_logger(TCallback cb) const
For backend use only.
Definition: LoggerManager.h:135
Configuration options for the PatternFormatter.
Definition: PatternFormatterOptions.h:23
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:24
Definition: Spinlock.h:18
Definition: LoggerBase.h:35
void set_log_level(LogLevel new_log_level)
Set the log level of the logger.
Definition: LoggerBase.h:135
Definition: Spinlock.h:58
Base class that provides a timestamp for log statements based on a user-provided clock source...
Definition: UserClockSource.h:25
Definition: LoggerManager.h:33
void mark_invalid()
This function sets the logger&#39;s validity flag to false, indicating that the logger is no longer valid...
Definition: LoggerBase.h:104