From 4c6d68abc1e6cea6d784404463ccc256649a0189 Mon Sep 17 00:00:00 2001 From: yurekami Date: Sat, 27 Dec 2025 06:59:57 +0900 Subject: [PATCH 1/2] Add logging abstraction layer for glog deprecation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces include/util/logging.h that provides glog-compatible macros (LOG, VLOG, CHECK, DCHECK) with a lightweight fallback implementation. This enables gradual migration away from glog dependency: - When USE_GLOG is defined: uses glog as before - Otherwise: uses minimal stderr-based logging Updated key headers to use the new abstraction: - include/util/util.h - include/util/timer.h - include/util/net.h - include/util/shared_pool.h Addresses #295 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- include/util/logging.h | 178 +++++++++++++++++++++++++++++++++++++ include/util/net.h | 2 +- include/util/shared_pool.h | 2 +- include/util/timer.h | 2 +- include/util/util.h | 2 +- 5 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 include/util/logging.h diff --git a/include/util/logging.h b/include/util/logging.h new file mode 100644 index 000000000..c8fbf74ca --- /dev/null +++ b/include/util/logging.h @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 UCCL Contributors +// +// Logging abstraction layer for UCCL. +// +// Provides glog-compatible macros with a lightweight fallback when glog is +// not available. This allows gradual migration away from glog dependency. +// +// Usage: +// Replace: #include +// With: #include "util/logging.h" +// +// The behavior is controlled by the USE_GLOG macro: +// - If USE_GLOG is defined: Uses glog for all logging +// - Otherwise: Uses lightweight stderr-based logging + +#pragma once + +#ifdef USE_GLOG +#include +#else + +#include +#include +#include +#include + +namespace uccl { +namespace logging { + +enum LogLevel { INFO = 0, WARNING = 1, ERROR = 2, FATAL = 3 }; + +inline const char* LogLevelName(LogLevel level) { + switch (level) { + case INFO: + return "INFO"; + case WARNING: + return "WARNING"; + case ERROR: + return "ERROR"; + case FATAL: + return "FATAL"; + default: + return "UNKNOWN"; + } +} + +// Minimal log message class that outputs to stderr +class LogMessage { + public: + LogMessage(const char* file, int line, LogLevel level) + : level_(level), file_(file), line_(line) {} + + ~LogMessage() { + std::cerr << "[" << LogLevelName(level_) << "] " << file_ << ":" << line_ + << "] " << stream_.str() << std::endl; + if (level_ == FATAL) { + std::abort(); + } + } + + std::ostream& stream() { return stream_; } + + private: + LogLevel level_; + const char* file_; + int line_; + std::ostringstream stream_; +}; + +// Null stream for disabled logging (e.g., VLOG when verbosity is too low) +class NullStream { + public: + template + NullStream& operator<<(const T&) { + return *this; + } + // Handle stream manipulators like std::endl + NullStream& operator<<(std::ostream& (*)(std::ostream&)) { return *this; } +}; + +// Check message that aborts on destruction if condition was false +class CheckMessage { + public: + CheckMessage(const char* file, int line, const char* condition) + : file_(file), line_(line), condition_(condition) {} + + ~CheckMessage() { + std::cerr << "[FATAL] " << file_ << ":" << line_ << "] Check failed: " + << condition_ << " " << stream_.str() << std::endl; + std::abort(); + } + + std::ostream& stream() { return stream_; } + + private: + const char* file_; + int line_; + const char* condition_; + std::ostringstream stream_; +}; + +// Verbose logging level (default: 0, can be set via UCCL_VLOG_LEVEL env var) +inline int VerboseLevel() { + static int level = []() { + const char* env = std::getenv("UCCL_VLOG_LEVEL"); + return env ? std::atoi(env) : 0; + }(); + return level; +} + +} // namespace logging +} // namespace uccl + +// LOG macros +#define LOG(level) \ + uccl::logging::LogMessage(__FILE__, __LINE__, uccl::logging::level).stream() + +// VLOG macro - verbose logging controlled by UCCL_VLOG_LEVEL env var +#define VLOG(level) \ + (uccl::logging::VerboseLevel() >= (level)) \ + ? uccl::logging::LogMessage(__FILE__, __LINE__, \ + uccl::logging::INFO) \ + .stream() \ + : uccl::logging::NullStream() + +// CHECK macros +#define CHECK(condition) \ + (condition) ? (void)0 \ + : (void)(uccl::logging::CheckMessage(__FILE__, __LINE__, \ + #condition) \ + .stream()) + +#define CHECK_NOTNULL(ptr) \ + ([&]() -> decltype(ptr) { \ + if ((ptr) == nullptr) { \ + uccl::logging::CheckMessage(__FILE__, __LINE__, #ptr " != nullptr") \ + .stream() \ + << "Pointer is null"; \ + } \ + return (ptr); \ + }()) + +// DCHECK macros - only active in debug builds +#ifdef NDEBUG +#define DCHECK(condition) \ + while (false) CHECK(condition) +#define DCHECK_EQ(a, b) \ + while (false) CHECK((a) == (b)) +#define DCHECK_NE(a, b) \ + while (false) CHECK((a) != (b)) +#define DCHECK_LT(a, b) \ + while (false) CHECK((a) < (b)) +#define DCHECK_LE(a, b) \ + while (false) CHECK((a) <= (b)) +#define DCHECK_GT(a, b) \ + while (false) CHECK((a) > (b)) +#define DCHECK_GE(a, b) \ + while (false) CHECK((a) >= (b)) +#else +#define DCHECK(condition) CHECK(condition) +#define DCHECK_EQ(a, b) CHECK((a) == (b)) +#define DCHECK_NE(a, b) CHECK((a) != (b)) +#define DCHECK_LT(a, b) CHECK((a) < (b)) +#define DCHECK_LE(a, b) CHECK((a) <= (b)) +#define DCHECK_GT(a, b) CHECK((a) > (b)) +#define DCHECK_GE(a, b) CHECK((a) >= (b)) +#endif + +// CHECK comparison macros +#define CHECK_EQ(a, b) CHECK((a) == (b)) +#define CHECK_NE(a, b) CHECK((a) != (b)) +#define CHECK_LT(a, b) CHECK((a) < (b)) +#define CHECK_LE(a, b) CHECK((a) <= (b)) +#define CHECK_GT(a, b) CHECK((a) > (b)) +#define CHECK_GE(a, b) CHECK((a) >= (b)) + +#endif // USE_GLOG diff --git a/include/util/net.h b/include/util/net.h index fcabfb1ed..c7719b69f 100644 --- a/include/util/net.h +++ b/include/util/net.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include "util/logging.h" #include #include #include diff --git a/include/util/shared_pool.h b/include/util/shared_pool.h index 8a0a4c4a0..f9ac66ec4 100644 --- a/include/util/shared_pool.h +++ b/include/util/shared_pool.h @@ -2,7 +2,7 @@ #include "cb.h" #include "util.h" -#include +#include "util/logging.h" #include #include #include diff --git a/include/util/timer.h b/include/util/timer.h index 293429f80..48242c2f4 100644 --- a/include/util/timer.h +++ b/include/util/timer.h @@ -5,7 +5,7 @@ #pragma once -#include "glog/logging.h" +#include "util/logging.h" #include #include #include diff --git a/include/util/util.h b/include/util/util.h index 172b99166..72875fec8 100644 --- a/include/util/util.h +++ b/include/util/util.h @@ -5,7 +5,7 @@ #include "util/gpu_rt.h" #include "util/jring.h" #include -#include +#include "util/logging.h" #include #include #include From 115153416d37ef86c5a9d0941c95a29fca390d76 Mon Sep 17 00:00:00 2001 From: yurekami Date: Sat, 27 Dec 2025 16:51:55 +0900 Subject: [PATCH 2/2] refactor(logging): use NCCL-style logging API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revise logging abstraction to match NCCL's logging interface per maintainer feedback. The new implementation: - Uses ncclDebugLogger_t function pointer passed via pluginInit() - Provides INFO(flags, fmt, ...), WARN(fmt, ...), TRACE(flags, ...) macros - Defines subsystem flags (UCCL_INIT, UCCL_NET, UCCL_P2P, UCCL_ALL) - Includes fallback logger for stderr when NCCL logger unavailable - Updates RDMA plugin as reference implementation This aligns with NCCL's debug.cc interface as requested: https://github.com/NVIDIA/nccl/blob/master/src/debug.cc 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- collective/rdma/nccl_plugin.cc | 5 +- include/util/logging.h | 282 +++++++++++++++++---------------- 2 files changed, 148 insertions(+), 139 deletions(-) diff --git a/collective/rdma/nccl_plugin.cc b/collective/rdma/nccl_plugin.cc index 578485cc3..9642d4d5a 100644 --- a/collective/rdma/nccl_plugin.cc +++ b/collective/rdma/nccl_plugin.cc @@ -2,7 +2,7 @@ #include "transport.h" #include "transport_config.h" #include "util_rdma.h" -#include +#include "util/logging.h" #include #include #include @@ -83,7 +83,8 @@ struct ucclSendComm { }; ncclResult_t pluginInit(ncclDebugLogger_t logFunction) { - std::cout << "Hello UCCL from PID: " << getpid() << std::endl; + uccl::logging::initLogger(logFunction); + INFO(UCCL_INIT, "UCCL RDMA plugin initialized, PID: %d", getpid()); ep = std::make_shared(ucclParamNUM_ENGINES()); diff --git a/include/util/logging.h b/include/util/logging.h index c8fbf74ca..391cddd1e 100644 --- a/include/util/logging.h +++ b/include/util/logging.h @@ -1,178 +1,186 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright 2024 UCCL Contributors // -// Logging abstraction layer for UCCL. +// NCCL-compatible logging abstraction layer for UCCL. // -// Provides glog-compatible macros with a lightweight fallback when glog is -// not available. This allows gradual migration away from glog dependency. +// Provides NCCL-style logging macros (INFO, WARN, TRACE) that integrate +// with NCCL's debug logging infrastructure. The logger function is passed +// to plugins via pluginInit(ncclDebugLogger_t logFunction). // // Usage: -// Replace: #include -// With: #include "util/logging.h" -// -// The behavior is controlled by the USE_GLOG macro: -// - If USE_GLOG is defined: Uses glog for all logging -// - Otherwise: Uses lightweight stderr-based logging +// 1. In plugin init: uccl_log_func = logFunction; +// 2. Throughout code: INFO(UCCL_NET, "message %d", value); +// WARN("error: %s", msg); +// TRACE(UCCL_INIT, "trace msg"); #pragma once -#ifdef USE_GLOG -#include +#include +#include +#include + +// Include NCCL types if building as plugin, otherwise define our own +#ifdef NCCL_NET_H_ +// Already included from nccl_net.h #else -#include -#include -#include -#include +// NCCL debug log levels +typedef enum { + NCCL_LOG_NONE = 0, + NCCL_LOG_VERSION = 1, + NCCL_LOG_WARN = 2, + NCCL_LOG_INFO = 3, + NCCL_LOG_ABORT = 4, + NCCL_LOG_TRACE = 5 +} ncclDebugLogLevel; + +// NCCL debug subsystems (can be OR'd together) +typedef enum { + NCCL_INIT = 0x1, + NCCL_COLL = 0x2, + NCCL_P2P = 0x4, + NCCL_SHM = 0x8, + NCCL_NET = 0x10, + NCCL_GRAPH = 0x20, + NCCL_TUNING = 0x40, + NCCL_ENV = 0x80, + NCCL_ALLOC = 0x100, + NCCL_CALL = 0x200, + NCCL_PROXY = 0x400, + NCCL_NVLS = 0x800, + NCCL_BOOTSTRAP = 0x1000, + NCCL_REG = 0x2000, + NCCL_PROFILE = 0x4000, + NCCL_ALL = ~0 +} ncclDebugLogSubSys; + +// NCCL logger function type +typedef void (*ncclDebugLogger_t)(ncclDebugLogLevel level, unsigned long flags, + const char* file, int line, const char* fmt, + ...); + +#endif // NCCL_NET_H_ + +// UCCL-specific subsystems (use high bits to avoid conflicts) +#define UCCL_INIT NCCL_INIT +#define UCCL_NET NCCL_NET +#define UCCL_P2P NCCL_P2P +#define UCCL_ALL NCCL_ALL namespace uccl { namespace logging { -enum LogLevel { INFO = 0, WARNING = 1, ERROR = 2, FATAL = 3 }; +// Global logger function pointer - set during plugin initialization +inline ncclDebugLogger_t& getLogFunc() { + static ncclDebugLogger_t log_func = nullptr; + return log_func; +} -inline const char* LogLevelName(LogLevel level) { +// Fallback logger that writes to stderr when NCCL logger is not available +inline void fallbackLogger(ncclDebugLogLevel level, unsigned long flags, + const char* file, int line, const char* fmt, ...) { + (void)flags; + const char* level_str = "UNKNOWN"; switch (level) { - case INFO: - return "INFO"; - case WARNING: - return "WARNING"; - case ERROR: - return "ERROR"; - case FATAL: - return "FATAL"; + case NCCL_LOG_WARN: + level_str = "WARN"; + break; + case NCCL_LOG_INFO: + level_str = "INFO"; + break; + case NCCL_LOG_TRACE: + level_str = "TRACE"; + break; default: - return "UNKNOWN"; + break; } -} -// Minimal log message class that outputs to stderr -class LogMessage { - public: - LogMessage(const char* file, int line, LogLevel level) - : level_(level), file_(file), line_(line) {} + std::fprintf(stderr, "UCCL %s %s:%d ", level_str, file, line); - ~LogMessage() { - std::cerr << "[" << LogLevelName(level_) << "] " << file_ << ":" << line_ - << "] " << stream_.str() << std::endl; - if (level_ == FATAL) { - std::abort(); - } + va_list args; + va_start(args, fmt); + std::vfprintf(stderr, fmt, args); + va_end(args); + + std::fprintf(stderr, "\n"); + + if (level == NCCL_LOG_ABORT) { + std::abort(); } +} - std::ostream& stream() { return stream_; } +// Initialize the logger (call from pluginInit) +inline void initLogger(ncclDebugLogger_t logFunction) { + getLogFunc() = logFunction ? logFunction : fallbackLogger; +} - private: - LogLevel level_; - const char* file_; - int line_; - std::ostringstream stream_; -}; +// Get the active logger function +inline ncclDebugLogger_t getLogger() { + ncclDebugLogger_t func = getLogFunc(); + return func ? func : fallbackLogger; +} -// Null stream for disabled logging (e.g., VLOG when verbosity is too low) -class NullStream { - public: - template - NullStream& operator<<(const T&) { - return *this; - } - // Handle stream manipulators like std::endl - NullStream& operator<<(std::ostream& (*)(std::ostream&)) { return *this; } -}; +} // namespace logging +} // namespace uccl + +// Convenience macro to get the logger +#define UCCL_LOG_FUNC (uccl::logging::getLogger()) + +// NCCL-style logging macros +#define WARN(fmt, ...) \ + (*UCCL_LOG_FUNC)(NCCL_LOG_WARN, NCCL_ALL, __PRETTY_FUNCTION__, \ + __LINE__, fmt, ##__VA_ARGS__) + +#define INFO(flags, fmt, ...) \ + (*UCCL_LOG_FUNC)(NCCL_LOG_INFO, (flags), __PRETTY_FUNCTION__, \ + __LINE__, fmt, ##__VA_ARGS__) + +#define TRACE(flags, fmt, ...) \ + (*UCCL_LOG_FUNC)(NCCL_LOG_TRACE, (flags), __PRETTY_FUNCTION__, \ + __LINE__, fmt, ##__VA_ARGS__) + +// For backward compatibility with code using LOG(severity) << msg pattern +// These create a LogMessage that collects stream output and logs on destruction +#ifdef UCCL_COMPAT_GLOG_STYLE + +#include +#include -// Check message that aborts on destruction if condition was false -class CheckMessage { +namespace uccl { +namespace logging { + +class LogMessage { public: - CheckMessage(const char* file, int line, const char* condition) - : file_(file), line_(line), condition_(condition) {} + LogMessage(ncclDebugLogLevel level, unsigned long flags, const char* file, + int line) + : level_(level), flags_(flags), file_(file), line_(line) {} - ~CheckMessage() { - std::cerr << "[FATAL] " << file_ << ":" << line_ << "] Check failed: " - << condition_ << " " << stream_.str() << std::endl; - std::abort(); + ~LogMessage() { + (*UCCL_LOG_FUNC)(level_, flags_, file_, line_, "%s", stream_.str().c_str()); } std::ostream& stream() { return stream_; } private: + ncclDebugLogLevel level_; + unsigned long flags_; const char* file_; int line_; - const char* condition_; std::ostringstream stream_; }; -// Verbose logging level (default: 0, can be set via UCCL_VLOG_LEVEL env var) -inline int VerboseLevel() { - static int level = []() { - const char* env = std::getenv("UCCL_VLOG_LEVEL"); - return env ? std::atoi(env) : 0; - }(); - return level; -} - } // namespace logging } // namespace uccl -// LOG macros -#define LOG(level) \ - uccl::logging::LogMessage(__FILE__, __LINE__, uccl::logging::level).stream() - -// VLOG macro - verbose logging controlled by UCCL_VLOG_LEVEL env var -#define VLOG(level) \ - (uccl::logging::VerboseLevel() >= (level)) \ - ? uccl::logging::LogMessage(__FILE__, __LINE__, \ - uccl::logging::INFO) \ - .stream() \ - : uccl::logging::NullStream() - -// CHECK macros -#define CHECK(condition) \ - (condition) ? (void)0 \ - : (void)(uccl::logging::CheckMessage(__FILE__, __LINE__, \ - #condition) \ - .stream()) - -#define CHECK_NOTNULL(ptr) \ - ([&]() -> decltype(ptr) { \ - if ((ptr) == nullptr) { \ - uccl::logging::CheckMessage(__FILE__, __LINE__, #ptr " != nullptr") \ - .stream() \ - << "Pointer is null"; \ - } \ - return (ptr); \ - }()) - -// DCHECK macros - only active in debug builds -#ifdef NDEBUG -#define DCHECK(condition) \ - while (false) CHECK(condition) -#define DCHECK_EQ(a, b) \ - while (false) CHECK((a) == (b)) -#define DCHECK_NE(a, b) \ - while (false) CHECK((a) != (b)) -#define DCHECK_LT(a, b) \ - while (false) CHECK((a) < (b)) -#define DCHECK_LE(a, b) \ - while (false) CHECK((a) <= (b)) -#define DCHECK_GT(a, b) \ - while (false) CHECK((a) > (b)) -#define DCHECK_GE(a, b) \ - while (false) CHECK((a) >= (b)) -#else -#define DCHECK(condition) CHECK(condition) -#define DCHECK_EQ(a, b) CHECK((a) == (b)) -#define DCHECK_NE(a, b) CHECK((a) != (b)) -#define DCHECK_LT(a, b) CHECK((a) < (b)) -#define DCHECK_LE(a, b) CHECK((a) <= (b)) -#define DCHECK_GT(a, b) CHECK((a) > (b)) -#define DCHECK_GE(a, b) CHECK((a) >= (b)) -#endif - -// CHECK comparison macros -#define CHECK_EQ(a, b) CHECK((a) == (b)) -#define CHECK_NE(a, b) CHECK((a) != (b)) -#define CHECK_LT(a, b) CHECK((a) < (b)) -#define CHECK_LE(a, b) CHECK((a) <= (b)) -#define CHECK_GT(a, b) CHECK((a) > (b)) -#define CHECK_GE(a, b) CHECK((a) >= (b)) - -#endif // USE_GLOG +#define LOG_INFO \ + uccl::logging::LogMessage(NCCL_LOG_INFO, NCCL_ALL, __FILE__, __LINE__).stream() +#define LOG_WARNING \ + uccl::logging::LogMessage(NCCL_LOG_WARN, NCCL_ALL, __FILE__, __LINE__).stream() +#define LOG_ERROR \ + uccl::logging::LogMessage(NCCL_LOG_WARN, NCCL_ALL, __FILE__, __LINE__).stream() +#define LOG_FATAL \ + uccl::logging::LogMessage(NCCL_LOG_ABORT, NCCL_ALL, __FILE__, __LINE__).stream() + +#define LOG(severity) LOG_##severity + +#endif // UCCL_COMPAT_GLOG_STYLE