Commit d073b97b authored by Rob Patro's avatar Rob Patro

Merge pull request #4 from gabime/master

Merge with upstream
parents 956e9143 06e0b038
......@@ -5,18 +5,16 @@ Very fast, header only, C++ logging library.
## Install
Just copy the files to your build tree and use a C++11 compiler
## Tested on:
* gcc 4.8.1 and above
* clang 3.5
* visual studio 2013
* mingw with g++ 4.9.x
## Platforms
* Linux (gcc 4.8.1+, clang 3.5+)
* Windows (visual studio 2013+, mingw with g++ 4.9.1+)
* Mac OSX (clang 3.5+)
##Features
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
* Headers only.
* No dependencies - just copy and use.
* Cross platform - Linux / Windows on 32/64 bits.
* **new!** Feature rich [call style](#usage-example) using the excellent [cppformat](http://cppformat.github.io/) library.
* Feature rich [call style](#usage-example) using the excellent [cppformat](http://cppformat.github.io/) library.
* ostream call style is supported too.
* Extremely fast asynchronous mode (optional) - using lockfree queues and other tricks to reach millions of calls/sec.
* [Custom](https://github.com/gabime/spdlog/wiki/Custom-formatting) formatting.
......@@ -73,7 +71,7 @@ int main(int, char* [])
console->info("An info message example {}..", 1);
console->info() << "Streams are supported too " << 1;
//Formatting examples
//Formatting examples
console->info("Easy padding in numbers like {:08d}", 12);
console->info("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
console->info("Support for floats {:03.2f}", 1.23456);
......@@ -98,6 +96,11 @@ int main(int, char* [])
for(int i = 0; i < 10; ++i)
file_logger->info("{} * {} equals {:>10}", i, i, i*i);
//
// Create a daily logger - a new file is created every day on 2:30am
//
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
//
// Customize msg format for all messages
//
......@@ -148,5 +151,8 @@ void custom_class_example()
spdlog::get("console")->info("custom class with operator<<: {}..", c);
spdlog::get("console")->info() << "custom class with operator<<: " << c << "..";
}
```
## Documentation
Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages.
----------------------------------------------------------
Single threaded benchmarks.. (1 thread, 1,000,000 lines)
----------------------------------------------------------
**************** boost-bench ****************
real 0m4.382s
user 0m4.213s
sys 0m0.048s
real 0m4.159s
user 0m4.120s
sys 0m0.040s
real 0m4.169s
user 0m4.117s
sys 0m0.052s
**************** glog-bench ****************
real 0m1.082s
user 0m0.944s
sys 0m0.136s
real 0m1.079s
user 0m0.977s
sys 0m0.101s
real 0m1.066s
user 0m0.951s
sys 0m0.114s
**************** easylogging-bench ****************
real 0m0.975s
user 0m0.963s
sys 0m0.012s
real 0m0.986s
user 0m0.954s
sys 0m0.033s
real 0m0.963s
user 0m0.919s
sys 0m0.044s
**************** spdlog-bench ****************
real 0m0.302s
user 0m0.285s
sys 0m0.016s
real 0m0.311s
user 0m0.287s
sys 0m0.025s
real 0m0.308s
user 0m0.276s
sys 0m0.032s
----------------------------------------------------------
Multi threaded benchmarks.. (10 threads, 1,000,000 lines)
----------------------------------------------------------
**************** boost-bench-mt ****************
real 0m16.293s
user 0m38.723s
sys 0m8.469s
real 0m16.029s
user 0m39.186s
sys 0m8.413s
real 0m16.257s
user 0m38.322s
sys 0m7.880s
**************** glog-bench-mt ****************
real 0m4.455s
user 0m12.871s
sys 0m13.508s
real 0m5.039s
user 0m14.239s
sys 0m15.900s
real 0m3.032s
user 0m8.654s
sys 0m9.473s
**************** easylogging-bench-mt ****************
real 0m4.076s
user 0m4.350s
sys 0m2.861s
real 0m2.857s
user 0m3.270s
sys 0m1.744s
real 0m4.588s
user 0m5.085s
sys 0m3.058s
**************** spdlog-bench-mt ****************
real 0m2.374s
user 0m4.369s
sys 0m10.426s
real 0m0.968s
user 0m1.804s
sys 0m4.186s
real 0m1.527s
user 0m3.132s
sys 0m6.427s
----------------------------------------------------------
Multi threaded benchmarks.. (100 threads, 1,000,000 lines)
----------------------------------------------------------
**************** boost-bench-mt ****************
real 0m15.623s
user 0m39.283s
sys 0m8.428s
real 0m15.008s
user 0m36.851s
sys 0m7.956s
real 0m15.478s
user 0m38.873s
sys 0m8.368s
**************** glog-bench-mt ****************
real 0m1.139s
user 0m3.003s
sys 0m5.214s
real 0m1.167s
user 0m3.004s
sys 0m5.431s
real 0m1.159s
user 0m2.909s
sys 0m5.456s
**************** easylogging-bench-mt ****************
real 0m4.510s
user 0m4.565s
sys 0m3.510s
real 0m8.841s
user 0m8.363s
sys 0m7.057s
real 0m5.638s
user 0m5.531s
sys 0m4.168s
**************** spdlog-bench-mt ****************
real 0m0.497s
user 0m0.951s
sys 0m2.743s
real 0m0.502s
user 0m0.940s
sys 0m2.816s
real 0m0.504s
user 0m0.911s
sys 0m2.860s
---------------------------------------------------------------
Async, single threaded benchmark.. (1 thread, 1,000,000 lines)
---------------------------------------------------------------
**************** spdlog-async ****************
Total: 1000000
Threads: 1
Delta = 0.216366 seconds
Rate = 4.62179e+06/sec
Total: 1000000
Threads: 1
Delta = 0.215076 seconds
Rate = 4.64953e+06/sec
Total: 1000000
Threads: 1
Delta = 0.210712 seconds
Rate = 4.74581e+06/sec
**************** g2log-async ****************
Total: 1000000
Threads: 1
Delta = 1.85039 seconds
Rate = 540428/sec
Exiting, log location: logs/g2log-async.g2log.20141220-214929.log
Total: 1000000
Threads: 1
Delta = 1.85434 seconds
Rate = 539274/sec
Exiting, log location: logs/g2log-async.g2log.20141220-214935.log
Total: 1000000
Threads: 1
Delta = 1.86829 seconds
Rate = 535249/sec
Exiting, log location: logs/g2log-async.g2log.20141220-214941.log
---------------------------------------------------------------
Async, multi threaded benchmark.. (10 threads, 1,000,000 lines)
---------------------------------------------------------------
**************** spdlog-async ****************
Total: 1000000
Threads: 10
Delta = 0.175684 seconds
Rate = 5.69204e+06/sec
Total: 1000000
Threads: 10
Delta = 0.173104 seconds
Rate = 5.77688e+06/sec
Total: 1000000
Threads: 10
Delta = 0.173881 seconds
Rate = 5.75106e+06/sec
**************** g2log-async ****************
Total: 1000000
Threads: 10
Delta = 0.945252 seconds
Rate = 1.05792e+06/sec
Exiting, log location: logs/g2log-async.g2log.20141220-214958.log
Total: 1000000
Threads: 10
Delta = 0.950362 seconds
Rate = 1.05223e+06/sec
Exiting, log location: logs/g2log-async.g2log.20141220-215004.log
Total: 1000000
Threads: 10
Delta = 0.943302 seconds
Rate = 1.06011e+06/sec
Exiting, log location: logs/g2log-async.g2log.20141220-215011.log
......@@ -50,7 +50,8 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count
int main(int argc, char* argv[])
{
int howmany = 1048576;
int queue_size = 1048576;
int howmany = 1000000;
int threads = 10;
bool auto_flush = false;
int file_size = 30 * 1024 * 1024;
......@@ -63,6 +64,8 @@ int main(int argc, char* argv[])
howmany = atoi(argv[1]);
if (argc > 2)
threads = atoi(argv[2]);
if (argc > 3)
queue_size = atoi(argv[3]);
cout << "*******************************************************************************\n";
......@@ -92,7 +95,7 @@ int main(int argc, char* argv[])
cout << "*******************************************************************************\n";
spdlog::set_async_mode(howmany);
spdlog::set_async_mode(queue_size);
for(int i = 0; i < 3; ++i)
{
......
......@@ -28,20 +28,18 @@
#include <iostream>
#include "spdlog/spdlog.h"
int main(int, char* [])
int main(int, char*[])
{
namespace spd = spdlog;
try
{
// Set log level to all loggers to debug and above
spd::set_level(spd::level::debug);
// Create console, multithreaded logger
auto console = spd::stdout_logger_mt("console");
console->info("Hello {}", 1);
//Create console, multithreaded logger
auto console = spd::stdout_logger_mt("console");
console->info("Welcome to spdlog!");
console->info("An info message example {}..", 1);
console->info() << "Streams are supported too " << 1;
//Formatting examples
console->info("Easy padding in numbers like {:08d}", 12);
console->info("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
console->info("Support for floats {:03.2f}", 1.23456);
......@@ -51,37 +49,59 @@ int main(int, char* [])
console->info("{:>30}", "right aligned");
console->info("{:^30}", "centered");
//
// Runtime log levels
//
spd::set_level(spd::level::info); //Set global log level to info
console->debug("This message shold not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("Now it should..");
//
// Create a file rotating logger with 5mb size max and 3 rotated files
//
auto file_logger = spd::rotating_logger_mt("file_logger", "logs/mylogfile", 1048576 * 5, 3);
file_logger->set_level(spd::level::info);
for(int i = 0; i < 10; ++i)
for (int i = 0; i < 10; ++i)
file_logger->info("{} * {} equals {:>10}", i, i, i*i);
//
// Create a daily logger - a new file is created every day on 2:30am
//
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
//
// Customize msg format for all messages
//
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
file_logger->info("This is another message with custom format");
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
//
// Compile time debug or trace macros.
// Enabled #ifdef SPDLOG_DEBUG_ON or #ifdef SPDLOG_TRACE_ON
//
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
//
// Asynchronous logging is very fast..
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
//
size_t q_size = 1048576; //queue size must be power of 2
spdlog::set_async_mode(q_size);
auto async_file= spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
async_file->info() << "This is async log.." << "Should be very fast!";
spdlog::drop_all(); //Close all loggers
//
// syslog example. linux only..
//
#ifdef __linux__
std::string ident = "spdlog-example";
auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID);
syslog_logger->warn("This is warning that will end up in syslog. This is Linux only!");
#endif
//Close all loggers
spd::drop_all();
}
catch (const spd::spdlog_ex& ex)
{
......@@ -90,16 +110,17 @@ int main(int, char* [])
}
//
// Example of user defined class with operator<<
//
class some_class {};
std::ostream& operator<<(std::ostream& os, const some_class&) { return os << "some_class"; }
std::ostream& operator<<(std::ostream& os, const some_class&)
{
return os << "some_class";
}
void custom_class_example()
{
some_class c;
spdlog::get("console")->info("custom class with operator<<: {}", c);
spdlog::get("console")->info() << "custom class with operator<<: " << c;
spdlog::get("console")->info("custom class with operator<<: {}..", c);
spdlog::get("console")->info() << "custom class with operator<<: " << c << "..";
}
......@@ -33,16 +33,9 @@
#ifndef _MSC_VER
#define SPDLOG_NOEXCEPT noexcept
#else
#define SPDLOG_NOEXCEPT
#define SPDLOG_NOEXCEPT throw()
#endif
// under linux, you can use the much faster CLOCK_REALTIME_COARSE clock.
// this clock is less accurate - can be off by few millis - depending on the kernel HZ
// uncomment to use it instead of the regular (and slower) clock
//#ifdef __linux__
//#define SPDLOG_CLOCK_COARSE
//#endif
namespace spdlog
{
......@@ -60,6 +53,7 @@ using sink_ptr = std::shared_ptr < sinks::sink > ;
using sinks_init_list = std::initializer_list < sink_ptr > ;
using formatter_ptr = std::shared_ptr<spdlog::formatter>;
//Log level enum
namespace level
{
......
......@@ -59,44 +59,50 @@ class async_log_helper
std::string logger_name;
level::level_enum level;
log_clock::time_point time;
size_t thread_id;
std::string txt;
async_msg() = default;
~async_msg() = default;
async_msg(const async_msg&) = delete;
async_msg& operator=(async_msg& other) = delete;
async_msg(const details::log_msg& m) :
logger_name(m.logger_name),
level(m.level),
time(m.time),
txt(m.raw.data(), m.raw.size())
{}
async_msg(async_msg&& other) :
logger_name(std::move(other.logger_name)),
level(std::move(other.level)),
time(std::move(other.time)),
txt(std::move(other.txt))
async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
logger_name(std::move(other.logger_name)),
level(std::move(other.level)),
time(std::move(other.time)),
txt(std::move(other.txt))
{}
async_msg& operator=(async_msg&& other)
async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT
{
logger_name = std::move(other.logger_name);
level = other.level;
time = std::move(other.time);
thread_id = other.thread_id;
txt = std::move(other.txt);
return *this;
}
// never copy or assign. should only be moved..
async_msg(const async_msg&) = delete;
async_msg& operator=(async_msg& other) = delete;
// construct from log_msg
async_msg(const details::log_msg& m) :
logger_name(m.logger_name),
level(m.level),
time(m.time),
thread_id(m.thread_id),
txt(m.raw.data(), m.raw.size())
{}
// copy into log_msg
void fill_log_msg(log_msg &msg)
{
msg.clear();
msg.logger_name = logger_name;
msg.level = level;
msg.time = time;
msg.thread_id = thread_id;
msg.raw << txt;
}
};
......@@ -117,8 +123,9 @@ public:
void log(const details::log_msg& msg);
//Stop logging and join the back thread
// stop logging and join the back thread
~async_log_helper();
void set_formatter(formatter_ptr);
......@@ -147,11 +154,11 @@ private:
// worker thread main loop
void worker_loop();
//pop next message from the queue and process it
//return true if a message was available (queue was not empty), will set the last_pop to the pop time
// pop next message from the queue and process it
// return true if a message was available (queue was not empty), will set the last_pop to the pop time
bool process_next_msg(clock::time_point& last_pop);
// guess how much to sleep if queue is empty/full using last successful op time as hint
// sleep,yield or return immediatly using the time passed since last message as a hint
static void sleep_or_yield(const clock::time_point& last_op_time);
};
......@@ -220,8 +227,8 @@ inline void spdlog::details::async_log_helper::worker_loop()
}
}
// Process next message in the queue
// Return true if this thread should still be active (no msg with level::off was received)
// process next message in the queue
// return true if this thread should still be active (no msg with level::off was received)
inline bool spdlog::details::async_log_helper::process_next_msg(clock::time_point& last_pop)
{
......@@ -253,7 +260,7 @@ inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_f
}
// Sleep,yield or return immediatly using the time passed since last message as a hint
// sleep,yield or return immediatly using the time passed since last message as a hint
inline void spdlog::details::async_log_helper::sleep_or_yield(const clock::time_point& last_op_time)
{
using std::chrono::milliseconds;
......@@ -261,7 +268,7 @@ inline void spdlog::details::async_log_helper::sleep_or_yield(const clock::time_
auto time_since_op = clock::now() - last_op_time;
//spin upto 1 ms
// spin upto 1 ms
if (time_since_op <= milliseconds(1))
return;
......@@ -277,7 +284,7 @@ inline void spdlog::details::async_log_helper::sleep_or_yield(const clock::time_
return sleep_for(milliseconds(100));
}
//throw if the worker thread threw an exception or not active
// throw if the worker thread threw an exception or not active
inline void spdlog::details::async_log_helper::throw_if_bad_worker()
{
if (_last_workerthread_ex)
......
/*
Modified version of cppformat formatting library
Orginal license:
Copyright (c) 2012 - 2014, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
Formatting library for C++
Copyright (c) 2012 - 2015, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "format.h"
#include <string.h>
......@@ -39,18 +36,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <cstdarg>
#ifdef _WIN32
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# ifdef __MINGW32__
# include <cstring>
# endif
# include <windows.h>
#endif
using spdlog::details::fmt::LongLong;
using spdlog::details::fmt::ULongLong;
using spdlog::details::fmt::internal::Arg;
using fmt::internal::Arg;
// Check if exceptions are disabled.
#if __GNUC__ && !__EXCEPTIONS
......@@ -88,45 +80,64 @@ using spdlog::details::fmt::internal::Arg;
#if _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant
# pragma warning(disable: 4702) // unreachable code
// Disable deprecation warning for strerror. The latter is not called but
// MSVC fails to detect it.
# pragma warning(disable: 4996)
#endif
// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
static inline fmt::internal::None<> strerror_r(int, char *, ...) {
return fmt::internal::None<>();
}
static inline fmt::internal::None<> strerror_s(char *, std::size_t, ...) {
return fmt::internal::None<>();
}
namespace {
#ifndef _MSC_VER
# define FMT_SNPRINTF snprintf
#else // _MSC_VER
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
va_list args;
va_start(args, format);
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
va_end(args);
return result;
va_list args;
va_start(args, format);
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
va_end(args);
return result;
}
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
# define FMT_SWPRINTF snwprintf
#else
# define FMT_SWPRINTF swprintf
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned>
struct IntChecker {
template <typename T>
static bool fits_in_int(T value) {
unsigned max = INT_MAX;
return value <= max;
}
template <typename T>
static bool fits_in_int(T value) {
unsigned max = INT_MAX;
return value <= max;
}
};
template <>
struct IntChecker<true> {
template <typename T>
static bool fits_in_int(T value) {
return value >= INT_MIN && value <= INT_MAX;
}
template <typename T>
static bool fits_in_int(T value) {
return value >= INT_MIN && value <= INT_MAX;
}
};
const char RESET_COLOR[] = "\x1b[0m";
typedef void(*FormatFunc)(spdlog::details::fmt::Writer &, int, spdlog::details::fmt::StringRef);
typedef void (*FormatFunc)(fmt::Writer &, int, fmt::StringRef);
// Portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
......@@ -137,209 +148,236 @@ typedef void(*FormatFunc)(spdlog::details::fmt::Writer &, int, spdlog::details::
// ERANGE - buffer is not large enough to store the error message
// other - failure
// Buffer should be at least of size 1.
FMT_FUNC int safe_strerror(
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT(true) {
assert(buffer != 0 && buffer_size != 0);
int result = 0;
#ifdef _GNU_SOURCE
char *message = strerror_r(error_code, buffer, buffer_size);
// If the buffer is full then the message is probably truncated.
if (message == buffer && strlen(buffer) == buffer_size - 1)
result = ERANGE;
buffer = message;
#elif __MINGW32__
errno = 0;
(void)buffer_size;
buffer = strerror(error_code);
result = errno;
#elif _WIN32
result = strerror_s(buffer, buffer_size, error_code);
// If the buffer is full then the message is probably truncated.
if (result == 0 && std::strlen(buffer) == buffer_size - 1)
result = ERANGE;
#else
result = strerror_r(error_code, buffer, buffer_size);
if (result == -1)
result = errno; // glibc versions before 2.13 return result in errno.
#endif
return result;
int safe_strerror(
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
assert(buffer != 0 && buffer_size != 0);
class StrError {
private:
int error_code_;
char *&buffer_;
std::size_t buffer_size_;
// A noop assignment operator to avoid bogus warnings.
void operator=(const StrError &) {}
// Handle the result of XSI-compliant version of strerror_r.
int handle(int result) {
// glibc versions before 2.13 return result in errno.
return result == -1 ? errno : result;
}
// Handle the result of GNU-specific version of strerror_r.
int handle(char *message) {
// If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
return ERANGE;
buffer_ = message;
return 0;
}
// Handle the case when strerror_r is not available.
int handle(fmt::internal::None<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
// Fallback to strerror_s when strerror_r is not available.
int fallback(int result) {
// If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
ERANGE : result;
}
// Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(fmt::internal::None<>) {
errno = 0;
buffer_ = strerror(error_code_);
return errno;
}
public:
StrError(int error_code, char *&buffer, std::size_t buffer_size)
: error_code_(error_code), buffer_(buffer), buffer_size_(buffer_size) {}
int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); }
};
return StrError(error_code, buffer, buffer_size).run();
}
FMT_FUNC void format_error_code(spdlog::details::fmt::Writer &out, int error_code,
spdlog::details::fmt::StringRef message) FMT_NOEXCEPT(true) {
// Report error code making sure that the output fits into
// INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
// bad_alloc.
out.clear();
static const char SEP[] = ": ";
static const char FMT_ERROR[] = "error ";
spdlog::details::fmt::internal::IntTraits<int>::MainType ec_value = error_code;
// Subtract 2 to account for terminating null characters in SEP and FMT_ERROR.
std::size_t error_code_size =
sizeof(SEP) + sizeof(FMT_ERROR) + spdlog::details::fmt::internal::count_digits(ec_value) - 2;
if (message.size() <= spdlog::details::fmt::internal::INLINE_BUFFER_SIZE - error_code_size)
out << message << SEP;
out << FMT_ERROR << error_code;
assert(out.size() <= spdlog::details::fmt::internal::INLINE_BUFFER_SIZE);
void format_error_code(fmt::Writer &out, int error_code,
fmt::StringRef message) FMT_NOEXCEPT {
// Report error code making sure that the output fits into
// INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
// bad_alloc.
out.clear();
static const char SEP[] = ": ";
static const char ERROR_STR[] = "error ";
fmt::internal::IntTraits<int>::MainType ec_value = error_code;
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
error_code_size += fmt::internal::count_digits(ec_value);
if (message.size() <= fmt::internal::INLINE_BUFFER_SIZE - error_code_size)
out << message << SEP;
out << ERROR_STR << error_code;
assert(out.size() <= fmt::internal::INLINE_BUFFER_SIZE);
}
FMT_FUNC void report_error(FormatFunc func,
int error_code, spdlog::details::fmt::StringRef message) FMT_NOEXCEPT(true) {
spdlog::details::fmt::MemoryWriter full_message;
func(full_message, error_code, message);
// Use Writer::data instead of Writer::c_str to avoid potential memory
// allocation.
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
std::fputc('\n', stderr);
void report_error(FormatFunc func,
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
fmt::MemoryWriter full_message;
func(full_message, error_code, message);
// Use Writer::data instead of Writer::c_str to avoid potential memory
// allocation.
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
std::fputc('\n', stderr);
}
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt : public spdlog::details::fmt::internal::ArgVisitor<IsZeroInt, bool> {
public:
template <typename T>
bool visit_any_int(T value) {
return value == 0;
}
class IsZeroInt : public fmt::internal::ArgVisitor<IsZeroInt, bool> {
public:
template <typename T>
bool visit_any_int(T value) { return value == 0; }
};
// Parses an unsigned integer advancing s to the end of the parsed input.
// This function assumes that the first character of s is a digit.
template <typename Char>
FMT_FUNC int parse_nonnegative_int(const Char *&s) {
assert('0' <= *s && *s <= '9');
unsigned value = 0;
do {
unsigned new_value = value * 10 + (*s++ - '0');
// Check if value wrapped around.
if (new_value < value) {
value = UINT_MAX;
break;
}
value = new_value;
} while ('0' <= *s && *s <= '9');
if (value > INT_MAX)
FMT_THROW(spdlog::details::fmt::FormatError("number is too big"));
return value;
int parse_nonnegative_int(const Char *&s) {
assert('0' <= *s && *s <= '9');
unsigned value = 0;
do {
unsigned new_value = value * 10 + (*s++ - '0');
// Check if value wrapped around.
if (new_value < value) {
value = UINT_MAX;
break;
}
value = new_value;
} while ('0' <= *s && *s <= '9');
if (value > INT_MAX)
FMT_THROW(fmt::FormatError("number is too big"));
return value;
}
inline void require_numeric_argument(const Arg &arg, char spec) {
if (arg.type > Arg::LAST_NUMERIC_TYPE) {
std::string message =
spdlog::details::fmt::format("format specifier '{}' requires numeric argument", spec);
FMT_THROW(spdlog::details::fmt::FormatError(message));
}
if (arg.type > Arg::LAST_NUMERIC_TYPE) {
std::string message =
fmt::format("format specifier '{}' requires numeric argument", spec);
FMT_THROW(fmt::FormatError(message));
}
}
template <typename Char>
FMT_FUNC void check_sign(const Char *&s, const Arg &arg) {
char sign = static_cast<char>(*s);
require_numeric_argument(arg, sign);
if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) {
FMT_THROW(spdlog::details::fmt::FormatError(spdlog::details::fmt::format(
"format specifier '{}' requires signed argument", sign)));
}
++s;
void check_sign(const Char *&s, const Arg &arg) {
char sign = static_cast<char>(*s);
require_numeric_argument(arg, sign);
if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) {
FMT_THROW(fmt::FormatError(fmt::format(
"format specifier '{}' requires signed argument", sign)));
}
++s;
}
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
class WidthHandler : public spdlog::details::fmt::internal::ArgVisitor<WidthHandler, unsigned> {
private:
spdlog::details::fmt::FormatSpec &spec_;
public:
explicit WidthHandler(spdlog::details::fmt::FormatSpec &spec) : spec_(spec) {}
unsigned visit_unhandled_arg() {
FMT_THROW(spdlog::details::fmt::FormatError("width is not integer"));
return 0;
}
template <typename T>
unsigned visit_any_int(T value) {
typedef typename spdlog::details::fmt::internal::IntTraits<T>::MainType UnsignedType;
UnsignedType width = value;
if (spdlog::details::fmt::internal::is_negative(value)) {
spec_.align_ = spdlog::details::fmt::ALIGN_LEFT;
width = 0 - width;
}
if (width > INT_MAX)
FMT_THROW(spdlog::details::fmt::FormatError("number is too big"));
return static_cast<unsigned>(width);
class WidthHandler : public fmt::internal::ArgVisitor<WidthHandler, unsigned> {
private:
fmt::FormatSpec &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
public:
explicit WidthHandler(fmt::FormatSpec &spec) : spec_(spec) {}
void report_unhandled_arg() {
FMT_THROW(fmt::FormatError("width is not integer"));
}
template <typename T>
unsigned visit_any_int(T value) {
typedef typename fmt::internal::IntTraits<T>::MainType UnsignedType;
UnsignedType width = value;
if (fmt::internal::is_negative(value)) {
spec_.align_ = fmt::ALIGN_LEFT;
width = 0 - width;
}
if (width > INT_MAX)
FMT_THROW(fmt::FormatError("number is too big"));
return static_cast<unsigned>(width);
}
};
class PrecisionHandler :
public spdlog::details::fmt::internal::ArgVisitor<PrecisionHandler, int> {
public:
unsigned visit_unhandled_arg() {
FMT_THROW(spdlog::details::fmt::FormatError("precision is not integer"));
return 0;
}
template <typename T>
int visit_any_int(T value) {
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(spdlog::details::fmt::FormatError("number is too big"));
return static_cast<int>(value);
}
public fmt::internal::ArgVisitor<PrecisionHandler, int> {
public:
void report_unhandled_arg() {
FMT_THROW(fmt::FormatError("precision is not integer"));
}
template <typename T>
int visit_any_int(T value) {
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(fmt::FormatError("number is too big"));
return static_cast<int>(value);
}
};
// Converts an integer argument to an integral type T for printf.
template <typename T>
class ArgConverter : public spdlog::details::fmt::internal::ArgVisitor<ArgConverter<T>, void> {
private:
spdlog::details::fmt::internal::Arg &arg_;
wchar_t type_;
public:
ArgConverter(spdlog::details::fmt::internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type) {}
template <typename U>
void visit_any_int(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
using spdlog::details::fmt::internal::Arg;
if (sizeof(T) <= sizeof(int)) {
// Extra casts are used to silence warnings.
if (is_signed) {
arg_.type = Arg::INT;
arg_.int_value = static_cast<int>(static_cast<T>(value));
}
else {
arg_.type = Arg::UINT;
arg_.uint_value = static_cast<unsigned>(
static_cast<typename spdlog::details::fmt::internal::MakeUnsigned<T>::Type>(value));
}
}
else {
if (is_signed) {
arg_.type = Arg::LONG_LONG;
arg_.long_long_value =
static_cast<typename spdlog::details::fmt::internal::MakeUnsigned<U>::Type>(value);
}
else {
arg_.type = Arg::ULONG_LONG;
arg_.ulong_long_value =
static_cast<typename spdlog::details::fmt::internal::MakeUnsigned<U>::Type>(value);
}
}
class ArgConverter : public fmt::internal::ArgVisitor<ArgConverter<T>, void> {
private:
fmt::internal::Arg &arg_;
wchar_t type_;
FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
public:
ArgConverter(fmt::internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type) {}
template <typename U>
void visit_any_int(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
using fmt::internal::Arg;
if (sizeof(T) <= sizeof(int)) {
// Extra casts are used to silence warnings.
if (is_signed) {
arg_.type = Arg::INT;
arg_.int_value = static_cast<int>(static_cast<T>(value));
} else {
arg_.type = Arg::UINT;
arg_.uint_value = static_cast<unsigned>(
static_cast<typename fmt::internal::MakeUnsigned<T>::Type>(value));
}
} else {
if (is_signed) {
arg_.type = Arg::LONG_LONG;
arg_.long_long_value =
static_cast<typename fmt::internal::MakeUnsigned<U>::Type>(value);
} else {
arg_.type = Arg::ULONG_LONG;
arg_.ulong_long_value =
static_cast<typename fmt::internal::MakeUnsigned<U>::Type>(value);
}
}
}
};
// Converts an integer argument to char for printf.
class CharConverter : public spdlog::details::fmt::internal::ArgVisitor<CharConverter, void> {
private:
spdlog::details::fmt::internal::Arg &arg_;
class CharConverter : public fmt::internal::ArgVisitor<CharConverter, void> {
private:
fmt::internal::Arg &arg_;
public:
explicit CharConverter(spdlog::details::fmt::internal::Arg &arg) : arg_(arg) {}
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
template <typename T>
void visit_any_int(T value) {
arg_.type = Arg::CHAR;
arg_.int_value = static_cast<char>(value);
}
public:
explicit CharConverter(fmt::internal::Arg &arg) : arg_(arg) {}
template <typename T>
void visit_any_int(T value) {
arg_.type = Arg::CHAR;
arg_.int_value = static_cast<char>(value);
}
};
// This function template is used to prevent compile errors when handling
......@@ -350,56 +388,52 @@ Arg::StringValue<Char> ignore_incompatible_str(Arg::StringValue<wchar_t>);
template <>
inline Arg::StringValue<char> ignore_incompatible_str(
Arg::StringValue<wchar_t>) {
return Arg::StringValue<char>();
}
Arg::StringValue<wchar_t>) { return Arg::StringValue<char>(); }
template <>
inline Arg::StringValue<wchar_t> ignore_incompatible_str(
Arg::StringValue<wchar_t> s) {
return s;
}
Arg::StringValue<wchar_t> s) { return s; }
} // namespace
FMT_FUNC void spdlog::details::fmt::SystemError::init(
int error_code, StringRef format_str, ArgList args) {
error_code_ = error_code;
MemoryWriter w;
internal::format_system_error(w, error_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
FMT_FUNC void fmt::SystemError::init(
int err_code, StringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
internal::format_system_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
template <typename T>
FMT_FUNC int spdlog::details::fmt::internal::CharTraits<char>::format_float(
int fmt::internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, T value) {
if (width == 0) {
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, value) :
FMT_SNPRINTF(buffer, size, format, precision, value);
}
if (width == 0) {
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, width, value) :
FMT_SNPRINTF(buffer, size, format, width, precision, value);
FMT_SNPRINTF(buffer, size, format, value) :
FMT_SNPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, width, value) :
FMT_SNPRINTF(buffer, size, format, width, precision, value);
}
template <typename T>
FMT_FUNC int spdlog::details::fmt::internal::CharTraits<wchar_t>::format_float(
int fmt::internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, T value) {
if (width == 0) {
return precision < 0 ?
swprintf(buffer, size, format, value) :
swprintf(buffer, size, format, precision, value);
}
if (width == 0) {
return precision < 0 ?
swprintf(buffer, size, format, width, value) :
swprintf(buffer, size, format, width, precision, value);
FMT_SWPRINTF(buffer, size, format, value) :
FMT_SWPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, width, value) :
FMT_SWPRINTF(buffer, size, format, width, precision, value);
}
template <typename T>
const char spdlog::details::fmt::internal::BasicData<T>::DIGITS[] =
const char fmt::internal::BasicData<T>::DIGITS[] =
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
......@@ -418,753 +452,756 @@ const char spdlog::details::fmt::internal::BasicData<T>::DIGITS[] =
factor * 1000000000
template <typename T>
const uint32_t spdlog::details::fmt::internal::BasicData<T>::POWERS_OF_10_32[] = {
0, FMT_POWERS_OF_10(1)
const uint32_t fmt::internal::BasicData<T>::POWERS_OF_10_32[] = {
0, FMT_POWERS_OF_10(1)
};
template <typename T>
const uint64_t spdlog::details::fmt::internal::BasicData<T>::POWERS_OF_10_64[] = {
0,
FMT_POWERS_OF_10(1),
FMT_POWERS_OF_10(ULongLong(1000000000)),
// Multiply several constants instead of using a single long long constant
// to avoid warnings about C++98 not supporting long long.
ULongLong(1000000000) * ULongLong(1000000000) * 10
const uint64_t fmt::internal::BasicData<T>::POWERS_OF_10_64[] = {
0,
FMT_POWERS_OF_10(1),
FMT_POWERS_OF_10(fmt::ULongLong(1000000000)),
// Multiply several constants instead of using a single long long constant
// to avoid warnings about C++98 not supporting long long.
fmt::ULongLong(1000000000) * fmt::ULongLong(1000000000) * 10
};
FMT_FUNC void spdlog::details::fmt::internal::report_unknown_type(char code, const char *type) {
if (std::isprint(static_cast<unsigned char>(code))) {
FMT_THROW(spdlog::details::fmt::FormatError(
spdlog::details::fmt::format("unknown format code '{}' for {}", code, type)));
}
FMT_THROW(spdlog::details::fmt::FormatError(
spdlog::details::fmt::format("unknown format code '\\x{:02x}' for {}",
static_cast<unsigned>(code), type)));
FMT_FUNC void fmt::internal::report_unknown_type(char code, const char *type) {
(void)type;
if (std::isprint(static_cast<unsigned char>(code))) {
FMT_THROW(fmt::FormatError(
fmt::format("unknown format code '{}' for {}", code, type)));
}
FMT_THROW(fmt::FormatError(
fmt::format("unknown format code '\\x{:02x}' for {}",
static_cast<unsigned>(code), type)));
}
#ifdef _WIN32
FMT_FUNC spdlog::details::fmt::internal::UTF8ToUTF16::UTF8ToUTF16(spdlog::details::fmt::StringRef s) {
int length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, 0, 0);
static const char FMT_ERROR[] = "cannot convert string from UTF-8 to UTF-16";
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), FMT_ERROR));
buffer_.resize(length);
length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, &buffer_[0], length);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), FMT_ERROR));
FMT_FUNC fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::StringRef s) {
int length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, 0, 0);
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_.resize(length);
length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, &buffer_[0], length);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
}
FMT_FUNC spdlog::details::fmt::internal::UTF16ToUTF8::UTF16ToUTF8(spdlog::details::fmt::WStringRef s) {
if (int error_code = convert(s)) {
FMT_THROW(WindowsError(error_code,
"cannot convert string from UTF-16 to UTF-8"));
}
FMT_FUNC fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) {
if (int error_code = convert(s)) {
FMT_THROW(WindowsError(error_code,
"cannot convert string from UTF-16 to UTF-8"));
}
}
FMT_FUNC int spdlog::details::fmt::internal::UTF16ToUTF8::convert(spdlog::details::fmt::WStringRef s) {
int length = WideCharToMultiByte(CP_UTF8, 0, s.c_str(), -1, 0, 0, 0, 0);
if (length == 0)
return GetLastError();
buffer_.resize(length);
length = WideCharToMultiByte(
CP_UTF8, 0, s.c_str(), -1, &buffer_[0], length, 0, 0);
if (length == 0)
return GetLastError();
return 0;
FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) {
int length = WideCharToMultiByte(CP_UTF8, 0, s.c_str(), -1, 0, 0, 0, 0);
if (length == 0)
return GetLastError();
buffer_.resize(length);
length = WideCharToMultiByte(
CP_UTF8, 0, s.c_str(), -1, &buffer_[0], length, 0, 0);
if (length == 0)
return GetLastError();
return 0;
}
FMT_FUNC void spdlog::details::fmt::WindowsError::init(
int error_code, StringRef format_str, ArgList args) {
error_code_ = error_code;
MemoryWriter w;
internal::format_windows_error(w, error_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
FMT_FUNC void fmt::WindowsError::init(
int err_code, StringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
internal::format_windows_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
#endif
FMT_FUNC void spdlog::details::fmt::internal::format_system_error(
spdlog::details::fmt::Writer &out, int error_code,
spdlog::details::fmt::StringRef message) FMT_NOEXCEPT(true) {
FMT_TRY {
MemoryBuffer<char, INLINE_BUFFER_SIZE> buffer;
buffer.resize(INLINE_BUFFER_SIZE);
for (;;) {
char *system_message = &buffer[0];
int result = safe_strerror(error_code, system_message, buffer.size());
if (result == 0) {
out << message << ": " << system_message;
return;
}
if (result != ERANGE)
break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2);
}
} FMT_CATCH(...) {}
format_error_code(out, error_code, message);
FMT_FUNC void fmt::internal::format_system_error(
fmt::Writer &out, int error_code,
fmt::StringRef message) FMT_NOEXCEPT {
FMT_TRY {
MemoryBuffer<char, INLINE_BUFFER_SIZE> buffer;
buffer.resize(INLINE_BUFFER_SIZE);
for (;;) {
char *system_message = &buffer[0];
int result = safe_strerror(error_code, system_message, buffer.size());
if (result == 0) {
out << message << ": " << system_message;
return;
}
if (result != ERANGE)
break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2);
}
} FMT_CATCH(...) {}
format_error_code(out, error_code, message);
}
#ifdef _WIN32
FMT_FUNC void spdlog::details::fmt::internal::format_windows_error(
spdlog::details::fmt::Writer &out, int error_code,
spdlog::details::fmt::StringRef message) FMT_NOEXCEPT(true) {
class String {
private:
LPWSTR str_;
public:
String() : str_() {}
~String() {
LocalFree(str_);
}
LPWSTR *ptr() {
return &str_;
}
LPCWSTR c_str() const {
return str_;
}
};
FMT_TRY {
String system_message;
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FMT_FUNC void fmt::internal::format_windows_error(
fmt::Writer &out, int error_code,
fmt::StringRef message) FMT_NOEXCEPT {
class String {
private:
LPWSTR str_;
public:
String() : str_() {}
~String() { LocalFree(str_); }
LPWSTR *ptr() { return &str_; }
LPCWSTR c_str() const { return str_; }
};
FMT_TRY {
String system_message;
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0,
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPWSTR>(system_message.ptr()), 0, 0)) {
UTF16ToUTF8 utf8_message;
if (utf8_message.convert(system_message.c_str()) == ERROR_SUCCESS) {
out << message << ": " << utf8_message;
return;
}
}
} FMT_CATCH(...) {}
format_error_code(out, error_code, message);
UTF16ToUTF8 utf8_message;
if (utf8_message.convert(system_message.c_str()) == ERROR_SUCCESS) {
out << message << ": " << utf8_message;
return;
}
}
} FMT_CATCH(...) {}
format_error_code(out, error_code, message);
}
#endif
// An argument formatter.
template <typename Char>
class spdlog::details::fmt::internal::ArgFormatter :
public spdlog::details::fmt::internal::ArgVisitor<spdlog::details::fmt::internal::ArgFormatter<Char>, void> {
private:
spdlog::details::fmt::BasicFormatter<Char> &formatter_;
spdlog::details::fmt::BasicWriter<Char> &writer_;
spdlog::details::fmt::FormatSpec &spec_;
const Char *format_;
public:
ArgFormatter(
spdlog::details::fmt::BasicFormatter<Char> &f, spdlog::details::fmt::FormatSpec &s, const Char *fmt)
: formatter_(f), writer_(f.writer()), spec_(s), format_(fmt) {}
template <typename T>
void visit_any_int(T value) {
writer_.write_int(value, spec_);
class fmt::internal::ArgFormatter :
public fmt::internal::ArgVisitor<fmt::internal::ArgFormatter<Char>, void> {
private:
fmt::BasicFormatter<Char> &formatter_;
fmt::BasicWriter<Char> &writer_;
fmt::FormatSpec &spec_;
const Char *format_;
FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatter);
public:
ArgFormatter(
fmt::BasicFormatter<Char> &f,fmt::FormatSpec &s, const Char *fmt)
: formatter_(f), writer_(f.writer()), spec_(s), format_(fmt) {}
template <typename T>
void visit_any_int(T value) { writer_.write_int(value, spec_); }
template <typename T>
void visit_any_double(T value) { writer_.write_double(value, spec_); }
void visit_char(int value) {
if (spec_.type_ && spec_.type_ != 'c') {
spec_.flags_ |= CHAR_FLAG;
writer_.write_int(value, spec_);
return;
}
template <typename T>
void visit_any_double(T value) {
writer_.write_double(value, spec_);
if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0)
FMT_THROW(FormatError("invalid format specifier for char"));
typedef typename fmt::BasicWriter<Char>::CharPtr CharPtr;
Char fill = static_cast<Char>(spec_.fill());
if (spec_.precision_ == 0) {
std::fill_n(writer_.grow_buffer(spec_.width_), spec_.width_, fill);
return;
}
void visit_char(int value) {
if (spec_.type_ && spec_.type_ != 'c') {
spec_.flags_ |= CHAR_FLAG;
writer_.write_int(value, spec_);
return;
}
if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0)
FMT_THROW(FormatError("invalid format specifier for char"));
typedef typename spdlog::details::fmt::BasicWriter<Char>::CharPtr CharPtr;
CharPtr out = CharPtr();
if (spec_.width_ > 1) {
Char fill = static_cast<Char>(spec_.fill());
out = writer_.grow_buffer(spec_.width_);
if (spec_.align_ == spdlog::details::fmt::ALIGN_RIGHT) {
std::fill_n(out, spec_.width_ - 1, fill);
out += spec_.width_ - 1;
}
else if (spec_.align_ == spdlog::details::fmt::ALIGN_CENTER) {
out = writer_.fill_padding(out, spec_.width_, 1, fill);
}
else {
std::fill_n(out + 1, spec_.width_ - 1, fill);
}
}
else {
out = writer_.grow_buffer(1);
}
*out = static_cast<Char>(value);
}
void visit_string(Arg::StringValue<char> value) {
writer_.write_str(value, spec_);
}
void visit_wstring(Arg::StringValue<wchar_t> value) {
writer_.write_str(ignore_incompatible_str<Char>(value), spec_);
}
void visit_pointer(const void *value) {
if (spec_.type_ && spec_.type_ != 'p')
spdlog::details::fmt::internal::report_unknown_type(spec_.type_, "pointer");
spec_.flags_ = spdlog::details::fmt::HASH_FLAG;
spec_.type_ = 'x';
writer_.write_int(reinterpret_cast<uintptr_t>(value), spec_);
}
void visit_custom(Arg::CustomValue c) {
c.format(&formatter_, c.value, &format_);
CharPtr out = CharPtr();
if (spec_.width_ > 1) {
out = writer_.grow_buffer(spec_.width_);
if (spec_.align_ == fmt::ALIGN_RIGHT) {
std::fill_n(out, spec_.width_ - 1, fill);
out += spec_.width_ - 1;
} else if (spec_.align_ == fmt::ALIGN_CENTER) {
out = writer_.fill_padding(out, spec_.width_, 1, fill);
} else {
std::fill_n(out + 1, spec_.width_ - 1, fill);
}
} else {
out = writer_.grow_buffer(1);
}
*out = static_cast<Char>(value);
}
void visit_string(Arg::StringValue<char> value) {
writer_.write_str(value, spec_);
}
void visit_wstring(Arg::StringValue<wchar_t> value) {
writer_.write_str(ignore_incompatible_str<Char>(value), spec_);
}
void visit_pointer(const void *value) {
if (spec_.type_ && spec_.type_ != 'p')
fmt::internal::report_unknown_type(spec_.type_, "pointer");
spec_.flags_ = fmt::HASH_FLAG;
spec_.type_ = 'x';
writer_.write_int(reinterpret_cast<uintptr_t>(value), spec_);
}
void visit_custom(Arg::CustomValue c) {
c.format(&formatter_, c.value, &format_);
}
};
template <typename Char>
void fmt::internal::FixedBuffer<Char>::grow(std::size_t) {
FMT_THROW(std::runtime_error("buffer overflow"));
}
template <typename Char>
template <typename StrChar>
FMT_FUNC void spdlog::details::fmt::BasicWriter<Char>::write_str(
const Arg::StringValue<StrChar> &str, const FormatSpec &spec) {
// Check if StrChar is convertible to Char.
internal::CharTraits<Char>::convert(StrChar());
if (spec.type_ && spec.type_ != 's')
internal::report_unknown_type(spec.type_, "string");
const StrChar *s = str.value;
std::size_t size = str.size;
if (size == 0) {
if (!s)
FMT_THROW(FormatError("string pointer is null"));
if (*s)
size = std::char_traits<StrChar>::length(s);
}
write_str(s, size, spec);
void fmt::BasicWriter<Char>::write_str(
const Arg::StringValue<StrChar> &s, const FormatSpec &spec) {
// Check if StrChar is convertible to Char.
internal::CharTraits<Char>::convert(StrChar());
if (spec.type_ && spec.type_ != 's')
internal::report_unknown_type(spec.type_, "string");
const StrChar *str_value = s.value;
std::size_t str_size = s.size;
if (str_size == 0) {
if (!str_value)
FMT_THROW(FormatError("string pointer is null"));
if (*str_value)
str_size = std::char_traits<StrChar>::length(str_value);
}
std::size_t precision = spec.precision_;
if (spec.precision_ >= 0 && precision < str_size)
str_size = spec.precision_;
write_str(str_value, str_size, spec);
}
template <typename Char>
inline Arg spdlog::details::fmt::BasicFormatter<Char>::parse_arg_index(const Char *&s) {
const char *error = 0;
Arg arg = *s < '0' || *s > '9' ?
next_arg(error) : get_arg(parse_nonnegative_int(s), error);
if (error) {
FMT_THROW(FormatError(
*s != '}' && *s != ':' ? "invalid format string" : error));
}
return arg;
inline Arg fmt::BasicFormatter<Char>::parse_arg_index(const Char *&s) {
const char *error = 0;
Arg arg = *s < '0' || *s > '9' ?
next_arg(error) : get_arg(parse_nonnegative_int(s), error);
if (error) {
FMT_THROW(FormatError(
*s != '}' && *s != ':' ? "invalid format string" : error));
}
return arg;
}
FMT_FUNC Arg spdlog::details::fmt::internal::FormatterBase::do_get_arg(
FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg(
unsigned arg_index, const char *&error) {
Arg arg = args_[arg_index];
if (arg.type == Arg::NONE)
error = "argument index out of range";
return arg;
Arg arg = args_[arg_index];
if (arg.type == Arg::NONE)
error = "argument index out of range";
return arg;
}
inline Arg spdlog::details::fmt::internal::FormatterBase::next_arg(const char *&error) {
if (next_arg_index_ >= 0)
return do_get_arg(next_arg_index_++, error);
error = "cannot switch from manual to automatic argument indexing";
return Arg();
inline Arg fmt::internal::FormatterBase::next_arg(const char *&error) {
if (next_arg_index_ >= 0)
return do_get_arg(next_arg_index_++, error);
error = "cannot switch from manual to automatic argument indexing";
return Arg();
}
inline Arg spdlog::details::fmt::internal::FormatterBase::get_arg(
inline Arg fmt::internal::FormatterBase::get_arg(
unsigned arg_index, const char *&error) {
if (next_arg_index_ <= 0) {
next_arg_index_ = -1;
return do_get_arg(arg_index, error);
}
error = "cannot switch from automatic to manual argument indexing";
return Arg();
if (next_arg_index_ <= 0) {
next_arg_index_ = -1;
return do_get_arg(arg_index, error);
}
error = "cannot switch from automatic to manual argument indexing";
return Arg();
}
template <typename Char>
FMT_FUNC void spdlog::details::fmt::internal::PrintfFormatter<Char>::parse_flags(
void fmt::internal::PrintfFormatter<Char>::parse_flags(
FormatSpec &spec, const Char *&s) {
for (;;) {
switch (*s++) {
case '-':
spec.align_ = ALIGN_LEFT;
break;
case '+':
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
break;
case '0':
spec.fill_ = '0';
break;
case ' ':
spec.flags_ |= SIGN_FLAG;
break;
case '#':
spec.flags_ |= HASH_FLAG;
break;
default:
--s;
return;
}
for (;;) {
switch (*s++) {
case '-':
spec.align_ = ALIGN_LEFT;
break;
case '+':
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
break;
case '0':
spec.fill_ = '0';
break;
case ' ':
spec.flags_ |= SIGN_FLAG;
break;
case '#':
spec.flags_ |= HASH_FLAG;
break;
default:
--s;
return;
}
}
}
template <typename Char>
FMT_FUNC Arg spdlog::details::fmt::internal::PrintfFormatter<Char>::get_arg(
Arg fmt::internal::PrintfFormatter<Char>::get_arg(
const Char *s, unsigned arg_index) {
const char *error = 0;
Arg arg = arg_index == UINT_MAX ?
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
if (error)
FMT_THROW(FormatError(!*s ? "invalid format string" : error));
return arg;
(void)s;
const char *error = 0;
Arg arg = arg_index == UINT_MAX ?
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
if (error)
FMT_THROW(FormatError(!*s ? "invalid format string" : error));
return arg;
}
template <typename Char>
FMT_FUNC unsigned spdlog::details::fmt::internal::PrintfFormatter<Char>::parse_header(
const Char *&s, FormatSpec &spec) {
unsigned arg_index = UINT_MAX;
Char c = *s;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
unsigned value = parse_nonnegative_int(s);
if (*s == '$') { // value is an argument index
++s;
arg_index = value;
}
else {
if (c == '0')
spec.fill_ = '0';
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
spec.width_ = value;
return arg_index;
}
}
}
parse_flags(spec, s);
// Parse width.
if (*s >= '0' && *s <= '9') {
spec.width_ = parse_nonnegative_int(s);
}
else if (*s == '*') {
++s;
spec.width_ = WidthHandler(spec).visit(get_arg(s));
unsigned fmt::internal::PrintfFormatter<Char>::parse_header(
const Char *&s, FormatSpec &spec) {
unsigned arg_index = UINT_MAX;
Char c = *s;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
unsigned value = parse_nonnegative_int(s);
if (*s == '$') { // value is an argument index
++s;
arg_index = value;
} else {
if (c == '0')
spec.fill_ = '0';
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
spec.width_ = value;
return arg_index;
}
}
return arg_index;
}
parse_flags(spec, s);
// Parse width.
if (*s >= '0' && *s <= '9') {
spec.width_ = parse_nonnegative_int(s);
} else if (*s == '*') {
++s;
spec.width_ = WidthHandler(spec).visit(get_arg(s));
}
return arg_index;
}
template <typename Char>
FMT_FUNC void spdlog::details::fmt::internal::PrintfFormatter<Char>::format(
BasicWriter<Char> &writer, BasicStringRef<Char> format,
void fmt::internal::PrintfFormatter<Char>::format(
BasicWriter<Char> &writer, BasicStringRef<Char> format_str,
const ArgList &args) {
const Char *start = format.c_str();
set_args(args);
const Char *s = start;
while (*s) {
Char c = *s++;
if (c != '%') continue;
if (*s == c) {
write(writer, start, s);
start = ++s;
continue;
}
write(writer, start, s - 1);
FormatSpec spec;
spec.align_ = ALIGN_RIGHT;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(s, spec);
// Parse precision.
if (*s == '.') {
++s;
if ('0' <= *s && *s <= '9') {
spec.precision_ = parse_nonnegative_int(s);
}
else if (*s == '*') {
++s;
spec.precision_ = PrecisionHandler().visit(get_arg(s));
}
}
const Char *start = format_str.c_str();
set_args(args);
const Char *s = start;
while (*s) {
Char c = *s++;
if (c != '%') continue;
if (*s == c) {
write(writer, start, s);
start = ++s;
continue;
}
write(writer, start, s - 1);
Arg arg = get_arg(s, arg_index);
if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg))
spec.flags_ &= ~HASH_FLAG;
if (spec.fill_ == '0') {
if (arg.type <= Arg::LAST_NUMERIC_TYPE)
spec.align_ = ALIGN_NUMERIC;
else
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
}
FormatSpec spec;
spec.align_ = ALIGN_RIGHT;
// Parse length and convert the argument to the required type.
switch (*s++) {
case 'h':
if (*s == 'h')
ArgConverter<signed char>(arg, *++s).visit(arg);
else
ArgConverter<short>(arg, *s).visit(arg);
break;
case 'l':
if (*s == 'l')
ArgConverter<spdlog::details::fmt::LongLong>(arg, *++s).visit(arg);
else
ArgConverter<long>(arg, *s).visit(arg);
break;
case 'j':
ArgConverter<intmax_t>(arg, *s).visit(arg);
break;
case 'z':
ArgConverter<size_t>(arg, *s).visit(arg);
break;
case 't':
ArgConverter<ptrdiff_t>(arg, *s).visit(arg);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--s;
ArgConverter<int>(arg, *s).visit(arg);
}
// Parse argument index, flags and width.
unsigned arg_index = parse_header(s, spec);
// Parse type.
if (!*s)
FMT_THROW(FormatError("invalid format string"));
spec.type_ = static_cast<char>(*s++);
if (arg.type <= Arg::LAST_INTEGER_TYPE) {
// Normalize type.
switch (spec.type_) {
case 'i':
case 'u':
spec.type_ = 'd';
break;
case 'c':
// TODO: handle wchar_t
CharConverter(arg).visit(arg);
break;
}
}
// Parse precision.
if (*s == '.') {
++s;
if ('0' <= *s && *s <= '9') {
spec.precision_ = parse_nonnegative_int(s);
} else if (*s == '*') {
++s;
spec.precision_ = PrecisionHandler().visit(get_arg(s));
}
}
start = s;
Arg arg = get_arg(s, arg_index);
if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg))
spec.flags_ &= ~HASH_FLAG;
if (spec.fill_ == '0') {
if (arg.type <= Arg::LAST_NUMERIC_TYPE)
spec.align_ = ALIGN_NUMERIC;
else
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
}
// Format argument.
switch (arg.type) {
case Arg::INT:
writer.write_int(arg.int_value, spec);
break;
case Arg::UINT:
writer.write_int(arg.uint_value, spec);
break;
case Arg::LONG_LONG:
writer.write_int(arg.long_long_value, spec);
break;
case Arg::ULONG_LONG:
writer.write_int(arg.ulong_long_value, spec);
break;
case Arg::CHAR: {
if (spec.type_ && spec.type_ != 'c')
writer.write_int(arg.int_value, spec);
typedef typename BasicWriter<Char>::CharPtr CharPtr;
CharPtr out = CharPtr();
if (spec.width_ > 1) {
Char fill = ' ';
out = writer.grow_buffer(spec.width_);
if (spec.align_ != ALIGN_LEFT) {
std::fill_n(out, spec.width_ - 1, fill);
out += spec.width_ - 1;
}
else {
std::fill_n(out + 1, spec.width_ - 1, fill);
}
}
else {
out = writer.grow_buffer(1);
}
*out = static_cast<Char>(arg.int_value);
break;
}
case Arg::DOUBLE:
writer.write_double(arg.double_value, spec);
break;
case Arg::LONG_DOUBLE:
writer.write_double(arg.long_double_value, spec);
break;
case Arg::CSTRING:
arg.string.size = 0;
writer.write_str(arg.string, spec);
break;
case Arg::STRING:
writer.write_str(arg.string, spec);
break;
case Arg::WSTRING:
writer.write_str(ignore_incompatible_str<Char>(arg.wstring), spec);
break;
case Arg::POINTER:
if (spec.type_ && spec.type_ != 'p')
internal::report_unknown_type(spec.type_, "pointer");
spec.flags_ = HASH_FLAG;
spec.type_ = 'x';
writer.write_int(reinterpret_cast<uintptr_t>(arg.pointer), spec);
break;
case Arg::CUSTOM: {
if (spec.type_)
internal::report_unknown_type(spec.type_, "object");
const void *s = "s";
arg.custom.format(&writer, arg.custom.value, &s);
break;
}
default:
assert(false);
break;
// Parse length and convert the argument to the required type.
switch (*s++) {
case 'h':
if (*s == 'h')
ArgConverter<signed char>(arg, *++s).visit(arg);
else
ArgConverter<short>(arg, *s).visit(arg);
break;
case 'l':
if (*s == 'l')
ArgConverter<fmt::LongLong>(arg, *++s).visit(arg);
else
ArgConverter<long>(arg, *s).visit(arg);
break;
case 'j':
ArgConverter<intmax_t>(arg, *s).visit(arg);
break;
case 'z':
ArgConverter<size_t>(arg, *s).visit(arg);
break;
case 't':
ArgConverter<ptrdiff_t>(arg, *s).visit(arg);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--s;
ArgConverter<int>(arg, *s).visit(arg);
}
// Parse type.
if (!*s)
FMT_THROW(FormatError("invalid format string"));
spec.type_ = static_cast<char>(*s++);
if (arg.type <= Arg::LAST_INTEGER_TYPE) {
// Normalize type.
switch (spec.type_) {
case 'i': case 'u':
spec.type_ = 'd';
break;
case 'c':
// TODO: handle wchar_t
CharConverter(arg).visit(arg);
break;
}
}
start = s;
// Format argument.
switch (arg.type) {
case Arg::INT:
writer.write_int(arg.int_value, spec);
break;
case Arg::UINT:
writer.write_int(arg.uint_value, spec);
break;
case Arg::LONG_LONG:
writer.write_int(arg.long_long_value, spec);
break;
case Arg::ULONG_LONG:
writer.write_int(arg.ulong_long_value, spec);
break;
case Arg::CHAR: {
if (spec.type_ && spec.type_ != 'c')
writer.write_int(arg.int_value, spec);
typedef typename BasicWriter<Char>::CharPtr CharPtr;
CharPtr out = CharPtr();
if (spec.width_ > 1) {
Char fill = ' ';
out = writer.grow_buffer(spec.width_);
if (spec.align_ != ALIGN_LEFT) {
std::fill_n(out, spec.width_ - 1, fill);
out += spec.width_ - 1;
} else {
std::fill_n(out + 1, spec.width_ - 1, fill);
}
} else {
out = writer.grow_buffer(1);
}
*out = static_cast<Char>(arg.int_value);
break;
}
case Arg::DOUBLE:
writer.write_double(arg.double_value, spec);
break;
case Arg::LONG_DOUBLE:
writer.write_double(arg.long_double_value, spec);
break;
case Arg::CSTRING:
arg.string.size = 0;
writer.write_str(arg.string, spec);
break;
case Arg::STRING:
writer.write_str(arg.string, spec);
break;
case Arg::WSTRING:
writer.write_str(ignore_incompatible_str<Char>(arg.wstring), spec);
break;
case Arg::POINTER:
if (spec.type_ && spec.type_ != 'p')
internal::report_unknown_type(spec.type_, "pointer");
spec.flags_= HASH_FLAG;
spec.type_ = 'x';
writer.write_int(reinterpret_cast<uintptr_t>(arg.pointer), spec);
break;
case Arg::CUSTOM: {
if (spec.type_)
internal::report_unknown_type(spec.type_, "object");
const void *str_format = "s";
arg.custom.format(&writer, arg.custom.value, &str_format);
break;
}
write(writer, start, s);
default:
assert(false);
break;
}
}
write(writer, start, s);
}
template <typename Char>
FMT_FUNC const Char *spdlog::details::fmt::BasicFormatter<Char>::format(
const Char *fmt::BasicFormatter<Char>::format(
const Char *&format_str, const Arg &arg) {
const Char *s = format_str;
FormatSpec spec;
if (*s == ':') {
if (arg.type == Arg::CUSTOM) {
arg.custom.format(this, arg.custom.value, &s);
return s;
}
++s;
// Parse fill and alignment.
if (Char c = *s) {
const Char *p = s + 1;
spec.align_ = ALIGN_DEFAULT;
do {
switch (*p) {
case '<':
spec.align_ = ALIGN_LEFT;
break;
case '>':
spec.align_ = ALIGN_RIGHT;
break;
case '=':
spec.align_ = ALIGN_NUMERIC;
break;
case '^':
spec.align_ = ALIGN_CENTER;
break;
}
if (spec.align_ != ALIGN_DEFAULT) {
if (p != s) {
if (c == '}') break;
if (c == '{')
FMT_THROW(FormatError("invalid fill character '{'"));
s += 2;
spec.fill_ = c;
}
else ++s;
if (spec.align_ == ALIGN_NUMERIC)
require_numeric_argument(arg, '=');
break;
}
} while (--p >= s);
}
// Parse sign.
switch (*s) {
case '+':
check_sign(s, arg);
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
const Char *s = format_str;
FormatSpec spec;
if (*s == ':') {
if (arg.type == Arg::CUSTOM) {
arg.custom.format(this, arg.custom.value, &s);
return s;
}
++s;
// Parse fill and alignment.
if (Char c = *s) {
const Char *p = s + 1;
spec.align_ = ALIGN_DEFAULT;
do {
switch (*p) {
case '<':
spec.align_ = ALIGN_LEFT;
break;
case '>':
spec.align_ = ALIGN_RIGHT;
break;
case '-':
check_sign(s, arg);
spec.flags_ |= MINUS_FLAG;
case '=':
spec.align_ = ALIGN_NUMERIC;
break;
case ' ':
check_sign(s, arg);
spec.flags_ |= SIGN_FLAG;
case '^':
spec.align_ = ALIGN_CENTER;
break;
}
if (*s == '#') {
require_numeric_argument(arg, '#');
spec.flags_ |= HASH_FLAG;
++s;
if (spec.align_ != ALIGN_DEFAULT) {
if (p != s) {
if (c == '}') break;
if (c == '{')
FMT_THROW(FormatError("invalid fill character '{'"));
s += 2;
spec.fill_ = c;
} else ++s;
if (spec.align_ == ALIGN_NUMERIC)
require_numeric_argument(arg, '=');
break;
}
} while (--p >= s);
}
// Parse width and zero flag.
if ('0' <= *s && *s <= '9') {
if (*s == '0') {
require_numeric_argument(arg, '0');
spec.align_ = ALIGN_NUMERIC;
spec.fill_ = '0';
}
// Zero may be parsed again as a part of the width, but it is simpler
// and more efficient than checking if the next char is a digit.
spec.width_ = parse_nonnegative_int(s);
}
// Parse sign.
switch (*s) {
case '+':
check_sign(s, arg);
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
break;
case '-':
check_sign(s, arg);
spec.flags_ |= MINUS_FLAG;
break;
case ' ':
check_sign(s, arg);
spec.flags_ |= SIGN_FLAG;
break;
}
// Parse precision.
if (*s == '.') {
++s;
spec.precision_ = 0;
if ('0' <= *s && *s <= '9') {
spec.precision_ = parse_nonnegative_int(s);
}
else if (*s == '{') {
++s;
const Arg &precision_arg = parse_arg_index(s);
if (*s++ != '}')
FMT_THROW(FormatError("invalid format string"));
ULongLong value = 0;
switch (precision_arg.type) {
case Arg::INT:
if (precision_arg.int_value < 0)
FMT_THROW(FormatError("negative precision"));
value = precision_arg.int_value;
break;
case Arg::UINT:
value = precision_arg.uint_value;
break;
case Arg::LONG_LONG:
if (precision_arg.long_long_value < 0)
FMT_THROW(FormatError("negative precision"));
value = precision_arg.long_long_value;
break;
case Arg::ULONG_LONG:
value = precision_arg.ulong_long_value;
break;
default:
FMT_THROW(FormatError("precision is not integer"));
}
if (value > INT_MAX)
FMT_THROW(FormatError("number is too big"));
spec.precision_ = static_cast<int>(value);
}
else {
FMT_THROW(FormatError("missing precision specifier"));
}
if (arg.type != Arg::DOUBLE && arg.type != Arg::LONG_DOUBLE) {
FMT_THROW(FormatError(
"precision specifier requires floating-point argument"));
}
}
if (*s == '#') {
require_numeric_argument(arg, '#');
spec.flags_ |= HASH_FLAG;
++s;
}
// Parse width and zero flag.
if ('0' <= *s && *s <= '9') {
if (*s == '0') {
require_numeric_argument(arg, '0');
spec.align_ = ALIGN_NUMERIC;
spec.fill_ = '0';
}
// Zero may be parsed again as a part of the width, but it is simpler
// and more efficient than checking if the next char is a digit.
spec.width_ = parse_nonnegative_int(s);
}
// Parse type.
if (*s != '}' && *s)
spec.type_ = static_cast<char>(*s++);
// Parse precision.
if (*s == '.') {
++s;
spec.precision_ = 0;
if ('0' <= *s && *s <= '9') {
spec.precision_ = parse_nonnegative_int(s);
} else if (*s == '{') {
++s;
const Arg &precision_arg = parse_arg_index(s);
if (*s++ != '}')
FMT_THROW(FormatError("invalid format string"));
ULongLong value = 0;
switch (precision_arg.type) {
case Arg::INT:
if (precision_arg.int_value < 0)
FMT_THROW(FormatError("negative precision"));
value = precision_arg.int_value;
break;
case Arg::UINT:
value = precision_arg.uint_value;
break;
case Arg::LONG_LONG:
if (precision_arg.long_long_value < 0)
FMT_THROW(FormatError("negative precision"));
value = precision_arg.long_long_value;
break;
case Arg::ULONG_LONG:
value = precision_arg.ulong_long_value;
break;
default:
FMT_THROW(FormatError("precision is not integer"));
}
if (value > INT_MAX)
FMT_THROW(FormatError("number is too big"));
spec.precision_ = static_cast<int>(value);
} else {
FMT_THROW(FormatError("missing precision specifier"));
}
if (arg.type < Arg::LAST_INTEGER_TYPE || arg.type == Arg::POINTER) {
FMT_THROW(FormatError(
fmt::format("precision not allowed in {} format specifier",
arg.type == Arg::POINTER ? "pointer" : "integer")));
}
}
if (*s++ != '}')
FMT_THROW(FormatError("missing '}' in format string"));
start_ = s;
// Parse type.
if (*s != '}' && *s)
spec.type_ = static_cast<char>(*s++);
}
// Format argument.
internal::ArgFormatter<Char>(*this, spec, s - 1).visit(arg);
return s;
if (*s++ != '}')
FMT_THROW(FormatError("missing '}' in format string"));
start_ = s;
// Format argument.
internal::ArgFormatter<Char>(*this, spec, s - 1).visit(arg);
return s;
}
template <typename Char>
FMT_FUNC void spdlog::details::fmt::BasicFormatter<Char>::format(
void fmt::BasicFormatter<Char>::format(
BasicStringRef<Char> format_str, const ArgList &args) {
const Char *s = start_ = format_str.c_str();
set_args(args);
while (*s) {
Char c = *s++;
if (c != '{' && c != '}') continue;
if (*s == c) {
write(writer_, start_, s);
start_ = ++s;
continue;
}
if (c == '}')
FMT_THROW(FormatError("unmatched '}' in format string"));
write(writer_, start_, s - 1);
Arg arg = parse_arg_index(s);
s = format(s, arg);
const Char *s = start_ = format_str.c_str();
set_args(args);
while (*s) {
Char c = *s++;
if (c != '{' && c != '}') continue;
if (*s == c) {
write(writer_, start_, s);
start_ = ++s;
continue;
}
write(writer_, start_, s);
if (c == '}')
FMT_THROW(FormatError("unmatched '}' in format string"));
write(writer_, start_, s - 1);
Arg arg = parse_arg_index(s);
s = format(s, arg);
}
write(writer_, start_, s);
}
FMT_FUNC void spdlog::details::fmt::report_system_error(
int error_code, spdlog::details::fmt::StringRef message) FMT_NOEXCEPT(true) {
report_error(internal::format_system_error, error_code, message);
FMT_FUNC void fmt::report_system_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
report_error(internal::format_system_error, error_code, message);
}
#ifdef _WIN32
FMT_FUNC void spdlog::details::fmt::report_windows_error(
int error_code, spdlog::details::fmt::StringRef message) FMT_NOEXCEPT(true) {
report_error(internal::format_windows_error, error_code, message);
FMT_FUNC void fmt::report_windows_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
report_error(internal::format_windows_error, error_code, message);
}
#endif
FMT_FUNC void spdlog::details::fmt::print(std::FILE *f, StringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
std::fwrite(w.data(), 1, w.size(), f);
FMT_FUNC void fmt::print(std::FILE *f, StringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
std::fwrite(w.data(), 1, w.size(), f);
}
FMT_FUNC void spdlog::details::fmt::print(StringRef format_str, ArgList args) {
print(stdout, format_str, args);
FMT_FUNC void fmt::print(StringRef format_str, ArgList args) {
print(stdout, format_str, args);
}
FMT_FUNC void spdlog::details::fmt::print(std::ostream &os, StringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
os.write(w.data(), w.size());
FMT_FUNC void fmt::print(std::ostream &os, StringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
os.write(w.data(), w.size());
}
FMT_FUNC void spdlog::details::fmt::print_colored(Color c, StringRef format, ArgList args) {
char escape[] = "\x1b[30m";
escape[3] = '0' + static_cast<char>(c);
std::fputs(escape, stdout);
print(format, args);
std::fputs(RESET_COLOR, stdout);
FMT_FUNC void fmt::print_colored(Color c, StringRef format, ArgList args) {
char escape[] = "\x1b[30m";
escape[3] = '0' + static_cast<char>(c);
std::fputs(escape, stdout);
print(format, args);
std::fputs(RESET_COLOR, stdout);
}
FMT_FUNC int spdlog::details::fmt::fprintf(std::FILE *f, StringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
std::size_t size = w.size();
return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
FMT_FUNC int fmt::fprintf(std::FILE *f, StringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
std::size_t size = w.size();
return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
}
#ifndef FMT_HEADER_ONLY
// Explicit instantiations for char.
template const char *spdlog::details::fmt::BasicFormatter<char>::format(
const char *&format_str, const spdlog::details::fmt::internal::Arg &arg);
template void fmt::internal::FixedBuffer<char>::grow(std::size_t);
template const char *fmt::BasicFormatter<char>::format(
const char *&format_str, const fmt::internal::Arg &arg);
template void spdlog::details::fmt::BasicFormatter<char>::format(
BasicStringRef<char> format, const ArgList &args);
template void fmt::BasicFormatter<char>::format(
BasicStringRef<char> format, const ArgList &args);
template void spdlog::details::fmt::internal::PrintfFormatter<char>::format(
BasicWriter<char> &writer, BasicStringRef<char> format, const ArgList &args);
template void fmt::internal::PrintfFormatter<char>::format(
BasicWriter<char> &writer, BasicStringRef<char> format, const ArgList &args);
template int spdlog::details::fmt::internal::CharTraits<char>::format_float(
template int fmt::internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, double value);
template int spdlog::details::fmt::internal::CharTraits<char>::format_float(
template int fmt::internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, long double value);
// Explicit instantiations for wchar_t.
template const wchar_t *spdlog::details::fmt::BasicFormatter<wchar_t>::format(
const wchar_t *&format_str, const spdlog::details::fmt::internal::Arg &arg);
template void fmt::internal::FixedBuffer<wchar_t>::grow(std::size_t);
template void spdlog::details::fmt::BasicFormatter<wchar_t>::format(
template const wchar_t *fmt::BasicFormatter<wchar_t>::format(
const wchar_t *&format_str, const fmt::internal::Arg &arg);
template void fmt::BasicFormatter<wchar_t>::format(
BasicStringRef<wchar_t> format, const ArgList &args);
template void spdlog::details::fmt::internal::PrintfFormatter<wchar_t>::format(
template void fmt::internal::PrintfFormatter<wchar_t>::format(
BasicWriter<wchar_t> &writer, BasicStringRef<wchar_t> format,
const ArgList &args);
template int spdlog::details::fmt::internal::CharTraits<wchar_t>::format_float(
template int fmt::internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, double value);
template int spdlog::details::fmt::internal::CharTraits<wchar_t>::format_float(
template int fmt::internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, long double value);
#endif // FMT_HEADER_ONLY
#if _MSC_VER
# pragma warning(pop)
#endif
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -27,7 +27,6 @@
#include "../common.h"
#include "../logger.h"
// Line logger class - aggregates operator<< calls to fast ostream
// and logs upon destruction
......@@ -63,8 +62,16 @@ public:
{
if (_enabled)
{
#ifndef SPDLOG_NO_NAME
_log_msg.logger_name = _callback_logger->name();
#endif
#ifndef SPDLOG_NO_DATETIME
_log_msg.time = os::now();
#endif
#ifndef SPDLOG_NO_THREAD_ID
_log_msg.thread_id = os::thread_id();
#endif
_callback_logger->_log_msg(_log_msg);
}
}
......
......@@ -24,6 +24,7 @@
#pragma once
#include <thread>
#include "../common.h"
#include "./format.h"
......@@ -37,7 +38,6 @@ struct log_msg
log_msg(level::level_enum l):
logger_name(),
level(l),
time(),
raw(),
formatted() {}
......@@ -45,7 +45,8 @@ struct log_msg
log_msg(const log_msg& other) :
logger_name(other.logger_name),
level(other.level),
time(other.time)
time(other.time),
thread_id(other.thread_id)
{
if (other.raw.size())
raw << fmt::BasicStringRef<char>(other.raw.data(), other.raw.size());
......@@ -57,6 +58,7 @@ struct log_msg
logger_name(std::move(other.logger_name)),
level(other.level),
time(std::move(other.time)),
thread_id(other.thread_id),
raw(std::move(other.raw)),
formatted(std::move(other.formatted))
{
......@@ -71,6 +73,7 @@ struct log_msg
logger_name = std::move(other.logger_name);
level = other.level;
time = std::move(other.time);
thread_id = other.thread_id;
raw = std::move(other.raw);
formatted = std::move(other.formatted);
other.clear();
......@@ -87,6 +90,7 @@ struct log_msg
std::string logger_name;
level::level_enum level;
log_clock::time_point time;
size_t thread_id;
fmt::MemoryWriter raw;
fmt::MemoryWriter formatted;
};
......
......@@ -32,6 +32,16 @@
# define WIN32_LEAN_AND_MEAN
# endif
# include <Windows.h>
#ifdef __MINGW32__
#include <share.h>
#endif
#elif __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
#include <unistd.h>
#else
#include <thread>
#endif
#include "../common.h"
......@@ -46,7 +56,7 @@ namespace os
inline spdlog::log_clock::time_point now()
{
#ifdef SPDLOG_CLOCK_COARSE
#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
timespec ts;
::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
return std::chrono::time_point<log_clock, typename log_clock::duration>(
......@@ -73,7 +83,7 @@ inline std::tm localtime(const std::time_t &time_tt)
inline std::tm localtime()
{
std::time_t now_t = time(0);
std::time_t now_t = time(nullptr);
return localtime(now_t);
}
......@@ -93,7 +103,7 @@ inline std::tm gmtime(const std::time_t &time_tt)
inline std::tm gmtime()
{
std::time_t now_t = time(0);
std::time_t now_t = time(nullptr);
return gmtime(now_t);
}
inline bool operator==(const std::tm& tm1, const std::tm& tm2)
......@@ -166,6 +176,19 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
#endif
}
//Return current thread id as size_t
//It exists because the std::this_thread::get_id() is much slower(espcially under VS 2013)
inline size_t thread_id()
{
#ifdef _WIN32
return static_cast<size_t>(::GetCurrentThreadId());
#elif __linux__
return static_cast<size_t>(syscall(SYS_gettid));
#else //Default to standard C++11 (OSX and other Unix)
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
#endif
}
} //os
} //details
......
......@@ -354,7 +354,7 @@ class t_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << std::hash<std::thread::id>()(std::this_thread::get_id());
msg.formatted << msg.thread_id;
}
};
......@@ -405,6 +405,7 @@ class full_formatter :public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
#ifndef SPDLOG_NO_DATETIME
auto duration = msg.time.time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
......@@ -421,6 +422,7 @@ class full_formatter :public flag_formatter
level::to_str(msg.level),
msg.raw.str());*/
// Faster (albeit uglier) way to format the line (5.6 million lines/sec under 10 threads)
msg.formatted << '[' << static_cast<unsigned int>(tm_time.tm_year + 1900) << '-'
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_mon + 1), 2, '0') << '-'
......@@ -430,7 +432,16 @@ class full_formatter :public flag_formatter
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_sec), 2, '0') << '.'
<< fmt::pad(static_cast<unsigned int>(millis), 3, '0') << "] ";
msg.formatted << '[' << msg.logger_name << "] [" << level::to_str(msg.level) << "] ";
//no datetime needed
#else
(void)tm_time;
#endif
#ifndef SPDLOG_NO_NAME
msg.formatted << '[' << msg.logger_name << "] ";
#endif
msg.formatted << '[' << level::to_str(msg.level) << "] ";
msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
}
};
......@@ -478,7 +489,7 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
{
switch (flag)
{
// logger name
// logger name
case 'n':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::name_formatter()));
break;
......@@ -490,7 +501,7 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
case 'L':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::short_level_formatter()));
break;
case('t') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::t_formatter()));
break;
......@@ -610,8 +621,8 @@ inline void spdlog::pattern_formatter::format(details::log_msg& msg)
//write eol
msg.formatted << details::os::eol();
}
catch(const details::fmt::FormatError& e)
catch(const fmt::FormatError& e)
{
throw spdlog_ex(details::fmt::format("formatting error while processing format string: {}", e.what()));
throw spdlog_ex(fmt::format("formatting error while processing format string: {}", e.what()));
}
}
......@@ -41,10 +41,17 @@ namespace spdlog
{
namespace details
{
class registry
{
public:
void register_logger(std::shared_ptr<logger> logger)
{
std::lock_guard<std::mutex> lock(_mutex);
register_logger_impl(logger);
}
std::shared_ptr<logger> get(const std::string& logger_name)
{
std::lock_guard<std::mutex> lock(_mutex);
......@@ -55,12 +62,12 @@ public:
template<class It>
std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
{
std::lock_guard<std::mutex> lock(_mutex);
//If already exists, just return it
auto found = _loggers.find(logger_name);
if (found != _loggers.end())
return found->second;
std::shared_ptr<logger> new_logger;
std::lock_guard<std::mutex> lock(_mutex);
if (_async_mode)
new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb);
else
......@@ -68,8 +75,9 @@ public:
if (_formatter)
new_logger->set_formatter(_formatter);
new_logger->set_level(_level);
_loggers[logger_name] = new_logger;
register_logger_impl(new_logger);
return new_logger;
}
......@@ -103,14 +111,12 @@ public:
l.second->set_formatter(_formatter);
}
void set_pattern(const std::string& pattern)
{
std::lock_guard<std::mutex> lock(_mutex);
_formatter = std::make_shared<pattern_formatter>(pattern);
for (auto& l : _loggers)
l.second->set_formatter(_formatter);
}
void set_level(level::level_enum log_level)
......@@ -118,6 +124,7 @@ public:
std::lock_guard<std::mutex> lock(_mutex);
for (auto& l : _loggers)
l.second->set_level(log_level);
_level = log_level;
}
void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb)
......@@ -135,7 +142,6 @@ public:
_async_mode = false;
}
static registry& instance()
{
static registry s_instance;
......@@ -143,6 +149,13 @@ public:
}
private:
void register_logger_impl(std::shared_ptr<logger> logger)
{
auto logger_name = logger->name();
if (_loggers.find(logger_name) != std::end(_loggers))
throw spdlog_ex("logger with name " + logger_name + " already exists");
_loggers[logger->name()] = logger;
}
registry() = default;
registry(const registry&) = delete;
registry& operator=(const registry&) = delete;
......
......@@ -32,6 +32,11 @@
#include "../sinks/stdout_sinks.h"
#include "../sinks/syslog_sink.h"
inline void spdlog::register_logger(std::shared_ptr<logger> logger)
{
return details::registry::instance().register_logger(logger);
}
inline std::shared_ptr<spdlog::logger> spdlog::get(const std::string& name)
{
return details::registry::instance().get(name);
......@@ -54,35 +59,35 @@ inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(const std::str
}
// Create file logger which creates new file at midnight):
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const std::string& filename, bool force_flush)
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const std::string& filename, int hour, int minute, bool force_flush)
{
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, "txt", force_flush);
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, "txt", hour, minute, force_flush);
}
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const std::string& filename, bool force_flush)
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const std::string& filename, int hour, int minute, bool force_flush)
{
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, "txt", force_flush);
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, "txt", hour, minute, force_flush);
}
// Create stdout/stderr loggers
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name)
{
return create<spdlog::sinks::stdout_sink_mt>(logger_name);
return details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name)
{
return create<spdlog::sinks::stdout_sink_st>(logger_name);
return details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name)
{
return create<spdlog::sinks::stderr_sink_mt>(logger_name);
return details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name)
{
return create<spdlog::sinks::stderr_sink_st>(logger_name);
return details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance());
}
#ifdef __linux__
......
......@@ -30,21 +30,19 @@
#include "../details/file_helper.h"
#include "../details/format.h"
namespace spdlog
{
namespace sinks
{
/*
* Trivial file sink with single file as target
*/
template<class Mutex>
class simple_file_sink : public base_sink<Mutex>
class simple_file_sink : public base_sink < Mutex >
{
public:
explicit simple_file_sink(const std::string &filename,
bool force_flush=false):
bool force_flush = false) :
_file_helper(force_flush)
{
_file_helper.open(filename);
......@@ -64,14 +62,14 @@ typedef simple_file_sink<details::null_mutex> simple_file_sink_st;
/*
* Rotating file sink based on size
*/
*/
template<class Mutex>
class rotating_file_sink : public base_sink<Mutex>
class rotating_file_sink : public base_sink < Mutex >
{
public:
rotating_file_sink(const std::string &base_filename, const std::string &extension,
std::size_t max_size, std::size_t max_files,
bool force_flush=false):
bool force_flush = false) :
_base_filename(base_filename),
_extension(extension),
_max_size(max_size),
......@@ -82,12 +80,11 @@ public:
_file_helper.open(calc_filename(_base_filename, 0, _extension));
}
protected:
void _sink_it(const details::log_msg& msg) override
{
_current_size += msg.formatted.size();
if (_current_size > _max_size)
if (_current_size > _max_size)
{
_rotate();
_current_size = msg.formatted.size();
......@@ -95,11 +92,10 @@ protected:
_file_helper.write(msg);
}
private:
static std::string calc_filename(const std::string& filename, std::size_t index, const std::string& extension)
{
details::fmt::MemoryWriter w;
fmt::MemoryWriter w;
if (index)
w.write("{}.{}.{}", filename, index, extension);
else
......@@ -107,14 +103,12 @@ private:
return w.str();
}
// Rotate files:
// log.txt -> log.1.txt
// log.1.txt -> log2.txt
// log.2.txt -> log3.txt
// log.3.txt -> delete
void _rotate()
{
_file_helper.close();
......@@ -152,60 +146,70 @@ typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
* Rotating file sink based on date. rotates at midnight
*/
template<class Mutex>
class daily_file_sink:public base_sink<Mutex>
class daily_file_sink :public base_sink < Mutex >
{
public:
explicit daily_file_sink(const std::string& base_filename,
const std::string& extension,
bool force_flush=false):
_base_filename(base_filename),
//create daily file sink which rotates on given time
daily_file_sink(
const std::string& base_filename,
const std::string& extension,
int rotation_hour,
int rotation_minute,
bool force_flush = false) : _base_filename(base_filename),
_extension(extension),
_midnight_tp (_calc_midnight_tp() ),
_rotation_h(rotation_hour),
_rotation_m(rotation_minute),
_file_helper(force_flush)
{
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
_rotation_tp = _next_rotation_tp();
_file_helper.open(calc_filename(_base_filename, _extension));
}
protected:
void _sink_it(const details::log_msg& msg) override
{
if (std::chrono::system_clock::now() >= _midnight_tp)
if (std::chrono::system_clock::now() >= _rotation_tp)
{
_file_helper.close();
_file_helper.open(calc_filename(_base_filename, _extension));
_midnight_tp = _calc_midnight_tp();
_rotation_tp = _next_rotation_tp();
}
_file_helper.write(msg);
}
private:
// Return next midnight's time_point
static std::chrono::system_clock::time_point _calc_midnight_tp()
std::chrono::system_clock::time_point _next_rotation_tp()
{
using namespace std::chrono;
auto now = system_clock::now();
time_t tnow = std::chrono::system_clock::to_time_t(now);
tm date = spdlog::details::os::localtime(tnow);
date.tm_hour = date.tm_min = date.tm_sec = 0;
auto midnight = std::chrono::system_clock::from_time_t(std::mktime(&date));
return system_clock::time_point(midnight + hours(24));
date.tm_hour = _rotation_h;
date.tm_min = _rotation_m;
date.tm_sec = 0;
auto rotation_time = std::chrono::system_clock::from_time_t(std::mktime(&date));
if (rotation_time > now)
return rotation_time;
else
return system_clock::time_point(rotation_time + hours(24));
}
//Create filename for the form basename.YYYY-MM-DD.extension
static std::string calc_filename(const std::string& basename, const std::string& extension)
{
std::tm tm = spdlog::details::os::localtime();
details::fmt::MemoryWriter w;
w.write("{}.{:04d}-{:02d}-{:02d}.{}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, extension);
fmt::MemoryWriter w;
w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}.{}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, extension);
return w.str();
}
std::string _base_filename;
std::string _extension;
std::chrono::system_clock::time_point _midnight_tp;
int _rotation_h;
int _rotation_m;
std::chrono::system_clock::time_point _rotation_tp;
details::file_helper _file_helper;
};
typedef daily_file_sink<std::mutex> daily_file_sink_mt;
......
......@@ -37,9 +37,14 @@ namespace sinks
template <class Mutex>
class stdout_sink : public ostream_sink<Mutex>
{
using MyType = stdout_sink<Mutex>;
public:
stdout_sink() : ostream_sink<Mutex>(std::cout, true) {}
static std::shared_ptr<MyType> instance()
{
static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
return instance;
}
};
typedef stdout_sink<details::null_mutex> stdout_sink_st;
......@@ -49,8 +54,15 @@ typedef stdout_sink<std::mutex> stdout_sink_mt;
template <class Mutex>
class stderr_sink : public ostream_sink<Mutex>
{
using MyType = stderr_sink<Mutex>;
public:
stderr_sink() : ostream_sink<Mutex>(std::cerr, true) {}
static std::shared_ptr<MyType> instance()
{
static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
return instance;
}
};
typedef stderr_sink<std::mutex> stderr_sink_mt;
......
......@@ -28,16 +28,12 @@
#pragma once
#include "tweakme.h"
#include "common.h"
#include "logger.h"
namespace spdlog
{
// Return an existing logger or nullptr if a logger with such name doesn't exist.
// Examples:
//
......@@ -55,7 +51,7 @@ void set_pattern(const std::string& format_string);
void set_formatter(formatter_ptr f);
//
// Set global logging level for
// Set global logging level for
//
void set_level(level::level_enum log_level);
......@@ -69,7 +65,7 @@ void set_level(level::level_enum log_level);
// async_overflow_policy::block_retry - if queue is full, block until queue has room for the new log entry.
// async_overflow_policy::discard_log_msg - never block and discard any new messages when queue overflows.
//
// worker_warmup_cb (optional):
// worker_warmup_cb (optional):
// callback function that will be called in worker thread upon start (can be used to init stuff like thread affinity)
//
void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr);
......@@ -78,20 +74,20 @@ void set_async_mode(size_t queue_size, const async_overflow_policy overflow_poli
void set_sync_mode();
//
// Create multi/single threaded rotating file logger
// Create and register multi/single threaded rotating file logger
//
std::shared_ptr<logger> rotating_logger_mt(const std::string& logger_name, const std::string& filename, size_t max_file_size, size_t max_files, bool force_flush = false);
std::shared_ptr<logger> rotating_logger_st(const std::string& logger_name, const std::string& filename, size_t max_file_size, size_t max_files, bool force_flush = false);
//
// Create file logger which creates new file at midnight):
// Create file logger which creates new file on the given time (default in midnight):
//
std::shared_ptr<logger> daily_logger_mt(const std::string& logger_name, const std::string& filename, bool force_flush = false);
std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const std::string& filename, bool force_flush = false);
std::shared_ptr<logger> daily_logger_mt(const std::string& logger_name, const std::string& filename, int hour=0, int minute=0, bool force_flush = false);
std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const std::string& filename, int hour=0, int minute=0, bool force_flush = false);
//
// Create stdout/stderr loggers
// Create and register stdout/stderr loggers
//
std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name);
std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name);
......@@ -100,57 +96,59 @@ std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name);
//
// Create a syslog logger
// Create and register a syslog logger
//
#ifdef __linux__
std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0);
#endif
// Create a logger with multiple sinks
// Create and register a logger with multiple sinks
std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks);
template<class It>
std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end);
// Create a logger with templated sink type
// Create and register a logger with templated sink type
// Example: spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename", "txt");
template <typename Sink, typename... Args>
std::shared_ptr<spdlog::logger> create(const std::string& logger_name, const Args&...);
// Register the given logger with the given name
void register_logger(std::shared_ptr<logger> logger);
// Drop the reference to the given logger
void drop(const std::string &name);
// Drop all references
void drop_all();
///////////////////////////////////////////////////////////////////////////////
//
// Trace & debug macros to be switched on/off at compile time for zero cost debug statements.
// Note: using these mactors overrides the runtime log threshold of the logger.
// Macros to be display source file & line
// Trace & Debug can be switched on/off at compile time for zero cost debug statements.
// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in teakme.h to enable.
//
// Example:
//
// Enable debug macro, must be defined before including spdlog.h
// #define SPDLOG_DEBUG_ON
// include "spdlog/spdlog.h"
// spdlog::set_level(spdlog::level::debug);
// SPDLOG_DEBUG(my_logger, "Some debug message {} {}", 1, 3.2);
//
///////////////////////////////////////////////////////////////////////////////
#ifdef SPDLOG_TRACE_ON
#define SPDLOG_TRACE(logger, ...) logger->force_log(spdlog::level::trace, __VA_ARGS__) << " (" << __FILE__ << " #" << __LINE__ <<")";
#define SPDLOG_TRACE(logger, ...) logger->trace(__VA_ARGS__) << " (" << __FILE__ << " #" << __LINE__ <<")";
#else
#define SPDLOG_TRACE(logger, ...)
#endif
#ifdef SPDLOG_DEBUG_ON
#define SPDLOG_DEBUG(logger, ...) logger->force_log(spdlog::level::debug, __VA_ARGS__)
#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__) << " (" << __FILE__ << " #" << __LINE__ <<")";
#else
#define SPDLOG_DEBUG(logger, ...)
#endif
// Drop the reference to the given logger
void drop(const std::string &name);
// Drop all references
void drop_all();
}
......
/*************************************************************************/
/* spdlog - an extremely fast and easy to use c++11 logging library. */
/* Copyright (c) 2014 Gabi Melman. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#pragma once
///////////////////////////////////////////////////////////////////////////////
// Edit this file to squeeze every last drop of performance out of spdlog.
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Under Linux, the much faster CLOCK_REALTIME_COARSE clock can be used.
// This clock is less accurate - can be off by dozens of millis - depending on the kernel HZ
// Uncomment to use it instead of the regular (but slower) clock.
// #define SPDLOG_CLOCK_COARSE
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment if date/time logging is not needed.
// This will prevent spdlog from quering the clock on each log call.
// #define SPDLOG_NO_DATETIME
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment if thread id logging is not needed (i.e. no %t in the log pattern)
// This will prevent spdlog from quering the thread id on each log call.
// #define SPDLOG_NO_THREAD_ID
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// 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 enable the SPDLOG_DEBUG/SPDLOG_TRACE macros
// #define SPDLOG_DEBUG_ON
// #define SPDLOG_TRACE_ON
///////////////////////////////////////////////////////////////////////////////
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment