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