Skip to content

Commit 88af8a3

Browse files
committed
Update CHANGELOG for v1.1.1; enhance source location logging and expose log_to_sink_with_location API
1 parent 659a7d6 commit 88af8a3

6 files changed

Lines changed: 151 additions & 38 deletions

File tree

CHANGELOG

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
# v1.1.1 - 07-06-2026
2+
- Use compiler-provided `__FILE_NAME__` for `LOG_*` call-site filenames when predefined
3+
- Falls back to the internal constexpr basename helper for older GCC/Clang-compatible C++20 compilers and MSVC
4+
- Keeps `LOG_*` macro source locations in a `static constexpr` call-site path without requiring GCC 12+
5+
- Replace the public `source_location_file_name()` helper with internal `detail::file_name_from_path()` basename handling
6+
- Preserves dynamic direct-call path support while avoiding an extra public helper API
7+
- Clarify the direct `log_with_location(level, path, line, ...)` overload by treating the argument as a file path
8+
- Only derives the basename when source-location output is enabled and full-path output is disabled
9+
- Make `log_to_sink_with_location()` public so callers can log to a specific sink while supplying source-location metadata
10+
111
# v1.1.0 - 07-02-2026
212
- Add source-location logging for `LOG_*` macros
313
- Capture the macro call-site file and line by default

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cmake_minimum_required(VERSION 3.20)
22

33
project(slick-logger
4-
VERSION 1.1.0
4+
VERSION 1.1.1
55
LANGUAGES CXX)
66

77
set(CMAKE_CXX_STANDARD 20)

include/slick/logger.hpp

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@
5555

5656
#define SLICK_LOGGER_VERSION_MAJOR 1
5757
#define SLICK_LOGGER_VERSION_MINOR 1
58-
#define SLICK_LOGGER_VERSION_PATCH 0
59-
#define SLICK_LOGGER_VERSION "1.1.0"
58+
#define SLICK_LOGGER_VERSION_PATCH 1
59+
#define SLICK_LOGGER_VERSION "1.1.1"
6060

6161
#ifndef SLICK_LOGGER_MAX_ARGS
6262
#define SLICK_LOGGER_MAX_ARGS 20
@@ -70,9 +70,19 @@
7070
#define SLICK_LOGGER_FILE_PATH __FILE__
7171
#endif
7272

73+
#ifndef SLICK_LOGGER_FILE_NAME
74+
#if defined(__FILE_NAME__)
75+
#define SLICK_LOGGER_FILE_NAME __FILE_NAME__
76+
#else
77+
#define SLICK_LOGGER_FILE_NAME slick::logger::detail::file_name_from_path(SLICK_LOGGER_FILE_PATH)
78+
#endif
79+
#endif
80+
7381
namespace slick::logger {
7482

75-
inline constexpr const char* source_location_file_name(const char* path) noexcept {
83+
namespace detail {
84+
85+
inline constexpr const char* file_name_from_path(const char* path) noexcept {
7686
if (!path) {
7787
return nullptr;
7888
}
@@ -85,6 +95,8 @@ inline constexpr const char* source_location_file_name(const char* path) noexcep
8595
return file_name;
8696
}
8797

98+
} // namespace detail
99+
88100
inline constexpr bool has_source_location(const char* file_name, uint32_t line) noexcept {
89101
return file_name && *file_name != '\0' && line != 0;
90102
}
@@ -739,6 +751,21 @@ class Logger {
739751
template<typename FormatT, typename... Args>
740752
void log_to_sink(int sink_index, LogLevel level, FormatT&& format, Args&&... args);
741753

754+
/**
755+
* @brief Log a message to a specific sink by index with a source path and precomputed file name
756+
* @param index Index of the sink to log to
757+
* @param level LogLevel of the message
758+
* @param file_name Source file path; copied before the entry is queued when full path output is enabled
759+
* @param line Source line; copied before the entry is queued
760+
* @param copy_file_name Whether to copy the file name into the log entry (true) or store a pointer to it (false)
761+
* @param format Format string (printf-style)
762+
* @param args Arguments for the format string
763+
* @note Set copy_file_name to false only if the file name is guaranteed to remain valid until the log entry is written to the sink. Otherwise, set it to true to copy the file name into the log entry.
764+
*/
765+
template<typename FormatT, typename... Args>
766+
void log_to_sink_with_location(int sink_index, LogLevel level, const char* file_name, uint32_t line,
767+
bool copy_file_name, FormatT&& format, Args&&... args);
768+
742769
/**
743770
* @brief Shutdown the logger and flush all pending log entries
744771
* @param clear_sinks Clear sink list
@@ -803,11 +830,6 @@ class Logger {
803830
template<typename T>
804831
void enqueue_argument(LogArgument& arg, T&& value);
805832

806-
template<typename FormatT, typename... Args>
807-
void log_to_sink_with_location(int sink_index, LogLevel level, const char* file_name, uint32_t line,
808-
bool copy_file_name,
809-
FormatT&& format, Args&&... args);
810-
811833
void enqueue_format_args(LogEntry& entry, std::format_args fa);
812834

813835
StringRef store_string_in_queue(std::string_view str);
@@ -909,16 +931,16 @@ inline std::pair<std::string, bool> ISink::format_log_message(const LogEntry& en
909931
continue;
910932
}
911933

912-
if (format_str[brace_start] == '}') {
913-
if (brace_start + 1 < format_str.length() &&
914-
format_str[brace_start + 1] == '}') {
915-
result += '}';
916-
pos = brace_start + 2;
917-
} else {
918-
throw std::format_error("unmatched '}' in format string");
919-
}
920-
continue;
921-
}
934+
if (format_str[brace_start] == '}') {
935+
if (brace_start + 1 < format_str.length() &&
936+
format_str[brace_start + 1] == '}') {
937+
result += '}';
938+
pos = brace_start + 2;
939+
} else {
940+
throw std::format_error("unmatched '}' in format string");
941+
}
942+
continue;
943+
}
922944

923945
// Find the closing brace
924946
size_t brace_end = format_str.find('}', brace_start);
@@ -1671,8 +1693,20 @@ inline void Logger::log(LogLevel level, FormatT&& format, Args&&... args) {
16711693
}
16721694

16731695
template<typename FormatT, typename... Args>
1674-
inline void Logger::log_with_location(LogLevel level, const char* file_name, uint32_t line, FormatT&& format, Args&&... args) {
1675-
log_with_location(level, file_name, source_location_file_name(file_name), line, std::forward<FormatT>(format), std::forward<Args>(args)...);
1696+
inline void Logger::log_with_location(LogLevel level, const char* file_path, uint32_t line, FormatT&& format, Args&&... args) {
1697+
const uint8_t source_options = source_location_options_.load(std::memory_order_relaxed);
1698+
const bool include_source_location = (source_options & kSourceLocationEnabled) != 0;
1699+
const char* source_file = nullptr;
1700+
if (include_source_location) {
1701+
const bool include_full_path = (source_options & kSourceLocationFullPath) != 0;
1702+
source_file = include_full_path ? file_path : detail::file_name_from_path(file_path);
1703+
}
1704+
log_to_sink_with_location(-1, level,
1705+
source_file,
1706+
include_source_location ? line : 0,
1707+
true,
1708+
std::forward<FormatT>(format),
1709+
std::forward<Args>(args)...);
16761710
}
16771711

16781712
template<typename FormatT, typename... Args>
@@ -2077,8 +2111,7 @@ inline size_t Logger::round_up_to_power_of_2(size_t value) noexcept {
20772111
#if SLICK_LOGGER_ENABLE_SOURCE_LOCATION
20782112
#define SLICK_LOGGER_LOG_AT_CALL_SITE(logger_instance, level, ...) \
20792113
do { \
2080-
static constexpr const char* slick_logger_file_name__ = \
2081-
slick::logger::source_location_file_name(SLICK_LOGGER_FILE_PATH); \
2114+
static constexpr const char* slick_logger_file_name__ = SLICK_LOGGER_FILE_NAME; \
20822115
(logger_instance).log_with_static_location( \
20832116
level, \
20842117
slick::logger::Logger::static_source_location(SLICK_LOGGER_FILE_PATH, slick_logger_file_name__), \

src/logger.hpp

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,19 @@
6868
#define SLICK_LOGGER_FILE_PATH __FILE__
6969
#endif
7070

71+
#ifndef SLICK_LOGGER_FILE_NAME
72+
#if defined(__FILE_NAME__)
73+
#define SLICK_LOGGER_FILE_NAME __FILE_NAME__
74+
#else
75+
#define SLICK_LOGGER_FILE_NAME slick::logger::detail::file_name_from_path(SLICK_LOGGER_FILE_PATH)
76+
#endif
77+
#endif
78+
7179
namespace slick::logger {
7280

73-
inline constexpr const char* source_location_file_name(const char* path) noexcept {
81+
namespace detail {
82+
83+
inline constexpr const char* file_name_from_path(const char* path) noexcept {
7484
if (!path) {
7585
return nullptr;
7686
}
@@ -83,6 +93,8 @@ inline constexpr const char* source_location_file_name(const char* path) noexcep
8393
return file_name;
8494
}
8595

96+
} // namespace detail
97+
8698
inline constexpr bool has_source_location(const char* file_name, uint32_t line) noexcept {
8799
return file_name && *file_name != '\0' && line != 0;
88100
}
@@ -737,6 +749,21 @@ class Logger {
737749
template<typename FormatT, typename... Args>
738750
void log_to_sink(int sink_index, LogLevel level, FormatT&& format, Args&&... args);
739751

752+
/**
753+
* @brief Log a message to a specific sink by index with a source path and precomputed file name
754+
* @param index Index of the sink to log to
755+
* @param level LogLevel of the message
756+
* @param file_name Source file path; copied before the entry is queued when full path output is enabled
757+
* @param line Source line; copied before the entry is queued
758+
* @param copy_file_name Whether to copy the file name into the log entry (true) or store a pointer to it (false)
759+
* @param format Format string (printf-style)
760+
* @param args Arguments for the format string
761+
* @note Set copy_file_name to false only if the file name is guaranteed to remain valid until the log entry is written to the sink. Otherwise, set it to true to copy the file name into the log entry.
762+
*/
763+
template<typename FormatT, typename... Args>
764+
void log_to_sink_with_location(int sink_index, LogLevel level, const char* file_name, uint32_t line,
765+
bool copy_file_name, FormatT&& format, Args&&... args);
766+
740767
/**
741768
* @brief Shutdown the logger and flush all pending log entries
742769
* @param clear_sinks Clear sink list
@@ -801,11 +828,6 @@ class Logger {
801828
template<typename T>
802829
void enqueue_argument(LogArgument& arg, T&& value);
803830

804-
template<typename FormatT, typename... Args>
805-
void log_to_sink_with_location(int sink_index, LogLevel level, const char* file_name, uint32_t line,
806-
bool copy_file_name,
807-
FormatT&& format, Args&&... args);
808-
809831
void enqueue_format_args(LogEntry& entry, std::format_args fa);
810832

811833
StringRef store_string_in_queue(std::string_view str);
@@ -1669,8 +1691,20 @@ inline void Logger::log(LogLevel level, FormatT&& format, Args&&... args) {
16691691
}
16701692

16711693
template<typename FormatT, typename... Args>
1672-
inline void Logger::log_with_location(LogLevel level, const char* file_name, uint32_t line, FormatT&& format, Args&&... args) {
1673-
log_with_location(level, file_name, source_location_file_name(file_name), line, std::forward<FormatT>(format), std::forward<Args>(args)...);
1694+
inline void Logger::log_with_location(LogLevel level, const char* file_path, uint32_t line, FormatT&& format, Args&&... args) {
1695+
const uint8_t source_options = source_location_options_.load(std::memory_order_relaxed);
1696+
const bool include_source_location = (source_options & kSourceLocationEnabled) != 0;
1697+
const char* source_file = nullptr;
1698+
if (include_source_location) {
1699+
const bool include_full_path = (source_options & kSourceLocationFullPath) != 0;
1700+
source_file = include_full_path ? file_path : detail::file_name_from_path(file_path);
1701+
}
1702+
log_to_sink_with_location(-1, level,
1703+
source_file,
1704+
include_source_location ? line : 0,
1705+
true,
1706+
std::forward<FormatT>(format),
1707+
std::forward<Args>(args)...);
16741708
}
16751709

16761710
template<typename FormatT, typename... Args>
@@ -2075,8 +2109,7 @@ inline size_t Logger::round_up_to_power_of_2(size_t value) noexcept {
20752109
#if SLICK_LOGGER_ENABLE_SOURCE_LOCATION
20762110
#define SLICK_LOGGER_LOG_AT_CALL_SITE(logger_instance, level, ...) \
20772111
do { \
2078-
static constexpr const char* slick_logger_file_name__ = \
2079-
slick::logger::source_location_file_name(SLICK_LOGGER_FILE_PATH); \
2112+
static constexpr const char* slick_logger_file_name__ = SLICK_LOGGER_FILE_NAME; \
20802113
(logger_instance).log_with_static_location( \
20812114
level, \
20822115
slick::logger::Logger::static_source_location(SLICK_LOGGER_FILE_PATH, slick_logger_file_name__), \

tests/test_logger.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,6 @@ TEST_F(SlickLoggerTest, DisabledMacrosDoNotEvaluateArguments) {
124124
TEST_F(SlickLoggerTest, MacrosIncludeCallSiteSourceLocationByDefault) {
125125
std::filesystem::remove("test_source_location.log");
126126

127-
EXPECT_STREQ(slick::logger::source_location_file_name(
128-
"C:\\repo\\slick-logger\\tests\\test_logger.cpp"), "test_logger.cpp");
129-
EXPECT_STREQ(slick::logger::source_location_file_name(
130-
"/repo/slick-logger/tests/test_logger.cpp"), "test_logger.cpp");
131-
132127
slick::logger::Logger::instance().init("test_source_location.log", 1024);
133128

134129
const int expected_line = __LINE__ + 1;

tests/test_sinks.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class SinkTest : public ::testing::Test {
2626
"rotating_test_1.log", "rotating_test_2.log", "rotating_test_3.log",
2727
"daily_test.log", "daily_rotation_test.log", "daily_rotation_test_2025-08-25.log",
2828
"daily_size_test.log", "args_sink.log", "dedicated_sink.log", "filtered_sink.log",
29+
"location_sink1.log", "location_sink2.log",
2930
"named_sink1.log", "named_sink2.log", "regular_sink.log", "daily_no_size_rotation.log",
3031
"daily_multi_rotation.log", "daily_restart_test.log", "daily_restart_existing.log"
3132
};
@@ -450,6 +451,47 @@ TEST_F(SinkTest, NamedSinkDirectLogging) {
450451
EXPECT_FALSE(sink2_content.find("Warning to console only") != std::string::npos);
451452
}
452453

454+
TEST_F(SinkTest, LogToSinkWithLocationTargetsSpecificSink) {
455+
slick::logger::Logger::instance().clear_sinks();
456+
slick::logger::Logger::instance().add_file_sink("location_sink1.log", "location_sink1");
457+
slick::logger::Logger::instance().add_file_sink("location_sink2.log", "location_sink2");
458+
slick::logger::Logger::instance().init(1024);
459+
460+
auto sink1 = slick::logger::Logger::instance().get_sink("location_sink1");
461+
auto sink2 = slick::logger::Logger::instance().get_sink("location_sink2");
462+
ASSERT_TRUE(sink1 != nullptr);
463+
ASSERT_TRUE(sink2 != nullptr);
464+
465+
std::string source_file = "public_sink_source.cpp";
466+
slick::logger::Logger::instance().log_to_sink_with_location(
467+
sink2->index(),
468+
slick::logger::LogLevel::L_INFO,
469+
source_file.c_str(),
470+
456,
471+
true,
472+
"Specific sink source-location message {}", 7);
473+
source_file.assign("overwritten.cpp");
474+
475+
slick::logger::Logger::instance().reset();
476+
477+
ASSERT_TRUE(std::filesystem::exists("location_sink1.log"));
478+
std::ifstream sink1_file("location_sink1.log");
479+
std::string sink1_content((std::istreambuf_iterator<char>(sink1_file)),
480+
std::istreambuf_iterator<char>());
481+
sink1_file.close();
482+
483+
ASSERT_TRUE(std::filesystem::exists("location_sink2.log"));
484+
std::ifstream sink2_file("location_sink2.log");
485+
std::string sink2_content((std::istreambuf_iterator<char>(sink2_file)),
486+
std::istreambuf_iterator<char>());
487+
sink2_file.close();
488+
489+
EXPECT_EQ(sink1_content.find("Specific sink source-location message 7"), std::string::npos);
490+
EXPECT_NE(sink2_content.find("public_sink_source.cpp:456"), std::string::npos);
491+
EXPECT_NE(sink2_content.find("Specific sink source-location message 7"), std::string::npos);
492+
EXPECT_EQ(sink2_content.find("overwritten.cpp"), std::string::npos);
493+
}
494+
453495
TEST_F(SinkTest, SinkDirectLoggingWithArgs) {
454496
// Test direct sink logging with format arguments
455497
slick::logger::Logger::instance().clear_sinks();

0 commit comments

Comments
 (0)