From 20344ab999e75b5e46876d8f37f160f7e0fe3ff4 Mon Sep 17 00:00:00 2001 From: Nicholas Dudfield Date: Fri, 25 Jul 2025 17:46:50 +0700 Subject: [PATCH] feat(logging): add colored file:line numbers to JLOG macro - Add LOG_LINE_NUMBERS CMake option (ON for Debug, OFF for Release) - Implement constexpr path stripping using SOURCE_ROOT_PATH - Add smart TTY detection with NO_COLOR/FORCE_COLOR support - Extend Journal::ScopedStream with file/line constructor - Update JLOG macro to conditionally include source location - Maintain backward compatibility when disabled --- CMakeLists.txt | 18 +++++ src/ripple/basics/Log.h | 11 ++- src/ripple/beast/utility/Journal.h | 110 +++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 57fc86e2c..9d65b2c1c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,24 @@ if(Git_FOUND) endif() endif() #git +# make SOURCE_ROOT_PATH define available for logging +set(SOURCE_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/src/") +add_definitions(-DSOURCE_ROOT_PATH="${SOURCE_ROOT_PATH}") + +# LOG_LINE_NUMBERS option - default to ON for Debug builds, OFF for Release +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + option(LOG_LINE_NUMBERS "Include file and line numbers in log messages" ON) +else() + option(LOG_LINE_NUMBERS "Include file and line numbers in log messages" OFF) +endif() + +if(LOG_LINE_NUMBERS) + add_definitions(-DLOG_LINE_NUMBERS=1) + message(STATUS "Log line numbers enabled") +else() + message(STATUS "Log line numbers disabled") +endif() + if(thread_safety_analysis) add_compile_options(-Wthread-safety -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -DRIPPLE_ENABLE_THREAD_SAFETY_ANNOTATIONS) add_compile_options("-stdlib=libc++") diff --git a/src/ripple/basics/Log.h b/src/ripple/basics/Log.h index 6722ab813..ff60b1167 100644 --- a/src/ripple/basics/Log.h +++ b/src/ripple/basics/Log.h @@ -249,13 +249,22 @@ private: // Wraps a Journal::Stream to skip evaluation of // expensive argument lists if the stream is not active. #ifndef JLOG +#ifdef LOG_LINE_NUMBERS #define JLOG(x) \ - if (!x) \ + if (!(x)) \ + { \ + } \ + else \ + (x).writeWithLocation("", __FILE__, __LINE__) +#else +#define JLOG(x) \ + if (!(x)) \ { \ } \ else \ x #endif +#endif //------------------------------------------------------------------------------ // Debug logging: diff --git a/src/ripple/beast/utility/Journal.h b/src/ripple/beast/utility/Journal.h index 333a743a6..b594e13e0 100644 --- a/src/ripple/beast/utility/Journal.h +++ b/src/ripple/beast/utility/Journal.h @@ -21,7 +21,15 @@ #define BEAST_UTILITY_JOURNAL_H_INCLUDED #include +#include #include +#ifdef _WIN32 +#include +#define isatty _isatty +#define STDERR_FILENO 2 +#else +#include +#endif namespace beast { @@ -149,6 +157,15 @@ private: template ScopedStream(Stream const& stream, T const& t); +#ifdef LOG_LINE_NUMBERS + template + ScopedStream( + Stream const& stream, + T const& t, + const char* file, + int line); +#endif + ScopedStream(Stream const& stream, std::ostream& manip(std::ostream&)); ScopedStream& @@ -253,6 +270,12 @@ public: template ScopedStream operator<<(T const& t) const; + +#ifdef LOG_LINE_NUMBERS + template + ScopedStream + writeWithLocation(T const& t, const char* file, int line) const; +#endif /** @} */ private: @@ -361,6 +384,84 @@ Journal::ScopedStream::ScopedStream(Journal::Stream const& stream, T const& t) m_ostream << t; } +namespace detail { +// Helper to strip source root path from __FILE__ at compile time +constexpr const char* +stripSourceRoot(const char* file) +{ +#ifdef SOURCE_ROOT_PATH + constexpr const char* sourceRoot = SOURCE_ROOT_PATH; + constexpr auto strlen_constexpr = [](const char* s) constexpr + { + const char* p = s; + while (*p) + ++p; + return p - s; + }; + constexpr auto strncmp_constexpr = + [](const char* a, const char* b, size_t n) constexpr + { + for (size_t i = 0; i < n; ++i) + { + if (a[i] != b[i]) + return a[i] - b[i]; + if (a[i] == '\0') + break; + } + return 0; + }; + constexpr size_t sourceRootLen = strlen_constexpr(sourceRoot); + return (strncmp_constexpr(file, sourceRoot, sourceRootLen) == 0) + ? file + sourceRootLen + : file; +#else + return file; +#endif +} + +// Check if we should use colors - cached at startup +inline bool +shouldUseColors() +{ + static const bool useColors = []() { + // Honor NO_COLOR environment variable (standard) + if (std::getenv("NO_COLOR")) + return false; + + // Honor FORCE_COLOR to override terminal detection + if (std::getenv("FORCE_COLOR")) + return true; + + // Check if stderr is a terminal + return isatty(STDERR_FILENO) != 0; + }(); + return useColors; +} +} // namespace detail + +#ifdef LOG_LINE_NUMBERS +template +Journal::ScopedStream::ScopedStream( + Journal::Stream const& stream, + T const& t, + const char* file, + int line) + : ScopedStream(stream.sink(), stream.level()) +{ + // Use constexpr path stripping and conditional color codes + if (detail::shouldUseColors()) + { + m_ostream << "\033[36m[" << detail::stripSourceRoot(file) << ":" << line + << "]\033[0m " << t; + } + else + { + m_ostream << "[" << detail::stripSourceRoot(file) << ":" << line << "] " + << t; + } +} +#endif + template std::ostream& Journal::ScopedStream::operator<<(T const& t) const @@ -378,6 +479,15 @@ Journal::Stream::operator<<(T const& t) const return ScopedStream(*this, t); } +#ifdef LOG_LINE_NUMBERS +template +Journal::ScopedStream +Journal::Stream::writeWithLocation(T const& t, const char* file, int line) const +{ + return ScopedStream(*this, t, file, line); +} +#endif + namespace detail { template >