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