From de0d0dc3f41f02e59371dc9d2ef62cf0e1faa6ad Mon Sep 17 00:00:00 2001 From: LowLevelLore Date: Mon, 30 Jun 2025 16:39:56 +0530 Subject: [PATCH 1/8] Now lets test on windows --- include/spdlog/details/tcp_client-windows.h | 53 ++++++++++++-- include/spdlog/details/tcp_client.h | 81 ++++++++++++++++++++- include/spdlog/sinks/tcp_sink.h | 17 ++++- 3 files changed, 139 insertions(+), 12 deletions(-) diff --git a/include/spdlog/details/tcp_client-windows.h b/include/spdlog/details/tcp_client-windows.h index bf8f7b87..4b05acbb 100644 --- a/include/spdlog/details/tcp_client-windows.h +++ b/include/spdlog/details/tcp_client-windows.h @@ -59,11 +59,11 @@ public: SOCKET fd() const { return socket_; } // 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) { if (is_connected()) { close(); } - struct addrinfo hints {}; + struct addrinfo hints{}; ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_UNSPEC; // To work with IPv4, IPv6, and so on @@ -90,12 +90,49 @@ public: WSACleanup(); continue; } - if (::connect(socket_, rp->ai_addr, (int)rp->ai_addrlen) == 0) { + // set non-blocking if timeout specified + u_long mode = (timeout_ms > 0) ? 1UL : 0UL; + ::ioctlsocket(socket_, FIONBIO, &mode); + + rv = ::connect(socket_, rp->ai_addr, (int)rp->ai_addrlen); + if (rv == SOCKET_ERROR) { + last_error = WSAGetLastError(); + if (timeout_ms > 0 && last_error == WSAEWOULDBLOCK) { + fd_set wfds; + FD_ZERO(&wfds); + FD_SET(socket_, &wfds); + timeval tv; + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; + rv = ::select(0, nullptr, &wfds, nullptr, &tv); + if (rv > 0) { + int so_error = 0; + int len = sizeof(so_error); + ::getsockopt(socket_, SOL_SOCKET, SO_ERROR, (char *)&so_error, &len); + if (so_error == 0) + rv = 0; + else { + last_error = so_error; + rv = SOCKET_ERROR; + } + } else { + last_error = (rv == 0 ? WSAETIMEDOUT : WSAGetLastError()); + rv = SOCKET_ERROR; + } + } + } + + // restore blocking mode + mode = 0UL; + ::ioctlsocket(socket_, FIONBIO, &mode); + + if (rv == 0) { + last_error = 0; break; - } else { - last_error = ::WSAGetLastError(); - close(); } + + ::closesocket(socket_); + socket_ = INVALID_SOCKET; } ::freeaddrinfo(addrinfo_result); if (socket_ == INVALID_SOCKET) { @@ -103,6 +140,10 @@ public: throw_winsock_error_("connect failed", last_error); } + 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; ::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&enable_flag), diff --git a/include/spdlog/details/tcp_client.h b/include/spdlog/details/tcp_client.h index 9d3c40d5..87d292b1 100644 --- a/include/spdlog/details/tcp_client.h +++ b/include/spdlog/details/tcp_client.h @@ -39,16 +39,84 @@ 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 {}; + struct addrinfo hints{}; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; // To work with IPv4, IPv6, and so on hints.ai_socktype = SOCK_STREAM; // TCP 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,10 @@ public: throw_spdlog_ex("::connect failed", last_errno); } + // 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..de8b2558 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; 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()); } From 4f9b630d4691a536b7f41b0efed5863e57da70f4 Mon Sep 17 00:00:00 2001 From: Mihir Patel <82491937+LowLevelLore@users.noreply.github.com> Date: Mon, 30 Jun 2025 12:13:22 +0530 Subject: [PATCH 2/8] I guess testing on windows passes. --- include/spdlog/details/tcp_client-windows.h | 110 ++++++++++++-------- include/spdlog/details/tcp_client.h | 10 +- include/spdlog/sinks/tcp_sink.h | 2 +- 3 files changed, 73 insertions(+), 49 deletions(-) diff --git a/include/spdlog/details/tcp_client-windows.h b/include/spdlog/details/tcp_client-windows.h index 4b05acbb..62f80c76 100644 --- a/include/spdlog/details/tcp_client-windows.h +++ b/include/spdlog/details/tcp_client-windows.h @@ -58,12 +58,66 @@ public: SOCKET fd() const { return socket_; } + int connect_socket_with_timeout(SOCKET sockfd, + const struct sockaddr *addr, + int addrlen, + const timeval &tv) { + // Blocking if no timeout + 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; + } + + // Set non-blocking + u_long mode = 1UL; + ::ioctlsocket(sockfd, FIONBIO, &mode); + + int rv = ::connect(sockfd, addr, addrlen); + int last_error = WSAGetLastError(); + if (rv == 0 || last_error == WSAEISCONN) { + mode = 0UL; + ::ioctlsocket(sockfd, FIONBIO, &mode); + return 0; + } + if (last_error != WSAEWOULDBLOCK) { + mode = 0UL; + ::ioctlsocket(sockfd, FIONBIO, &mode); + return SOCKET_ERROR; + } + + // Wait for writability + fd_set wfds; + FD_ZERO(&wfds); + FD_SET(sockfd, &wfds); + + rv = ::select(0, nullptr, &wfds, nullptr, &tv); + mode = 0UL; + ::ioctlsocket(sockfd, FIONBIO, &mode); + + if (rv <= 0) { + // timeout or error + if (rv == 0) WSASetLastError(WSAETIMEDOUT); + return SOCKET_ERROR; + } + + // Check socket for errors + int so_error = 0; + int len = sizeof(so_error); + ::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&so_error, &len); + if (so_error != 0 && so_error != WSAEISCONN) { + WSASetLastError(so_error); + return SOCKET_ERROR; + } + return 0; + } + // try to connect or throw on failure void connect(const std::string &host, int port, int timeout_ms) { if (is_connected()) { close(); } - struct addrinfo hints{}; + struct addrinfo hints {}; ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_UNSPEC; // To work with IPv4, IPv6, and so on @@ -71,6 +125,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 +140,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,47 +147,11 @@ public: WSACleanup(); continue; } - // set non-blocking if timeout specified - u_long mode = (timeout_ms > 0) ? 1UL : 0UL; - ::ioctlsocket(socket_, FIONBIO, &mode); - - rv = ::connect(socket_, rp->ai_addr, (int)rp->ai_addrlen); - if (rv == SOCKET_ERROR) { - last_error = WSAGetLastError(); - if (timeout_ms > 0 && last_error == WSAEWOULDBLOCK) { - fd_set wfds; - FD_ZERO(&wfds); - FD_SET(socket_, &wfds); - timeval tv; - tv.tv_sec = timeout_ms / 1000; - tv.tv_usec = (timeout_ms % 1000) * 1000; - rv = ::select(0, nullptr, &wfds, nullptr, &tv); - if (rv > 0) { - int so_error = 0; - int len = sizeof(so_error); - ::getsockopt(socket_, SOL_SOCKET, SO_ERROR, (char *)&so_error, &len); - if (so_error == 0) - rv = 0; - else { - last_error = so_error; - rv = SOCKET_ERROR; - } - } else { - last_error = (rv == 0 ? WSAETIMEDOUT : WSAGetLastError()); - rv = SOCKET_ERROR; - } - } - } - - // restore blocking mode - mode = 0UL; - ::ioctlsocket(socket_, FIONBIO, &mode); - - if (rv == 0) { + if (connect_socket_with_timeout(socket_, rp->ai_addr, (int)rp->ai_addrlen, tv) == 0) { last_error = 0; break; } - + last_error = WSAGetLastError(); ::closesocket(socket_); socket_ = INVALID_SOCKET; } @@ -139,10 +160,11 @@ public: WSACleanup(); throw_winsock_error_("connect failed", last_error); } - - 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)); + 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 87d292b1..1eaa3cb9 100644 --- a/include/spdlog/details/tcp_client.h +++ b/include/spdlog/details/tcp_client.h @@ -106,7 +106,7 @@ public: // try to connect or throw on failure void connect(const std::string &host, int port, int timeout_ms = 0) { close(); - struct addrinfo hints{}; + struct addrinfo hints {}; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; // To work with IPv4, IPv6, and so on hints.ai_socktype = SOCK_STREAM; // TCP @@ -151,9 +151,11 @@ public: throw_spdlog_ex("::connect failed", last_errno); } - // 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)); + 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; diff --git a/include/spdlog/sinks/tcp_sink.h b/include/spdlog/sinks/tcp_sink.h index de8b2558..9ed64133 100644 --- a/include/spdlog/sinks/tcp_sink.h +++ b/include/spdlog/sinks/tcp_sink.h @@ -31,7 +31,7 @@ namespace sinks { struct tcp_sink_config { std::string server_host; int server_port; - int timeout_ms = 0; + 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) From 6b231754f2f5c5ccf4d5ac5c7abbff9915d3bc49 Mon Sep 17 00:00:00 2001 From: Mihir Patel <82491937+LowLevelLore@users.noreply.github.com> Date: Mon, 30 Jun 2025 18:47:00 +0530 Subject: [PATCH 3/8] Update tcp_client-windows.h Added default value to argument --- include/spdlog/details/tcp_client-windows.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/details/tcp_client-windows.h b/include/spdlog/details/tcp_client-windows.h index 62f80c76..524a4bdc 100644 --- a/include/spdlog/details/tcp_client-windows.h +++ b/include/spdlog/details/tcp_client-windows.h @@ -113,7 +113,7 @@ public: } // try to connect or throw on failure - void connect(const std::string &host, int port, int timeout_ms) { + void connect(const std::string &host, int port, int timeout_ms = 0) { if (is_connected()) { close(); } From e8984f6fdfef9095f433d7b99c182e153a4c1404 Mon Sep 17 00:00:00 2001 From: Mihir Patel <82491937+LowLevelLore@users.noreply.github.com> Date: Wed, 16 Jul 2025 20:33:35 +0530 Subject: [PATCH 4/8] Final edit --- include/spdlog/details/tcp_client-windows.h | 53 +++++++++++++++------ 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/include/spdlog/details/tcp_client-windows.h b/include/spdlog/details/tcp_client-windows.h index 524a4bdc..a82687b2 100644 --- a/include/spdlog/details/tcp_client-windows.h +++ b/include/spdlog/details/tcp_client-windows.h @@ -58,58 +58,81 @@ public: SOCKET fd() const { return socket_; } +#include +#include + + // Returns 0 on success, SOCKET_ERROR on failure (check WSAGetLastError()). int connect_socket_with_timeout(SOCKET sockfd, const struct sockaddr *addr, int addrlen, const timeval &tv) { - // Blocking if no timeout + // 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; + if (rv == SOCKET_ERROR && WSAGetLastError() == WSAEISCONN) { + return 0; + } return rv; } - // Set non-blocking + // Switch to non‐blocking mode u_long mode = 1UL; - ::ioctlsocket(sockfd, FIONBIO, &mode); + 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; - ::ioctlsocket(sockfd, FIONBIO, &mode); + if (::ioctlsocket(sockfd, FIONBIO, &mode) == SOCKET_ERROR) { + return SOCKET_ERROR; + } return 0; } if (last_error != WSAEWOULDBLOCK) { + // Real error mode = 0UL; - ::ioctlsocket(sockfd, FIONBIO, &mode); + if (::ioctlsocket(sockfd, FIONBIO, &mode)) { + return SOCKET_ERROR; + } return SOCKET_ERROR; } - // Wait for writability + // Wait until socket is writable or timeout expires fd_set wfds; FD_ZERO(&wfds); FD_SET(sockfd, &wfds); - rv = ::select(0, nullptr, &wfds, nullptr, &tv); + rv = ::select(0, nullptr, &wfds, nullptr, const_cast(&tv)); + + // Restore blocking mode regardless of select result mode = 0UL; - ::ioctlsocket(sockfd, FIONBIO, &mode); + if (::ioctlsocket(sockfd, FIONBIO, &mode) == SOCKET_ERROR) { + return SOCKET_ERROR; + } - if (rv <= 0) { - // timeout or error - if (rv == 0) WSASetLastError(WSAETIMEDOUT); + if (rv == 0) { + WSASetLastError(WSAETIMEDOUT); + return SOCKET_ERROR; + } + if (rv == SOCKET_ERROR) { return SOCKET_ERROR; } - // Check socket for errors int so_error = 0; int len = sizeof(so_error); - ::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&so_error, &len); + 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; + + return 0; // success } // try to connect or throw on failure From 731594394fa9ecfbdf2b97c45cf2e5a8fad82bcf Mon Sep 17 00:00:00 2001 From: Mihir Patel <82491937+LowLevelLore@users.noreply.github.com> Date: Thu, 17 Jul 2025 02:18:42 +0530 Subject: [PATCH 5/8] Update tcp_client-windows.h Changed improper misplaced includes. --- include/spdlog/details/tcp_client-windows.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/include/spdlog/details/tcp_client-windows.h b/include/spdlog/details/tcp_client-windows.h index a82687b2..76e73a36 100644 --- a/include/spdlog/details/tcp_client-windows.h +++ b/include/spdlog/details/tcp_client-windows.h @@ -57,11 +57,7 @@ public: } SOCKET fd() const { return socket_; } - -#include -#include - - // Returns 0 on success, SOCKET_ERROR on failure (check WSAGetLastError()). + int connect_socket_with_timeout(SOCKET sockfd, const struct sockaddr *addr, int addrlen, From d09e13d29daf4223990d8d1ae50013dfcd8e4129 Mon Sep 17 00:00:00 2001 From: Mihir Patel <82491937+LowLevelLore@users.noreply.github.com> Date: Fri, 18 Jul 2025 15:59:07 +0530 Subject: [PATCH 6/8] ANSI color sink on Windows --- CMakeLists.txt | 1 + include/spdlog/sinks/ansicolor_sink-inl.h | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 882aa90e..67cf3a20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,6 +156,7 @@ message(STATUS "Build type: " ${CMAKE_BUILD_TYPE}) # Static/Shared library # --------------------------------------------------------------------------------------- set(SPDLOG_SRCS src/spdlog.cpp src/stdout_sinks.cpp src/color_sinks.cpp src/file_sinks.cpp src/async.cpp src/cfg.cpp) +list(APPEND SPDLOG_SRCS include/spdlog/sinks/ansicolor_sink-inl.h) if(NOT SPDLOG_USE_STD_FORMAT AND NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO) list(APPEND SPDLOG_SRCS src/bundled_fmtlib_format.cpp) diff --git a/include/spdlog/sinks/ansicolor_sink-inl.h b/include/spdlog/sinks/ansicolor_sink-inl.h index 6a23f6c7..74f9937f 100644 --- a/include/spdlog/sinks/ansicolor_sink-inl.h +++ b/include/spdlog/sinks/ansicolor_sink-inl.h @@ -10,6 +10,13 @@ #include #include +#if defined(_WIN32) + #include + #include // WriteFile + #include // _get_osfhandle + #include // _fileno +#endif + namespace spdlog { namespace sinks { @@ -113,14 +120,30 @@ SPDLOG_INLINE void ansicolor_sink::set_color_mode_(color_mode mode template SPDLOG_INLINE void ansicolor_sink::print_ccode_( const string_view_t &color_code) const { +#ifdef _WIN32 + DWORD bytes_written = 0; + HANDLE h = + (target_file_ == stdout) ? GetStdHandle(STD_OUTPUT_HANDLE) : GetStdHandle(STD_ERROR_HANDLE); + // WriteFile bypasses the extra \r injection + WriteFile(h, color_code.data(), static_cast(color_code.size()), &bytes_written, nullptr); +#else details::os::fwrite_bytes(color_code.data(), color_code.size(), target_file_); +#endif } template SPDLOG_INLINE void ansicolor_sink::print_range_(const memory_buf_t &formatted, size_t start, size_t end) const { +#ifdef _WIN32 + DWORD bytes_written = 0; + HANDLE h = + (target_file_ == stdout) ? GetStdHandle(STD_OUTPUT_HANDLE) : GetStdHandle(STD_ERROR_HANDLE); + WriteFile(h, formatted.data() + start, static_cast(end - start), &bytes_written, + nullptr); +#else details::os::fwrite_bytes(formatted.data() + start, end - start, target_file_); +#endif } template From ce53eaed7fcc1e5849d445ead2ed4fc1e91b5f40 Mon Sep 17 00:00:00 2001 From: Mihir Patel <82491937+LowLevelLore@users.noreply.github.com> Date: Fri, 18 Jul 2025 16:04:49 +0530 Subject: [PATCH 7/8] Removed Extra Headers. --- include/spdlog/sinks/ansicolor_sink-inl.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/spdlog/sinks/ansicolor_sink-inl.h b/include/spdlog/sinks/ansicolor_sink-inl.h index 74f9937f..523f7dde 100644 --- a/include/spdlog/sinks/ansicolor_sink-inl.h +++ b/include/spdlog/sinks/ansicolor_sink-inl.h @@ -12,9 +12,6 @@ #if defined(_WIN32) #include - #include // WriteFile - #include // _get_osfhandle - #include // _fileno #endif namespace spdlog { From 8a780f6dbc68c1c69b0c04dc58c28cf3f81933ec Mon Sep 17 00:00:00 2001 From: Mihir Patel <82491937+LowLevelLore@users.noreply.github.com> Date: Fri, 18 Jul 2025 16:12:05 +0530 Subject: [PATCH 8/8] Robust implementation for getting handles. --- include/spdlog/sinks/ansicolor_sink-inl.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/spdlog/sinks/ansicolor_sink-inl.h b/include/spdlog/sinks/ansicolor_sink-inl.h index 523f7dde..84831961 100644 --- a/include/spdlog/sinks/ansicolor_sink-inl.h +++ b/include/spdlog/sinks/ansicolor_sink-inl.h @@ -119,8 +119,7 @@ SPDLOG_INLINE void ansicolor_sink::print_ccode_( const string_view_t &color_code) const { #ifdef _WIN32 DWORD bytes_written = 0; - HANDLE h = - (target_file_ == stdout) ? GetStdHandle(STD_OUTPUT_HANDLE) : GetStdHandle(STD_ERROR_HANDLE); + HANDLE h = reinterpret_cast(_get_osfhandle(_fileno(target_file_))); // WriteFile bypasses the extra \r injection WriteFile(h, color_code.data(), static_cast(color_code.size()), &bytes_written, nullptr); #else @@ -134,8 +133,7 @@ SPDLOG_INLINE void ansicolor_sink::print_range_(const memory_buf_t size_t end) const { #ifdef _WIN32 DWORD bytes_written = 0; - HANDLE h = - (target_file_ == stdout) ? GetStdHandle(STD_OUTPUT_HANDLE) : GetStdHandle(STD_ERROR_HANDLE); + HANDLE h = reinterpret_cast(_get_osfhandle(_fileno(target_file_))); WriteFile(h, formatted.data() + start, static_cast(end - start), &bytes_written, nullptr); #else