Compare commits

...

20 Commits

Author SHA1 Message Date
Loïc Lopez
75e93f4bdd Set C++ standard to 20 if not defined (#3583)
Why? to be able to use a greater c++ when using conan.

Because conan reports: Warning: Standard CMAKE_CXX_STANDARD value defined in conan_toolchain.cmake to 23 has been modified to 20 by .conan2/p/b/spdlo815dcec129526/b/src/CMakeLists.txt
2026-04-09 10:07:23 +03:00
Christoph Grüninger
fb1227486b [bench] Update Google benchmark to version 1.8.4 (#3579)
Fix CMake warning from more recent versions.
Latest benchmark release support C++11.
Does not increase CMake requirements.
2026-04-07 17:26:55 +03:00
Christoph Grüninger
3c61b051d2 [ci] Update actions/checkout to latest major relese (#3575)
Fix warning that Node.js 20 will stop working in June
2026-04-03 02:19:24 +03:00
Niram7777
45b67eee66 Add constructor for dup_filter_sink with sinks parameter (#3549)
* Add constructor for dup_filter_sink with sinks parameter

* dup_filter_sink switch order construction
2026-03-14 23:23:06 +02:00
Ömer BULUT
1fbc60a5e7 docs: fix SPDLOG_LEVEL env example (#3561)
Co-authored-by: Omer Bulut <omer.bulut@asisguard.com.tr>
2026-03-14 23:18:51 +02:00
Yao Zi
0f7562a0f9 tests: timezone: Provide DST rules when setting TZ on POSIX systems (#3542)
POSIX 2024 defines three formats for the TZ environment variable,

1. Implementation defined format which always starts with a colon:
   ":characters".
2. A specifier which fully describes the timezone rule in format
   "stdoffset[dst[offset][,start[/time],end[/time]]]". Note the
   offset and start/end part could be omitted, in which case one hour
   is implied, or it's considered implementation-defined when changing
   to and from Daylight Saving Time occurs.
3. Geographical or special timezone from an implementation-defined
   timezone database.

POSIX 2024 requires the format 1 and 2 to take precedence over format 3.

In tests/test_timezone.cpp, we set TZ to "EST5EDT" or "IST-2IDT".
According to POSIX, "EST5EDT" should be interpreted as

- timezone "EST", which is five hours behind UTC
- corresponding DST timezone is "EDT", which is one hour ahead of
  standard time
- it's implementation-defined when changing to and from DST occurs

The interpretion is similar for TZ="IST-2IDT". Obviously we're hitting
implementation-defined behavior here, which is inconsistent across
platforms, e.g., musl considers DST is always active if both DST start
and end rules are omitted, thus test_timezone.cpp would fail.

Let's also provide DST rules when setting TZ variables to avoid
depending on implementation-defined behavior.

Fixes: b656d1ceec ("Windows utc_minutes_offset(): Fix historical DST accuracy and improve offset calculation speed (~2.5x) (#3508)")

Signed-off-by: Yao Zi <me@ziyao.cc>
2026-02-24 09:07:37 +02:00
Yancey
d5af52d903 Fix format_string propagation (#3543) 2026-02-23 17:16:21 +02:00
Yancey
1685e694c5 Fix deprecated copy constructor usage of fmt::format_string (#3541)
* Fix deprecated copy constructor usage of fmt::format_string

* Fix copy constructor usage of fmt::wformat_string_t

* Fix usage of std::basic_format_string

* remove spdlog::to_string_view()
2026-02-23 02:10:25 +02:00
Thomas LE BERRE
c49c7cf960 Allow empty DEBUG_POSTFIX property in CMakeLists (#3530) 2026-02-21 00:55:46 +02:00
Gabi Melman
fc7e9c8721 Update common-inl.h
Added missing include
2026-02-09 21:59:45 +02:00
SamareshSingh
566b2d1404 Fix #3525: Make level name matching case-insensitive (#3535)
Modified from_str() to perform case-insensitive comparison for all level names.
This allows environment variables and SPDLOG_LEVEL_NAMES to use uppercase or
mixed case level names (e.g., DEBUG, INFO, Warning) while maintaining full
backward compatibility with existing lowercase names.
2026-02-09 21:58:20 +02:00
Eli Boyarski
6c5d63291a Fix should_log comment (#3534) 2026-02-09 21:54:24 +02:00
Kağan Can Şit
472945ba48 Fix shadow member warning in example file (#3521) 2026-01-15 22:54:27 +02:00
Kağan Can Şit
687226d95d The upd_sink and dist_sink files have been modified to address Passed by value warnings. (#3520) 2026-01-12 09:21:17 +02:00
Kağan Can Şit
f2a9dec029 Fix function arguments names different warnings (#3519)
* helpers-inl.h - load_levels function arguments names different style warning fixed

* async_logger-inl.h - backend_sink_it_ function arguments names different style warning fixed

* spdlog-inl.g - should_log function arguments names different style warning fixed
2026-01-11 21:56:46 +02:00
Kağan Can Şit
309204d53c Rename local variables to avoid shadowing member functions (#3516) 2026-01-11 00:05:14 +02:00
Kağan Can Şit
1774e70082 Add const qualifier to get_time_ and filter_ member functions (#3515) 2026-01-10 23:33:43 +02:00
Kağan Can Şit
d299603e45 Add missing const qualifiers to reference variables (#3514) 2026-01-10 20:07:34 +02:00
Kağan Can Şit
33375433e0 fix: initialize null_atomic_int::value to zero (#3513) 2026-01-09 23:31:01 +02:00
Kağan Can Şit
6b240a892d Replace C-style cast with reinterpret_cast in udp_client (#3509)
* Replace C-style cast with reinterpret_cast in udp_Client

* Update include/spdlog/details/udp_client.h

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Gabi Melman <gmelman1@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-04 21:45:53 +02:00
31 changed files with 194 additions and 101 deletions

View File

@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
name: Coverity Scan
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Install dependencies
run: |

View File

@@ -28,7 +28,7 @@ jobs:
image: ${{ matrix.config.compiler == 'clang' && 'teeks99/clang-ubuntu' || matrix.config.compiler }}:${{ matrix.config.version }}
name: "${{ matrix.config.compiler}} ${{ matrix.config.version }} (C++${{ matrix.config.cppstd }} ${{ matrix.config.build_type }} ${{ matrix.config.asan == 'ON' && 'ASAN' || '' }}${{ matrix.config.tsan == 'ON' && 'TSAN' || '' }})"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Setup
run: |
apt-get update
@@ -68,7 +68,7 @@ jobs:
runs-on: macOS-latest
name: "OS X Clang (C++11, Release)"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Build
run: |
mkdir -p build && cd build

View File

@@ -19,7 +19,7 @@ jobs:
BUILD_EXAMPLE: 'ON'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Build
run: |
mkdir -p build && cd build

View File

@@ -42,7 +42,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: CMake ${{ matrix.config.GENERATOR }} CXX=${{matrix.config.CXX_STANDARD}} WCHAR=${{matrix.config.WCHAR_FILES}} STD_FORMAT=${{matrix.config.USE_STD_FORMAT}}
shell: pwsh

View File

@@ -30,7 +30,9 @@ endif()
# Compiler config
# ---------------------------------------------------------------------------------------
if(SPDLOG_USE_STD_FORMAT)
if(NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 20)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)
elseif(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
@@ -199,7 +201,7 @@ spdlog_enable_warnings(spdlog)
set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION
${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR})
set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX ${SPDLOG_DEBUG_POSTFIX})
set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX "${SPDLOG_DEBUG_POSTFIX}")
if(COMMAND target_precompile_headers AND SPDLOG_ENABLE_PCH)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/pch.h.in ${PROJECT_BINARY_DIR}/spdlog_pch.h @ONLY)

View File

@@ -19,7 +19,7 @@ if(NOT benchmark_FOUND)
# disable tests
set(BENCHMARK_ENABLE_TESTING OFF CACHE INTERNAL "")
# Do not build and run googlebenchmark tests
FetchContent_Declare(googlebenchmark GIT_REPOSITORY https://github.com/google/benchmark.git GIT_TAG v1.6.0)
FetchContent_Declare(googlebenchmark GIT_REPOSITORY https://github.com/google/benchmark.git GIT_TAG v1.8.4)
FetchContent_MakeAvailable(googlebenchmark)
endif()

View File

@@ -267,16 +267,16 @@ void multi_sink_example() {
// User defined types logging
struct my_type {
int i = 0;
explicit my_type(int i)
: i(i) {}
int value_ = 0;
explicit my_type(int value)
: value_(value) {}
};
#ifndef SPDLOG_USE_STD_FORMAT // when using fmtlib
template <>
struct fmt::formatter<my_type> : fmt::formatter<std::string> {
auto format(my_type my, format_context &ctx) const -> decltype(ctx.out()) {
return fmt::format_to(ctx.out(), "[my_type i={}]", my.i);
return fmt::format_to(ctx.out(), "[my_type value={}]", my.value_);
}
};
@@ -284,7 +284,7 @@ struct fmt::formatter<my_type> : fmt::formatter<std::string> {
template <>
struct std::formatter<my_type> : std::formatter<std::string> {
auto format(my_type my, format_context &ctx) const -> decltype(ctx.out()) {
return std::format_to(ctx.out(), "[my_type i={}]", my.i);
return std::format_to(ctx.out(), "[my_type value={}]", my.value_);
}
};
#endif

View File

@@ -57,15 +57,15 @@ SPDLOG_LOGGER_CATCH(source_loc())
//
// backend functions - called from the thread pool to do the actual job
//
SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg &msg) {
SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg &incoming_log_msg) {
for (auto &sink : sinks_) {
if (sink->should_log(msg.level)) {
SPDLOG_TRY { sink->log(msg); }
SPDLOG_LOGGER_CATCH(msg.source)
if (sink->should_log(incoming_log_msg.level)) {
SPDLOG_TRY { sink->log(incoming_log_msg); }
SPDLOG_LOGGER_CATCH(incoming_log_msg.source)
}
}
if (should_flush_(msg)) {
if (should_flush_(incoming_log_msg)) {
backend_flush_();
}
}

View File

@@ -26,8 +26,8 @@ inline void load_argv_levels(int argc, const char **argv) {
for (int i = 1; i < argc; i++) {
std::string arg = argv[i];
if (arg.find(spdlog_level_prefix) == 0) {
auto levels_string = arg.substr(spdlog_level_prefix.size());
helpers::load_levels(levels_string);
const auto levels_spec = arg.substr(spdlog_level_prefix.size());
helpers::load_levels(levels_spec);
}
}
}

View File

@@ -17,7 +17,7 @@
// export SPDLOG_LEVEL=debug
//
// turn off all logging except for logger1:
// export SPDLOG_LEVEL="*=off,logger1=debug"
// export SPDLOG_LEVEL="off,logger1=debug"
//
// turn off all logging except for logger1 and logger2:
@@ -26,9 +26,9 @@
namespace spdlog {
namespace cfg {
inline void load_env_levels(const char* var = "SPDLOG_LEVEL") {
auto env_val = details::os::getenv(var);
if (!env_val.empty()) {
helpers::load_levels(env_val);
const auto levels_spec = details::os::getenv(var);
if (!levels_spec.empty()) {
helpers::load_levels(levels_spec);
}
}

View File

@@ -70,12 +70,12 @@ inline std::unordered_map<std::string, std::string> extract_key_vals_(const std:
return rv;
}
SPDLOG_INLINE void load_levels(const std::string &input) {
if (input.empty() || input.size() >= 32768) {
SPDLOG_INLINE void load_levels(const std::string &levels_spec) {
if (levels_spec.empty() || levels_spec.size() >= 32768) {
return;
}
auto key_vals = extract_key_vals_(input);
auto key_vals = extract_key_vals_(levels_spec);
std::unordered_map<std::string, level::level_enum> levels;
level::level_enum global_level = level::info;
bool global_level_found = false;
@@ -83,7 +83,7 @@ SPDLOG_INLINE void load_levels(const std::string &input) {
for (auto &name_level : key_vals) {
const auto &logger_name = name_level.first;
const auto &level_name = to_lower_(name_level.second);
auto level = level::from_str(level_name);
const auto level = level::from_str(level_name);
// ignore unrecognized level names
if (level == level::off && level_name != "off") {
continue;

View File

@@ -18,7 +18,7 @@ namespace helpers {
// turn off all logging except for logger1: "off,logger1=debug"
// turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info"
//
SPDLOG_API void load_levels(const std::string &txt);
SPDLOG_API void load_levels(const std::string &levels_spec);
} // namespace helpers
} // namespace cfg

View File

@@ -9,6 +9,7 @@
#include <algorithm>
#include <iterator>
#include <cctype>
namespace spdlog {
namespace level {
@@ -29,15 +30,31 @@ SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOE
}
SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT {
auto it = std::find(std::begin(level_string_views), std::end(level_string_views), name);
auto it = std::find_if(std::begin(level_string_views), std::end(level_string_views),
[&name](const string_view_t &level_name) {
return level_name.size() == name.size() &&
std::equal(name.begin(), name.end(), level_name.begin(),
[](char a, char b) {
return std::tolower(static_cast<unsigned char>(a)) ==
std::tolower(static_cast<unsigned char>(b));
});
});
if (it != std::end(level_string_views))
return static_cast<level::level_enum>(std::distance(std::begin(level_string_views), it));
// check also for "warn" and "err" before giving up..
if (name == "warn") {
auto iequals = [](const std::string &a, const std::string &b) {
return a.size() == b.size() &&
std::equal(a.begin(), a.end(), b.begin(), [](char ac, char bc) {
return std::tolower(static_cast<unsigned char>(ac)) ==
std::tolower(static_cast<unsigned char>(bc));
});
};
if (iequals(name, "warn")) {
return level::warn;
}
if (name == "err") {
if (iequals(name, "err")) {
return level::err;
}
return level::off;

View File

@@ -340,38 +340,6 @@ struct file_event_handlers {
namespace details {
// to_string_view
SPDLOG_CONSTEXPR_FUNC spdlog::string_view_t to_string_view(const memory_buf_t &buf)
SPDLOG_NOEXCEPT {
return spdlog::string_view_t{buf.data(), buf.size()};
}
SPDLOG_CONSTEXPR_FUNC spdlog::string_view_t to_string_view(spdlog::string_view_t str)
SPDLOG_NOEXCEPT {
return str;
}
#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
SPDLOG_CONSTEXPR_FUNC spdlog::wstring_view_t to_string_view(const wmemory_buf_t &buf)
SPDLOG_NOEXCEPT {
return spdlog::wstring_view_t{buf.data(), buf.size()};
}
SPDLOG_CONSTEXPR_FUNC spdlog::wstring_view_t to_string_view(spdlog::wstring_view_t str)
SPDLOG_NOEXCEPT {
return str;
}
#endif
#if defined(SPDLOG_USE_STD_FORMAT) && __cpp_lib_format >= 202207L
template <typename T, typename... Args>
SPDLOG_CONSTEXPR_FUNC std::basic_string_view<T> to_string_view(
std::basic_format_string<T, Args...> fmt) SPDLOG_NOEXCEPT {
return fmt.get();
}
#endif
// make_unique support for pre c++14
#if __cplusplus >= 201402L // C++14 and beyond
using std::enable_if_t;

View File

@@ -15,7 +15,7 @@ struct null_mutex {
};
struct null_atomic_int {
int value;
int value{0};
null_atomic_int() = default;
explicit null_atomic_int(int new_value)

View File

@@ -257,7 +257,7 @@ SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name) {
}
SPDLOG_INLINE void registry::register_logger_(std::shared_ptr<logger> new_logger) {
auto &logger_name = new_logger->name();
const auto &logger_name = new_logger->name();
throw_if_exists_(logger_name);
loggers_[logger_name] = std::move(new_logger);
}

View File

@@ -69,10 +69,9 @@ public:
// Send exactly n_bytes of the given data.
// On error close the connection and throw.
void send(const char *data, size_t n_bytes) {
ssize_t toslen = 0;
socklen_t tolen = sizeof(struct sockaddr);
if ((toslen = ::sendto(socket_, data, n_bytes, 0, (struct sockaddr *)&sockAddr_, tolen)) ==
-1) {
socklen_t tolen = sizeof(sockAddr_);
if (::sendto(socket_, data, n_bytes, 0, reinterpret_cast<const sockaddr *>(&sockAddr_),
tolen) == -1) {
throw_spdlog_ex("sendto(2) failed", errno);
}
}

View File

@@ -164,8 +164,7 @@ SPDLOG_INLINE void logger::dump_backtrace_() {
}
SPDLOG_INLINE bool logger::should_flush_(const details::log_msg &msg) const {
auto flush_level = flush_level_.load(std::memory_order_relaxed);
return (msg.level >= flush_level) && (msg.level != level::off);
return (msg.level >= flush_level()) && (msg.level != level::off);
}
SPDLOG_INLINE void logger::err_handler_(const std::string &msg) const {

View File

@@ -77,7 +77,11 @@ public:
template <typename... Args>
void log(source_loc loc, level::level_enum lvl, format_string_t<Args...> fmt, Args &&...args) {
log_(loc, lvl, details::to_string_view(fmt), std::forward<Args>(args)...);
#if defined(SPDLOG_USE_STD_FORMAT) && __cpp_lib_format < 202207L
log_(loc, lvl, fmt, std::forward<Args>(args)...);
#else
log_(loc, lvl, fmt.get(), std::forward<Args>(args)...);
#endif
}
template <typename... Args>
@@ -158,7 +162,11 @@ public:
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
template <typename... Args>
void log(source_loc loc, level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&...args) {
log_(loc, lvl, details::to_string_view(fmt), std::forward<Args>(args)...);
#if defined(SPDLOG_USE_STD_FORMAT) && __cpp_lib_format < 202207L
log_(loc, lvl, fmt, std::forward<Args>(args)...);
#else
log_(loc, lvl, fmt.get(), std::forward<Args>(args)...);
#endif
}
template <typename... Args>
@@ -258,7 +266,7 @@ public:
log(level::critical, msg);
}
// return true logging is enabled for the given level.
// return true if logging is enabled for the given level.
bool should_log(level::level_enum msg_level) const {
return msg_level >= level_.load(std::memory_order_relaxed);
}

View File

@@ -813,7 +813,7 @@ public:
: flag_formatter(padinfo) {}
void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override {
auto &mdc_map = mdc::get_context();
const auto &mdc_map = mdc::get_context();
if (mdc_map.empty()) {
ScopedPadder p(0, padinfo_, dest);
return;
@@ -823,11 +823,10 @@ public:
}
void format_mdc(const mdc::mdc_map_t &mdc_map, memory_buf_t &dest) {
auto last_element = --mdc_map.end();
const auto last_element = std::prev(mdc_map.end());
for (auto it = mdc_map.begin(); it != mdc_map.end(); ++it) {
auto &pair = *it;
const auto &key = pair.first;
const auto &value = pair.second;
const auto &key = it->first;
const auto &value = it->second;
size_t content_size = key.size() + value.size() + 1; // 1 for ':'
if (it != last_element) {
@@ -1012,7 +1011,7 @@ SPDLOG_INLINE void pattern_formatter::set_pattern(std::string pattern) {
SPDLOG_INLINE void pattern_formatter::need_localtime(bool need) { need_localtime_ = need; }
SPDLOG_INLINE std::tm pattern_formatter::get_time_(const details::log_msg &msg) {
SPDLOG_INLINE std::tm pattern_formatter::get_time_(const details::log_msg &msg) const {
if (pattern_time_type_ == pattern_time_type::local) {
return details::os::localtime(log_clock::to_time_t(msg.time));
}

View File

@@ -99,7 +99,7 @@ private:
std::vector<std::unique_ptr<details::flag_formatter>> formatters_;
custom_flags custom_handlers_;
std::tm get_time_(const details::log_msg &msg);
std::tm get_time_(const details::log_msg &msg) const;
template <typename Padder>
void handle_flag_(char flag, details::padding_info padding);

View File

@@ -88,8 +88,8 @@ public:
}
auto now = log_clock::now();
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
file_helper_.open(filename, truncate_);
const auto new_filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
file_helper_.open(new_filename, truncate_);
rotation_tp_ = next_rotation_tp_();
if (max_files_ > 0) {
@@ -107,8 +107,8 @@ protected:
auto time = msg.time;
bool should_rotate = time >= rotation_tp_;
if (should_rotate) {
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));
file_helper_.open(filename, truncate_);
const auto new_filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));
file_helper_.open(new_filename, truncate_);
rotation_tp_ = next_rotation_tp_();
}
memory_buf_t formatted;
@@ -131,11 +131,11 @@ private:
std::vector<filename_t> filenames;
auto now = log_clock::now();
while (filenames.size() < max_files_) {
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
if (!path_exists(filename)) {
const auto new_filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
if (!path_exists(new_filename)) {
break;
}
filenames.emplace_back(filename);
filenames.emplace_back(new_filename);
now -= std::chrono::hours(24);
}
for (auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter) {

View File

@@ -24,7 +24,7 @@ class dist_sink : public base_sink<Mutex> {
public:
dist_sink() = default;
explicit dist_sink(std::vector<std::shared_ptr<sink>> sinks)
: sinks_(sinks) {}
: sinks_(std::move(sinks)) {}
dist_sink(const dist_sink &) = delete;
dist_sink &operator=(const dist_sink &) = delete;

View File

@@ -43,6 +43,11 @@ public:
explicit dup_filter_sink(std::chrono::duration<Rep, Period> max_skip_duration)
: max_skip_duration_{max_skip_duration} {}
template <class Rep, class Period>
explicit dup_filter_sink(std::chrono::duration<Rep, Period> max_skip_duration, std::vector<std::shared_ptr<sink>> sinks)
: max_skip_duration_{max_skip_duration}
, dist_sink<Mutex>(std::move(sinks)) {}
protected:
std::chrono::microseconds max_skip_duration_;
log_clock::time_point last_msg_time_;
@@ -78,8 +83,8 @@ protected:
}
// return whether the log msg should be displayed (true) or skipped (false)
bool filter_(const details::log_msg &msg) {
auto filter_duration = msg.time - last_msg_time_;
bool filter_(const details::log_msg &msg) const {
const auto filter_duration = msg.time - last_msg_time_;
return (filter_duration > max_skip_duration_) || (msg.payload != last_msg_payload_);
}
};

View File

@@ -36,7 +36,7 @@ template <typename Mutex>
class udp_sink : public spdlog::sinks::base_sink<Mutex> {
public:
// host can be hostname or ip address
explicit udp_sink(udp_sink_config sink_config)
explicit udp_sink(const udp_sink_config& sink_config)
: client_{sink_config.server_host, sink_config.server_port} {}
~udp_sink() override = default;

View File

@@ -75,7 +75,7 @@ SPDLOG_API level::level_enum get_level();
SPDLOG_API void set_level(level::level_enum log_level);
// Determine whether the default logger should log messages with a certain level
SPDLOG_API bool should_log(level::level_enum lvl);
SPDLOG_API bool should_log(level::level_enum log_level);
// Set a global flush level
SPDLOG_API void flush_on(level::level_enum log_level);

View File

@@ -176,3 +176,33 @@ TEST_CASE("restore-to-default", "[cfg]") {
load_argv_levels(2, argv);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
}
TEST_CASE("uppercase-level-names", "[cfg]") {
spdlog::drop("l1");
spdlog::drop("l2");
#ifdef CATCH_PLATFORM_WINDOWS
_putenv_s("SPDLOG_LEVEL", "l1=DEBUG,INFO");
#else
setenv("SPDLOG_LEVEL", "l1=DEBUG,INFO", 1);
#endif
load_env_levels();
auto l1 = spdlog::create<test_sink_st>("l1");
auto l2 = spdlog::create<test_sink_st>("l2");
REQUIRE(l1->level() == spdlog::level::debug);
REQUIRE(l2->level() == spdlog::level::info);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
// Test with argv
spdlog::drop("l3");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=l3=WARN,ERROR"};
load_argv_levels(2, argv);
auto l3 = spdlog::create<test_sink_st>("l3");
REQUIRE(l3->level() == spdlog::level::warn);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::err);
// Reset to info
spdlog::set_level(spdlog::level::info);
}

View File

@@ -2,7 +2,11 @@
#include "includes.h"
using spdlog::memory_buf_t;
using spdlog::details::to_string_view;
SPDLOG_CONSTEXPR_FUNC spdlog::string_view_t to_string_view(const memory_buf_t &buf)
SPDLOG_NOEXCEPT {
return spdlog::string_view_t{buf.data(), buf.size()};
}
void test_pad2(int n, const char *expected) {
memory_buf_t buf;

View File

@@ -73,6 +73,17 @@ TEST_CASE("to_level_enum", "[convert_to_level_enum]") {
REQUIRE(spdlog::level::from_str("critical") == spdlog::level::critical);
REQUIRE(spdlog::level::from_str("off") == spdlog::level::off);
REQUIRE(spdlog::level::from_str("null") == spdlog::level::off);
REQUIRE(spdlog::level::from_str("TRACE") == spdlog::level::trace);
REQUIRE(spdlog::level::from_str("DEBUG") == spdlog::level::debug);
REQUIRE(spdlog::level::from_str("INFO") == spdlog::level::info);
REQUIRE(spdlog::level::from_str("WARNING") == spdlog::level::warn);
REQUIRE(spdlog::level::from_str("WARN") == spdlog::level::warn);
REQUIRE(spdlog::level::from_str("ERROR") == spdlog::level::err);
REQUIRE(spdlog::level::from_str("ERR") == spdlog::level::err);
REQUIRE(spdlog::level::from_str("CRITICAL") == spdlog::level::critical);
REQUIRE(spdlog::level::from_str("OFF") == spdlog::level::off);
REQUIRE(spdlog::level::from_str("TrAcE") == spdlog::level::trace);
REQUIRE(spdlog::level::from_str("DeBuG") == spdlog::level::debug);
}
TEST_CASE("periodic flush", "[periodic_flush]") {

View File

@@ -4,7 +4,11 @@
#include <chrono>
using spdlog::memory_buf_t;
using spdlog::details::to_string_view;
SPDLOG_CONSTEXPR_FUNC spdlog::string_view_t to_string_view(const memory_buf_t &buf)
SPDLOG_NOEXCEPT {
return spdlog::string_view_t{buf.data(), buf.size()};
}
// log to str and return it
template <typename... Args>

View File

@@ -76,9 +76,56 @@ public:
using spdlog::details::os::utc_minutes_offset;
/*
* POSIX 2024 defines three formats for the TZ environment variable,
*
* 1. Implementation defined format which always starts with a colon:
* ":characters".
* 2. A specifier which fully describes the timezone rule in format
* "stdoffset[dst[offset][,start[/time],end[/time]]]". Note the
* offset and start/end part could be omitted, in which case one hour
* is implied, or it's considered implementation-defined when changing
* to and from Daylight Saving Time occurs.
* 3. Geographical or special timezone from an implementation-defined
* timezone database.
*
* On POSIX-compilant systems, we prefer format 2, and explicitly specify the
* DST rules to avoid implementation-defined behavior.
*
* See also IEEE 1003.1-2024 8.3 Other Environment Variables.
*/
#ifndef _WIN32
/*
* Standard time is UTC-5 ("EST"), DST time is UTC-4 ("EDT"). DST is active
* from 2:00 on the 2nd Sunday in March, to 2:00 on 1st Sunday in November.
*/
#define EST5EDT "EST5EDT,M3.2.0,M11.1.0"
/*
* Standard time is UTC+2 ("IST"), DST time is UTC+3 ("IDT"). DST is active
* from 2:00 on following day of the 4th Thursday in March, to 2:00 on the
* last Sunday in October.
*/
#define IST_MINUS2_IDT "IST-2IDT,M3.4.4/26,M10.5.0"
#else
/*
* However, Windows doesn't follow the POSIX rules and only accept a TZ
* environment variable in format
*
* tzn [+|-]hh[:mm[:ss] ][dzn]
*
* thus we couldn't specify the DST rules. Luckily, Windows C runtime library
* assumes the United State's rules for implementing the calculation of DST,
* which is fine for our test cases.
*
* See also https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/tzset?view=msvc-170
*/
#define EST5EDT "EST5EDT"
#define IST_MINUS2_IDT "IST-2IDT"
#endif
TEST_CASE("UTC Offset - Western Hemisphere (USA - Standard Time)", "[timezone][west]") {
// EST5EDT: Eastern Standard Time (UTC-5)
ScopedTZ tz("EST5EDT");
ScopedTZ tz(EST5EDT);
// Jan 15th (Winter)
auto tm = make_tm(2023, 1, 15, 12, 0);
@@ -87,7 +134,7 @@ TEST_CASE("UTC Offset - Western Hemisphere (USA - Standard Time)", "[timezone][w
TEST_CASE("UTC Offset - Eastern Hemisphere (Europe/Israel - Standard Time)", "[timezone][east]") {
// IST-2IDT: Israel Standard Time (UTC+2)
ScopedTZ tz("IST-2IDT");
ScopedTZ tz(IST_MINUS2_IDT);
// Jan 15th (Winter)
auto tm = make_tm(2023, 1, 15, 12, 0);
@@ -115,14 +162,14 @@ TEST_CASE("UTC Offset - Non-Integer Hour Offsets (India)", "[timezone][partial]"
}
TEST_CASE("UTC Offset - Edge Case: Negative Offset Crossing Midnight", "[timezone][edge]") {
ScopedTZ tz("EST5EDT");
ScopedTZ tz(EST5EDT);
// Late night Dec 31st, 2023
auto tm = make_tm(2023, 12, 31, 23, 59);
REQUIRE(utc_minutes_offset(tm) == -300);
}
TEST_CASE("UTC Offset - Edge Case: Leap Year", "[timezone][edge]") {
ScopedTZ tz("EST5EDT");
ScopedTZ tz(EST5EDT);
// Feb 29, 2024 (Leap Day) - Winter
auto tm = make_tm(2024, 2, 29, 12, 0);
REQUIRE(utc_minutes_offset(tm) == -300);
@@ -137,10 +184,10 @@ TEST_CASE("UTC Offset - Edge Case: Invalid Date (Pre-Epoch)", "[timezone][edge]"
#else
// Unix mktime handles pre-1970 dates correctly.
// We expect the actual historical offset (EST was UTC-5 in 1960).
ScopedTZ tz("EST5EDT");
ScopedTZ tz(EST5EDT);
auto tm = make_tm(1960, 1, 1, 12, 0);
REQUIRE(utc_minutes_offset(tm) == -300);
#endif
}
#endif // !SPDLOG_NO_TZ_OFFSET
#endif // !SPDLOG_NO_TZ_OFFSET