From a6215527f42ca7899d3bc2e24c9d2d985bf2670a Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sun, 6 Jul 2025 08:38:48 +0300 Subject: [PATCH 1/7] Fix ringbuffer tests for newline (#3436) --- include/spdlog/sinks/ringbuffer_sink.h | 6 ++- tests/CMakeLists.txt | 3 +- tests/test_ringbuffer.cpp | 53 ++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 tests/test_ringbuffer.cpp diff --git a/include/spdlog/sinks/ringbuffer_sink.h b/include/spdlog/sinks/ringbuffer_sink.h index 6156c6a5..bcdf0ffc 100644 --- a/include/spdlog/sinks/ringbuffer_sink.h +++ b/include/spdlog/sinks/ringbuffer_sink.h @@ -21,7 +21,11 @@ template class ringbuffer_sink final : public base_sink { public: explicit ringbuffer_sink(size_t n_items) - : q_{n_items} {} + : q_{n_items} { + if (n_items == 0) { + throw_spdlog_ex("ringbuffer_sink: n_items cannot be zero"); + } + } std::vector last_raw(size_t lim = 0) { std::lock_guard lock(base_sink::mutex_); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f6b17272..457504c3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -49,7 +49,8 @@ set(SPDLOG_UTESTS_SOURCES test_time_point.cpp test_stopwatch.cpp test_circular_q.cpp - test_bin_to_hex.cpp) + test_bin_to_hex.cpp + test_ringbuffer.cpp) if(NOT SPDLOG_NO_EXCEPTIONS) list(APPEND SPDLOG_UTESTS_SOURCES test_errors.cpp) diff --git a/tests/test_ringbuffer.cpp b/tests/test_ringbuffer.cpp new file mode 100644 index 00000000..da473df5 --- /dev/null +++ b/tests/test_ringbuffer.cpp @@ -0,0 +1,53 @@ +#include "includes.h" +#include "spdlog/sinks/ringbuffer_sink.h" + +TEST_CASE("ringbuffer invalid size", "[ringbuffer]") { + REQUIRE_THROWS_AS(spdlog::sinks::ringbuffer_sink_mt(0), spdlog::spdlog_ex); +} + +TEST_CASE("ringbuffer stores formatted messages", "[ringbuffer]") { + spdlog::sinks::ringbuffer_sink_st sink(3); + sink.set_pattern("%v"); + + sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "msg1"}); + sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "msg2"}); + sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "msg3"}); + + auto formatted = sink.last_formatted(); + REQUIRE(formatted.size() == 3); + using spdlog::details::os::default_eol; + REQUIRE(formatted[0] == spdlog::fmt_lib::format("msg1{}", default_eol)); + REQUIRE(formatted[1] == spdlog::fmt_lib::format("msg2{}", default_eol)); + REQUIRE(formatted[2] == spdlog::fmt_lib::format("msg3{}", default_eol)); +} + +TEST_CASE("ringbuffer overrun keeps last items", "[ringbuffer]") { + spdlog::sinks::ringbuffer_sink_st sink(2); + sink.set_pattern("%v"); + + sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "first"}); + sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "second"}); + sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "third"}); + + auto formatted = sink.last_formatted(); + REQUIRE(formatted.size() == 2); + using spdlog::details::os::default_eol; + REQUIRE(formatted[0] == spdlog::fmt_lib::format("second{}", default_eol)); + REQUIRE(formatted[1] == spdlog::fmt_lib::format("third{}", default_eol)); +} + +TEST_CASE("ringbuffer retrieval limit", "[ringbuffer]") { + spdlog::sinks::ringbuffer_sink_st sink(3); + sink.set_pattern("%v"); + + sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "A"}); + sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "B"}); + sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "C"}); + + auto formatted = sink.last_formatted(2); + REQUIRE(formatted.size() == 2); + using spdlog::details::os::default_eol; + REQUIRE(formatted[0] == spdlog::fmt_lib::format("B{}", default_eol)); + REQUIRE(formatted[1] == spdlog::fmt_lib::format("C{}", default_eol)); +} + From 4619e18a166d4a19fd83c974fecde816e6700bc9 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sun, 6 Jul 2025 09:53:42 +0300 Subject: [PATCH 2/7] Update windows.yml --- .github/workflows/windows.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 710e4090..c14bfc92 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -76,10 +76,10 @@ jobs: build\tests\${{ matrix.config.BUILD_TYPE }}\spdlog-utests.exe # ----------------------------------------------------------------------- - # MSVC 2019 build matrix + # Windows 2025 mscv 2019 build matrix # ----------------------------------------------------------------------- build_2019: - runs-on: windows-2019 + runs-on: windows-2025 strategy: fail-fast: true matrix: From 6fd67ce16927f3fd6760cf8dccc0d6e7c03dad29 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sun, 6 Jul 2025 10:08:55 +0300 Subject: [PATCH 3/7] Update windows.yml remove msvc 2019 build --- .github/workflows/windows.yml | 71 +---------------------------------- 1 file changed, 1 insertion(+), 70 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index c14bfc92..e3b547c8 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -75,74 +75,5 @@ jobs: run: | build\tests\${{ matrix.config.BUILD_TYPE }}\spdlog-utests.exe - # ----------------------------------------------------------------------- - # Windows 2025 mscv 2019 build matrix - # ----------------------------------------------------------------------- - build_2019: - runs-on: windows-2025 - strategy: - fail-fast: true - matrix: - config: - - GENERATOR: "Visual Studio 16 2019" - BUILD_TYPE: Release - BUILD_SHARED: 'ON' - FATAL_ERRORS: 'ON' - WCHAR: 'OFF' - WCHAR_FILES: 'OFF' - BUILD_EXAMPLE: 'ON' - USE_STD_FORMAT: 'OFF' - CXX_STANDARD: 17 - - GENERATOR: "Visual Studio 16 2019" - BUILD_TYPE: Release - BUILD_SHARED: 'ON' - FATAL_ERRORS: 'ON' - WCHAR: 'OFF' - WCHAR_FILES: 'OFF' - BUILD_EXAMPLE: 'ON' - USE_STD_FORMAT: 'OFF' - CXX_STANDARD: 14 - - GENERATOR: "Visual Studio 16 2019" - BUILD_TYPE: Release - BUILD_SHARED: 'ON' - FATAL_ERRORS: 'ON' - WCHAR: 'OFF' - WCHAR_FILES: 'OFF' - BUILD_EXAMPLE: 'ON' - USE_STD_FORMAT: 'OFF' - CXX_STANDARD: 11 - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: CMake ${{ matrix.config.GENERATOR }} CXX=${{matrix.config.CXX_STANDARD}} WCHAR=${{matrix.config.WCHAR_FILES}} STD_FORMAT=${{matrix.config.USE_STD_FORMAT}} - shell: pwsh - run: | - mkdir build - cd build - cmake -G "${{ matrix.config.GENERATOR }}" -A x64 ` - -D CMAKE_BUILD_TYPE=${{ matrix.config.BUILD_TYPE }} ` - -D BUILD_SHARED_LIBS=${{ matrix.config.BUILD_SHARED }} ` - -D SPDLOG_WCHAR_SUPPORT=${{ matrix.config.WCHAR }} ` - -D SPDLOG_WCHAR_FILENAMES=${{ matrix.config.WCHAR_FILES }} ` - -D SPDLOG_BUILD_EXAMPLE=${{ matrix.config.BUILD_EXAMPLE }} ` - -D SPDLOG_BUILD_EXAMPLE_HO=${{ matrix.config.BUILD_EXAMPLE }} ` - -D SPDLOG_BUILD_TESTS=ON ` - -D SPDLOG_BUILD_TESTS_HO=OFF ` - -D SPDLOG_BUILD_WARNINGS=${{ matrix.config.FATAL_ERRORS }} ` - -D SPDLOG_USE_STD_FORMAT=${{ matrix.config.USE_STD_FORMAT }} ` - -D CMAKE_CXX_STANDARD=${{ matrix.config.CXX_STANDARD }} .. - - - name: Build - shell: pwsh - run: | - cd build - cmake --build . --parallel --config ${{ matrix.config.BUILD_TYPE }} - - - name: Run Tests - shell: pwsh - env: - PATH: ${{ env.PATH }};${{ github.workspace }}\build\_deps\catch2-build\src\${{ matrix.config.BUILD_TYPE }};${{ github.workspace }}\build\${{ matrix.config.BUILD_TYPE }} - run: | - build\tests\${{ matrix.config.BUILD_TYPE }}\spdlog-utests.exe + From 4397dac510274c7ccf79b5f40b1b747033c12dda Mon Sep 17 00:00:00 2001 From: Joshua Chapman Date: Tue, 8 Jul 2025 21:52:42 +0200 Subject: [PATCH 4/7] chore(cmake): add option to override CMAKE_DEBUG_POSTFIX (#3433) This will make it possible to use the pkg-config with CMake debug build. --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3368e4dd..882aa90e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,9 @@ option(SPDLOG_ENABLE_PCH "Build static or shared library using precompiled heade # build position independent code option(SPDLOG_BUILD_PIC "Build position independent code (-fPIC)" OFF) +# debug build postfix +set(SPDLOG_DEBUG_POSTFIX "d" CACHE STRING "Filename postfix for libraries in debug builds") + # example options option(SPDLOG_BUILD_EXAMPLE "Build example" ${SPDLOG_MASTER_PROJECT}) option(SPDLOG_BUILD_EXAMPLE_HO "Build header only example" OFF) @@ -191,7 +194,7 @@ spdlog_enable_warnings(spdlog) set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION ${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR}) -set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX d) +set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX ${SPDLOG_DEBUG_POSTFIX}) if(COMMAND target_precompile_headers AND SPDLOG_ENABLE_PCH) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/pch.h.in ${PROJECT_BINARY_DIR}/spdlog_pch.h @ONLY) From 4f2b3d52f947ed0628b6fe37a024e74018cdcbb2 Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 16 Jul 2025 08:59:51 +0300 Subject: [PATCH 5/7] Update README.md (#3437) * Update README.md add example showcasing 2 loggers and `spdlog::set_level()` which set level not only to default logger, but to all registed loggers * Update README.md * simplify * simplify --- README.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e8c8c31d..a558a6e4 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ int main() spdlog::info("Positional args are {1} {0}..", "too", "supported"); spdlog::info("{:<30}", "left aligned"); - spdlog::set_level(spdlog::level::debug); // Set global log level to debug + spdlog::set_level(spdlog::level::debug); // Set *global* log level to debug spdlog::debug("This message should be displayed.."); // change log pattern @@ -224,7 +224,7 @@ void binary_example() ```c++ // create a logger with 2 targets, with different log levels and formats. -// The console will show only warnings or errors, while the file will log all. +// The console will show only warnings or errors, while the file will log all. void multi_sink_example() { auto console_sink = std::make_shared(); @@ -241,6 +241,29 @@ void multi_sink_example() } ``` +--- +#### Register several loggers - change global level +```c++ + +// Creation of loggers. Set levels to all registered loggers. +void set_level_example() +{ + auto logger1 = spdlog::basic_logger_mt("logger1", "logs/logger1.txt"); + auto logger2 = spdlog::basic_logger_mt("logger2", "logs/logger2.txt"); + + spdlog::set_default_logger(logger2); + spdlog::default_logger()->set_level(spdlog::level::trace); // set level for the default logger (logger2) to trace + + spdlog::trace("trace message to the logger2 (specified as default)"); + + spdlog::set_level(spdlog::level::off) // (sic!) set level for *all* registered loggers to off (disable) + + logger1.warn("warn message will not appear because the level set to off"); + logger2.warn("warn message will not appear because the level set to off"); + spdlog::warn("warn message will not appear because the level set to off"); +} +``` + --- #### User-defined callbacks about log events ```c++ From 737347d2df9180f07bb133c15c3e3e630fcecdee Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Wed, 16 Jul 2025 09:55:57 +0300 Subject: [PATCH 6/7] Update linux.yml --- .github/workflows/linux.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 4e97d077..a7a61dd3 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -18,9 +18,8 @@ jobs: fail-fast: false matrix: config: - - { compiler: gcc, version: 7, build_type: Release, cppstd: 11 } - - { compiler: gcc, version: 9, build_type: Release, cppstd: 17 } - - { compiler: gcc, version: 11, build_type: Debug, cppstd: 20 } + - { compiler: gcc, version: 9, build_type: Release, cppstd: 11 } + - { compiler: gcc, version: 11, build_type: Debug, cppstd: 17 } - { compiler: gcc, version: 12, build_type: Release, cppstd: 20 } - { compiler: gcc, version: 12, build_type: Debug, cppstd: 20, asan: ON } - { compiler: clang, version: 12, build_type: Debug, cppstd: 17 } From 9ecdf5c8a1a3ace5673f9fd1857da1b6375b5114 Mon Sep 17 00:00:00 2001 From: Mihir Patel <82491937+LowLevelLore@users.noreply.github.com> Date: Fri, 18 Jul 2025 02:17:35 +0530 Subject: [PATCH 7/7] Added timeout for TCP calls such as connect, send, recv (#3432) * Now lets test on windows * I guess testing on windows passes. * Update tcp_client-windows.h Added default value to argument * Final edit * Update tcp_client-windows.h Changed improper misplaced includes. --- include/spdlog/details/tcp_client-windows.h | 94 +++++++++++++++++++-- include/spdlog/details/tcp_client.h | 81 +++++++++++++++++- include/spdlog/sinks/tcp_sink.h | 17 +++- 3 files changed, 181 insertions(+), 11 deletions(-) diff --git a/include/spdlog/details/tcp_client-windows.h b/include/spdlog/details/tcp_client-windows.h index bf8f7b87..76e73a36 100644 --- a/include/spdlog/details/tcp_client-windows.h +++ b/include/spdlog/details/tcp_client-windows.h @@ -57,9 +57,82 @@ public: } SOCKET fd() const { return socket_; } + + int connect_socket_with_timeout(SOCKET sockfd, + const struct sockaddr *addr, + int addrlen, + const timeval &tv) { + // If no timeout requested, do a normal blocking connect. + if (tv.tv_sec == 0 && tv.tv_usec == 0) { + int rv = ::connect(sockfd, addr, addrlen); + if (rv == SOCKET_ERROR && WSAGetLastError() == WSAEISCONN) { + return 0; + } + return rv; + } + + // Switch to non‐blocking mode + u_long mode = 1UL; + if (::ioctlsocket(sockfd, FIONBIO, &mode) == SOCKET_ERROR) { + return SOCKET_ERROR; + } + + int rv = ::connect(sockfd, addr, addrlen); + int last_error = WSAGetLastError(); + if (rv == 0 || last_error == WSAEISCONN) { + mode = 0UL; + if (::ioctlsocket(sockfd, FIONBIO, &mode) == SOCKET_ERROR) { + return SOCKET_ERROR; + } + return 0; + } + if (last_error != WSAEWOULDBLOCK) { + // Real error + mode = 0UL; + if (::ioctlsocket(sockfd, FIONBIO, &mode)) { + return SOCKET_ERROR; + } + return SOCKET_ERROR; + } + + // Wait until socket is writable or timeout expires + fd_set wfds; + FD_ZERO(&wfds); + FD_SET(sockfd, &wfds); + + rv = ::select(0, nullptr, &wfds, nullptr, const_cast(&tv)); + + // Restore blocking mode regardless of select result + mode = 0UL; + if (::ioctlsocket(sockfd, FIONBIO, &mode) == SOCKET_ERROR) { + return SOCKET_ERROR; + } + + if (rv == 0) { + WSASetLastError(WSAETIMEDOUT); + return SOCKET_ERROR; + } + if (rv == SOCKET_ERROR) { + return SOCKET_ERROR; + } + + int so_error = 0; + int len = sizeof(so_error); + if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, reinterpret_cast(&so_error), &len) == + SOCKET_ERROR) { + return SOCKET_ERROR; + } + if (so_error != 0 && so_error != WSAEISCONN) { + // connection failed + WSASetLastError(so_error); + return SOCKET_ERROR; + } + + return 0; // success + } // try to connect or throw on failure - void connect(const std::string &host, int port) { + void connect(const std::string &host, int port, int timeout_ms = 0) { if (is_connected()) { close(); } @@ -71,6 +144,10 @@ public: hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value hints.ai_protocol = 0; + timeval tv; + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; + auto port_str = std::to_string(port); struct addrinfo *addrinfo_result; auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result); @@ -82,7 +159,6 @@ public: } // Try each address until we successfully connect(2). - for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next) { socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (socket_ == INVALID_SOCKET) { @@ -90,18 +166,24 @@ public: WSACleanup(); continue; } - if (::connect(socket_, rp->ai_addr, (int)rp->ai_addrlen) == 0) { + if (connect_socket_with_timeout(socket_, rp->ai_addr, (int)rp->ai_addrlen, tv) == 0) { + last_error = 0; break; - } else { - last_error = ::WSAGetLastError(); - close(); } + last_error = WSAGetLastError(); + ::closesocket(socket_); + socket_ = INVALID_SOCKET; } ::freeaddrinfo(addrinfo_result); if (socket_ == INVALID_SOCKET) { WSACleanup(); throw_winsock_error_("connect failed", last_error); } + if (timeout_ms > 0) { + DWORD tv = static_cast(timeout_ms); + ::setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv)); + ::setsockopt(socket_, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof(tv)); + } // set TCP_NODELAY int enable_flag = 1; diff --git a/include/spdlog/details/tcp_client.h b/include/spdlog/details/tcp_client.h index 9d3c40d5..1eaa3cb9 100644 --- a/include/spdlog/details/tcp_client.h +++ b/include/spdlog/details/tcp_client.h @@ -39,8 +39,72 @@ public: ~tcp_client() { close(); } + int connect_socket_with_timeout(int sockfd, + const struct sockaddr *addr, + socklen_t addrlen, + const timeval &tv) { + // Blocking connect if timeout is zero + if (tv.tv_sec == 0 && tv.tv_usec == 0) { + int rv = ::connect(sockfd, addr, addrlen); + if (rv < 0 && errno == EISCONN) { + // already connected, treat as success + return 0; + } + return rv; + } + + // Non-blocking path + int orig_flags = ::fcntl(sockfd, F_GETFL, 0); + if (orig_flags < 0) { + return -1; + } + if (::fcntl(sockfd, F_SETFL, orig_flags | O_NONBLOCK) < 0) { + return -1; + } + + int rv = ::connect(sockfd, addr, addrlen); + if (rv == 0 || (rv < 0 && errno == EISCONN)) { + // immediate connect or already connected + ::fcntl(sockfd, F_SETFL, orig_flags); + return 0; + } + if (errno != EINPROGRESS) { + ::fcntl(sockfd, F_SETFL, orig_flags); + return -1; + } + + // wait for writability + fd_set wfds; + FD_ZERO(&wfds); + FD_SET(sockfd, &wfds); + + struct timeval tv_copy = tv; + rv = ::select(sockfd + 1, nullptr, &wfds, nullptr, &tv_copy); + if (rv <= 0) { + // timeout or error + ::fcntl(sockfd, F_SETFL, orig_flags); + if (rv == 0) errno = ETIMEDOUT; + return -1; + } + + // check socket error + int so_error = 0; + socklen_t len = sizeof(so_error); + if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &so_error, &len) < 0) { + ::fcntl(sockfd, F_SETFL, orig_flags); + return -1; + } + ::fcntl(sockfd, F_SETFL, orig_flags); + if (so_error != 0 && so_error != EISCONN) { + errno = so_error; + return -1; + } + + return 0; + } + // try to connect or throw on failure - void connect(const std::string &host, int port) { + void connect(const std::string &host, int port, int timeout_ms = 0) { close(); struct addrinfo hints {}; memset(&hints, 0, sizeof(struct addrinfo)); @@ -49,6 +113,10 @@ public: hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value hints.ai_protocol = 0; + struct timeval tv; + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; + auto port_str = std::to_string(port); struct addrinfo *addrinfo_result; auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result); @@ -69,8 +137,9 @@ public: last_errno = errno; continue; } - rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen); - if (rv == 0) { + ::fcntl(socket_, F_SETFD, FD_CLOEXEC); + if (connect_socket_with_timeout(socket_, rp->ai_addr, rp->ai_addrlen, tv) == 0) { + last_errno = 0; break; } last_errno = errno; @@ -82,6 +151,12 @@ public: throw_spdlog_ex("::connect failed", last_errno); } + if (timeout_ms > 0) { + // Set timeouts for send and recv + ::setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv)); + ::setsockopt(socket_, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof(tv)); + } + // set TCP_NODELAY int enable_flag = 1; ::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&enable_flag), diff --git a/include/spdlog/sinks/tcp_sink.h b/include/spdlog/sinks/tcp_sink.h index 25349645..9ed64133 100644 --- a/include/spdlog/sinks/tcp_sink.h +++ b/include/spdlog/sinks/tcp_sink.h @@ -31,6 +31,7 @@ namespace sinks { struct tcp_sink_config { std::string server_host; int server_port; + int timeout_ms = 0; // The timeout for all 3 major socket operations that is connect, send, and recv bool lazy_connect = false; // if true connect on first log call instead of on construction tcp_sink_config(std::string host, int port) @@ -44,10 +45,22 @@ public: // connect to tcp host/port or throw if failed // host can be hostname or ip address + explicit tcp_sink(const std::string &host, + int port, + int timeout_ms = 0, + bool lazy_connect = false) + : config_{host, port} { + config_.timeout_ms = timeout_ms; + config_.lazy_connect = lazy_connect; + if (!config_.lazy_connect) { + client_.connect(config_.server_host, config_.server_port, config_.timeout_ms); + } + } + explicit tcp_sink(tcp_sink_config sink_config) : config_{std::move(sink_config)} { if (!config_.lazy_connect) { - this->client_.connect(config_.server_host, config_.server_port); + client_.connect(config_.server_host, config_.server_port, config_.timeout_ms); } } @@ -58,7 +71,7 @@ protected: spdlog::memory_buf_t formatted; spdlog::sinks::base_sink::formatter_->format(msg, formatted); if (!client_.is_connected()) { - client_.connect(config_.server_host, config_.server_port); + client_.connect(config_.server_host, config_.server_port, config_.timeout_ms); } client_.send(formatted.data(), formatted.size()); }