diff --git a/example/example.cpp b/example/example.cpp index b3d6c439..eb54ecf8 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -13,6 +13,7 @@ void async_example(); void syslog_example(); +void test_custom_error_handler(); namespace spd = spdlog; int main(int, char*[]) @@ -68,6 +69,7 @@ int main(int, char*[]) // syslog example. linux/osx only.. syslog_example(); + test_custom_error_handler(); // Release and close all loggers spdlog::drop_all(); @@ -101,6 +103,31 @@ void syslog_example() #endif } +// Example of user-provided error handler +void test_custom_error_handler() +{ + // Trigger default error handler + try { + spd::error("test default error handler (throw an exception)"); + } catch (const spd::spdlog_ex& e) { + std::cerr << "caught spdlog_ex: " << e.what() << std::endl; + } + + // Set custom error handler + spd::set_error_handler( + [](const std::string& message) { + std::cerr << "SPDLOG ERROR " << message << std::endl; + } + ); + + // Trigger custom error handler + try { + spd::error("test custom error handler"); + } catch (const spd::spdlog_ex& e) { + std::cerr << "caught spdlog_ex: " << e.what() << std::endl; + } +} + // Example of user defined class with operator<< class some_class {}; diff --git a/include/spdlog/async_logger.h b/include/spdlog/async_logger.h index 786eb02e..39ff1468 100644 --- a/include/spdlog/async_logger.h +++ b/include/spdlog/async_logger.h @@ -12,7 +12,7 @@ // Upon each log write the logger: // 1. Checks if its log level is enough to log the message // 2. Push a new copy of the message to a queue (or block the caller until space is available in the queue) -// 3. will throw spdlog_ex upon log exceptions +// 3. will call error handler upon log exceptions (which, by default, will throw spdlog_ex exception) // Upon destruction, logs all remaining messages in the queue before destructing.. #include diff --git a/include/spdlog/common.h b/include/spdlog/common.h index 1e446555..2a96430f 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -15,6 +15,7 @@ #include #include #endif +#include #include @@ -107,6 +108,32 @@ private: }; +// Function pointer to error handler. Defaults to throwing a spdlog_ex +// exception. May be overridden by the user with set_error_handler(). +static std::function error_handler = [](const std::string& message) { + throw spdlog_ex(message); +}; + +// Call the user-provided error handler when an internal spdlog error occurs. +inline void error(const std::string& message) +{ + error_handler(message); +} + +// Set an error handler to be called in case of errors during logging (e.g., +// can't write to file because the disk is full). Defaults to an error handler +// which logs the errors to the console. +// +// Function must have the following signature (but can be named whatever you +// like): +// +// void error_handler(const std::string &message); +// +inline void set_error_handler(std::function func) +{ + error_handler = func; +} + // // wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) // diff --git a/include/spdlog/details/file_helper.h b/include/spdlog/details/file_helper.h index a465c4d9..b5547f88 100644 --- a/include/spdlog/details/file_helper.h +++ b/include/spdlog/details/file_helper.h @@ -8,10 +8,11 @@ // Helper class for file sink // When failing to open a file, retry several times(5) with small delay between the tries(10 ms) // Can be set to auto flush on every line -// Throw spdlog_ex exception on errors +// Call error handler on errors (default: throw spdlog_ex) #include #include +#include #include #include @@ -58,13 +59,15 @@ public: std::this_thread::sleep_for(std::chrono::milliseconds(open_interval)); } - throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing"); + error("Failed opening file " + os::filename_to_str(_filename) + " for writing"); } void reopen(bool truncate) { - if (_filename.empty()) - throw spdlog_ex("Failed re opening file - was not opened before"); + if (_filename.empty()) { + error("Failed re opening file - was not opened before"); + return; + } open(_filename, truncate); } @@ -88,8 +91,10 @@ public: size_t msg_size = msg.formatted.size(); auto data = msg.formatted.data(); - if (std::fwrite(data, 1, msg_size, _fd) != msg_size) - throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename)); + if (std::fwrite(data, 1, msg_size, _fd) != msg_size) { + error("Failed writing to file " + os::filename_to_str(_filename)); + return; + } if (_force_flush) std::fflush(_fd); @@ -97,20 +102,26 @@ public: long size() { - if (!_fd) - throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename)); + if (!_fd) { + error("Cannot use size() on closed file " + os::filename_to_str(_filename)); + return -1; + } auto pos = ftell(_fd); if (fseek(_fd, 0, SEEK_END) != 0) - throw spdlog_ex("fseek failed on file " + os::filename_to_str(_filename)); + error("fseek failed on file " + os::filename_to_str(_filename)); auto file_size = ftell(_fd); - if(fseek(_fd, pos, SEEK_SET) !=0) - throw spdlog_ex("fseek failed on file " + os::filename_to_str(_filename)); + if(fseek(_fd, pos, SEEK_SET) !=0) { + error("fseek failed on file " + os::filename_to_str(_filename)); + return -1; + } - if (file_size == -1) - throw spdlog_ex("ftell failed on file " + os::filename_to_str(_filename)); + if (file_size == -1) { + error("ftell failed on file " + os::filename_to_str(_filename)); + return -1; + } return file_size; } diff --git a/include/spdlog/details/line_logger_impl.h b/include/spdlog/details/line_logger_impl.h index d61225af..61bff9fc 100644 --- a/include/spdlog/details/line_logger_impl.h +++ b/include/spdlog/details/line_logger_impl.h @@ -70,7 +70,7 @@ inline void spdlog::details::line_logger::write(const char* fmt, const Args&... } catch (const fmt::FormatError& e) { - throw spdlog_ex(fmt::format("formatting error while processing format string '{}': {}", fmt, e.what())); + error(fmt::format("formatting error while processing format string '{}': {}", fmt, e.what())); } } diff --git a/include/spdlog/details/mpmc_bounded_q.h b/include/spdlog/details/mpmc_bounded_q.h index ad14d6f2..e74f99c2 100644 --- a/include/spdlog/details/mpmc_bounded_q.h +++ b/include/spdlog/details/mpmc_bounded_q.h @@ -64,8 +64,11 @@ public: buffer_mask_(buffer_size - 1) { //queue size must be power of two - if(!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0))) + if(!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0))) { + // FIXME: call error() instead + // FIXME: can we just round buffer_size down to the next power of two and skip the error altogether? throw spdlog_ex("async logger queue size must be power of two"); + } for (size_t i = 0; i != buffer_size; i += 1) buffer_[i].sequence_.store(i, std::memory_order_relaxed); diff --git a/include/spdlog/details/os.h b/include/spdlog/details/os.h index fda4d0a0..dc978ee9 100644 --- a/include/spdlog/details/os.h +++ b/include/spdlog/details/os.h @@ -198,8 +198,10 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime()) DYNAMIC_TIME_ZONE_INFORMATION tzinfo; auto rv = GetDynamicTimeZoneInformation(&tzinfo); #endif - if (rv == TIME_ZONE_ID_INVALID) - throw spdlog::spdlog_ex("Failed getting timezone info. Last error: " + std::to_string(GetLastError())); + if (rv == TIME_ZONE_ID_INVALID) { + error("Failed getting timezone info. Last error: " + std::to_string(GetLastError())); + return 0; + } int offset = -tzinfo.Bias; if (tm.tm_isdst) diff --git a/include/spdlog/details/pattern_formatter_impl.h b/include/spdlog/details/pattern_formatter_impl.h index 313bfc85..eaaa57d5 100644 --- a/include/spdlog/details/pattern_formatter_impl.h +++ b/include/spdlog/details/pattern_formatter_impl.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -623,6 +624,6 @@ inline void spdlog::pattern_formatter::format(details::log_msg& msg) } catch(const fmt::FormatError& e) { - throw spdlog_ex(fmt::format("formatting error while processing format string: {}", e.what())); + error(fmt::format("formatting error while processing format string: {}", e.what())); } } diff --git a/include/spdlog/details/registry.h b/include/spdlog/details/registry.h index 7d744f89..934b7d56 100644 --- a/include/spdlog/details/registry.h +++ b/include/spdlog/details/registry.h @@ -34,7 +34,7 @@ public: { std::lock_guard lock(_mutex); auto logger_name = logger->name(); - throw_if_exists(logger_name); + error_if_exists(logger_name); _loggers[logger_name] = logger; } @@ -50,7 +50,7 @@ public: std::shared_ptr create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end) { std::lock_guard lock(_mutex); - throw_if_exists(logger_name); + error_if_exists(logger_name); std::shared_ptr new_logger; if (_async_mode) new_logger = std::make_shared(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb); @@ -140,10 +140,10 @@ private: registry_t(const registry_t&) = delete; registry_t& operator=(const registry_t&) = delete; - void throw_if_exists(const std::string &logger_name) + void error_if_exists(const std::string &logger_name) { if (_loggers.find(logger_name) != _loggers.end()) - throw spdlog_ex("logger with name '" + logger_name + "' already exists"); + error("logger with name '" + logger_name + "' already exists"); } Mutex _mutex; std::unordered_map > _loggers; @@ -155,6 +155,7 @@ private: std::function _worker_warmup_cb = nullptr; std::chrono::milliseconds _flush_interval_ms; std::function _worker_teardown_cb = nullptr; + std::function _error_handler = [](const std::string& message) { throw spdlog_ex(message); }; }; #ifdef SPDLOG_NO_REGISTRY_MUTEX typedef registry_t registry; diff --git a/include/spdlog/details/spdlog_impl.h b/include/spdlog/details/spdlog_impl.h index 8337ab47..b34cf53d 100644 --- a/include/spdlog/details/spdlog_impl.h +++ b/include/spdlog/details/spdlog_impl.h @@ -146,3 +146,4 @@ inline void spdlog::drop_all() { details::registry::instance().drop_all(); } + diff --git a/include/spdlog/sinks/android_sink.h b/include/spdlog/sinks/android_sink.h index 885f78da..aacbca1c 100644 --- a/include/spdlog/sinks/android_sink.h +++ b/include/spdlog/sinks/android_sink.h @@ -9,6 +9,7 @@ #include #include +#include #include @@ -48,7 +49,7 @@ protected: } else { - throw spdlog_ex("Send to Android logcat failed"); + error("Send to Android logcat failed"); } } @@ -76,7 +77,8 @@ private: case spdlog::level::emerg: return ANDROID_LOG_FATAL; default: - throw spdlog_ex("Incorrect level value"); + error("Incorrect level value"); + return ANDROID_LOG_INFO; } } diff --git a/include/spdlog/sinks/file_sinks.h b/include/spdlog/sinks/file_sinks.h index 14b3cbff..bd1cc3a3 100644 --- a/include/spdlog/sinks/file_sinks.h +++ b/include/spdlog/sinks/file_sinks.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -119,12 +120,14 @@ private: { if (details::os::remove(target) != 0) { - throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target)); + error("rotating_file_sink: failed removing " + filename_to_str(target)); + return; } } if (details::file_helper::file_exists(src) && details::os::rename(src, target)) { - throw spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target)); + error("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target)); + return; } } _file_helper.reopen(true); @@ -189,8 +192,16 @@ public: _rotation_m(rotation_minute), _file_helper(force_flush) { - if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59) - throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor"); + if (rotation_hour < 0 || rotation_hour > 23) { + error("daily_file_sink: Invalid rotation hour (" + std::to_string(rotation_hour) + "). Must be between 0 and 23."); + rotation_hour = 0; + } + + if (rotation_minute < 0 || rotation_minute > 59) { + error("daily_file_sink: Invalid rotation minute (" + std::to_string(rotation_minute) + "). Must be between 0 and 59."); + rotation_minute = 0; + } + _rotation_tp = _next_rotation_tp(); _file_helper.open(FileNameCalc::calc_filename(_base_filename, _extension)); } diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h index 043e8141..b563a988 100644 --- a/include/spdlog/spdlog.h +++ b/include/spdlog/spdlog.h @@ -112,7 +112,6 @@ void drop(const std::string &name); // Drop all references void drop_all(); - /////////////////////////////////////////////////////////////////////////////// // // Macros to be display source file & line