diff --git a/CMakeLists.txt b/CMakeLists.txt index a66e8405..3389c04b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,7 @@ option(SPDLOG_SANITIZE_ADDRESS "Enable address sanitizer in tests" OFF) option(SPDLOG_BUILD_WARNINGS "Enable compiler warnings" OFF) # install options +option(SPDLOG_SYSTEM_INCLUDES "Include as system headers (skip for clang-tidy)." OFF) option(SPDLOG_INSTALL "Generate the install target" ${SPDLOG_MASTER_PROJECT}) option(SPDLOG_USE_STD_FORMAT "Use std::format instead of fmt library." OFF) option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF) @@ -170,8 +171,13 @@ endif() add_library(spdlog::spdlog ALIAS spdlog) +set(SPDLOG_INCLUDES_LEVEL "") +if(SPDLOG_SYSTEM_INCLUDES) + set(SPDLOG_INCLUDES_LEVEL "SYSTEM") +endif() + target_compile_definitions(spdlog PUBLIC SPDLOG_COMPILED_LIB) -target_include_directories(spdlog PUBLIC "$" +target_include_directories(spdlog ${SPDLOG_INCLUDES_LEVEL} PUBLIC "$" "$") target_link_libraries(spdlog PUBLIC Threads::Threads) spdlog_enable_warnings(spdlog) @@ -190,7 +196,7 @@ endif() add_library(spdlog_header_only INTERFACE) add_library(spdlog::spdlog_header_only ALIAS spdlog_header_only) -target_include_directories(spdlog_header_only INTERFACE "$" +target_include_directories(spdlog_header_only ${SPDLOG_INCLUDES_LEVEL} INTERFACE "$" "$") target_link_libraries(spdlog_header_only INTERFACE Threads::Threads) diff --git a/README.md b/README.md index 46adb9f0..9f6291b9 100644 --- a/README.md +++ b/README.md @@ -146,17 +146,16 @@ void daily_example() ```c++ // Debug messages can be stored in a ring buffer instead of being logged immediately. // This is useful in order to display debug logs only when really needed (e.g. when error happens). -// When needed, call dump_backtrace() to see them. +// When needed, call dump_backtrace() to dump them to your log. -spdlog::enable_backtrace(32); // Store the latest 32 messages in a buffer. Older messages will be dropped. +spdlog::enable_backtrace(32); // Store the latest 32 messages in a buffer. // or my_logger->enable_backtrace(32).. for(int i = 0; i < 100; i++) { spdlog::debug("Backtrace message {}", i); // not logged yet.. } -// e.g. if some error happened: +// e.g. if some has error happened: spdlog::dump_backtrace(); // log them now! show the last 32 messages - // or my_logger->dump_backtrace(32).. ``` @@ -233,6 +232,27 @@ void multi_sink_example() } ``` +--- +#### User defined callbacks about log events +```c++ + +// create logger with a lambda function callback, the callback will be called +// each time something is logged to the logger +void callback_example() +{ + auto callback_sink = std::make_shared([](const spdlog::details::log_msg &msg) { + // for example you can be notified by sending an email to yourself + }); + callback_sink->set_level(spdlog::level::err); + + auto console_sink = std::make_shared(); + spdlog::logger logger("custom_callback_logger", {console_sink, callback_sink}); + + logger.info("some info log"); + logger.error("critical issue"); // will notify you +} +``` + --- #### Asynchronous logging ```c++ @@ -376,7 +396,7 @@ $ ./example #### Log file open/close event handlers ```c++ // You can get callbacks from spdlog before/after log file has been opened or closed. -// This is useful for cleanup procedures or for adding someting the start/end of the log files. +// This is useful for cleanup procedures or for adding something the start/end of the log files. void file_events_example() { // pass the spdlog::file_event_handlers to file sinks for open/close log file notifications diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 83336e98..a7863493 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -12,7 +12,7 @@ endif() # Example of using pre-compiled library # --------------------------------------------------------------------------------------- add_executable(example example.cpp) -target_link_libraries(example PRIVATE spdlog::spdlog) +target_link_libraries(example PRIVATE spdlog::spdlog $<$:ws2_32>) # --------------------------------------------------------------------------------------- # Example of using header-only library diff --git a/example/example.cpp b/example/example.cpp index ccfdcf2f..316a22b1 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -12,6 +12,7 @@ void stdout_logger_example(); void basic_example(); void rotating_example(); void daily_example(); +void callback_example(); void async_example(); void binary_example(); void vector_example(); @@ -72,6 +73,7 @@ int main(int, char *[]) basic_example(); rotating_example(); daily_example(); + callback_example(); async_example(); binary_example(); vector_example(); @@ -136,6 +138,15 @@ void daily_example() auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30); } +#include "spdlog/sinks/callback_sink.h" +void callback_example() +{ + // Create the logger + auto logger = spdlog::callback_logger_mt("custom_callback_logger", [](const spdlog::details::log_msg &/*msg*/) { + // do what you need to do with msg + }); +} + #include "spdlog/cfg/env.h" void load_levels_example() { diff --git a/include/spdlog/details/backtracer-inl.h b/include/spdlog/details/backtracer-inl.h index 2621c8f7..40eba408 100644 --- a/include/spdlog/details/backtracer-inl.h +++ b/include/spdlog/details/backtracer-inl.h @@ -54,6 +54,12 @@ SPDLOG_INLINE void backtracer::push_back(const log_msg &msg) messages_.push_back(log_msg_buffer{msg}); } +SPDLOG_INLINE bool backtracer::empty() const +{ + std::lock_guard lock{mutex_}; + return messages_.empty(); +} + // pop all items in the q and apply the given fun on each of them. SPDLOG_INLINE void backtracer::foreach_pop(std::function fun) { diff --git a/include/spdlog/details/backtracer.h b/include/spdlog/details/backtracer.h index b336ee77..13785d85 100644 --- a/include/spdlog/details/backtracer.h +++ b/include/spdlog/details/backtracer.h @@ -32,6 +32,7 @@ public: void disable(); bool enabled() const; void push_back(const log_msg &msg); + bool empty() const; // pop all items in the q and apply the given fun on each of them. void foreach_pop(std::function fun); diff --git a/include/spdlog/details/file_helper-inl.h b/include/spdlog/details/file_helper-inl.h index d4528711..3c45d8c0 100644 --- a/include/spdlog/details/file_helper-inl.h +++ b/include/spdlog/details/file_helper-inl.h @@ -90,6 +90,14 @@ SPDLOG_INLINE void file_helper::flush() } } +SPDLOG_INLINE void file_helper::sync() +{ + if(!os::fsync(fd_)) + { + throw_spdlog_ex("Failed to fsync file " + os::filename_to_str(filename_), errno); + } +} + SPDLOG_INLINE void file_helper::close() { if (fd_ != nullptr) diff --git a/include/spdlog/details/file_helper.h b/include/spdlog/details/file_helper.h index 0f5988b9..f42a5eb1 100644 --- a/include/spdlog/details/file_helper.h +++ b/include/spdlog/details/file_helper.h @@ -26,6 +26,7 @@ public: void open(const filename_t &fname, bool truncate = false); void reopen(bool truncate); void flush(); + void sync(); void close(); void write(const memory_buf_t &buf); size_t size() const; diff --git a/include/spdlog/details/mpmc_blocking_q.h b/include/spdlog/details/mpmc_blocking_q.h index 785180c1..101ea8c0 100644 --- a/include/spdlog/details/mpmc_blocking_q.h +++ b/include/spdlog/details/mpmc_blocking_q.h @@ -49,7 +49,7 @@ public: push_cv_.notify_one(); } - // try to dequeue item. if no item found. wait up to timeout and try again + // dequeue with a timeout. // Return true, if succeeded dequeue item, false otherwise bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) { @@ -66,6 +66,18 @@ public: return true; } + // blocking dequeue without a timeout. + void dequeue(T &popped_item) + { + { + std::unique_lock lock(queue_mutex_); + push_cv_.wait(lock, [this] { return !this->q_.empty(); }); + popped_item = std::move(q_.front()); + q_.pop_front(); + } + pop_cv_.notify_one(); + } + #else // apparently mingw deadlocks if the mutex is released before cv.notify_one(), // so release the mutex at the very end each function. @@ -87,7 +99,7 @@ public: push_cv_.notify_one(); } - // try to dequeue item. if no item found. wait up to timeout and try again + // dequeue with a timeout. // Return true, if succeeded dequeue item, false otherwise bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) { @@ -102,6 +114,16 @@ public: return true; } + // blocking dequeue without a timeout. + void dequeue(T &popped_item) + { + std::unique_lock lock(queue_mutex_); + push_cv_.wait(lock, [this] { return !this->q_.empty(); }); + popped_item = std::move(q_.front()); + q_.pop_front(); + pop_cv_.notify_one(); + } + #endif size_t overrun_counter() diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h index b9bab53c..19d4bdc5 100644 --- a/include/spdlog/details/os-inl.h +++ b/include/spdlog/details/os-inl.h @@ -23,9 +23,10 @@ #ifdef _WIN32 -# include // _get_osfhandle and _isatty support -# include // _get_pid support +# include // for _get_osfhandle, _isatty, _fileno +# include // for _get_pid # include +# include // for FlushFileBuffers # ifdef __MINGW32__ # include @@ -236,8 +237,8 @@ SPDLOG_INLINE size_t filesize(FILE *f) # else int fd = ::fileno(f); # endif -// 64 bits(but not in osx or cygwin, where fstat64 is deprecated) -# if (defined(__linux__) || defined(__sun) || defined(_AIX)) && (defined(__LP64__) || defined(_LP64)) +// 64 bits(but not in osx, linux/musl or cygwin, where fstat64 is deprecated) +# if ((defined(__linux__) && defined(__GLIBC__)) || defined(__sun) || defined(_AIX)) && (defined(__LP64__) || defined(_LP64)) struct stat64 st; if (::fstat64(fd, &st) == 0) { @@ -286,7 +287,7 @@ SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) return offset; #else -# if defined(sun) || defined(__sun) || defined(_AIX) || (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE)) +# if defined(sun) || defined(__sun) || defined(_AIX) || (defined(__NEWLIB__) && !defined(__TM_GMTOFF)) || (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE)) // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris struct helper { @@ -601,6 +602,17 @@ std::string SPDLOG_INLINE getenv(const char *field) #endif } +// Do fsync by FILE handlerpointer +// Return true on success +SPDLOG_INLINE bool fsync(FILE *fp) +{ +#ifdef _WIN32 + return FlushFileBuffers(reinterpret_cast(_get_osfhandle(_fileno(fp)))) != 0; +#else + return ::fsync(fileno(fp)) == 0; +#endif +} + } // namespace os } // namespace details } // namespace spdlog diff --git a/include/spdlog/details/os.h b/include/spdlog/details/os.h index b154bc47..f55642c1 100644 --- a/include/spdlog/details/os.h +++ b/include/spdlog/details/os.h @@ -109,6 +109,10 @@ SPDLOG_API bool create_dir(const filename_t &path); // return empty string if field not found SPDLOG_API std::string getenv(const char *field); +// Do fsync by FILE objectpointer. +// Return true on success. +SPDLOG_API bool fsync(FILE * fp); + } // namespace os } // namespace details } // namespace spdlog diff --git a/include/spdlog/details/registry-inl.h b/include/spdlog/details/registry-inl.h index e6ecc9b0..323a5c13 100644 --- a/include/spdlog/details/registry-inl.h +++ b/include/spdlog/details/registry-inl.h @@ -287,6 +287,14 @@ SPDLOG_INLINE registry ®istry::instance() return s_instance; } +SPDLOG_INLINE void registry::apply_logger_env_levels(std::shared_ptr new_logger) +{ + std::lock_guard lock(logger_map_mutex_); + auto it = log_levels_.find(new_logger->name()); + auto new_level = it != log_levels_.end() ? it->second : global_log_level_; + new_logger->set_level(new_level); +} + SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name) { if (loggers_.find(logger_name) != loggers_.end()) diff --git a/include/spdlog/details/registry.h b/include/spdlog/details/registry.h index 6a4a5abc..4666fa29 100644 --- a/include/spdlog/details/registry.h +++ b/include/spdlog/details/registry.h @@ -91,6 +91,8 @@ public: static registry &instance(); + void apply_logger_env_levels(std::shared_ptr new_logger); + private: registry(); ~registry(); diff --git a/include/spdlog/details/tcp_client.h b/include/spdlog/details/tcp_client.h index 0daff0eb..e4d7a48e 100644 --- a/include/spdlog/details/tcp_client.h +++ b/include/spdlog/details/tcp_client.h @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -110,7 +111,7 @@ public: #endif #if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL) -# error "tcp_sink would raise SIGPIPE since niether SO_NOSIGPIPE nor MSG_NOSIGNAL are available" +# error "tcp_sink would raise SIGPIPE since neither SO_NOSIGPIPE nor MSG_NOSIGNAL are available" #endif } diff --git a/include/spdlog/details/thread_pool-inl.h b/include/spdlog/details/thread_pool-inl.h index 369f30fe..dbd424ff 100644 --- a/include/spdlog/details/thread_pool-inl.h +++ b/include/spdlog/details/thread_pool-inl.h @@ -108,11 +108,7 @@ void SPDLOG_INLINE thread_pool::worker_loop_() bool SPDLOG_INLINE thread_pool::process_next_msg_() { async_msg incoming_async_msg; - bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10)); - if (!dequeued) - { - return true; - } + q_.dequeue(incoming_async_msg); switch (incoming_async_msg.msg_type) { diff --git a/include/spdlog/details/udp_client-windows.h b/include/spdlog/details/udp_client-windows.h index 8e763356..7d25f037 100644 --- a/include/spdlog/details/udp_client-windows.h +++ b/include/spdlog/details/udp_client-windows.h @@ -15,9 +15,11 @@ #include #include -#pragma comment(lib, "Ws2_32.lib") -#pragma comment(lib, "Mswsock.lib") -#pragma comment(lib, "AdvApi32.lib") +#if defined(_MSC_VER) +# pragma comment(lib, "Ws2_32.lib") +# pragma comment(lib, "Mswsock.lib") +# pragma comment(lib, "AdvApi32.lib") +#endif namespace spdlog { namespace details { @@ -25,7 +27,7 @@ class udp_client { static constexpr int TX_BUFFER_SIZE = 1024 * 10; SOCKET socket_ = INVALID_SOCKET; - sockaddr_in addr_ = {0}; + sockaddr_in addr_ = {}; static void init_winsock_() { diff --git a/include/spdlog/fmt/bin_to_hex.h b/include/spdlog/fmt/bin_to_hex.h index 47fec05b..1cb51ce6 100644 --- a/include/spdlog/fmt/bin_to_hex.h +++ b/include/spdlog/fmt/bin_to_hex.h @@ -196,7 +196,7 @@ struct formatter, char> continue; } - if (put_delimiters) + if (put_delimiters && i != the_range.get_begin()) { *inserter++ = delimiter; } diff --git a/include/spdlog/logger-inl.h b/include/spdlog/logger-inl.h index 411f2cb5..ff82db4c 100644 --- a/include/spdlog/logger-inl.h +++ b/include/spdlog/logger-inl.h @@ -210,7 +210,7 @@ SPDLOG_INLINE void logger::flush_() SPDLOG_INLINE void logger::dump_backtrace_() { using details::log_msg; - if (tracer_.enabled()) + if (tracer_.enabled() && !tracer_.empty()) { sink_it_(log_msg{name(), level::info, "****************** Backtrace Start ******************"}); tracer_.foreach_pop([this](const log_msg &msg) { this->sink_it_(msg); }); diff --git a/include/spdlog/sinks/android_sink.h b/include/spdlog/sinks/android_sink.h index 07dbeea8..8e79638b 100644 --- a/include/spdlog/sinks/android_sink.h +++ b/include/spdlog/sinks/android_sink.h @@ -74,7 +74,7 @@ protected: private: // There might be liblog versions used, that do not support __android_log_buf_write. So we only compile and link against - // __android_log_buf_write, if user explicitely provides a non-default log buffer. Otherwise, when using the default log buffer, always + // __android_log_buf_write, if user explicitly provides a non-default log buffer. Otherwise, when using the default log buffer, always // log via __android_log_write. template typename std::enable_if(log_id::LOG_ID_MAIN), int>::type android_log(int prio, const char *tag, const char *text) @@ -139,4 +139,4 @@ inline std::shared_ptr android_logger_st(const std::string &logger_name, } // namespace spdlog -#endif // __ANDROID__ \ No newline at end of file +#endif // __ANDROID__ diff --git a/include/spdlog/sinks/callback_sink.h b/include/spdlog/sinks/callback_sink.h new file mode 100644 index 00000000..bcd31383 --- /dev/null +++ b/include/spdlog/sinks/callback_sink.h @@ -0,0 +1,61 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#include +#include +#include + +#include +#include + +namespace spdlog { + +// callbacks type +typedef std::function custom_log_callback; + +namespace sinks { +/* + * Trivial callback sink, gets a callback function and calls it on each log + */ +template +class callback_sink final : public base_sink +{ +public: + explicit callback_sink(const custom_log_callback &callback) + : callback_{callback} + {} + +protected: + void sink_it_(const details::log_msg &msg) override + { + callback_(msg); + } + void flush_() override{}; + +private: + custom_log_callback callback_; +}; + +using callback_sink_mt = callback_sink; +using callback_sink_st = callback_sink; + +} // namespace sinks + +// +// factory functions +// +template +inline std::shared_ptr callback_logger_mt(const std::string &logger_name, const custom_log_callback &callback) +{ + return Factory::template create(logger_name, callback); +} + +template +inline std::shared_ptr callback_logger_st(const std::string &logger_name, const custom_log_callback &callback) +{ + return Factory::template create(logger_name, callback); +} + +} // namespace spdlog diff --git a/include/spdlog/sinks/dist_sink.h b/include/spdlog/sinks/dist_sink.h index 065048ad..7ec3a2ec 100644 --- a/include/spdlog/sinks/dist_sink.h +++ b/include/spdlog/sinks/dist_sink.h @@ -31,16 +31,16 @@ public: dist_sink(const dist_sink &) = delete; dist_sink &operator=(const dist_sink &) = delete; - void add_sink(std::shared_ptr sink) + void add_sink(std::shared_ptr sub_sink) { std::lock_guard lock(base_sink::mutex_); - sinks_.push_back(sink); + sinks_.push_back(sub_sink); } - void remove_sink(std::shared_ptr sink) + void remove_sink(std::shared_ptr sub_sink) { std::lock_guard lock(base_sink::mutex_); - sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end()); + sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sub_sink), sinks_.end()); } void set_sinks(std::vector> sinks) diff --git a/include/spdlog/sinks/systemd_sink.h b/include/spdlog/sinks/systemd_sink.h index e1e97bff..f889f7d5 100644 --- a/include/spdlog/sinks/systemd_sink.h +++ b/include/spdlog/sinks/systemd_sink.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include @@ -75,11 +76,17 @@ protected: { // Note: function call inside '()' to avoid macro expansion err = (sd_journal_send)("MESSAGE=%.*s", static_cast(length), payload.data(), "PRIORITY=%d", syslog_level(msg.level), +#ifndef SPDLOG_NO_THREAD_ID + "TID=%zu", os::thread_id(), +#endif "SYSLOG_IDENTIFIER=%.*s", static_cast(syslog_identifier.size()), syslog_identifier.data(), nullptr); } else { err = (sd_journal_send)("MESSAGE=%.*s", static_cast(length), payload.data(), "PRIORITY=%d", syslog_level(msg.level), +#ifndef SPDLOG_NO_THREAD_ID + "TID=%zu", os::thread_id(), +#endif "SYSLOG_IDENTIFIER=%.*s", static_cast(syslog_identifier.size()), syslog_identifier.data(), "CODE_FILE=%s", msg.source.filename, "CODE_LINE=%d", msg.source.line, "CODE_FUNC=%s", msg.source.funcname, nullptr); } diff --git a/include/spdlog/sinks/win_eventlog_sink.h b/include/spdlog/sinks/win_eventlog_sink.h index 2f2aacb5..def4b135 100644 --- a/include/spdlog/sinks/win_eventlog_sink.h +++ b/include/spdlog/sinks/win_eventlog_sink.h @@ -210,7 +210,7 @@ private: HANDLE hEventLog_{NULL}; internal::sid_t current_user_sid_; std::string source_; - WORD event_id_; + DWORD event_id_; HANDLE event_log_handle() { @@ -258,7 +258,7 @@ protected: void flush_() override {} public: - win_eventlog_sink(std::string const &source, WORD event_id = 1000 /* according to mscoree.dll */) + win_eventlog_sink(std::string const &source, DWORD event_id = 1000 /* according to mscoree.dll */) : source_(source) , event_id_(event_id) { diff --git a/include/spdlog/spdlog-inl.h b/include/spdlog/spdlog-inl.h index 708399c1..22ea22bb 100644 --- a/include/spdlog/spdlog-inl.h +++ b/include/spdlog/spdlog-inl.h @@ -117,4 +117,9 @@ SPDLOG_INLINE void set_default_logger(std::shared_ptr default_lo details::registry::instance().set_default_logger(std::move(default_logger)); } +SPDLOG_INLINE void apply_logger_env_levels(std::shared_ptr logger) +{ + details::registry::instance().apply_logger_env_levels(std::move(logger)); +} + } // namespace spdlog diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h index ee83e8de..6b7b221a 100644 --- a/include/spdlog/spdlog.h +++ b/include/spdlog/spdlog.h @@ -131,6 +131,15 @@ SPDLOG_API spdlog::logger *default_logger_raw(); SPDLOG_API void set_default_logger(std::shared_ptr default_logger); +// Initialize logger level based on environment configs. +// +// Useful for applying SPDLOG_LEVEL to manually created loggers. +// +// Example: +// auto mylogger = std::make_shared("mylogger", ...); +// spdlog::apply_logger_env_levels(mylogger); +SPDLOG_API void apply_logger_env_levels(std::shared_ptr logger); + template inline void log(source_loc source, level::level_enum lvl, format_string_t fmt, Args &&... args) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7fe4791e..12204e2b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -31,6 +31,7 @@ set(SPDLOG_UTESTS_SOURCES test_stdout_api.cpp test_backtrace.cpp test_create_dir.cpp + test_custom_callbacks.cpp test_cfg.cpp test_time_point.cpp test_stopwatch.cpp) diff --git a/tests/includes.h b/tests/includes.h index 16394440..100984cc 100644 --- a/tests/includes.h +++ b/tests/includes.h @@ -32,4 +32,5 @@ #include "spdlog/sinks/ostream_sink.h" #include "spdlog/sinks/rotating_file_sink.h" #include "spdlog/sinks/stdout_color_sinks.h" +#include "spdlog/sinks/msvc_sink.h" #include "spdlog/pattern_formatter.h" diff --git a/tests/test_backtrace.cpp b/tests/test_backtrace.cpp index 9504b82b..6cf9ec55 100644 --- a/tests/test_backtrace.cpp +++ b/tests/test_backtrace.cpp @@ -31,6 +31,19 @@ TEST_CASE("bactrace1", "[bactrace]") REQUIRE(test_sink->lines()[7] == "****************** Backtrace End ********************"); } +TEST_CASE("bactrace-empty", "[bactrace]") +{ + using spdlog::sinks::test_sink_st; + auto test_sink = std::make_shared(); + size_t backtrace_size = 5; + + spdlog::logger logger("test-backtrace", test_sink); + logger.set_pattern("%v"); + logger.enable_backtrace(backtrace_size); + logger.dump_backtrace(); + REQUIRE(test_sink->lines().size() == 0); +} + TEST_CASE("bactrace-async", "[bactrace]") { using spdlog::sinks::test_sink_mt; diff --git a/tests/test_custom_callbacks.cpp b/tests/test_custom_callbacks.cpp new file mode 100644 index 00000000..877e1608 --- /dev/null +++ b/tests/test_custom_callbacks.cpp @@ -0,0 +1,34 @@ +/* + * This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE + */ +#include "includes.h" +#include "test_sink.h" +#include "spdlog/sinks/callback_sink.h" +#include "spdlog/async.h" +#include "spdlog/common.h" + +TEST_CASE("custom_callback_logger", "[custom_callback_logger]]") +{ + std::vector lines; + spdlog::pattern_formatter formatter; + auto callback_logger = std::make_shared([&](const spdlog::details::log_msg &msg) { + spdlog::memory_buf_t formatted; + formatter.format(msg, formatted); + auto eol_len = strlen(spdlog::details::os::default_eol); + lines.emplace_back(formatted.begin(), formatted.end() - eol_len); + }); + std::shared_ptr test_sink(new spdlog::sinks::test_sink_st); + + spdlog::logger logger("test-callback", {callback_logger, test_sink}); + + logger.info("test message 1"); + logger.info("test message 2"); + logger.info("test message 3"); + + std::vector ref_lines = test_sink->lines(); + + REQUIRE(lines[0] == ref_lines[0]); + REQUIRE(lines[1] == ref_lines[1]); + REQUIRE(lines[2] == ref_lines[2]); + spdlog::drop_all(); +} diff --git a/tests/test_mpmc_q.cpp b/tests/test_mpmc_q.cpp index 3b8aec36..1540dcc8 100644 --- a/tests/test_mpmc_q.cpp +++ b/tests/test_mpmc_q.cpp @@ -43,6 +43,26 @@ TEST_CASE("dequeue-empty-wait", "[mpmc_blocking_q]") REQUIRE(delta_ms <= wait_ms + tolerance_wait); } +TEST_CASE("dequeue-full-nowait", "[mpmc_blocking_q]") +{ + spdlog::details::mpmc_blocking_queue q(1); + q.enqueue(42); + + int item = 0; + q.dequeue_for(item, milliseconds::zero()); + REQUIRE(item == 42); +} + +TEST_CASE("dequeue-full-wait", "[mpmc_blocking_q]") +{ + spdlog::details::mpmc_blocking_queue q(1); + q.enqueue(42); + + int item = 0; + q.dequeue(item); + REQUIRE(item == 42); +} + TEST_CASE("enqueue_nowait", "[mpmc_blocking_q]") { @@ -95,12 +115,12 @@ TEST_CASE("full_queue", "[mpmc_blocking_q]") for (int i = 1; i < static_cast(q_size); i++) { int item = -1; - q.dequeue_for(item, milliseconds(0)); + q.dequeue(item); REQUIRE(item == i); } // last item pushed has overridden the oldest. int item = -1; - q.dequeue_for(item, milliseconds(0)); + q.dequeue(item); REQUIRE(item == 123456); }