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
This commit is contained in:
Nicholas Dudfield
2025-07-25 17:46:50 +07:00
parent 849d447a20
commit 20344ab999
3 changed files with 138 additions and 1 deletions

View File

@@ -33,6 +33,24 @@ if(Git_FOUND)
endif() endif()
endif() #git 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) if(thread_safety_analysis)
add_compile_options(-Wthread-safety -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -DRIPPLE_ENABLE_THREAD_SAFETY_ANNOTATIONS) add_compile_options(-Wthread-safety -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -DRIPPLE_ENABLE_THREAD_SAFETY_ANNOTATIONS)
add_compile_options("-stdlib=libc++") add_compile_options("-stdlib=libc++")

View File

@@ -249,13 +249,22 @@ private:
// Wraps a Journal::Stream to skip evaluation of // Wraps a Journal::Stream to skip evaluation of
// expensive argument lists if the stream is not active. // expensive argument lists if the stream is not active.
#ifndef JLOG #ifndef JLOG
#ifdef LOG_LINE_NUMBERS
#define JLOG(x) \ #define JLOG(x) \
if (!x) \ if (!(x)) \
{ \
} \
else \
(x).writeWithLocation("", __FILE__, __LINE__)
#else
#define JLOG(x) \
if (!(x)) \
{ \ { \
} \ } \
else \ else \
x x
#endif #endif
#endif
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Debug logging: // Debug logging:

View File

@@ -21,7 +21,15 @@
#define BEAST_UTILITY_JOURNAL_H_INCLUDED #define BEAST_UTILITY_JOURNAL_H_INCLUDED
#include <cassert> #include <cassert>
#include <cstring>
#include <sstream> #include <sstream>
#ifdef _WIN32
#include <io.h>
#define isatty _isatty
#define STDERR_FILENO 2
#else
#include <unistd.h>
#endif
namespace beast { namespace beast {
@@ -149,6 +157,15 @@ private:
template <typename T> template <typename T>
ScopedStream(Stream const& stream, T const& t); ScopedStream(Stream const& stream, T const& t);
#ifdef LOG_LINE_NUMBERS
template <typename T>
ScopedStream(
Stream const& stream,
T const& t,
const char* file,
int line);
#endif
ScopedStream(Stream const& stream, std::ostream& manip(std::ostream&)); ScopedStream(Stream const& stream, std::ostream& manip(std::ostream&));
ScopedStream& ScopedStream&
@@ -253,6 +270,12 @@ public:
template <typename T> template <typename T>
ScopedStream ScopedStream
operator<<(T const& t) const; operator<<(T const& t) const;
#ifdef LOG_LINE_NUMBERS
template <typename T>
ScopedStream
writeWithLocation(T const& t, const char* file, int line) const;
#endif
/** @} */ /** @} */
private: private:
@@ -361,6 +384,84 @@ Journal::ScopedStream::ScopedStream(Journal::Stream const& stream, T const& t)
m_ostream << 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 <typename T>
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 <typename T> template <typename T>
std::ostream& std::ostream&
Journal::ScopedStream::operator<<(T const& t) const Journal::ScopedStream::operator<<(T const& t) const
@@ -378,6 +479,15 @@ Journal::Stream::operator<<(T const& t) const
return ScopedStream(*this, t); return ScopedStream(*this, t);
} }
#ifdef LOG_LINE_NUMBERS
template <typename T>
Journal::ScopedStream
Journal::Stream::writeWithLocation(T const& t, const char* file, int line) const
{
return ScopedStream(*this, t, file, line);
}
#endif
namespace detail { namespace detail {
template <class CharT, class Traits = std::char_traits<CharT>> template <class CharT, class Traits = std::char_traits<CharT>>