1// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
2// Distributed under the MIT License (http://opensource.org/licenses/MIT)
3
4#pragma once
5
6#ifndef SPDLOG_HEADER_ONLY
7# include <spdlog/details/registry.h>
8#endif
9
10#include <spdlog/common.h>
11#include <spdlog/details/periodic_worker.h>
12#include <spdlog/logger.h>
13#include <spdlog/pattern_formatter.h>
14
15#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
16// support for the default stdout color logger
17# ifdef _WIN32
18# include <spdlog/sinks/wincolor_sink.h>
19# else
20# include <spdlog/sinks/ansicolor_sink.h>
21# endif
22#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
23
24#include <chrono>
25#include <functional>
26#include <memory>
27#include <string>
28#include <unordered_map>
29
30namespace spdlog {
31namespace details {
32
33SPDLOG_INLINE registry::registry()
34 : formatter_(new pattern_formatter())
35{
36
37#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
38 // create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows).
39# ifdef _WIN32
40 auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>();
41# else
42 auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
43# endif
44
45 const char *default_logger_name = "";
46 default_logger_ = std::make_shared<spdlog::logger>(default_logger_name, std::move(color_sink));
47 loggers_[default_logger_name] = default_logger_;
48
49#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
50}
51
52SPDLOG_INLINE registry::~registry() = default;
53
54SPDLOG_INLINE void registry::register_logger(std::shared_ptr<logger> new_logger)
55{
56 std::lock_guard<std::mutex> lock(logger_map_mutex_);
57 register_logger_(std::move(new_logger));
58}
59
60SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logger)
61{
62 std::lock_guard<std::mutex> lock(logger_map_mutex_);
63 new_logger->set_formatter(formatter_->clone());
64
65 if (err_handler_)
66 {
67 new_logger->set_error_handler(err_handler_);
68 }
69
70 // set new level according to previously configured level or default level
71 auto it = log_levels_.find(new_logger->name());
72 auto new_level = it != log_levels_.end() ? it->second : global_log_level_;
73 new_logger->set_level(new_level);
74
75 new_logger->flush_on(flush_level_);
76
77 if (backtrace_n_messages_ > 0)
78 {
79 new_logger->enable_backtrace(backtrace_n_messages_);
80 }
81
82 if (automatic_registration_)
83 {
84 register_logger_(std::move(new_logger));
85 }
86}
87
88SPDLOG_INLINE std::shared_ptr<logger> registry::get(const std::string &logger_name)
89{
90 std::lock_guard<std::mutex> lock(logger_map_mutex_);
91 auto found = loggers_.find(logger_name);
92 return found == loggers_.end() ? nullptr : found->second;
93}
94
95SPDLOG_INLINE std::shared_ptr<logger> registry::default_logger()
96{
97 std::lock_guard<std::mutex> lock(logger_map_mutex_);
98 return default_logger_;
99}
100
101// Return raw ptr to the default logger.
102// To be used directly by the spdlog default api (e.g. spdlog::info)
103// This make the default API faster, but cannot be used concurrently with set_default_logger().
104// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
105SPDLOG_INLINE logger *registry::get_default_raw()
106{
107 return default_logger_.get();
108}
109
110// set default logger.
111// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
112SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_default_logger)
113{
114 std::lock_guard<std::mutex> lock(logger_map_mutex_);
115 // remove previous default logger from the map
116 if (default_logger_ != nullptr)
117 {
118 loggers_.erase(default_logger_->name());
119 }
120 if (new_default_logger != nullptr)
121 {
122 loggers_[new_default_logger->name()] = new_default_logger;
123 }
124 default_logger_ = std::move(new_default_logger);
125}
126
127SPDLOG_INLINE void registry::set_tp(std::shared_ptr<thread_pool> tp)
128{
129 std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
130 tp_ = std::move(tp);
131}
132
133SPDLOG_INLINE std::shared_ptr<thread_pool> registry::get_tp()
134{
135 std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
136 return tp_;
137}
138
139// Set global formatter. Each sink in each logger will get a clone of this object
140SPDLOG_INLINE void registry::set_formatter(std::unique_ptr<formatter> formatter)
141{
142 std::lock_guard<std::mutex> lock(logger_map_mutex_);
143 formatter_ = std::move(formatter);
144 for (auto &l : loggers_)
145 {
146 l.second->set_formatter(formatter_->clone());
147 }
148}
149
150SPDLOG_INLINE void registry::enable_backtrace(size_t n_messages)
151{
152 std::lock_guard<std::mutex> lock(logger_map_mutex_);
153 backtrace_n_messages_ = n_messages;
154
155 for (auto &l : loggers_)
156 {
157 l.second->enable_backtrace(n_messages);
158 }
159}
160
161SPDLOG_INLINE void registry::disable_backtrace()
162{
163 std::lock_guard<std::mutex> lock(logger_map_mutex_);
164 backtrace_n_messages_ = 0;
165 for (auto &l : loggers_)
166 {
167 l.second->disable_backtrace();
168 }
169}
170
171SPDLOG_INLINE void registry::set_level(level::level_enum log_level)
172{
173 std::lock_guard<std::mutex> lock(logger_map_mutex_);
174 for (auto &l : loggers_)
175 {
176 l.second->set_level(log_level);
177 }
178 global_log_level_ = log_level;
179}
180
181SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
182{
183 std::lock_guard<std::mutex> lock(logger_map_mutex_);
184 for (auto &l : loggers_)
185 {
186 l.second->flush_on(log_level);
187 }
188 flush_level_ = log_level;
189}
190
191SPDLOG_INLINE void registry::set_error_handler(err_handler handler)
192{
193 std::lock_guard<std::mutex> lock(logger_map_mutex_);
194 for (auto &l : loggers_)
195 {
196 l.second->set_error_handler(handler);
197 }
198 err_handler_ = std::move(handler);
199}
200
201SPDLOG_INLINE void registry::apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun)
202{
203 std::lock_guard<std::mutex> lock(logger_map_mutex_);
204 for (auto &l : loggers_)
205 {
206 fun(l.second);
207 }
208}
209
210SPDLOG_INLINE void registry::flush_all()
211{
212 std::lock_guard<std::mutex> lock(logger_map_mutex_);
213 for (auto &l : loggers_)
214 {
215 l.second->flush();
216 }
217}
218
219SPDLOG_INLINE void registry::drop(const std::string &logger_name)
220{
221 std::lock_guard<std::mutex> lock(logger_map_mutex_);
222 loggers_.erase(logger_name);
223 if (default_logger_ && default_logger_->name() == logger_name)
224 {
225 default_logger_.reset();
226 }
227}
228
229SPDLOG_INLINE void registry::drop_all()
230{
231 std::lock_guard<std::mutex> lock(logger_map_mutex_);
232 loggers_.clear();
233 default_logger_.reset();
234}
235
236// clean all resources and threads started by the registry
237SPDLOG_INLINE void registry::shutdown()
238{
239 {
240 std::lock_guard<std::mutex> lock(flusher_mutex_);
241 periodic_flusher_.reset();
242 }
243
244 drop_all();
245
246 {
247 std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
248 tp_.reset();
249 }
250}
251
252SPDLOG_INLINE std::recursive_mutex &registry::tp_mutex()
253{
254 return tp_mutex_;
255}
256
257SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registration)
258{
259 std::lock_guard<std::mutex> lock(logger_map_mutex_);
260 automatic_registration_ = automatic_registration;
261}
262
263SPDLOG_INLINE void registry::set_levels(log_levels levels, level::level_enum *global_level)
264{
265 std::lock_guard<std::mutex> lock(logger_map_mutex_);
266 log_levels_ = std::move(levels);
267 auto global_level_requested = global_level != nullptr;
268 global_log_level_ = global_level_requested ? *global_level : global_log_level_;
269
270 for (auto &logger : loggers_)
271 {
272 auto logger_entry = log_levels_.find(logger.first);
273 if (logger_entry != log_levels_.end())
274 {
275 logger.second->set_level(logger_entry->second);
276 }
277 else if (global_level_requested)
278 {
279 logger.second->set_level(*global_level);
280 }
281 }
282}
283
284SPDLOG_INLINE registry &registry::instance()
285{
286 static registry s_instance;
287 return s_instance;
288}
289
290SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name)
291{
292 if (loggers_.find(logger_name) != loggers_.end())
293 {
294 throw_spdlog_ex("logger with name '" + logger_name + "' already exists");
295 }
296}
297
298SPDLOG_INLINE void registry::register_logger_(std::shared_ptr<logger> new_logger)
299{
300 auto logger_name = new_logger->name();
301 throw_if_exists_(logger_name);
302 loggers_[logger_name] = std::move(new_logger);
303}
304
305} // namespace details
306} // namespace spdlog
307