mirror of
https://github.com/gabime/spdlog.git
synced 2026-04-10 11:34:29 +08:00
Compare commits
17 Commits
v1.17.0
...
copilot/un
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45b67eee66 | ||
|
|
1fbc60a5e7 | ||
|
|
0f7562a0f9 | ||
|
|
d5af52d903 | ||
|
|
1685e694c5 | ||
|
|
c49c7cf960 | ||
|
|
fc7e9c8721 | ||
|
|
566b2d1404 | ||
|
|
6c5d63291a | ||
|
|
472945ba48 | ||
|
|
687226d95d | ||
|
|
f2a9dec029 | ||
|
|
309204d53c | ||
|
|
1774e70082 | ||
|
|
d299603e45 | ||
|
|
33375433e0 | ||
|
|
6b240a892d |
@@ -199,7 +199,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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]") {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user