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/logger.h> |
8 | #endif |
9 | |
10 | #include <spdlog/sinks/sink.h> |
11 | #include <spdlog/details/backtracer.h> |
12 | #include <spdlog/pattern_formatter.h> |
13 | |
14 | #include <cstdio> |
15 | |
16 | namespace spdlog { |
17 | |
18 | // public methods |
19 | SPDLOG_INLINE logger::logger(const logger &other) |
20 | : name_(other.name_) |
21 | , sinks_(other.sinks_) |
22 | , level_(other.level_.load(std::memory_order_relaxed)) |
23 | , flush_level_(other.flush_level_.load(std::memory_order_relaxed)) |
24 | , custom_err_handler_(other.custom_err_handler_) |
25 | , tracer_(other.tracer_) |
26 | {} |
27 | |
28 | SPDLOG_INLINE logger::logger(logger &&other) SPDLOG_NOEXCEPT : name_(std::move(other.name_)), |
29 | sinks_(std::move(other.sinks_)), |
30 | level_(other.level_.load(std::memory_order_relaxed)), |
31 | flush_level_(other.flush_level_.load(std::memory_order_relaxed)), |
32 | custom_err_handler_(std::move(other.custom_err_handler_)), |
33 | tracer_(std::move(other.tracer_)) |
34 | |
35 | {} |
36 | |
37 | SPDLOG_INLINE logger &logger::operator=(logger other) SPDLOG_NOEXCEPT |
38 | { |
39 | this->swap(other); |
40 | return *this; |
41 | } |
42 | |
43 | SPDLOG_INLINE void logger::swap(spdlog::logger &other) SPDLOG_NOEXCEPT |
44 | { |
45 | name_.swap(other.name_); |
46 | sinks_.swap(other.sinks_); |
47 | |
48 | // swap level_ |
49 | auto other_level = other.level_.load(); |
50 | auto my_level = level_.exchange(other_level); |
51 | other.level_.store(my_level); |
52 | |
53 | // swap flush level_ |
54 | other_level = other.flush_level_.load(); |
55 | my_level = flush_level_.exchange(other_level); |
56 | other.flush_level_.store(my_level); |
57 | |
58 | custom_err_handler_.swap(other.custom_err_handler_); |
59 | std::swap(tracer_, other.tracer_); |
60 | } |
61 | |
62 | SPDLOG_INLINE void swap(logger &a, logger &b) |
63 | { |
64 | a.swap(b); |
65 | } |
66 | |
67 | SPDLOG_INLINE void logger::set_level(level::level_enum log_level) |
68 | { |
69 | level_.store(log_level); |
70 | } |
71 | |
72 | SPDLOG_INLINE level::level_enum logger::level() const |
73 | { |
74 | return static_cast<level::level_enum>(level_.load(std::memory_order_relaxed)); |
75 | } |
76 | |
77 | SPDLOG_INLINE const std::string &logger::name() const |
78 | { |
79 | return name_; |
80 | } |
81 | |
82 | // set formatting for the sinks in this logger. |
83 | // each sink will get a separate instance of the formatter object. |
84 | SPDLOG_INLINE void logger::set_formatter(std::unique_ptr<formatter> f) |
85 | { |
86 | for (auto it = sinks_.begin(); it != sinks_.end(); ++it) |
87 | { |
88 | if (std::next(it) == sinks_.end()) |
89 | { |
90 | // last element - we can be move it. |
91 | (*it)->set_formatter(std::move(f)); |
92 | break; // to prevent clang-tidy warning |
93 | } |
94 | else |
95 | { |
96 | (*it)->set_formatter(f->clone()); |
97 | } |
98 | } |
99 | } |
100 | |
101 | SPDLOG_INLINE void logger::set_pattern(std::string pattern, pattern_time_type time_type) |
102 | { |
103 | auto new_formatter = details::make_unique<pattern_formatter>(std::move(pattern), time_type); |
104 | set_formatter(std::move(new_formatter)); |
105 | } |
106 | |
107 | // create new backtrace sink and move to it all our child sinks |
108 | SPDLOG_INLINE void logger::enable_backtrace(size_t n_messages) |
109 | { |
110 | tracer_.enable(n_messages); |
111 | } |
112 | |
113 | // restore orig sinks and level and delete the backtrace sink |
114 | SPDLOG_INLINE void logger::disable_backtrace() |
115 | { |
116 | tracer_.disable(); |
117 | } |
118 | |
119 | SPDLOG_INLINE void logger::dump_backtrace() |
120 | { |
121 | dump_backtrace_(); |
122 | } |
123 | |
124 | // flush functions |
125 | SPDLOG_INLINE void logger::flush() |
126 | { |
127 | flush_(); |
128 | } |
129 | |
130 | SPDLOG_INLINE void logger::flush_on(level::level_enum log_level) |
131 | { |
132 | flush_level_.store(log_level); |
133 | } |
134 | |
135 | SPDLOG_INLINE level::level_enum logger::flush_level() const |
136 | { |
137 | return static_cast<level::level_enum>(flush_level_.load(std::memory_order_relaxed)); |
138 | } |
139 | |
140 | // sinks |
141 | SPDLOG_INLINE const std::vector<sink_ptr> &logger::sinks() const |
142 | { |
143 | return sinks_; |
144 | } |
145 | |
146 | SPDLOG_INLINE std::vector<sink_ptr> &logger::sinks() |
147 | { |
148 | return sinks_; |
149 | } |
150 | |
151 | // error handler |
152 | SPDLOG_INLINE void logger::set_error_handler(err_handler handler) |
153 | { |
154 | custom_err_handler_ = std::move(handler); |
155 | } |
156 | |
157 | // create new logger with same sinks and configuration. |
158 | SPDLOG_INLINE std::shared_ptr<logger> logger::clone(std::string logger_name) |
159 | { |
160 | auto cloned = std::make_shared<logger>(*this); |
161 | cloned->name_ = std::move(logger_name); |
162 | return cloned; |
163 | } |
164 | |
165 | // protected methods |
166 | SPDLOG_INLINE void logger::log_it_(const spdlog::details::log_msg &log_msg, bool log_enabled, bool traceback_enabled) |
167 | { |
168 | if (log_enabled) |
169 | { |
170 | sink_it_(log_msg); |
171 | } |
172 | if (traceback_enabled) |
173 | { |
174 | tracer_.push_back(log_msg); |
175 | } |
176 | } |
177 | |
178 | SPDLOG_INLINE void logger::sink_it_(const details::log_msg &msg) |
179 | { |
180 | for (auto &sink : sinks_) |
181 | { |
182 | if (sink->should_log(msg.level)) |
183 | { |
184 | SPDLOG_TRY |
185 | { |
186 | sink->log(msg); |
187 | } |
188 | SPDLOG_LOGGER_CATCH(msg.source) |
189 | } |
190 | } |
191 | |
192 | if (should_flush_(msg)) |
193 | { |
194 | flush_(); |
195 | } |
196 | } |
197 | |
198 | SPDLOG_INLINE void logger::flush_() |
199 | { |
200 | for (auto &sink : sinks_) |
201 | { |
202 | SPDLOG_TRY |
203 | { |
204 | sink->flush(); |
205 | } |
206 | SPDLOG_LOGGER_CATCH(source_loc()) |
207 | } |
208 | } |
209 | |
210 | SPDLOG_INLINE void logger::dump_backtrace_() |
211 | { |
212 | using details::log_msg; |
213 | if (tracer_.enabled()) |
214 | { |
215 | sink_it_(log_msg{name(), level::info, "****************** Backtrace Start ******************" }); |
216 | tracer_.foreach_pop([this](const log_msg &msg) { this->sink_it_(msg); }); |
217 | sink_it_(log_msg{name(), level::info, "****************** Backtrace End ********************" }); |
218 | } |
219 | } |
220 | |
221 | SPDLOG_INLINE bool logger::should_flush_(const details::log_msg &msg) |
222 | { |
223 | auto flush_level = flush_level_.load(std::memory_order_relaxed); |
224 | return (msg.level >= flush_level) && (msg.level != level::off); |
225 | } |
226 | |
227 | SPDLOG_INLINE void logger::err_handler_(const std::string &msg) |
228 | { |
229 | if (custom_err_handler_) |
230 | { |
231 | custom_err_handler_(msg); |
232 | } |
233 | else |
234 | { |
235 | using std::chrono::system_clock; |
236 | static std::mutex mutex; |
237 | static std::chrono::system_clock::time_point last_report_time; |
238 | static size_t err_counter = 0; |
239 | std::lock_guard<std::mutex> lk{mutex}; |
240 | auto now = system_clock::now(); |
241 | err_counter++; |
242 | if (now - last_report_time < std::chrono::seconds(1)) |
243 | { |
244 | return; |
245 | } |
246 | last_report_time = now; |
247 | auto tm_time = details::os::localtime(system_clock::to_time_t(now)); |
248 | char date_buf[64]; |
249 | std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S" , &tm_time); |
250 | #if defined(USING_R) && defined(R_R_H) // if in R environment |
251 | REprintf("[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n" , err_counter, date_buf, name().c_str(), msg.c_str()); |
252 | #else |
253 | std::fprintf(stderr, "[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n" , err_counter, date_buf, name().c_str(), msg.c_str()); |
254 | #endif |
255 | } |
256 | } |
257 | } // namespace spdlog |
258 | |