(third_party deps) fix #177, update bundled spdlog to 1.6.0

This commit is contained in:
Benjamin Sergeant
2020-04-11 13:31:39 -07:00
parent 2eb5c9480e
commit f1c106728b
109 changed files with 2792 additions and 689 deletions

View File

@ -32,7 +32,7 @@ SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg)
}
else
{
SPDLOG_THROW(spdlog_ex("async log: thread pool doesn't exist anymore"));
throw_spdlog_ex("async log: thread pool doesn't exist anymore");
}
}
@ -45,7 +45,7 @@ SPDLOG_INLINE void spdlog::async_logger::flush_()
}
else
{
SPDLOG_THROW(spdlog_ex("async flush: thread pool doesn't exist anymore"));
throw_spdlog_ex("async flush: thread pool doesn't exist anymore");
}
}

View File

@ -30,7 +30,7 @@ namespace details {
class thread_pool;
}
class async_logger final : public std::enable_shared_from_this<async_logger>, public logger
class SPDLOG_API async_logger final : public std::enable_shared_from_this<async_logger>, public logger
{
friend class details::thread_pool;

View File

@ -0,0 +1,45 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/cfg/helpers.h>
#include <spdlog/details/registry.h>
//
// Init log levels using each argv entry that starts with "SPDLOG_LEVEL="
//
// set all loggers to debug level:
// example.exe "SPDLOG_LEVEL=debug"
// set logger1 to trace level
// example.exe "SPDLOG_LEVEL=logger1=trace"
// turn off all logging except for logger1 and logger2:
// example.exe "SPDLOG_LEVEL=off,logger1=debug,logger2=info"
namespace spdlog {
namespace cfg {
// search for SPDLOG_LEVEL= in the args and use it to init the levels
void load_argv_levels(int argc, const char **argv)
{
const std::string spdlog_level_prefix = "SPDLOG_LEVEL=";
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());
auto levels = helpers::extract_levels(levels_string);
details::registry::instance().update_levels(std::move(levels));
}
}
}
void load_argv_levels(int argc, char **argv)
{
load_argv_levels(argc, const_cast<const char **>(argv));
}
} // namespace cfg
} // namespace spdlog

View File

@ -0,0 +1,36 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/cfg/helpers.h>
#include <spdlog/details/registry.h>
#include <spdlog/details/os.h>
//
// Init levels and patterns from env variables SPDLOG_LEVEL
// Inspired from Rust's "env_logger" crate (https://crates.io/crates/env_logger).
// Note - fallback to "info" level on unrecognized levels
//
// Examples:
//
// set global level to debug:
// export SPDLOG_LEVEL=debug
//
// turn off all logging except for logger1:
// export SPDLOG_LEVEL="off,logger1=debug"
//
// turn off all logging except for logger1 and logger2:
// export SPDLOG_LEVEL="off,logger1=debug,logger2=info"
namespace spdlog {
namespace cfg {
void load_env_levels()
{
auto env_val = details::os::getenv("SPDLOG_LEVEL");
auto levels = helpers::extract_levels(env_val);
details::registry::instance().update_levels(std::move(levels));
}
} // namespace cfg
} // namespace spdlog

View File

@ -0,0 +1,103 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#ifndef SPDLOG_HEADER_ONLY
#include <spdlog/cfg/helpers.h>
#endif
#include <spdlog/spdlog.h>
#include <spdlog/details/os.h>
#include <spdlog/details/registry.h>
#include <string>
#include <utility>
#include <sstream>
namespace spdlog {
namespace cfg {
namespace helpers {
// inplace convert to lowercase
inline std::string &to_lower_(std::string &str)
{
std::transform(
str.begin(), str.end(), str.begin(), [](char ch) { return static_cast<char>((ch >= 'A' && ch <= 'Z') ? ch + ('a' - 'A') : ch); });
return str;
}
// inplace trim spaces
inline std::string &trim_(std::string &str)
{
const char *spaces = " \n\r\t";
str.erase(str.find_last_not_of(spaces) + 1);
str.erase(0, str.find_first_not_of(spaces));
return str;
}
// return (name,value) trimmed pair from given "name=value" string.
// return empty string on missing parts
// "key=val" => ("key", "val")
// " key = val " => ("key", "val")
// "key=" => ("key", "")
// "val" => ("", "val")
inline std::pair<std::string, std::string> extract_kv_(char sep, const std::string &str)
{
auto n = str.find(sep);
std::string k, v;
if (n == std::string::npos)
{
v = str;
}
else
{
k = str.substr(0, n);
v = str.substr(n + 1);
}
return std::make_pair(trim_(k), trim_(v));
}
// return vector of key/value pairs from sequence of "K1=V1,K2=V2,.."
// "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...}
inline std::unordered_map<std::string, std::string> extract_key_vals_(const std::string &str)
{
std::string token;
std::istringstream token_stream(str);
std::unordered_map<std::string, std::string> rv{};
while (std::getline(token_stream, token, ','))
{
if (token.empty())
{
continue;
}
auto kv = extract_kv_('=', token);
rv[kv.first] = kv.second;
}
return rv;
}
SPDLOG_INLINE log_levels extract_levels(const std::string &input)
{
auto key_vals = extract_key_vals_(input);
log_levels rv;
for (auto &name_level : key_vals)
{
auto &logger_name = name_level.first;
auto level_name = to_lower_(name_level.second);
auto level = level::from_str(level_name);
// fallback to "info" if unrecognized level name
if (level == level::off && level_name != "off")
{
level = level::info;
}
rv.set(logger_name, level);
}
return rv;
}
} // namespace helpers
} // namespace cfg
} // namespace spdlog

View File

@ -0,0 +1,28 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/cfg/log_levels.h>
namespace spdlog {
namespace cfg {
namespace helpers {
//
// Init levels from given string
//
// Examples:
//
// set global level to debug: "debug"
// 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 log_levels extract_levels(const std::string &txt);
} // namespace helpers
} // namespace cfg
} // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY
#include "helpers-inl.h"
#endif // SPDLOG_HEADER_ONLY

View File

@ -0,0 +1,47 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/common.h>
#include <string>
#include <unordered_map>
namespace spdlog {
namespace cfg {
class log_levels
{
std::unordered_map<std::string, spdlog::level::level_enum> levels_;
spdlog::level::level_enum default_level_ = level::info;
public:
void set(const std::string &logger_name, level::level_enum lvl)
{
if (logger_name.empty())
{
default_level_ = lvl;
}
else
{
levels_[logger_name] = lvl;
}
}
void set_default(level::level_enum lvl)
{
default_level_ = lvl;
}
level::level_enum get(const std::string &logger_name)
{
auto it = levels_.find(logger_name);
return it != levels_.end() ? it->second : default_level_;
}
level::level_enum default_level()
{
return default_level_;
}
};
} // namespace cfg
} // namespace spdlog

View File

@ -34,6 +34,15 @@ SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG
}
level++;
}
// check also for "warn" and "err" before giving up..
if (name == "warn")
{
return level::warn;
}
if (name == "err")
{
return level::err;
}
return level::off;
}
} // namespace level
@ -54,4 +63,14 @@ SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT
return msg_.c_str();
}
SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno)
{
SPDLOG_THROW(spdlog_ex(msg, last_errno));
}
SPDLOG_INLINE void throw_spdlog_ex(std::string msg)
{
SPDLOG_THROW(spdlog_ex(std::move(msg)));
}
} // namespace spdlog

View File

@ -15,22 +15,20 @@
#include <type_traits>
#include <functional>
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX // prevent windows redefining min/max
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#endif //_WIN32
#ifdef SPDLOG_COMPILED_LIB
#undef SPDLOG_HEADER_ONLY
#if defined(_WIN32) && defined(SPDLOG_SHARED_LIB)
#ifdef spdlog_EXPORTS
#define SPDLOG_API __declspec(dllexport)
#else
#define SPDLOG_API __declspec(dllimport)
#endif
#else
#define SPDLOG_API
#endif
#define SPDLOG_INLINE
#else
#define SPDLOG_API
#define SPDLOG_HEADER_ONLY
#define SPDLOG_INLINE inline
#endif
@ -147,6 +145,7 @@ enum level_enum
err = SPDLOG_LEVEL_ERROR,
critical = SPDLOG_LEVEL_CRITICAL,
off = SPDLOG_LEVEL_OFF,
n_levels
};
#if !defined(SPDLOG_LEVEL_NAMES)
@ -164,9 +163,9 @@ enum level_enum
}
#endif
string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
SPDLOG_API string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
using level_hasher = std::hash<int>;
} // namespace level
@ -194,7 +193,7 @@ enum class pattern_time_type
//
// Log exception
//
class spdlog_ex : public std::exception
class SPDLOG_API spdlog_ex : public std::exception
{
public:
explicit spdlog_ex(std::string msg);
@ -205,6 +204,9 @@ private:
std::string msg_;
};
void throw_spdlog_ex(const std::string &msg, int last_errno);
void throw_spdlog_ex(std::string msg);
struct source_loc
{
SPDLOG_CONSTEXPR source_loc() = default;
@ -237,7 +239,6 @@ std::unique_ptr<T> make_unique(Args &&... args)
}
#endif
} // namespace details
} // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY

View File

@ -15,7 +15,7 @@
namespace spdlog {
namespace details {
class backtracer
class SPDLOG_API backtracer
{
mutable std::mutex mutex_;
std::atomic<bool> enabled_{false};

View File

@ -43,14 +43,14 @@ SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate)
details::os::sleep_for_millis(open_interval_);
}
SPDLOG_THROW(spdlog_ex("Failed opening file " + os::filename_to_str(filename_) + " for writing", errno));
throw_spdlog_ex("Failed opening file " + os::filename_to_str(filename_) + " for writing", errno);
}
SPDLOG_INLINE void file_helper::reopen(bool truncate)
{
if (filename_.empty())
{
SPDLOG_THROW(spdlog_ex("Failed re opening file - was not opened before"));
throw_spdlog_ex("Failed re opening file - was not opened before");
}
this->open(filename_, truncate);
}
@ -75,7 +75,7 @@ SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf)
auto data = buf.data();
if (std::fwrite(data, 1, msg_size, fd_) != msg_size)
{
SPDLOG_THROW(spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno));
throw_spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno);
}
}
@ -83,7 +83,7 @@ SPDLOG_INLINE size_t file_helper::size() const
{
if (fd_ == nullptr)
{
SPDLOG_THROW(spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_)));
throw_spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_));
}
return os::filesize(fd_);
}

View File

@ -13,7 +13,7 @@ namespace details {
// When failing to open a file, retry several times(5) with a delay interval(10 ms).
// Throw spdlog_ex exception on errors.
class file_helper
class SPDLOG_API file_helper
{
public:
explicit file_helper() = default;

View File

@ -20,10 +20,7 @@ inline spdlog::string_view_t to_string_view(const memory_buf_t &buf) SPDLOG_NOEX
inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest)
{
auto *buf_ptr = view.data();
if (buf_ptr != nullptr)
{
dest.append(buf_ptr, buf_ptr + view.size());
}
dest.append(buf_ptr, buf_ptr + view.size());
}
template<typename T>
@ -34,10 +31,10 @@ inline void append_int(T n, memory_buf_t &dest)
}
template<typename T>
inline unsigned count_digits(T n)
inline unsigned int count_digits(T n)
{
using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
return static_cast<unsigned>(fmt::internal::count_digits(static_cast<count_type>(n)));
return static_cast<unsigned int>(fmt::internal::count_digits(static_cast<count_type>(n)));
}
inline void pad2(int n, memory_buf_t &dest)
@ -66,11 +63,9 @@ template<typename T>
inline void pad_uint(T n, unsigned int width, memory_buf_t &dest)
{
static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
auto digits = count_digits(n);
if (width > digits)
for (auto digits = count_digits(n); digits < width; digits++)
{
const char *zeroes = "0000000000000000000";
dest.append(zeroes, zeroes + width - digits);
dest.push_back('0');
}
append_int(n, dest);
}
@ -78,7 +73,18 @@ inline void pad_uint(T n, unsigned int width, memory_buf_t &dest)
template<typename T>
inline void pad3(T n, memory_buf_t &dest)
{
pad_uint(n, 3, dest);
static_assert(std::is_unsigned<T>::value, "pad3 must get unsigned T");
if(n < 1000)
{
dest.push_back(static_cast<char>(n / 100 + '0'));
n = n % 100;
dest.push_back(static_cast<char>((n / 10) + '0'));
dest.push_back(static_cast<char>((n % 10) + '0'));
}
else
{
append_int(n, dest);
}
}
template<typename T>

View File

@ -8,7 +8,7 @@
namespace spdlog {
namespace details {
struct log_msg
struct SPDLOG_API log_msg
{
log_msg() = default;
log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);

View File

@ -11,7 +11,7 @@ namespace details {
// Extend log_msg with internal buffer to store its payload.
// THis is needed since log_msg holds string_views that points to stack data.
class log_msg_buffer : public log_msg
class SPDLOG_API log_msg_buffer : public log_msg
{
memory_buf_t buffer;
void update_string_views();

View File

@ -23,16 +23,9 @@
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX // prevent windows redefining min/max
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <io.h> // _get_osfhandle and _isatty support
#include <process.h> // _get_pid support
#include <windows.h>
#include <spdlog/details/windows_include.h>
#ifdef __MINGW32__
#include <share.h>
@ -126,23 +119,6 @@ SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT
return gmtime(now_t);
}
#ifdef SPDLOG_PREVENT_CHILD_FD
SPDLOG_INLINE void prevent_child_fd(FILE *f)
{
#ifdef _WIN32
auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(f)));
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
SPDLOG_THROW(spdlog_ex("SetHandleInformation failed", errno));
#else
auto fd = ::fileno(f);
if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
{
SPDLOG_THROW(spdlog_ex("fcntl with FD_CLOEXEC failed", errno));
}
#endif
}
#endif // SPDLOG_PREVENT_CHILD_FD
// fopen_s on non windows for writing
SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
{
@ -152,17 +128,35 @@ SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename
#else
*fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
#endif
#else // unix
*fp = ::fopen((filename.c_str()), mode.c_str());
#endif
#ifdef SPDLOG_PREVENT_CHILD_FD
// prevent child processes from inheriting log file descriptors
#if defined(SPDLOG_PREVENT_CHILD_FD)
if (*fp != nullptr)
{
prevent_child_fd(*fp);
auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp)));
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
{
::fclose(*fp);
*fp = nullptr;
}
}
#endif
#else // unix
#if defined(SPDLOG_PREVENT_CHILD_FD)
const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC;
const int fd = ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644));
if (fd == -1)
{
return false;
}
*fp = ::fdopen(fd, mode.c_str());
if (*fp == nullptr)
{
::close(fd);
}
#else
*fp = ::fopen((filename.c_str()), mode.c_str());
#endif
#endif
return *fp == nullptr;
}
@ -210,7 +204,7 @@ SPDLOG_INLINE size_t filesize(FILE *f)
{
if (f == nullptr)
{
SPDLOG_THROW(spdlog_ex("Failed getting file size. fd is null"));
throw_spdlog_ex("Failed getting file size. fd is null");
}
#if defined(_WIN32) && !defined(__CYGWIN__)
int fd = ::_fileno(f);
@ -251,7 +245,8 @@ SPDLOG_INLINE size_t filesize(FILE *f)
}
#endif
#endif
SPDLOG_THROW(spdlog_ex("Failed getting file size from fd", errno));
throw_spdlog_ex("Failed getting file size from fd", errno);
return 0; // will not be reached.
}
// Return utc offset in minutes or throw spdlog_ex on failure
@ -267,7 +262,7 @@ SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
auto rv = ::GetDynamicTimeZoneInformation(&tzinfo);
#endif
if (rv == TIME_ZONE_ID_INVALID)
SPDLOG_THROW(spdlog::spdlog_ex("Failed getting timezone info. ", errno));
throw_spdlog_ex("Failed getting timezone info. ", errno);
int offset = -tzinfo.Bias;
if (tm.tm_isdst)
@ -396,13 +391,13 @@ SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT
}
// Determine if the terminal supports colors
// Source: https://github.com/agauniyal/rang/
// Based on: https://github.com/agauniyal/rang/
SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
{
#ifdef _WIN32
return true;
#else
static constexpr std::array<const char *, 14> Terms = {
static constexpr std::array<const char *, 14> terms = {
{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"}};
const char *env_p = std::getenv("TERM");
@ -412,7 +407,7 @@ SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
}
static const bool result =
std::any_of(std::begin(Terms), std::end(Terms), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
std::any_of(terms.begin(), terms.end(), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
return result;
#endif
}
@ -434,7 +429,7 @@ SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
{
if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()))
{
SPDLOG_THROW(spdlog::spdlog_ex("UTF-16 string is too big to be converted to UTF-8"));
throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
}
int wstr_size = static_cast<int>(wstr.size());
@ -462,7 +457,7 @@ SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
}
}
SPDLOG_THROW(spdlog::spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError())));
throw_spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
}
#endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
@ -536,6 +531,24 @@ SPDLOG_INLINE filename_t dir_name(filename_t path)
return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
}
std::string SPDLOG_INLINE getenv(const char *field)
{
#if defined(_MSC_VER)
#if defined(__cplusplus_winrt)
return std::string{}; // not supported under uwp
#else
size_t len = 0;
char buf[128];
bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
return ok ? buf : std::string{};
#endif
#else // revert to getenv
char *buf = ::getenv(field);
return buf ? buf : std::string{};
#endif
}
} // namespace os
} // namespace details
} // namespace spdlog

View File

@ -10,15 +10,15 @@ namespace spdlog {
namespace details {
namespace os {
spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT;
SPDLOG_API spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT;
std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
SPDLOG_API std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
std::tm localtime() SPDLOG_NOEXCEPT;
SPDLOG_API std::tm localtime() SPDLOG_NOEXCEPT;
std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
SPDLOG_API std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
std::tm gmtime() SPDLOG_NOEXCEPT;
SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT;
// eol definition
#if !defined(SPDLOG_EOL)
@ -38,54 +38,50 @@ static const char folder_sep = '\\';
SPDLOG_CONSTEXPR static const char folder_sep = '/';
#endif
#ifdef SPDLOG_PREVENT_CHILD_FD
void prevent_child_fd(FILE *f);
#endif
// fopen_s on non windows for writing
bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
// Remove filename. return 0 on success
int remove(const filename_t &filename) SPDLOG_NOEXCEPT;
SPDLOG_API int remove(const filename_t &filename) SPDLOG_NOEXCEPT;
// Remove file if exists. return 0 on success
// Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread)
int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
SPDLOG_API int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT;
SPDLOG_API int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT;
// Return if file exists.
bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
SPDLOG_API bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
// Return file size according to open FILE* object
size_t filesize(FILE *f);
SPDLOG_API size_t filesize(FILE *f);
// Return utc offset in minutes or throw spdlog_ex on failure
int utc_minutes_offset(const std::tm &tm = details::os::localtime());
SPDLOG_API int utc_minutes_offset(const std::tm &tm = details::os::localtime());
// Return current thread id as size_t
// It exists because the std::this_thread::get_id() is much slower(especially
// under VS 2013)
size_t _thread_id() SPDLOG_NOEXCEPT;
SPDLOG_API size_t _thread_id() SPDLOG_NOEXCEPT;
// Return current thread id as size_t (from thread local storage)
size_t thread_id() SPDLOG_NOEXCEPT;
SPDLOG_API size_t thread_id() SPDLOG_NOEXCEPT;
// This is avoid msvc issue in sleep_for that happens if the clock changes.
// See https://github.com/gabime/spdlog/issues/609
void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT;
SPDLOG_API void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT;
std::string filename_to_str(const filename_t &filename);
SPDLOG_API std::string filename_to_str(const filename_t &filename);
int pid() SPDLOG_NOEXCEPT;
SPDLOG_API int pid() SPDLOG_NOEXCEPT;
// Determine if the terminal supports colors
// Source: https://github.com/agauniyal/rang/
bool is_color_terminal() SPDLOG_NOEXCEPT;
SPDLOG_API bool is_color_terminal() SPDLOG_NOEXCEPT;
// Determine if the terminal attached
// Source: https://github.com/agauniyal/rang/
bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
SPDLOG_API bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
@ -96,11 +92,15 @@ void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
// "abc/" => "abc"
// "abc" => ""
// "abc///" => "abc//"
filename_t dir_name(filename_t path);
SPDLOG_API filename_t dir_name(filename_t path);
// Create a dir from the given path.
// Return true if succeeded or if this dir already exists.
bool create_dir(filename_t path);
SPDLOG_API bool create_dir(filename_t path);
// non thread safe, cross platform getenv/getenv_s
// return empty string if field not found
SPDLOG_API std::string getenv(const char *field);
} // namespace os
} // namespace details

View File

@ -17,7 +17,7 @@
namespace spdlog {
namespace details {
class periodic_worker
class SPDLOG_API periodic_worker
{
public:
periodic_worker(const std::function<void()> &callback_fun, std::chrono::seconds interval);

View File

@ -10,7 +10,7 @@
#include <spdlog/common.h>
#include <spdlog/details/periodic_worker.h>
#include <spdlog/logger.h>
#include <spdlog/details/pattern_formatter.h>
#include <spdlog/pattern_formatter.h>
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
// support for the default stdout color logger
@ -48,6 +48,9 @@ SPDLOG_INLINE registry::registry()
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
}
SPDLOG_INLINE registry::~registry() = default;
SPDLOG_INLINE void registry::register_logger(std::shared_ptr<logger> new_logger)
{
std::lock_guard<std::mutex> lock(logger_map_mutex_);
@ -64,7 +67,7 @@ SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logge
new_logger->set_error_handler(err_handler_);
}
new_logger->set_level(level_);
new_logger->set_level(levels_.get(new_logger->name()));
new_logger->flush_on(flush_level_);
if (backtrace_n_messages_ > 0)
@ -168,7 +171,7 @@ SPDLOG_INLINE void registry::set_level(level::level_enum log_level)
{
l.second->set_level(log_level);
}
level_ = log_level;
levels_.set_default(log_level);
}
SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
@ -184,7 +187,7 @@ SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
SPDLOG_INLINE void registry::flush_every(std::chrono::seconds interval)
{
std::lock_guard<std::mutex> lock(flusher_mutex_);
std::function<void()> clbk = std::bind(&registry::flush_all, this);
auto clbk = [this]() { this->flush_all(); };
periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval);
}
@ -260,6 +263,17 @@ SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registrat
automatic_registration_ = automatic_registration;
}
SPDLOG_INLINE void registry::update_levels(cfg::log_levels levels)
{
std::lock_guard<std::mutex> lock(logger_map_mutex_);
levels_ = std::move(levels);
for (auto &l : loggers_)
{
auto &logger = l.second;
logger->set_level(levels_.get(logger->name()));
}
}
SPDLOG_INLINE registry &registry::instance()
{
static registry s_instance;
@ -270,7 +284,7 @@ SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name)
{
if (loggers_.find(logger_name) != loggers_.end())
{
SPDLOG_THROW(spdlog_ex("logger with name '" + logger_name + "' already exists"));
throw_spdlog_ex("logger with name '" + logger_name + "' already exists");
}
}
@ -280,5 +294,6 @@ SPDLOG_INLINE void registry::register_logger_(std::shared_ptr<logger> new_logger
throw_if_exists_(logger_name);
loggers_[logger_name] = std::move(new_logger);
}
} // namespace details
} // namespace spdlog

View File

@ -9,6 +9,7 @@
// This class is thread safe
#include <spdlog/common.h>
#include <spdlog/cfg/log_levels.h>
#include <chrono>
#include <functional>
@ -24,7 +25,7 @@ namespace details {
class thread_pool;
class periodic_worker;
class registry
class SPDLOG_API registry
{
public:
registry(const registry &) = delete;
@ -79,19 +80,21 @@ public:
void set_automatic_registration(bool automatic_registration);
void update_levels(cfg::log_levels levels);
static registry &instance();
private:
registry();
~registry() = default;
~registry();
void throw_if_exists_(const std::string &logger_name);
void register_logger_(std::shared_ptr<logger> new_logger);
std::mutex logger_map_mutex_, flusher_mutex_;
std::recursive_mutex tp_mutex_;
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
cfg::log_levels levels_;
std::unique_ptr<formatter> formatter_;
level::level_enum level_ = level::info;
level::level_enum flush_level_ = level::off;
void (*err_handler_)(const std::string &msg);
std::shared_ptr<thread_pool> tp_;

View File

@ -0,0 +1,175 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#define WIN32_LEAN_AND_MEAN
// tcp client helper
#include <spdlog/common.h>
#include <spdlog/details/os.h>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "Mswsock.lib")
#pragma comment(lib, "AdvApi32.lib")
namespace spdlog {
namespace details {
class tcp_client
{
SOCKET socket_ = INVALID_SOCKET;
static bool winsock_initialized_()
{
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET)
{
return false;
}
else
{
closesocket(s);
return true;
}
}
static void init_winsock_()
{
WSADATA wsaData;
auto rv = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (rv != 0)
{
throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
}
}
static void throw_winsock_error_(const std::string &msg, int last_error)
{
char buf[512];
::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(char)), NULL);
throw_spdlog_ex(fmt::format("tcp_sink - {}: {}", msg, buf));
}
public:
bool is_connected() const
{
return socket_ != INVALID_SOCKET;
}
void close()
{
::closesocket(socket_);
socket_ = INVALID_SOCKET;
WSACleanup();
}
SOCKET fd() const
{
return socket_;
}
~tcp_client()
{
close();
}
// try to connect or throw on failure
void connect(const std::string &host, int port)
{
// initialize winsock if needed
if (!winsock_initialized_())
{
init_winsock_();
}
if (is_connected())
{
close();
}
struct addrinfo hints
{};
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; // IPv4
hints.ai_socktype = SOCK_STREAM; // TCP
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
hints.ai_protocol = 0;
auto port_str = std::to_string(port);
struct addrinfo *addrinfo_result;
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
int last_error = 0;
if (rv != 0)
{
last_error = ::WSAGetLastError();
WSACleanup();
throw_winsock_error_("getaddrinfo failed", last_error);
}
// Try each address until we successfully connect(2).
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
{
socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (socket_ == INVALID_SOCKET)
{
last_error = ::WSAGetLastError();
WSACleanup();
continue;
}
if (::connect(socket_, rp->ai_addr, (int)rp->ai_addrlen) == 0)
{
break;
}
else
{
last_error = ::WSAGetLastError();
close();
}
}
::freeaddrinfo(addrinfo_result);
if (socket_ == INVALID_SOCKET)
{
WSACleanup();
throw_winsock_error_("connect failed", last_error);
}
// set TCP_NODELAY
int enable_flag = 1;
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, (char *)&enable_flag, sizeof(enable_flag));
}
// Send exactly n_bytes of the given data.
// On error close the connection and throw.
void send(const char *data, size_t n_bytes)
{
size_t bytes_sent = 0;
while (bytes_sent < n_bytes)
{
const int send_flags = 0;
auto write_result = ::send(socket_, data + bytes_sent, (int)(n_bytes - bytes_sent), send_flags);
if (write_result == SOCKET_ERROR)
{
int last_error = ::WSAGetLastError();
close();
throw_winsock_error_("send failed", last_error);
}
if (write_result == 0) // (probably should not happen but in any case..)
{
break;
}
bytes_sent += static_cast<size_t>(write_result);
}
}
};
} // namespace details
} // namespace spdlog

View File

@ -0,0 +1,145 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#ifdef _WIN32
#error include tcp_client-windows.h instead
#endif
// tcp client helper
#include <spdlog/common.h>
#include <spdlog/details/os.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <string>
namespace spdlog {
namespace details {
class tcp_client
{
int socket_ = -1;
public:
bool is_connected() const
{
return socket_ != -1;
}
void close()
{
if (is_connected())
{
::close(socket_);
socket_ = -1;
}
}
int fd() const
{
return socket_;
}
~tcp_client()
{
close();
}
// try to connect or throw on failure
void connect(const std::string &host, int port)
{
close();
struct addrinfo hints
{};
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET; // IPv4
hints.ai_socktype = SOCK_STREAM; // TCP
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
hints.ai_protocol = 0;
auto port_str = std::to_string(port);
struct addrinfo *addrinfo_result;
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
if (rv != 0)
{
auto msg = fmt::format("::getaddrinfo failed: {}", gai_strerror(rv));
throw_spdlog_ex(msg);
}
// Try each address until we successfully connect(2).
int last_errno = 0;
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
{
int const flags = SOCK_CLOEXEC;
socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
if (socket_ == -1)
{
last_errno = errno;
continue;
}
rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen);
if (rv == 0)
{
break;
}
else
{
last_errno = errno;
::close(socket_);
socket_ = -1;
}
}
::freeaddrinfo(addrinfo_result);
if (socket_ == -1)
{
throw_spdlog_ex("::connect failed", last_errno);
}
// set TCP_NODELAY
int enable_flag = 1;
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, (char *)&enable_flag, sizeof(enable_flag));
// prevent sigpipe on systems where MSG_NOSIGNAL is not available
#if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
::setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, (char *)&enable_flag, sizeof(enable_flag));
#endif
#if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
#error "tcp_sink would raise SIGPIPE since niether SO_NOSIGPIPE nor MSG_NOSIGNAL are available"
#endif
}
// Send exactly n_bytes of the given data.
// On error close the connection and throw.
void send(const char *data, size_t n_bytes)
{
size_t bytes_sent = 0;
while (bytes_sent < n_bytes)
{
#if defined(MSG_NOSIGNAL)
const int send_flags = MSG_NOSIGNAL;
#else
const int send_flags = 0;
#endif
auto write_result = ::send(socket_, data + bytes_sent, n_bytes - bytes_sent, send_flags);
if (write_result < 0)
{
close();
throw_spdlog_ex("write(2) failed", errno);
}
if (write_result == 0) // (probably should not happen but in any case..)
{
break;
}
bytes_sent += static_cast<size_t>(write_result);
}
}
};
} // namespace details
} // namespace spdlog

View File

@ -18,8 +18,8 @@ SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std
{
if (threads_n == 0 || threads_n > 1000)
{
SPDLOG_THROW(spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid "
"range is 1-1000)"));
throw_spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid "
"range is 1-1000)");
}
for (size_t i = 0; i < threads_n; i++)
{
@ -113,7 +113,7 @@ bool SPDLOG_INLINE thread_pool::process_next_msg_()
}
default: {
assert(false && "Unexpected async_msg_type");
assert(false);
}
}

View File

@ -79,7 +79,7 @@ struct async_msg : log_msg_buffer
{}
};
class thread_pool
class SPDLOG_API thread_pool
{
public:
using item_type = async_msg;

View File

@ -0,0 +1,11 @@
#pragma once
#ifndef NOMINMAX
#define NOMINMAX // prevent windows redefining min/max
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>

View File

@ -5,6 +5,8 @@
#pragma once
#include <cctype>
//
// Support for logging binary data as hex
// format flags:
@ -12,6 +14,7 @@
// {:s} - don't separate each byte with space.
// {:p} - don't print the position on each line start.
// {:n} - don't split the output to lines.
// {:a} - show ASCII if :n is not set
//
// Examples:
@ -20,17 +23,19 @@
// logger->info("Some buffer {}", spdlog::to_hex(v));
// char buf[128];
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf), 16));
namespace spdlog {
namespace details {
template<typename It>
class bytes_range
class dump_info
{
public:
bytes_range(It range_begin, It range_end)
dump_info(It range_begin, It range_end, size_t size_per_line)
: begin_(range_begin)
, end_(range_end)
, size_per_line_(size_per_line)
{}
It begin() const
@ -41,26 +46,31 @@ public:
{
return end_;
}
size_t size_per_line() const
{
return size_per_line_;
}
private:
It begin_, end_;
size_t size_per_line_;
};
} // namespace details
// create a bytes_range that wraps the given container
// create a dump_info that wraps the given container
template<typename Container>
inline details::bytes_range<typename Container::const_iterator> to_hex(const Container &container)
inline details::dump_info<typename Container::const_iterator> to_hex(const Container &container, size_t size_per_line = 32)
{
static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
using Iter = typename Container::const_iterator;
return details::bytes_range<Iter>(std::begin(container), std::end(container));
return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
}
// create bytes_range from ranges
// create dump_info from ranges
template<typename It>
inline details::bytes_range<It> to_hex(const It range_begin, const It range_end)
inline details::dump_info<It> to_hex(const It range_begin, const It range_end, size_t size_per_line = 32)
{
return details::bytes_range<It>(range_begin, range_end);
return details::dump_info<It>(range_begin, range_end, size_per_line);
}
} // namespace spdlog
@ -68,15 +78,14 @@ inline details::bytes_range<It> to_hex(const It range_begin, const It range_end)
namespace fmt {
template<typename T>
struct formatter<spdlog::details::bytes_range<T>>
struct formatter<spdlog::details::dump_info<T>>
{
const std::size_t line_size = 100;
const char delimiter = ' ';
bool put_newlines = true;
bool put_delimiters = true;
bool use_uppercase = false;
bool put_positions = true; // position on start of each line
bool show_ascii = false;
// parse the format string flags
template<typename ParseContext>
@ -98,6 +107,13 @@ struct formatter<spdlog::details::bytes_range<T>>
break;
case 'n':
put_newlines = false;
show_ascii = false;
break;
case 'a':
if (put_newlines)
{
show_ascii = true;
}
break;
}
@ -108,53 +124,83 @@ struct formatter<spdlog::details::bytes_range<T>>
// format the given bytes range as hex
template<typename FormatContext, typename Container>
auto format(const spdlog::details::bytes_range<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
auto format(const spdlog::details::dump_info<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
{
SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
std::size_t pos = 0;
std::size_t column = line_size;
#if FMT_VERSION < 60000
auto inserter = ctx.begin();
#else
auto inserter = ctx.out();
#endif
for (auto &item : the_range)
int size_per_line = static_cast<int>(the_range.size_per_line());
auto start_of_line = the_range.begin();
for (auto i = the_range.begin(); i != the_range.end(); i++)
{
auto ch = static_cast<unsigned char>(item);
pos++;
auto ch = static_cast<unsigned char>(*i);
if (put_newlines && column >= line_size)
if (put_newlines && (i == the_range.begin() || i - start_of_line >= size_per_line))
{
column = put_newline(inserter, pos);
if (show_ascii && i != the_range.begin())
{
*inserter++ = delimiter;
*inserter++ = delimiter;
for (auto j = start_of_line; j < i; j++)
{
auto pc = static_cast<unsigned char>(*j);
*inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
}
}
put_newline(inserter, static_cast<size_t>(i - the_range.begin()));
// put first byte without delimiter in front of it
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
*inserter++ = hex_chars[ch & 0x0f];
column += 2;
start_of_line = i;
continue;
}
if (put_delimiters)
{
*inserter++ = delimiter;
++column;
}
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
*inserter++ = hex_chars[ch & 0x0f];
column += 2;
}
if (show_ascii) // add ascii to last line
{
if (the_range.end() - the_range.begin() > size_per_line)
{
auto blank_num = size_per_line - (the_range.end() - start_of_line);
while (blank_num-- > 0)
{
*inserter++ = delimiter;
*inserter++ = delimiter;
if (put_delimiters)
{
*inserter++ = delimiter;
}
}
}
*inserter++ = delimiter;
*inserter++ = delimiter;
for (auto j = start_of_line; j != the_range.end(); j++)
{
auto pc = static_cast<unsigned char>(*j);
*inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
}
}
return inserter;
}
// put newline(and position header)
// return the next column
template<typename It>
std::size_t put_newline(It inserter, std::size_t pos)
void put_newline(It inserter, std::size_t pos)
{
#ifdef _WIN32
*inserter++ = '\r';
@ -163,12 +209,7 @@ struct formatter<spdlog::details::bytes_range<T>>
if (put_positions)
{
fmt::format_to(inserter, "{:<04X}: ", pos - 1);
return 7;
}
else
{
return 1;
fmt::format_to(inserter, "{:<04X}: ", pos);
}
}
};

View File

@ -696,7 +696,7 @@ inline int to_nonnegative_int(T value, int upper) {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline T mod(T x, int y) {
return x % y;
return x % static_cast<T>(y);
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
inline T mod(T x, int y) {
@ -793,7 +793,10 @@ struct chrono_formatter {
explicit chrono_formatter(FormatContext& ctx, OutputIt o,
std::chrono::duration<Rep, Period> d)
: context(ctx), out(o), val(d.count()), negative(false) {
: context(ctx),
out(o),
val(static_cast<rep>(d.count())),
negative(false) {
if (d.count() < 0) {
val = 0 - val;
negative = true;
@ -1023,8 +1026,8 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
void on_fill(Char fill) { f.specs.fill[0] = fill; }
void on_align(align_t align) { f.specs.align = align; }
void on_width(unsigned width) { f.specs.width = width; }
void on_precision(unsigned _precision) { f.precision = _precision; }
void on_width(int width) { f.specs.width = width; }
void on_precision(int _precision) { f.precision = _precision; }
void end_precision() {}
template <typename Id> void on_dynamic_width(Id arg_id) {

View File

@ -26,11 +26,11 @@ template <typename Char> struct format_part {
kind part_kind;
union value {
unsigned arg_index;
int arg_index;
basic_string_view<Char> str;
replacement repl;
FMT_CONSTEXPR value(unsigned index = 0) : arg_index(index) {}
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {}
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
FMT_CONSTEXPR value(replacement r) : repl(r) {}
} val;
@ -40,7 +40,7 @@ template <typename Char> struct format_part {
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
: part_kind(k), val(v) {}
static FMT_CONSTEXPR format_part make_arg_index(unsigned index) {
static FMT_CONSTEXPR format_part make_arg_index(int index) {
return format_part(kind::arg_index, index);
}
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
@ -62,7 +62,7 @@ template <typename Char> struct part_counter {
}
FMT_CONSTEXPR void on_arg_id() { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(unsigned) { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(int) { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
@ -119,7 +119,7 @@ class format_string_compiler : public error_handler {
part_ = part::make_arg_index(parse_context_.next_arg_id());
}
FMT_CONSTEXPR void on_arg_id(unsigned id) {
FMT_CONSTEXPR void on_arg_id(int id) {
parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id);
}
@ -512,8 +512,6 @@ template <typename CompiledFormat, typename... Args,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using range = buffer_range<Char>;
using context = buffer_context<Char>;
cf.format(std::back_inserter(buffer), args...);
return to_string(buffer);
}

View File

@ -15,7 +15,7 @@
#include <type_traits>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 60101
#define FMT_VERSION 60102
#ifdef __has_feature
# define FMT_HAS_FEATURE(x) __has_feature(x)
@ -878,7 +878,7 @@ template <typename Context> struct arg_mapper {
FMT_ENABLE_IF(
std::is_constructible<std_string_view<char_type>, T>::value &&
!std::is_constructible<basic_string_view<char_type>, T>::value &&
!is_string<T>::value)>
!is_string<T>::value && !has_formatter<T, Context>::value)>
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
return std_string_view<char_type>(val);
}
@ -911,12 +911,14 @@ template <typename Context> struct arg_mapper {
map(static_cast<typename std::underlying_type<T>::type>(val))) {
return map(static_cast<typename std::underlying_type<T>::type>(val));
}
template <typename T,
FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
!std::is_constructible<basic_string_view<char_type>,
T>::value &&
(has_formatter<T, Context>::value ||
has_fallback_formatter<T, Context>::value))>
template <
typename T,
FMT_ENABLE_IF(
!is_string<T>::value && !is_char<T>::value &&
!std::is_constructible<basic_string_view<char_type>, T>::value &&
(has_formatter<T, Context>::value ||
(has_fallback_formatter<T, Context>::value &&
!std::is_constructible<std_string_view<char_type>, T>::value)))>
FMT_CONSTEXPR const T& map(const T& val) {
return val;
}
@ -1283,7 +1285,7 @@ template <typename Context> class basic_format_args {
*/
template <typename... Args>
basic_format_args(const format_arg_store<Context, Args...>& store)
: types_(static_cast<unsigned long long>(store.types)) {
: types_(store.types) {
set_data(store.data_);
}

View File

@ -69,7 +69,8 @@
# define FMT_HAS_BUILTIN(x) 0
#endif
#if FMT_HAS_CPP_ATTRIBUTE(fallthrough) >= 201603 && __cplusplus >= 201703
#if FMT_HAS_CPP_ATTRIBUTE(fallthrough) && \
(__cplusplus >= 201703 || FMT_GCC_VERSION != 0)
# define FMT_FALLTHROUGH [[fallthrough]]
#else
# define FMT_FALLTHROUGH
@ -801,60 +802,6 @@ template <> int count_digits<4>(internal::fallback_uintptr n);
# define FMT_ALWAYS_INLINE
#endif
// Computes g = floor(log10(n)) and calls h.on<g>(n);
template <typename Handler> FMT_ALWAYS_INLINE char* lg(uint32_t n, Handler h) {
return n < 100 ? n < 10 ? h.template on<0>(n) : h.template on<1>(n)
: n < 1000000
? n < 10000 ? n < 1000 ? h.template on<2>(n)
: h.template on<3>(n)
: n < 100000 ? h.template on<4>(n)
: h.template on<5>(n)
: n < 100000000 ? n < 10000000 ? h.template on<6>(n)
: h.template on<7>(n)
: n < 1000000000 ? h.template on<8>(n)
: h.template on<9>(n);
}
// An lg handler that formats a decimal number.
// Usage: lg(n, decimal_formatter(buffer));
class decimal_formatter {
private:
char* buffer_;
void write_pair(unsigned N, uint32_t index) {
std::memcpy(buffer_ + N, data::digits + index * 2, 2);
}
public:
explicit decimal_formatter(char* buf) : buffer_(buf) {}
template <unsigned N> char* on(uint32_t u) {
if (N == 0) {
*buffer_ = static_cast<char>(u) + '0';
} else if (N == 1) {
write_pair(0, u);
} else {
// The idea of using 4.32 fixed-point numbers is based on
// https://github.com/jeaiii/itoa
unsigned n = N - 1;
unsigned a = n / 5 * n * 53 / 16;
uint64_t t =
((1ULL << (32 + a)) / data::zero_or_powers_of_10_32[n] + 1 - n / 9);
t = ((t * u) >> a) + n / 5 * 4;
write_pair(0, t >> 32);
for (unsigned i = 2; i < N; i += 2) {
t = 100ULL * static_cast<uint32_t>(t);
write_pair(i, t >> 32);
}
if (N % 2 == 0) {
buffer_[N] =
static_cast<char>((10ULL * static_cast<uint32_t>(t)) >> 32) + '0';
}
}
return buffer_ += N + 1;
}
};
#ifdef FMT_BUILTIN_CLZ
// Optional version of count_digits for better performance on 32-bit platforms.
inline int count_digits(uint32_t n) {
@ -2620,7 +2567,7 @@ class format_string_checker {
public:
explicit FMT_CONSTEXPR format_string_checker(
basic_string_view<Char> format_str, ErrorHandler eh)
: arg_id_(max_value<unsigned>()),
: arg_id_(-1),
context_(format_str, eh),
parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
@ -2661,7 +2608,7 @@ class format_string_checker {
// Format specifier parsing function.
using parse_func = const Char* (*)(parse_context_type&);
unsigned arg_id_;
int arg_id_;
parse_context_type context_;
parse_func parse_funcs_[num_args > 0 ? num_args : 1];
};

View File

@ -333,12 +333,12 @@ template <typename OutputIt, typename Char> class basic_printf_context {
static void parse_flags(format_specs& specs, const Char*& it,
const Char* end);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
format_arg get_arg(unsigned arg_index = internal::max_value<unsigned>());
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
format_arg get_arg(int arg_index = -1);
// Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char*& it, const Char* end, format_specs& specs);
int parse_header(const Char*& it, const Char* end, format_specs& specs);
public:
/**
@ -355,7 +355,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
format_arg arg(unsigned id) const { return args_.get(id); }
format_arg arg(int id) const { return args_.get(id); }
basic_format_parse_context<Char>& parse_context() { return parse_ctx_; }
@ -397,8 +397,8 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
template <typename OutputIt, typename Char>
typename basic_printf_context<OutputIt, Char>::format_arg
basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
if (arg_index == internal::max_value<unsigned>())
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
if (arg_index < 0)
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
@ -406,15 +406,15 @@ basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
}
template <typename OutputIt, typename Char>
unsigned basic_printf_context<OutputIt, Char>::parse_header(
int basic_printf_context<OutputIt, Char>::parse_header(
const Char*& it, const Char* end, format_specs& specs) {
unsigned arg_index = internal::max_value<unsigned>();
int arg_index = -1;
char_type c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
internal::error_handler eh;
unsigned value = parse_nonnegative_int(it, end, eh);
int value = parse_nonnegative_int(it, end, eh);
if (it != end && *it == '$') { // value is an argument index
++it;
arg_index = value;
@ -436,8 +436,8 @@ unsigned basic_printf_context<OutputIt, Char>::parse_header(
specs.width = parse_nonnegative_int(it, end, eh);
} else if (*it == '*') {
++it;
specs.width = visit_format_arg(
internal::printf_width_handler<char_type>(specs), get_arg());
specs.width = static_cast<int>(visit_format_arg(
internal::printf_width_handler<char_type>(specs), get_arg()));
}
}
return arg_index;
@ -464,7 +464,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
specs.align = align::right;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(it, end, specs);
int arg_index = parse_header(it, end, specs);
if (arg_index == 0) on_error("argument index out of range");
// Parse precision.
@ -473,11 +473,11 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
c = it != end ? *it : 0;
if ('0' <= c && c <= '9') {
internal::error_handler eh;
specs.precision = static_cast<int>(parse_nonnegative_int(it, end, eh));
specs.precision = parse_nonnegative_int(it, end, eh);
} else if (c == '*') {
++it;
specs.precision =
visit_format_arg(internal::printf_precision_handler(), get_arg());
static_cast<int>(visit_format_arg(internal::printf_precision_handler(), get_arg()));
} else {
specs.precision = 0;
}

View File

@ -10,30 +10,16 @@
// By default spdlog include its own copy.
//
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#pragma GCC diagnostic ignored "-Wsign-conversion"
#endif // __GNUC__ || __clang__
#if !defined(SPDLOG_FMT_EXTERNAL)
#ifdef SPDLOG_HEADER_ONLY
#ifndef FMT_HEADER_ONLY
#if !defined(SPDLOG_COMPILED_LIB) && !defined(FMT_HEADER_ONLY)
#define FMT_HEADER_ONLY
#endif
#endif
#ifndef FMT_USE_WINDOWS_H
#define FMT_USE_WINDOWS_H 0
#endif
#include "bundled/core.h"
#include "bundled/format.h"
#include <spdlog/fmt/bundled/core.h>
#include <spdlog/fmt/bundled/format.h>
#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib
#include <fmt/core.h>
#include <fmt/format.h>
#endif
// pop warnings supressions
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
#endif

View File

@ -7,12 +7,14 @@
//
// include bundled or external copy of fmtlib's ostream support
//
#if !defined(SPDLOG_FMT_EXTERNAL)
#ifdef SPDLOG_HEADER_ONLY
#ifndef FMT_HEADER_ONLY
#define FMT_HEADER_ONLY
#endif
#include "bundled/ostream.h"
#include "fmt.h"
#endif
#include <spdlog/fmt/bundled/ostream.h>
#else
#include <fmt/ostream.h>
#endif

14
third_party/spdlog/include/spdlog/fwd.h vendored Normal file
View File

@ -0,0 +1,14 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
namespace spdlog {
class logger;
class formatter;
namespace sinks {
class sink;
}
} // namespace spdlog

View File

@ -9,7 +9,7 @@
#include <spdlog/sinks/sink.h>
#include <spdlog/details/backtracer.h>
#include <spdlog/details/pattern_formatter.h>
#include <spdlog/pattern_formatter.h>
#include <cstdio>
@ -89,6 +89,7 @@ SPDLOG_INLINE void logger::set_formatter(std::unique_ptr<formatter> f)
{
// last element - we can be move it.
(*it)->set_formatter(std::move(f));
break; // to prevent clang-tidy warning
}
else
{

View File

@ -39,7 +39,7 @@
namespace spdlog {
class logger
class SPDLOG_API logger
{
public:
// Empty logger
@ -143,6 +143,11 @@ public:
// T can be statically converted to string_view
template<class T, typename std::enable_if<std::is_convertible<const T &, spdlog::string_view_t>::value, T>::type * = nullptr>
void log(source_loc loc, level::level_enum lvl, const T &msg)
{
log(loc, lvl, string_view_t{msg});
}
void log(source_loc loc, level::level_enum lvl, string_view_t msg)
{
bool log_enabled = should_log(lvl);
bool traceback_enabled = tracer_.enabled();

View File

@ -4,7 +4,7 @@
#pragma once
#ifndef SPDLOG_HEADER_ONLY
#include <spdlog/details/pattern_formatter.h>
#include <spdlog/pattern_formatter.h>
#endif
#include <spdlog/details/fmt_helper.h>
@ -90,7 +90,7 @@ struct null_scoped_padder
};
template<typename ScopedPadder>
class name_formatter : public flag_formatter
class name_formatter final : public flag_formatter
{
public:
explicit name_formatter(padding_info padinfo)
@ -106,7 +106,7 @@ public:
// log level appender
template<typename ScopedPadder>
class level_formatter : public flag_formatter
class level_formatter final : public flag_formatter
{
public:
explicit level_formatter(padding_info padinfo)
@ -123,7 +123,7 @@ public:
// short log level appender
template<typename ScopedPadder>
class short_level_formatter : public flag_formatter
class short_level_formatter final : public flag_formatter
{
public:
explicit short_level_formatter(padding_info padinfo)
@ -156,7 +156,7 @@ static int to12h(const tm &t)
static std::array<const char *, 7> days{{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}};
template<typename ScopedPadder>
class a_formatter : public flag_formatter
class a_formatter final : public flag_formatter
{
public:
explicit a_formatter(padding_info padinfo)
@ -194,7 +194,7 @@ public:
static const std::array<const char *, 12> months{{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}};
template<typename ScopedPadder>
class b_formatter : public flag_formatter
class b_formatter final : public flag_formatter
{
public:
explicit b_formatter(padding_info padinfo)
@ -214,7 +214,7 @@ static const std::array<const char *, 12> full_months{
{"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}};
template<typename ScopedPadder>
class B_formatter : public flag_formatter
class B_formatter final : public flag_formatter
{
public:
explicit B_formatter(padding_info padinfo)
@ -933,16 +933,15 @@ public:
dest.push_back(']');
dest.push_back(' ');
#ifndef SPDLOG_NO_NAME
// append logger name if exists
if (msg.logger_name.size() > 0)
{
dest.push_back('[');
// fmt_helper::append_str(*msg.logger_name, dest);
fmt_helper::append_string_view(msg.logger_name, dest);
dest.push_back(']');
dest.push_back(' ');
}
#endif
dest.push_back('[');
// wrap the level name with color
msg.color_range_start = dest.size();
@ -974,11 +973,13 @@ private:
} // namespace details
SPDLOG_INLINE pattern_formatter::pattern_formatter(std::string pattern, pattern_time_type time_type, std::string eol)
SPDLOG_INLINE pattern_formatter::pattern_formatter(
std::string pattern, pattern_time_type time_type, std::string eol, custom_flags custom_user_flags)
: pattern_(std::move(pattern))
, eol_(std::move(eol))
, pattern_time_type_(time_type)
, last_log_secs_(0)
, custom_handlers_(std::move(custom_user_flags))
{
std::memset(&cached_tm_, 0, sizeof(cached_tm_));
compile_pattern_(pattern_);
@ -997,7 +998,12 @@ SPDLOG_INLINE pattern_formatter::pattern_formatter(pattern_time_type time_type,
SPDLOG_INLINE std::unique_ptr<formatter> pattern_formatter::clone() const
{
return details::make_unique<pattern_formatter>(pattern_, pattern_time_type_, eol_);
custom_flags cloned_custom_formatters;
for (auto &it : custom_handlers_)
{
cloned_custom_formatters[it.first] = it.second->clone();
}
return details::make_unique<pattern_formatter>(pattern_, pattern_time_type_, eol_, std::move(cloned_custom_formatters));
}
SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory_buf_t &dest)
@ -1017,6 +1023,12 @@ SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory
details::fmt_helper::append_string_view(eol_, dest);
}
SPDLOG_INLINE void pattern_formatter::set_pattern(std::string pattern)
{
pattern_ = std::move(pattern);
compile_pattern_(pattern_);
}
SPDLOG_INLINE std::tm pattern_formatter::get_time_(const details::log_msg &msg)
{
if (pattern_time_type_ == pattern_time_type::local)
@ -1029,9 +1041,19 @@ SPDLOG_INLINE std::tm pattern_formatter::get_time_(const details::log_msg &msg)
template<typename Padder>
SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_info padding)
{
// process custom flags
auto it = custom_handlers_.find(flag);
if (it != custom_handlers_.end())
{
auto custom_handler = it->second->clone();
custom_handler->set_padding_info(padding);
formatters_.push_back(std::move(custom_handler));
return;
}
// process built-in flags
switch (flag)
{
case ('+'): // default formatter
formatters_.push_back(details::make_unique<details::full_formatter>(padding));
break;

View File

@ -14,6 +14,7 @@
#include <string>
#include <vector>
#include <unordered_map>
namespace spdlog {
namespace details {
@ -40,13 +41,13 @@ struct padding_info
{
return enabled_;
}
const size_t width_ = 0;
const pad_side side_ = left;
size_t width_ = 0;
pad_side side_ = left;
bool truncate_ = false;
bool enabled_ = false;
};
class flag_formatter
class SPDLOG_API flag_formatter
{
public:
explicit flag_formatter(padding_info padinfo)
@ -62,11 +63,24 @@ protected:
} // namespace details
class pattern_formatter final : public formatter
class SPDLOG_API custom_flag_formatter : public details::flag_formatter
{
public:
explicit pattern_formatter(
std::string pattern, pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol);
virtual std::unique_ptr<custom_flag_formatter> clone() const = 0;
void set_padding_info(details::padding_info padding)
{
flag_formatter::padinfo_ = padding;
}
};
class SPDLOG_API pattern_formatter final : public formatter
{
public:
using custom_flags = std::unordered_map<char, std::unique_ptr<custom_flag_formatter>>;
explicit pattern_formatter(std::string pattern, pattern_time_type time_type = pattern_time_type::local,
std::string eol = spdlog::details::os::default_eol, custom_flags custom_user_flags = {});
// use default pattern is not given
explicit pattern_formatter(pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol);
@ -77,6 +91,14 @@ public:
std::unique_ptr<formatter> clone() const override;
void format(const details::log_msg &msg, memory_buf_t &dest) override;
template<typename T, typename... Args>
pattern_formatter &add_flag(char flag, const Args &... args)
{
custom_handlers_[flag] = details::make_unique<T>(args...);
return *this;
}
void set_pattern(std::string pattern);
private:
std::string pattern_;
std::string eol_;
@ -84,6 +106,7 @@ private:
std::tm cached_tm_;
std::chrono::seconds last_log_secs_;
std::vector<std::unique_ptr<details::flag_formatter>> formatters_;
custom_flags custom_handlers_;
std::tm get_time_(const details::log_msg &msg);
template<typename Padder>
@ -92,7 +115,7 @@ private:
// Extract given pad spec (e.g. %8X)
// Advance the given it pass the end of the padding spec found (if any)
// Return padding.
details::padding_info handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end);
static details::padding_info handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end);
void compile_pattern_(const std::string &pattern);
};

View File

@ -64,7 +64,7 @@ protected:
if (ret < 0)
{
SPDLOG_THROW(spdlog_ex("__android_log_write() failed", ret));
throw_spdlog_ex("__android_log_write() failed", ret);
}
}

View File

@ -7,7 +7,7 @@
#include <spdlog/sinks/ansicolor_sink.h>
#endif
#include <spdlog/details/pattern_formatter.h>
#include <spdlog/pattern_formatter.h>
#include <spdlog/details/os.h>
namespace spdlog {
@ -43,7 +43,8 @@ SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::log(const details::log_msg &msg
// Wrap the originally formatted message in color codes.
// If color is not supported in the terminal, log as is instead.
std::lock_guard<mutex_t> lock(mutex_);
msg.color_range_start = 0;
msg.color_range_end = 0;
memory_buf_t formatted;
formatter_->format(msg, formatted);
if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
@ -111,7 +112,7 @@ SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
template<typename ConsoleMutex>
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::print_ccode_(const string_view_t &color_code)
{
fwrite(color_code.data(), sizeof(string_view_t::char_type), color_code.size(), target_file_);
fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_);
}
template<typename ConsoleMutex>

View File

@ -9,7 +9,7 @@
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <array>
namespace spdlog {
namespace sinks {
@ -30,7 +30,11 @@ public:
~ansicolor_sink() override = default;
ansicolor_sink(const ansicolor_sink &other) = delete;
ansicolor_sink(ansicolor_sink &&other) = delete;
ansicolor_sink &operator=(const ansicolor_sink &other) = delete;
ansicolor_sink &operator=(ansicolor_sink &&other) = delete;
void set_color(level::level_enum color_level, string_view_t color);
void set_color_mode(color_mode mode);
bool should_color();
@ -80,7 +84,7 @@ private:
mutex_t &mutex_;
bool should_do_colors_;
std::unique_ptr<spdlog::formatter> formatter_;
std::unordered_map<level::level_enum, string_view_t, level::level_hasher> colors_;
std::array<string_view_t, level::n_levels> colors_;
void print_ccode_(const string_view_t &color_code);
void print_range_(const memory_buf_t &formatted, size_t start, size_t end);
};

View File

@ -8,7 +8,7 @@
#endif
#include <spdlog/common.h>
#include <spdlog/details/pattern_formatter.h>
#include <spdlog/pattern_formatter.h>
#include <memory>

View File

@ -21,8 +21,14 @@ class base_sink : public sink
public:
base_sink();
explicit base_sink(std::unique_ptr<spdlog::formatter> formatter);
~base_sink() override = default;
base_sink(const base_sink &) = delete;
base_sink(base_sink &&) = delete;
base_sink &operator=(const base_sink &) = delete;
base_sink &operator=(base_sink &&) = delete;
void log(const details::log_msg &msg) final;
void flush() final;
void set_pattern(const std::string &pattern) final;

View File

@ -56,7 +56,7 @@ public:
{
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
{
SPDLOG_THROW(spdlog_ex("daily_file_sink: Invalid rotation time in ctor"));
throw_spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
}
auto now = log_clock::now();
@ -66,13 +66,13 @@ public:
if (max_files_ > 0)
{
filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));
filenames_q_.push_back(std::move(filename));
init_filenames_q_();
}
}
const filename_t &filename() const
filename_t filename()
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
return file_helper_.filename();
}
@ -104,6 +104,29 @@ protected:
}
private:
void init_filenames_q_()
{
using details::os::path_exists;
filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));
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))
{
break;
}
filenames.emplace_back(filename);
now -= std::chrono::hours(24);
}
for (auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter)
{
filenames_q_.push_back(std::move(*iter));
}
}
tm now_tm(log_clock::time_point tp)
{
time_t tnow = log_clock::to_time_t(tp);
@ -141,7 +164,7 @@ private:
if (!ok)
{
filenames_q_.push_back(std::move(current_file));
SPDLOG_THROW(spdlog_ex("Failed removing daily file " + filename_to_str(old_filename), errno));
throw_spdlog_ex("Failed removing daily file " + filename_to_str(old_filename), errno);
}
}
filenames_q_.push_back(std::move(current_file));
@ -167,15 +190,15 @@ using daily_file_sink_st = daily_file_sink<details::null_mutex>;
//
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> daily_logger_mt(
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false)
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0)
{
return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate);
return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate, max_files);
}
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> daily_logger_st(
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false)
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0)
{
return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate);
return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate, max_files);
}
} // namespace spdlog

View File

@ -6,7 +6,7 @@
#include "base_sink.h"
#include <spdlog/details/log_msg.h>
#include <spdlog/details/null_mutex.h>
#include <spdlog/details/pattern_formatter.h>
#include <spdlog/pattern_formatter.h>
#include <algorithm>
#include <memory>

View File

@ -8,6 +8,7 @@
#include <spdlog/details/null_mutex.h>
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/windows_include.h>
#include <winbase.h>
#include <mutex>

View File

@ -54,8 +54,9 @@ SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::calc_filename(const filename
}
template<typename Mutex>
SPDLOG_INLINE const filename_t &rotating_file_sink<Mutex>::filename() const
SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::filename()
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
return file_helper_.filename();
}
@ -99,18 +100,17 @@ SPDLOG_INLINE void rotating_file_sink<Mutex>::rotate_()
}
filename_t target = calc_filename(base_filename_, i);
if (!rename_file(src, target))
if (!rename_file_(src, target))
{
// if failed try again after a small delay.
// this is a workaround to a windows issue, where very high rotation
// rates can cause the rename to fail with permission denied (because of antivirus?).
details::os::sleep_for_millis(100);
if (!rename_file(src, target))
if (!rename_file_(src, target))
{
file_helper_.reopen(true); // truncate the log file anyway to prevent it to grow beyond its limit!
current_size_ = 0;
SPDLOG_THROW(
spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno));
throw_spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
}
}
}
@ -120,7 +120,7 @@ SPDLOG_INLINE void rotating_file_sink<Mutex>::rotate_()
// delete the target if exists, and rename the src file to target
// return true on success, false otherwise.
template<typename Mutex>
SPDLOG_INLINE bool rotating_file_sink<Mutex>::rename_file(const filename_t &src_filename, const filename_t &target_filename)
SPDLOG_INLINE bool rotating_file_sink<Mutex>::rename_file_(const filename_t &src_filename, const filename_t &target_filename)
{
// try to delete the target file in case it already exists.
(void)details::os::remove(target_filename);

View File

@ -24,7 +24,7 @@ class rotating_file_sink final : public base_sink<Mutex>
public:
rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open = false);
static filename_t calc_filename(const filename_t &filename, std::size_t index);
const filename_t &filename() const;
filename_t filename();
protected:
void sink_it_(const details::log_msg &msg) override;
@ -40,7 +40,7 @@ private:
// delete the target if exists, and rename the src file to target
// return true on success, false otherwise.
bool rename_file(const filename_t &src_filename, const filename_t &target_filename);
bool rename_file_(const filename_t &src_filename, const filename_t &target_filename);
filename_t base_filename_;
std::size_t max_size_;
@ -75,4 +75,4 @@ inline std::shared_ptr<logger> rotating_logger_st(
#ifdef SPDLOG_HEADER_ONLY
#include "rotating_file_sink-inl.h"
#endif
#endif

View File

@ -9,7 +9,7 @@
namespace spdlog {
namespace sinks {
class sink
class SPDLOG_API sink
{
public:
virtual ~sink() = default;

View File

@ -8,7 +8,7 @@
#endif
#include <spdlog/details/console_globals.h>
#include <spdlog/details/pattern_formatter.h>
#include <spdlog/pattern_formatter.h>
#include <memory>
namespace spdlog {

View File

@ -19,8 +19,12 @@ public:
using mutex_t = typename ConsoleMutex::mutex_t;
explicit stdout_sink_base(FILE *file);
~stdout_sink_base() override = default;
stdout_sink_base(const stdout_sink_base &other) = delete;
stdout_sink_base(stdout_sink_base &&other) = delete;
stdout_sink_base &operator=(const stdout_sink_base &other) = delete;
stdout_sink_base &operator=(stdout_sink_base &&other) = delete;
void log(const details::log_msg &msg) override;
void flush() override;

View File

@ -72,7 +72,7 @@ protected:
if (err)
{
SPDLOG_THROW(spdlog_ex("Failed writing to systemd", errno));
throw_spdlog_ex("Failed writing to systemd", errno);
}
}

View File

@ -0,0 +1,81 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/common.h>
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/null_mutex.h>
#ifdef _WIN32
#include <spdlog/details/tcp_client-windows.h>
#else
#include <spdlog/details/tcp_client.h>
#endif
#include <mutex>
#include <string>
#include <chrono>
#include <functional>
#pragma once
// Simple tcp client sink
// Connects to remote address and send the formatted log.
// Will attempt to reconnect if connection drops.
// If more complicated behaviour is needed (i.e get responses), you can inherit it and override the sink_it_ method.
namespace spdlog {
namespace sinks {
struct tcp_sink_config
{
std::string server_host;
int server_port;
bool lazy_connect = false; // if true connect on first log call instead of on construction
tcp_sink_config(std::string host, int port)
: server_host{std::move(host)}
, server_port{port}
{}
};
template<typename Mutex>
class tcp_sink : public spdlog::sinks::base_sink<Mutex>
{
public:
// connect to tcp host/port or throw if failed
// host can be hostname or ip address
explicit tcp_sink(tcp_sink_config sink_config)
: config_{std::move(sink_config)}
{
if (!config_.lazy_connect)
{
this->client_.connect(config_.server_host, config_.server_port);
}
}
~tcp_sink() override = default;
protected:
void sink_it_(const spdlog::details::log_msg &msg) override
{
spdlog::memory_buf_t formatted;
spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted);
if (!client_.is_connected())
{
client_.connect(config_.server_host, config_.server_port);
}
client_.send(formatted.data(), formatted.size());
}
void flush_() override {}
tcp_sink_config config_;
details::tcp_client client_;
};
using tcp_sink_mt = tcp_sink<std::mutex>;
using tcp_sink_st = tcp_sink<spdlog::details::null_mutex>;
} // namespace sinks
} // namespace spdlog

View File

@ -0,0 +1,266 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
// Writing to Windows Event Log requires the registry entries below to be present, with the following modifications:
// 1. <log_name> should be replaced with your log name (e.g. your application name)
// 2. <source_name> should be replaced with the specific source name and the key should be duplicated for
// each source used in the application
//
// Since typically modifications of this kind require elevation, it's better to do it as a part of setup procedure.
// The snippet below uses mscoree.dll as the message file as it exists on most of the Windows systems anyway and
// happens to contain the needed resource.
//
// You can also specify a custom message file if needed.
// Please refer to Event Log functions descriptions in MSDN for more details on custom message files.
/*---------------------------------------------------------------------------------------
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\<log_name>]
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\<log_name>\<source_name>]
"TypesSupported"=dword:00000007
"EventMessageFile"=hex(2):25,00,73,00,79,00,73,00,74,00,65,00,6d,00,72,00,6f,\
00,6f,00,74,00,25,00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,\
5c,00,6d,00,73,00,63,00,6f,00,72,00,65,00,65,00,2e,00,64,00,6c,00,6c,00,00,\
00
-----------------------------------------------------------------------------------------*/
#pragma once
#include <spdlog/details/null_mutex.h>
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/windows_include.h>
#include <winbase.h>
#include <mutex>
#include <string>
#include <vector>
namespace spdlog {
namespace sinks {
namespace win_eventlog {
namespace internal {
/** Windows error */
struct win32_error : public spdlog_ex
{
/** Formats an error report line: "user-message: error-code (system message)" */
static std::string format(std::string const &user_message, DWORD error_code = GetLastError())
{
std::string system_message;
LPSTR format_message_result{};
auto format_message_succeeded =
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&format_message_result, 0, nullptr);
if (format_message_succeeded && format_message_result)
{
system_message = fmt::format(" ({})", format_message_result);
}
if (format_message_result)
{
LocalFree((HLOCAL)format_message_result);
}
return fmt::format("{}: {}{}", user_message, error_code, system_message);
}
explicit win32_error(std::string const &func_name, DWORD error = GetLastError())
: spdlog_ex(format(func_name, error))
{}
};
/** Wrapper for security identifiers (SID) on Windows */
struct sid_t
{
std::vector<char> buffer_;
public:
sid_t() {}
/** creates a wrapped SID copy */
static sid_t duplicate_sid(PSID psid)
{
if (!::IsValidSid(psid))
{
throw_spdlog_ex("sid_t::sid_t(): invalid SID received");
}
auto const sid_length{::GetLengthSid(psid)};
sid_t result;
result.buffer_.resize(sid_length);
if (!::CopySid(sid_length, (PSID)result.as_sid(), psid))
{
SPDLOG_THROW(win32_error("CopySid"));
}
return result;
}
/** Retrieves pointer to the internal buffer contents as SID* */
SID *as_sid() const
{
return buffer_.empty() ? nullptr : (SID *)buffer_.data();
}
/** Get SID for the current user */
static sid_t get_current_user_sid()
{
/* create and init RAII holder for process token */
struct process_token_t
{
HANDLE token_handle_ = INVALID_HANDLE_VALUE;
explicit process_token_t(HANDLE process)
{
if (!::OpenProcessToken(process, TOKEN_QUERY, &token_handle_))
{
SPDLOG_THROW(win32_error("OpenProcessToken"));
}
}
~process_token_t()
{
::CloseHandle(token_handle_);
}
} current_process_token(::GetCurrentProcess()); // GetCurrentProcess returns pseudohandle, no leak here!
// Get the required size, this is expected to fail with ERROR_INSUFFICIENT_BUFFER and return the token size
DWORD tusize = 0;
if (::GetTokenInformation(current_process_token.token_handle_, TokenUser, NULL, 0, &tusize))
{
SPDLOG_THROW(win32_error("GetTokenInformation should fail"));
}
// get user token
std::vector<unsigned char> buffer(static_cast<size_t>(tusize));
if (!::GetTokenInformation(current_process_token.token_handle_, TokenUser, (LPVOID)buffer.data(), tusize, &tusize))
{
SPDLOG_THROW(win32_error("GetTokenInformation"));
}
// create a wrapper of the SID data as stored in the user token
return sid_t::duplicate_sid(((TOKEN_USER *)buffer.data())->User.Sid);
}
};
struct eventlog
{
static WORD get_event_type(details::log_msg const &msg)
{
switch (msg.level)
{
case level::trace:
case level::debug:
return EVENTLOG_SUCCESS;
case level::info:
return EVENTLOG_INFORMATION_TYPE;
case level::warn:
return EVENTLOG_WARNING_TYPE;
case level::err:
case level::critical:
case level::off:
return EVENTLOG_ERROR_TYPE;
default:
return EVENTLOG_INFORMATION_TYPE;
}
}
static WORD get_event_category(details::log_msg const &msg)
{
return (WORD)msg.level;
}
};
} // namespace internal
/*
* Windows Event Log sink
*/
template<typename Mutex>
class win_eventlog_sink : public base_sink<Mutex>
{
private:
HANDLE hEventLog_{NULL};
internal::sid_t current_user_sid_;
std::string source_;
WORD event_id_;
HANDLE event_log_handle()
{
if (!hEventLog_)
{
hEventLog_ = ::RegisterEventSource(nullptr, source_.c_str());
if (!hEventLog_ || hEventLog_ == (HANDLE)ERROR_ACCESS_DENIED)
{
SPDLOG_THROW(internal::win32_error("RegisterEventSource"));
}
}
return hEventLog_;
}
protected:
void sink_it_(const details::log_msg &msg) override
{
using namespace internal;
memory_buf_t formatted;
base_sink<Mutex>::formatter_->format(msg, formatted);
formatted.push_back('\0');
LPCSTR lp_str = static_cast<LPCSTR>(formatted.data());
auto succeeded = ::ReportEvent(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_,
current_user_sid_.as_sid(), 1, 0, &lp_str, nullptr);
if (!succeeded)
{
SPDLOG_THROW(win32_error("ReportEvent"));
}
}
void flush_() override {}
public:
win_eventlog_sink(std::string const &source, WORD event_id = 1000 /* according to mscoree.dll */)
: source_(source)
, event_id_(event_id)
{
try
{
current_user_sid_ = internal::sid_t::get_current_user_sid();
}
catch (...)
{
// get_current_user_sid() is unlikely to fail and if it does, we can still proceed without
// current_user_sid but in the event log the record will have no user name
}
}
~win_eventlog_sink()
{
if (hEventLog_)
DeregisterEventSource(hEventLog_);
}
};
} // namespace win_eventlog
using win_eventlog_sink_mt = win_eventlog::win_eventlog_sink<std::mutex>;
using win_eventlog_sink_st = win_eventlog::win_eventlog_sink<details::null_mutex>;
} // namespace sinks
} // namespace spdlog

View File

@ -8,7 +8,7 @@
#endif
#include <spdlog/common.h>
#include <spdlog/details/pattern_formatter.h>
#include <spdlog/pattern_formatter.h>
namespace spdlog {
namespace sinks {
@ -52,6 +52,8 @@ template<typename ConsoleMutex>
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
{
std::lock_guard<mutex_t> lock(mutex_);
msg.color_range_start = 0;
msg.color_range_end = 0;
memory_buf_t formatted;
formatter_->format(msg, formatted);
if (!in_console_)
@ -59,7 +61,6 @@ void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
write_to_file_(formatted);
return;
}
if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
{
// before color range
@ -157,7 +158,7 @@ void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::write_to_file_(const memory_buf_
bool ok = ::WriteFile(out_handle_, formatted.data() + total_written, size - total_written, &bytes_written, nullptr) != 0;
if (!ok || bytes_written == 0)
{
SPDLOG_THROW(spdlog_ex("wincolor_sink: write_to_file_ failed. GetLastError(): " + std::to_string(::GetLastError())));
throw_spdlog_ex("wincolor_sink: write_to_file_ failed. GetLastError(): " + std::to_string(::GetLastError()));
}
total_written += bytes_written;
} while (total_written < size);

View File

@ -11,7 +11,9 @@
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <array>
#include <spdlog/details/windows_include.h>
#include <wincon.h>
namespace spdlog {
@ -52,7 +54,7 @@ protected:
bool in_console_;
bool should_do_colors_;
std::unique_ptr<spdlog::formatter> formatter_;
std::unordered_map<level::level_enum, WORD, level::level_hasher> colors_;
std::array<WORD, level::n_levels> colors_;
// set foreground color and return the orig console attributes (for resetting later)
WORD set_foreground_color_(WORD attribs);

View File

@ -8,7 +8,7 @@
#endif
#include <spdlog/common.h>
#include <spdlog/details/pattern_formatter.h>
#include <spdlog/pattern_formatter.h>
namespace spdlog {

View File

@ -39,68 +39,66 @@ inline std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs
// Initialize and register a logger,
// formatter and flush level will be set according the global settings.
//
// NOTE:
// Use this function when creating loggers manually.
// Useful for initializing manually created loggers with the global settings.
//
// Example:
// auto console_sink = std::make_shared<spdlog::sinks::stdout_sink_mt>();
// auto console_logger = std::make_shared<spdlog::logger>("console_logger", console_sink);
// spdlog::initialize_logger(console_logger);
void initialize_logger(std::shared_ptr<logger> logger);
// auto mylogger = std::make_shared<spdlog::logger>("mylogger", ...);
// spdlog::initialize_logger(mylogger);
SPDLOG_API void initialize_logger(std::shared_ptr<logger> logger);
// Return an existing logger or nullptr if a logger with such name doesn't
// exist.
// example: spdlog::get("my_logger")->info("hello {}", "world");
std::shared_ptr<logger> get(const std::string &name);
SPDLOG_API std::shared_ptr<logger> get(const std::string &name);
// Set global formatter. Each sink in each logger will get a clone of this object
void set_formatter(std::unique_ptr<spdlog::formatter> formatter);
SPDLOG_API void set_formatter(std::unique_ptr<spdlog::formatter> formatter);
// Set global format string.
// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v");
void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);
SPDLOG_API void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);
// enable global backtrace support
void enable_backtrace(size_t n_messages);
SPDLOG_API void enable_backtrace(size_t n_messages);
// disable global backtrace support
void disable_backtrace();
SPDLOG_API void disable_backtrace();
// call dump backtrace on default logger
void dump_backtrace();
SPDLOG_API void dump_backtrace();
// Set global logging level
void set_level(level::level_enum log_level);
SPDLOG_API void set_level(level::level_enum log_level);
// Set global flush level
void flush_on(level::level_enum log_level);
SPDLOG_API void flush_on(level::level_enum log_level);
// Start/Restart a periodic flusher thread
// Warning: Use only if all your loggers are thread safe!
void flush_every(std::chrono::seconds interval);
SPDLOG_API void flush_every(std::chrono::seconds interval);
// Set global error handler
void set_error_handler(void (*handler)(const std::string &msg));
SPDLOG_API void set_error_handler(void (*handler)(const std::string &msg));
// Register the given logger with the given name
void register_logger(std::shared_ptr<logger> logger);
SPDLOG_API void register_logger(std::shared_ptr<logger> logger);
// Apply a user defined function on all registered loggers
// Example:
// spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->flush();});
void apply_all(const std::function<void(std::shared_ptr<logger>)> &fun);
SPDLOG_API void apply_all(const std::function<void(std::shared_ptr<logger>)> &fun);
// Drop the reference to the given logger
void drop(const std::string &name);
SPDLOG_API void drop(const std::string &name);
// Drop all references from the registry
void drop_all();
SPDLOG_API void drop_all();
// stop any running threads started by spdlog and clean registry loggers
void shutdown();
SPDLOG_API void shutdown();
// Automatic registration of loggers when using spdlog::create() or spdlog::create_async
void set_automatic_registration(bool automatic_registration);
SPDLOG_API void set_automatic_registration(bool automatic_registration);
// API for using default logger (stdout_color_mt),
// e.g: spdlog::info("Message {}", 1);
@ -117,11 +115,11 @@ void set_automatic_registration(bool automatic_registration);
// set_default_logger() *should not* be used concurrently with the default API.
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
std::shared_ptr<spdlog::logger> default_logger();
SPDLOG_API std::shared_ptr<spdlog::logger> default_logger();
spdlog::logger *default_logger_raw();
SPDLOG_API spdlog::logger *default_logger_raw();
void set_default_logger(std::shared_ptr<spdlog::logger> default_logger);
SPDLOG_API void set_default_logger(std::shared_ptr<spdlog::logger> default_logger);
template<typename... Args>
inline void log(source_loc source, level::level_enum lvl, string_view_t fmt, const Args &... args)

View File

@ -24,7 +24,7 @@
// This will prevent spdlog from querying the thread id on each log call.
//
// WARNING: If the log pattern contains thread id (i.e, %t) while this flag is
// on, the result is undefined.
// on, zero will be logged as thread id.
//
// #define SPDLOG_NO_THREAD_ID
///////////////////////////////////////////////////////////////////////////////
@ -38,13 +38,6 @@
// #define SPDLOG_NO_TLS
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment if logger name logging is not needed.
// This will prevent spdlog from copying the logger name on each log call.
//
// #define SPDLOG_NO_NAME
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to avoid spdlog's usage of atomic log levels
// Use only if your code never modifies a logger's log levels concurrently by

View File

@ -4,7 +4,7 @@
#pragma once
#define SPDLOG_VER_MAJOR 1
#define SPDLOG_VER_MINOR 5
#define SPDLOG_VER_MINOR 6
#define SPDLOG_VER_PATCH 0
#define SPDLOG_VERSION (SPDLOG_VER_MAJOR * 10000 + SPDLOG_VER_MINOR * 100 + SPDLOG_VER_PATCH)