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. ...@@ -5,18 +5,16 @@ Very fast, header only, C++ logging library.
## Install ## Install
Just copy the files to your build tree and use a C++11 compiler Just copy the files to your build tree and use a C++11 compiler
## Tested on: ## Platforms
* gcc 4.8.1 and above * Linux (gcc 4.8.1+, clang 3.5+)
* clang 3.5 * Windows (visual studio 2013+, mingw with g++ 4.9.1+)
* visual studio 2013 * Mac OSX (clang 3.5+)
* mingw with g++ 4.9.x
##Features ##Features
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below). * Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
* Headers only. * Headers only.
* No dependencies - just copy and use. * No dependencies - just copy and use.
* Cross platform - Linux / Windows on 32/64 bits. * Feature rich [call style](#usage-example) using the excellent [cppformat](http://cppformat.github.io/) library.
* **new!** Feature rich [call style](#usage-example) using the excellent [cppformat](http://cppformat.github.io/) library.
* ostream call style is supported too. * ostream call style is supported too.
* Extremely fast asynchronous mode (optional) - using lockfree queues and other tricks to reach millions of calls/sec. * 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. * [Custom](https://github.com/gabime/spdlog/wiki/Custom-formatting) formatting.
...@@ -73,7 +71,7 @@ int main(int, char* []) ...@@ -73,7 +71,7 @@ int main(int, char* [])
console->info("An info message example {}..", 1); console->info("An info message example {}..", 1);
console->info() << "Streams are supported too " << 1; console->info() << "Streams are supported too " << 1;
//Formatting examples //Formatting examples
console->info("Easy padding in numbers like {:08d}", 12); 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 int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
console->info("Support for floats {:03.2f}", 1.23456); console->info("Support for floats {:03.2f}", 1.23456);
...@@ -98,6 +96,11 @@ int main(int, char* []) ...@@ -98,6 +96,11 @@ int main(int, char* [])
for(int i = 0; i < 10; ++i) for(int i = 0; i < 10; ++i)
file_logger->info("{} * {} equals {:>10}", i, i, i*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 // Customize msg format for all messages
// //
...@@ -148,5 +151,8 @@ void custom_class_example() ...@@ -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);
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 ...@@ -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 main(int argc, char* argv[])
{ {
int howmany = 1048576; int queue_size = 1048576;
int howmany = 1000000;
int threads = 10; int threads = 10;
bool auto_flush = false; bool auto_flush = false;
int file_size = 30 * 1024 * 1024; int file_size = 30 * 1024 * 1024;
...@@ -63,6 +64,8 @@ int main(int argc, char* argv[]) ...@@ -63,6 +64,8 @@ int main(int argc, char* argv[])
howmany = atoi(argv[1]); howmany = atoi(argv[1]);
if (argc > 2) if (argc > 2)
threads = atoi(argv[2]); threads = atoi(argv[2]);
if (argc > 3)
queue_size = atoi(argv[3]);
cout << "*******************************************************************************\n"; cout << "*******************************************************************************\n";
...@@ -92,7 +95,7 @@ int main(int argc, char* argv[]) ...@@ -92,7 +95,7 @@ int main(int argc, char* argv[])
cout << "*******************************************************************************\n"; cout << "*******************************************************************************\n";
spdlog::set_async_mode(howmany); spdlog::set_async_mode(queue_size);
for(int i = 0; i < 3; ++i) for(int i = 0; i < 3; ++i)
{ {
......
...@@ -28,20 +28,18 @@ ...@@ -28,20 +28,18 @@
#include <iostream> #include <iostream>
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
int main(int, char* []) int main(int, char*[])
{ {
namespace spd = spdlog; namespace spd = spdlog;
try try
{ {
// Set log level to all loggers to debug and above //Create console, multithreaded logger
spd::set_level(spd::level::debug); auto console = spd::stdout_logger_mt("console");
console->info("Welcome to spdlog!");
// Create console, multithreaded logger
auto console = spd::stdout_logger_mt("console");
console->info("Hello {}", 1);
console->info("An info message example {}..", 1); console->info("An info message example {}..", 1);
console->info() << "Streams are supported too " << 1; console->info() << "Streams are supported too " << 1;
//Formatting examples
console->info("Easy padding in numbers like {:08d}", 12); 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 int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
console->info("Support for floats {:03.2f}", 1.23456); console->info("Support for floats {:03.2f}", 1.23456);
...@@ -51,37 +49,59 @@ int main(int, char* []) ...@@ -51,37 +49,59 @@ int main(int, char* [])
console->info("{:>30}", "right aligned"); console->info("{:>30}", "right aligned");
console->info("{:^30}", "centered"); 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 // 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); 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); 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 // Customize msg format for all messages
//
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***"); spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
file_logger->info("This is another message with custom format"); 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"); 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_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23); SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
//
// Asynchronous logging is very fast.. // Asynchronous logging is very fast..
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous.. // 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 size_t q_size = 1048576; //queue size must be power of 2
spdlog::set_async_mode(q_size); 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!"; async_file->info() << "This is async log.." << "Should be very fast!";
spdlog::drop_all(); //Close all loggers
//
// syslog example. linux only.. // syslog example. linux only..
//
#ifdef __linux__ #ifdef __linux__
std::string ident = "spdlog-example"; std::string ident = "spdlog-example";
auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID); 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!"); syslog_logger->warn("This is warning that will end up in syslog. This is Linux only!");
#endif #endif
//Close all loggers
spd::drop_all();
} }
catch (const spd::spdlog_ex& ex) catch (const spd::spdlog_ex& ex)
{ {
...@@ -90,16 +110,17 @@ int main(int, char* []) ...@@ -90,16 +110,17 @@ int main(int, char* [])
} }
//
// Example of user defined class with operator<< // Example of user defined class with operator<<
//
class some_class {}; 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() void custom_class_example()
{ {
some_class c; 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 @@ ...@@ -33,16 +33,9 @@
#ifndef _MSC_VER #ifndef _MSC_VER
#define SPDLOG_NOEXCEPT noexcept #define SPDLOG_NOEXCEPT noexcept
#else #else
#define SPDLOG_NOEXCEPT #define SPDLOG_NOEXCEPT throw()
#endif #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 namespace spdlog
{ {
...@@ -60,6 +53,7 @@ using sink_ptr = std::shared_ptr < sinks::sink > ; ...@@ -60,6 +53,7 @@ using sink_ptr = std::shared_ptr < sinks::sink > ;
using sinks_init_list = std::initializer_list < sink_ptr > ; using sinks_init_list = std::initializer_list < sink_ptr > ;
using formatter_ptr = std::shared_ptr<spdlog::formatter>; using formatter_ptr = std::shared_ptr<spdlog::formatter>;
//Log level enum //Log level enum
namespace level namespace level
{ {
......
...@@ -59,44 +59,50 @@ class async_log_helper ...@@ -59,44 +59,50 @@ class async_log_helper
std::string logger_name; std::string logger_name;
level::level_enum level; level::level_enum level;
log_clock::time_point time; log_clock::time_point time;
size_t thread_id;
std::string txt; std::string txt;
async_msg() = default; async_msg() = default;
~async_msg() = default; ~async_msg() = default;
async_msg(const async_msg&) = delete; async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
async_msg& operator=(async_msg& other) = delete; logger_name(std::move(other.logger_name)),
level(std::move(other.level)),
async_msg(const details::log_msg& m) : time(std::move(other.time)),
logger_name(m.logger_name), txt(std::move(other.txt))
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& operator=(async_msg&& other) async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT
{ {
logger_name = std::move(other.logger_name); logger_name = std::move(other.logger_name);
level = other.level; level = other.level;
time = std::move(other.time); time = std::move(other.time);
thread_id = other.thread_id;
txt = std::move(other.txt); txt = std::move(other.txt);
return *this; 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) void fill_log_msg(log_msg &msg)
{ {
msg.clear(); msg.clear();
msg.logger_name = logger_name; msg.logger_name = logger_name;
msg.level = level; msg.level = level;
msg.time = time; msg.time = time;
msg.thread_id = thread_id;
msg.raw << txt; msg.raw << txt;
} }
}; };
...@@ -117,8 +123,9 @@ public: ...@@ -117,8 +123,9 @@ public:
void log(const details::log_msg& msg); void log(const details::log_msg& msg);
//Stop logging and join the back thread // stop logging and join the back thread
~async_log_helper(); ~async_log_helper();
void set_formatter(formatter_ptr); void set_formatter(formatter_ptr);
...@@ -147,11 +154,11 @@ private: ...@@ -147,11 +154,11 @@ private:
// worker thread main loop // worker thread main loop
void worker_loop(); void worker_loop();
//pop next message from the queue and process it // 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 // 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); 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); 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() ...@@ -220,8 +227,8 @@ inline void spdlog::details::async_log_helper::worker_loop()
} }
} }
// Process next message in the queue // process next message in the queue
// Return true if this thread should still be active (no msg with level::off was received) // 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) 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 ...@@ -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) inline void spdlog::details::async_log_helper::sleep_or_yield(const clock::time_point& last_op_time)
{ {
using std::chrono::milliseconds; using std::chrono::milliseconds;
...@@ -261,7 +268,7 @@ inline void spdlog::details::async_log_helper::sleep_or_yield(const clock::time_ ...@@ -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; auto time_since_op = clock::now() - last_op_time;
//spin upto 1 ms // spin upto 1 ms
if (time_since_op <= milliseconds(1)) if (time_since_op <= milliseconds(1))
return; return;
...@@ -277,7 +284,7 @@ inline void spdlog::details::async_log_helper::sleep_or_yield(const clock::time_ ...@@ -277,7 +284,7 @@ inline void spdlog::details::async_log_helper::sleep_or_yield(const clock::time_
return sleep_for(milliseconds(100)); 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() inline void spdlog::details::async_log_helper::throw_if_bad_worker()
{ {
if (_last_workerthread_ex) if (_last_workerthread_ex)
......
/* /*
Formatting library for C++
Modified version of cppformat formatting library
Copyright (c) 2012 - 2015, Victor Zverovich
Orginal license: All rights reserved.
Copyright (c) 2012 - 2014, Victor Zverovich Redistribution and use in source and binary forms, with or without
All rights reserved. modification, are permitted provided that the following conditions are met:
Redistribution and use in source and binary forms, with or without 1. Redistributions of source code must retain the above copyright notice, this
modification, are permitted provided that the following conditions are met: list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
1. Redistributions of source code must retain the above copyright notice, this this list of conditions and the following disclaimer in the documentation
list of conditions and the following disclaimer. and/or other materials provided with the distribution.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
and/or other materials provided with the distribution. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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> #include <string.h>
...@@ -39,18 +36,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ...@@ -39,18 +36,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <cstdarg> #include <cstdarg>
#ifdef _WIN32 #ifdef _WIN32
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# ifdef __MINGW32__ # ifdef __MINGW32__
# include <cstring> # include <cstring>
# endif # endif
# include <windows.h> # include <windows.h>
#endif #endif
using spdlog::details::fmt::LongLong; using fmt::internal::Arg;
using spdlog::details::fmt::ULongLong;
using spdlog::details::fmt::internal::Arg;
// Check if exceptions are disabled. // Check if exceptions are disabled.
#if __GNUC__ && !__EXCEPTIONS #if __GNUC__ && !__EXCEPTIONS
...@@ -88,45 +80,64 @@ using spdlog::details::fmt::internal::Arg; ...@@ -88,45 +80,64 @@ using spdlog::details::fmt::internal::Arg;
#if _MSC_VER #if _MSC_VER
# pragma warning(push) # pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant # 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 #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 { namespace {
#ifndef _MSC_VER #ifndef _MSC_VER
# define FMT_SNPRINTF snprintf # define FMT_SNPRINTF snprintf
#else // _MSC_VER #else // _MSC_VER
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
va_list args; va_list args;
va_start(args, format); va_start(args, format);
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
va_end(args); va_end(args);
return result; return result;
} }
# define FMT_SNPRINTF fmt_snprintf # define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER #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 // Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers. // signed and unsigned integers.
template <bool IsSigned> template <bool IsSigned>
struct IntChecker { struct IntChecker {
template <typename T> template <typename T>
static bool fits_in_int(T value) { static bool fits_in_int(T value) {
unsigned max = INT_MAX; unsigned max = INT_MAX;
return value <= max; return value <= max;
} }
}; };
template <> template <>
struct IntChecker<true> { struct IntChecker<true> {
template <typename T> template <typename T>
static bool fits_in_int(T value) { static bool fits_in_int(T value) {
return value >= INT_MIN && value <= INT_MAX; return value >= INT_MIN && value <= INT_MAX;
} }
}; };
const char RESET_COLOR[] = "\x1b[0m"; 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. // Portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code. // 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:: ...@@ -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 // ERANGE - buffer is not large enough to store the error message
// other - failure // other - failure
// Buffer should be at least of size 1. // Buffer should be at least of size 1.
FMT_FUNC int safe_strerror( int safe_strerror(
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT(true) { int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
assert(buffer != 0 && buffer_size != 0); assert(buffer != 0 && buffer_size != 0);
int result = 0;
#ifdef _GNU_SOURCE class StrError {
char *message = strerror_r(error_code, buffer, buffer_size); private:
// If the buffer is full then the message is probably truncated. int error_code_;
if (message == buffer && strlen(buffer) == buffer_size - 1) char *&buffer_;
result = ERANGE; std::size_t buffer_size_;
buffer = message;
#elif __MINGW32__ // A noop assignment operator to avoid bogus warnings.
errno = 0; void operator=(const StrError &) {}
(void)buffer_size;
buffer = strerror(error_code); // Handle the result of XSI-compliant version of strerror_r.
result = errno; int handle(int result) {
#elif _WIN32 // glibc versions before 2.13 return result in errno.
result = strerror_s(buffer, buffer_size, error_code); return result == -1 ? errno : result;
// If the buffer is full then the message is probably truncated. }
if (result == 0 && std::strlen(buffer) == buffer_size - 1)
result = ERANGE; // Handle the result of GNU-specific version of strerror_r.
#else int handle(char *message) {
result = strerror_r(error_code, buffer, buffer_size); // If the buffer is full then the message is probably truncated.
if (result == -1) if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
result = errno; // glibc versions before 2.13 return result in errno. return ERANGE;
#endif buffer_ = message;
return result; 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, void format_error_code(fmt::Writer &out, int error_code,
spdlog::details::fmt::StringRef message) FMT_NOEXCEPT(true) { fmt::StringRef message) FMT_NOEXCEPT {
// Report error code making sure that the output fits into // Report error code making sure that the output fits into
// INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
// bad_alloc. // bad_alloc.
out.clear(); out.clear();
static const char SEP[] = ": "; static const char SEP[] = ": ";
static const char FMT_ERROR[] = "error "; static const char ERROR_STR[] = "error ";
spdlog::details::fmt::internal::IntTraits<int>::MainType ec_value = error_code; fmt::internal::IntTraits<int>::MainType ec_value = error_code;
// Subtract 2 to account for terminating null characters in SEP and FMT_ERROR. // Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
std::size_t error_code_size = std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
sizeof(SEP) + sizeof(FMT_ERROR) + spdlog::details::fmt::internal::count_digits(ec_value) - 2; error_code_size += fmt::internal::count_digits(ec_value);
if (message.size() <= spdlog::details::fmt::internal::INLINE_BUFFER_SIZE - error_code_size) if (message.size() <= fmt::internal::INLINE_BUFFER_SIZE - error_code_size)
out << message << SEP; out << message << SEP;
out << FMT_ERROR << error_code; out << ERROR_STR << error_code;
assert(out.size() <= spdlog::details::fmt::internal::INLINE_BUFFER_SIZE); assert(out.size() <= fmt::internal::INLINE_BUFFER_SIZE);
} }
FMT_FUNC void report_error(FormatFunc func, void report_error(FormatFunc func,
int error_code, spdlog::details::fmt::StringRef message) FMT_NOEXCEPT(true) { int error_code, fmt::StringRef message) FMT_NOEXCEPT {
spdlog::details::fmt::MemoryWriter full_message; fmt::MemoryWriter full_message;
func(full_message, error_code, message); func(full_message, error_code, message);
// Use Writer::data instead of Writer::c_str to avoid potential memory // Use Writer::data instead of Writer::c_str to avoid potential memory
// allocation. // allocation.
std::fwrite(full_message.data(), full_message.size(), 1, stderr); std::fwrite(full_message.data(), full_message.size(), 1, stderr);
std::fputc('\n', stderr); std::fputc('\n', stderr);
} }
// IsZeroInt::visit(arg) returns true iff arg is a zero integer. // IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt : public spdlog::details::fmt::internal::ArgVisitor<IsZeroInt, bool> { class IsZeroInt : public fmt::internal::ArgVisitor<IsZeroInt, bool> {
public: public:
template <typename T> template <typename T>
bool visit_any_int(T value) { bool visit_any_int(T value) { return value == 0; }
return value == 0;
}
}; };
// Parses an unsigned integer advancing s to the end of the parsed input. // 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. // This function assumes that the first character of s is a digit.
template <typename Char> template <typename Char>
FMT_FUNC int parse_nonnegative_int(const Char *&s) { int parse_nonnegative_int(const Char *&s) {
assert('0' <= *s && *s <= '9'); assert('0' <= *s && *s <= '9');
unsigned value = 0; unsigned value = 0;
do { do {
unsigned new_value = value * 10 + (*s++ - '0'); unsigned new_value = value * 10 + (*s++ - '0');
// Check if value wrapped around. // Check if value wrapped around.
if (new_value < value) { if (new_value < value) {
value = UINT_MAX; value = UINT_MAX;
break; break;
} }
value = new_value; value = new_value;
} while ('0' <= *s && *s <= '9'); } while ('0' <= *s && *s <= '9');
if (value > INT_MAX) if (value > INT_MAX)
FMT_THROW(spdlog::details::fmt::FormatError("number is too big")); FMT_THROW(fmt::FormatError("number is too big"));
return value; return value;
} }
inline void require_numeric_argument(const Arg &arg, char spec) { inline void require_numeric_argument(const Arg &arg, char spec) {
if (arg.type > Arg::LAST_NUMERIC_TYPE) { if (arg.type > Arg::LAST_NUMERIC_TYPE) {
std::string message = std::string message =
spdlog::details::fmt::format("format specifier '{}' requires numeric argument", spec); fmt::format("format specifier '{}' requires numeric argument", spec);
FMT_THROW(spdlog::details::fmt::FormatError(message)); FMT_THROW(fmt::FormatError(message));
} }
} }
template <typename Char> template <typename Char>
FMT_FUNC void check_sign(const Char *&s, const Arg &arg) { void check_sign(const Char *&s, const Arg &arg) {
char sign = static_cast<char>(*s); char sign = static_cast<char>(*s);
require_numeric_argument(arg, sign); require_numeric_argument(arg, sign);
if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) { if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) {
FMT_THROW(spdlog::details::fmt::FormatError(spdlog::details::fmt::format( FMT_THROW(fmt::FormatError(fmt::format(
"format specifier '{}' requires signed argument", sign))); "format specifier '{}' requires signed argument", sign)));
} }
++s; ++s;
} }
// Checks if an argument is a valid printf width specifier and sets // Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative. // left alignment if it is negative.
class WidthHandler : public spdlog::details::fmt::internal::ArgVisitor<WidthHandler, unsigned> { class WidthHandler : public fmt::internal::ArgVisitor<WidthHandler, unsigned> {
private: private:
spdlog::details::fmt::FormatSpec &spec_; fmt::FormatSpec &spec_;
public: FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
explicit WidthHandler(spdlog::details::fmt::FormatSpec &spec) : spec_(spec) {}
public:
unsigned visit_unhandled_arg() { explicit WidthHandler(fmt::FormatSpec &spec) : spec_(spec) {}
FMT_THROW(spdlog::details::fmt::FormatError("width is not integer"));
return 0; void report_unhandled_arg() {
} FMT_THROW(fmt::FormatError("width is not integer"));
}
template <typename T>
unsigned visit_any_int(T value) { template <typename T>
typedef typename spdlog::details::fmt::internal::IntTraits<T>::MainType UnsignedType; unsigned visit_any_int(T value) {
UnsignedType width = value; typedef typename fmt::internal::IntTraits<T>::MainType UnsignedType;
if (spdlog::details::fmt::internal::is_negative(value)) { UnsignedType width = value;
spec_.align_ = spdlog::details::fmt::ALIGN_LEFT; if (fmt::internal::is_negative(value)) {
width = 0 - width; spec_.align_ = 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);
} }
if (width > INT_MAX)
FMT_THROW(fmt::FormatError("number is too big"));
return static_cast<unsigned>(width);
}
}; };
class PrecisionHandler : class PrecisionHandler :
public spdlog::details::fmt::internal::ArgVisitor<PrecisionHandler, int> { public fmt::internal::ArgVisitor<PrecisionHandler, int> {
public: public:
unsigned visit_unhandled_arg() { void report_unhandled_arg() {
FMT_THROW(spdlog::details::fmt::FormatError("precision is not integer")); FMT_THROW(fmt::FormatError("precision is not integer"));
return 0; }
}
template <typename T>
template <typename T> int visit_any_int(T value) {
int visit_any_int(T value) { if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) FMT_THROW(fmt::FormatError("number is too big"));
FMT_THROW(spdlog::details::fmt::FormatError("number is too big")); return static_cast<int>(value);
return static_cast<int>(value); }
}
}; };
// Converts an integer argument to an integral type T for printf. // Converts an integer argument to an integral type T for printf.
template <typename T> template <typename T>
class ArgConverter : public spdlog::details::fmt::internal::ArgVisitor<ArgConverter<T>, void> { class ArgConverter : public fmt::internal::ArgVisitor<ArgConverter<T>, void> {
private: private:
spdlog::details::fmt::internal::Arg &arg_; fmt::internal::Arg &arg_;
wchar_t type_; wchar_t type_;
public: FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
ArgConverter(spdlog::details::fmt::internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type) {} public:
ArgConverter(fmt::internal::Arg &arg, wchar_t type)
template <typename U> : arg_(arg), type_(type) {}
void visit_any_int(U value) {
bool is_signed = type_ == 'd' || type_ == 'i'; template <typename U>
using spdlog::details::fmt::internal::Arg; void visit_any_int(U value) {
if (sizeof(T) <= sizeof(int)) { bool is_signed = type_ == 'd' || type_ == 'i';
// Extra casts are used to silence warnings. using fmt::internal::Arg;
if (is_signed) { if (sizeof(T) <= sizeof(int)) {
arg_.type = Arg::INT; // Extra casts are used to silence warnings.
arg_.int_value = static_cast<int>(static_cast<T>(value)); if (is_signed) {
} arg_.type = Arg::INT;
else { arg_.int_value = static_cast<int>(static_cast<T>(value));
arg_.type = Arg::UINT; } else {
arg_.uint_value = static_cast<unsigned>( arg_.type = Arg::UINT;
static_cast<typename spdlog::details::fmt::internal::MakeUnsigned<T>::Type>(value)); arg_.uint_value = static_cast<unsigned>(
} static_cast<typename fmt::internal::MakeUnsigned<T>::Type>(value));
} }
else { } else {
if (is_signed) { if (is_signed) {
arg_.type = Arg::LONG_LONG; arg_.type = Arg::LONG_LONG;
arg_.long_long_value = arg_.long_long_value =
static_cast<typename spdlog::details::fmt::internal::MakeUnsigned<U>::Type>(value); static_cast<typename fmt::internal::MakeUnsigned<U>::Type>(value);
} } else {
else { arg_.type = Arg::ULONG_LONG;
arg_.type = Arg::ULONG_LONG; arg_.ulong_long_value =
arg_.ulong_long_value = static_cast<typename fmt::internal::MakeUnsigned<U>::Type>(value);
static_cast<typename spdlog::details::fmt::internal::MakeUnsigned<U>::Type>(value); }
}
}
} }
}
}; };
// Converts an integer argument to char for printf. // Converts an integer argument to char for printf.
class CharConverter : public spdlog::details::fmt::internal::ArgVisitor<CharConverter, void> { class CharConverter : public fmt::internal::ArgVisitor<CharConverter, void> {
private: private:
spdlog::details::fmt::internal::Arg &arg_; fmt::internal::Arg &arg_;
public: FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
explicit CharConverter(spdlog::details::fmt::internal::Arg &arg) : arg_(arg) {}
template <typename T> public:
void visit_any_int(T value) { explicit CharConverter(fmt::internal::Arg &arg) : arg_(arg) {}
arg_.type = Arg::CHAR;
arg_.int_value = static_cast<char>(value); 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 // 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>); ...@@ -350,56 +388,52 @@ Arg::StringValue<Char> ignore_incompatible_str(Arg::StringValue<wchar_t>);
template <> template <>
inline Arg::StringValue<char> ignore_incompatible_str( inline Arg::StringValue<char> ignore_incompatible_str(
Arg::StringValue<wchar_t>) { Arg::StringValue<wchar_t>) { return Arg::StringValue<char>(); }
return Arg::StringValue<char>();
}
template <> template <>
inline Arg::StringValue<wchar_t> ignore_incompatible_str( inline Arg::StringValue<wchar_t> ignore_incompatible_str(
Arg::StringValue<wchar_t> s) { Arg::StringValue<wchar_t> s) { return s; }
return s;
}
} // namespace } // namespace
FMT_FUNC void spdlog::details::fmt::SystemError::init( FMT_FUNC void fmt::SystemError::init(
int error_code, StringRef format_str, ArgList args) { int err_code, StringRef format_str, ArgList args) {
error_code_ = error_code; error_code_ = err_code;
MemoryWriter w; MemoryWriter w;
internal::format_system_error(w, error_code, format(format_str, args)); internal::format_system_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this; std::runtime_error &base = *this;
base = std::runtime_error(w.str()); base = std::runtime_error(w.str());
} }
template <typename T> 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, char *buffer, std::size_t size, const char *format,
unsigned width, int precision, T value) { unsigned width, int precision, T value) {
if (width == 0) { if (width == 0) {
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, value) :
FMT_SNPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ? return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, width, value) : FMT_SNPRINTF(buffer, size, format, value) :
FMT_SNPRINTF(buffer, size, format, width, precision, 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> 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, wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, T value) { unsigned width, int precision, T value) {
if (width == 0) { if (width == 0) {
return precision < 0 ?
swprintf(buffer, size, format, value) :
swprintf(buffer, size, format, precision, value);
}
return precision < 0 ? return precision < 0 ?
swprintf(buffer, size, format, width, value) : FMT_SWPRINTF(buffer, size, format, value) :
swprintf(buffer, size, format, width, precision, 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> template <typename T>
const char spdlog::details::fmt::internal::BasicData<T>::DIGITS[] = const char fmt::internal::BasicData<T>::DIGITS[] =
"0001020304050607080910111213141516171819" "0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839" "2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859" "4041424344454647484950515253545556575859"
...@@ -418,753 +452,756 @@ const char spdlog::details::fmt::internal::BasicData<T>::DIGITS[] = ...@@ -418,753 +452,756 @@ const char spdlog::details::fmt::internal::BasicData<T>::DIGITS[] =
factor * 1000000000 factor * 1000000000
template <typename T> template <typename T>
const uint32_t spdlog::details::fmt::internal::BasicData<T>::POWERS_OF_10_32[] = { const uint32_t fmt::internal::BasicData<T>::POWERS_OF_10_32[] = {
0, FMT_POWERS_OF_10(1) 0, FMT_POWERS_OF_10(1)
}; };
template <typename T> template <typename T>
const uint64_t spdlog::details::fmt::internal::BasicData<T>::POWERS_OF_10_64[] = { const uint64_t fmt::internal::BasicData<T>::POWERS_OF_10_64[] = {
0, 0,
FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1),
FMT_POWERS_OF_10(ULongLong(1000000000)), FMT_POWERS_OF_10(fmt::ULongLong(1000000000)),
// Multiply several constants instead of using a single long long constant // Multiply several constants instead of using a single long long constant
// to avoid warnings about C++98 not supporting long long. // to avoid warnings about C++98 not supporting long long.
ULongLong(1000000000) * ULongLong(1000000000) * 10 fmt::ULongLong(1000000000) * fmt::ULongLong(1000000000) * 10
}; };
FMT_FUNC void spdlog::details::fmt::internal::report_unknown_type(char code, const char *type) { FMT_FUNC void fmt::internal::report_unknown_type(char code, const char *type) {
if (std::isprint(static_cast<unsigned char>(code))) { (void)type;
FMT_THROW(spdlog::details::fmt::FormatError( if (std::isprint(static_cast<unsigned char>(code))) {
spdlog::details::fmt::format("unknown format code '{}' for {}", code, type))); FMT_THROW(fmt::FormatError(
} fmt::format("unknown format code '{}' for {}", code, type)));
FMT_THROW(spdlog::details::fmt::FormatError( }
spdlog::details::fmt::format("unknown format code '\\x{:02x}' for {}", FMT_THROW(fmt::FormatError(
static_cast<unsigned>(code), type))); fmt::format("unknown format code '\\x{:02x}' for {}",
static_cast<unsigned>(code), type)));
} }
#ifdef _WIN32 #ifdef _WIN32
FMT_FUNC spdlog::details::fmt::internal::UTF8ToUTF16::UTF8ToUTF16(spdlog::details::fmt::StringRef s) { FMT_FUNC fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::StringRef s) {
int length = MultiByteToWideChar( int length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, 0, 0); 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"; static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
if (length == 0) if (length == 0)
FMT_THROW(WindowsError(GetLastError(), FMT_ERROR)); FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_.resize(length); buffer_.resize(length);
length = MultiByteToWideChar( length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, &buffer_[0], length); CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, &buffer_[0], length);
if (length == 0) if (length == 0)
FMT_THROW(WindowsError(GetLastError(), FMT_ERROR)); FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
} }
FMT_FUNC spdlog::details::fmt::internal::UTF16ToUTF8::UTF16ToUTF8(spdlog::details::fmt::WStringRef s) { FMT_FUNC fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) {
if (int error_code = convert(s)) { if (int error_code = convert(s)) {
FMT_THROW(WindowsError(error_code, FMT_THROW(WindowsError(error_code,
"cannot convert string from UTF-16 to UTF-8")); "cannot convert string from UTF-16 to UTF-8"));
} }
} }
FMT_FUNC int spdlog::details::fmt::internal::UTF16ToUTF8::convert(spdlog::details::fmt::WStringRef s) { FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) {
int length = WideCharToMultiByte(CP_UTF8, 0, s.c_str(), -1, 0, 0, 0, 0); int length = WideCharToMultiByte(CP_UTF8, 0, s.c_str(), -1, 0, 0, 0, 0);
if (length == 0) if (length == 0)
return GetLastError(); return GetLastError();
buffer_.resize(length); buffer_.resize(length);
length = WideCharToMultiByte( length = WideCharToMultiByte(
CP_UTF8, 0, s.c_str(), -1, &buffer_[0], length, 0, 0); CP_UTF8, 0, s.c_str(), -1, &buffer_[0], length, 0, 0);
if (length == 0) if (length == 0)
return GetLastError(); return GetLastError();
return 0; return 0;
} }
FMT_FUNC void spdlog::details::fmt::WindowsError::init( FMT_FUNC void fmt::WindowsError::init(
int error_code, StringRef format_str, ArgList args) { int err_code, StringRef format_str, ArgList args) {
error_code_ = error_code; error_code_ = err_code;
MemoryWriter w; MemoryWriter w;
internal::format_windows_error(w, error_code, format(format_str, args)); internal::format_windows_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this; std::runtime_error &base = *this;
base = std::runtime_error(w.str()); base = std::runtime_error(w.str());
} }
#endif #endif
FMT_FUNC void spdlog::details::fmt::internal::format_system_error( FMT_FUNC void fmt::internal::format_system_error(
spdlog::details::fmt::Writer &out, int error_code, fmt::Writer &out, int error_code,
spdlog::details::fmt::StringRef message) FMT_NOEXCEPT(true) { fmt::StringRef message) FMT_NOEXCEPT {
FMT_TRY { FMT_TRY {
MemoryBuffer<char, INLINE_BUFFER_SIZE> buffer; MemoryBuffer<char, INLINE_BUFFER_SIZE> buffer;
buffer.resize(INLINE_BUFFER_SIZE); buffer.resize(INLINE_BUFFER_SIZE);
for (;;) { for (;;) {
char *system_message = &buffer[0]; char *system_message = &buffer[0];
int result = safe_strerror(error_code, system_message, buffer.size()); int result = safe_strerror(error_code, system_message, buffer.size());
if (result == 0) { if (result == 0) {
out << message << ": " << system_message; out << message << ": " << system_message;
return; return;
} }
if (result != ERANGE) if (result != ERANGE)
break; // Can't get error message, report error code instead. break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2); buffer.resize(buffer.size() * 2);
} }
} FMT_CATCH(...) {} } FMT_CATCH(...) {}
format_error_code(out, error_code, message); format_error_code(out, error_code, message);
} }
#ifdef _WIN32 #ifdef _WIN32
FMT_FUNC void spdlog::details::fmt::internal::format_windows_error( FMT_FUNC void fmt::internal::format_windows_error(
spdlog::details::fmt::Writer &out, int error_code, fmt::Writer &out, int error_code,
spdlog::details::fmt::StringRef message) FMT_NOEXCEPT(true) { fmt::StringRef message) FMT_NOEXCEPT {
class String { class String {
private: private:
LPWSTR str_; LPWSTR str_;
public: public:
String() : str_() {} String() : str_() {}
~String() { ~String() { LocalFree(str_); }
LocalFree(str_); LPWSTR *ptr() { return &str_; }
} LPCWSTR c_str() const { return str_; }
LPWSTR *ptr() { };
return &str_; FMT_TRY {
} String system_message;
LPCWSTR c_str() const { if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
return str_;
}
};
FMT_TRY {
String system_message;
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0,
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPWSTR>(system_message.ptr()), 0, 0)) { reinterpret_cast<LPWSTR>(system_message.ptr()), 0, 0)) {
UTF16ToUTF8 utf8_message; UTF16ToUTF8 utf8_message;
if (utf8_message.convert(system_message.c_str()) == ERROR_SUCCESS) { if (utf8_message.convert(system_message.c_str()) == ERROR_SUCCESS) {
out << message << ": " << utf8_message; out << message << ": " << utf8_message;
return; return;
} }
} }
} FMT_CATCH(...) {} } FMT_CATCH(...) {}
format_error_code(out, error_code, message); format_error_code(out, error_code, message);
} }
#endif #endif
// An argument formatter. // An argument formatter.
template <typename Char> template <typename Char>
class spdlog::details::fmt::internal::ArgFormatter : class fmt::internal::ArgFormatter :
public spdlog::details::fmt::internal::ArgVisitor<spdlog::details::fmt::internal::ArgFormatter<Char>, void> { public fmt::internal::ArgVisitor<fmt::internal::ArgFormatter<Char>, void> {
private: private:
spdlog::details::fmt::BasicFormatter<Char> &formatter_; fmt::BasicFormatter<Char> &formatter_;
spdlog::details::fmt::BasicWriter<Char> &writer_; fmt::BasicWriter<Char> &writer_;
spdlog::details::fmt::FormatSpec &spec_; fmt::FormatSpec &spec_;
const Char *format_; const Char *format_;
public: FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatter);
ArgFormatter(
spdlog::details::fmt::BasicFormatter<Char> &f, spdlog::details::fmt::FormatSpec &s, const Char *fmt) public:
: formatter_(f), writer_(f.writer()), spec_(s), format_(fmt) {} ArgFormatter(
fmt::BasicFormatter<Char> &f,fmt::FormatSpec &s, const Char *fmt)
template <typename T> : formatter_(f), writer_(f.writer()), spec_(s), format_(fmt) {}
void visit_any_int(T value) {
writer_.write_int(value, spec_); 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;
} }
if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0)
template <typename T> FMT_THROW(FormatError("invalid format specifier for char"));
void visit_any_double(T value) { typedef typename fmt::BasicWriter<Char>::CharPtr CharPtr;
writer_.write_double(value, spec_); Char fill = static_cast<Char>(spec_.fill());
if (spec_.precision_ == 0) {
std::fill_n(writer_.grow_buffer(spec_.width_), spec_.width_, fill);
return;
} }
CharPtr out = CharPtr();
void visit_char(int value) { if (spec_.width_ > 1) {
if (spec_.type_ && spec_.type_ != 'c') { out = writer_.grow_buffer(spec_.width_);
spec_.flags_ |= CHAR_FLAG; if (spec_.align_ == fmt::ALIGN_RIGHT) {
writer_.write_int(value, spec_); std::fill_n(out, spec_.width_ - 1, fill);
return; out += spec_.width_ - 1;
} } else if (spec_.align_ == fmt::ALIGN_CENTER) {
if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0) out = writer_.fill_padding(out, spec_.width_, 1, fill);
FMT_THROW(FormatError("invalid format specifier for char")); } else {
typedef typename spdlog::details::fmt::BasicWriter<Char>::CharPtr CharPtr; std::fill_n(out + 1, spec_.width_ - 1, fill);
CharPtr out = CharPtr(); }
if (spec_.width_ > 1) { } else {
Char fill = static_cast<Char>(spec_.fill()); out = writer_.grow_buffer(1);
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_);
} }
*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 Char>
template <typename StrChar> template <typename StrChar>
FMT_FUNC void spdlog::details::fmt::BasicWriter<Char>::write_str( void fmt::BasicWriter<Char>::write_str(
const Arg::StringValue<StrChar> &str, const FormatSpec &spec) { const Arg::StringValue<StrChar> &s, const FormatSpec &spec) {
// Check if StrChar is convertible to Char. // Check if StrChar is convertible to Char.
internal::CharTraits<Char>::convert(StrChar()); internal::CharTraits<Char>::convert(StrChar());
if (spec.type_ && spec.type_ != 's') if (spec.type_ && spec.type_ != 's')
internal::report_unknown_type(spec.type_, "string"); internal::report_unknown_type(spec.type_, "string");
const StrChar *s = str.value; const StrChar *str_value = s.value;
std::size_t size = str.size; std::size_t str_size = s.size;
if (size == 0) { if (str_size == 0) {
if (!s) if (!str_value)
FMT_THROW(FormatError("string pointer is null")); FMT_THROW(FormatError("string pointer is null"));
if (*s) if (*str_value)
size = std::char_traits<StrChar>::length(s); str_size = std::char_traits<StrChar>::length(str_value);
} }
write_str(s, size, spec); 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> template <typename Char>
inline Arg spdlog::details::fmt::BasicFormatter<Char>::parse_arg_index(const Char *&s) { inline Arg fmt::BasicFormatter<Char>::parse_arg_index(const Char *&s) {
const char *error = 0; const char *error = 0;
Arg arg = *s < '0' || *s > '9' ? Arg arg = *s < '0' || *s > '9' ?
next_arg(error) : get_arg(parse_nonnegative_int(s), error); next_arg(error) : get_arg(parse_nonnegative_int(s), error);
if (error) { if (error) {
FMT_THROW(FormatError( FMT_THROW(FormatError(
*s != '}' && *s != ':' ? "invalid format string" : error)); *s != '}' && *s != ':' ? "invalid format string" : error));
} }
return arg; 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) { unsigned arg_index, const char *&error) {
Arg arg = args_[arg_index]; Arg arg = args_[arg_index];
if (arg.type == Arg::NONE) if (arg.type == Arg::NONE)
error = "argument index out of range"; error = "argument index out of range";
return arg; return arg;
} }
inline Arg spdlog::details::fmt::internal::FormatterBase::next_arg(const char *&error) { inline Arg fmt::internal::FormatterBase::next_arg(const char *&error) {
if (next_arg_index_ >= 0) if (next_arg_index_ >= 0)
return do_get_arg(next_arg_index_++, error); return do_get_arg(next_arg_index_++, error);
error = "cannot switch from manual to automatic argument indexing"; error = "cannot switch from manual to automatic argument indexing";
return Arg(); return Arg();
} }
inline Arg spdlog::details::fmt::internal::FormatterBase::get_arg( inline Arg fmt::internal::FormatterBase::get_arg(
unsigned arg_index, const char *&error) { unsigned arg_index, const char *&error) {
if (next_arg_index_ <= 0) { if (next_arg_index_ <= 0) {
next_arg_index_ = -1; next_arg_index_ = -1;
return do_get_arg(arg_index, error); return do_get_arg(arg_index, error);
} }
error = "cannot switch from automatic to manual argument indexing"; error = "cannot switch from automatic to manual argument indexing";
return Arg(); return Arg();
} }
template <typename Char> 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) { FormatSpec &spec, const Char *&s) {
for (;;) { for (;;) {
switch (*s++) { switch (*s++) {
case '-': case '-':
spec.align_ = ALIGN_LEFT; spec.align_ = ALIGN_LEFT;
break; break;
case '+': case '+':
spec.flags_ |= SIGN_FLAG | PLUS_FLAG; spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
break; break;
case '0': case '0':
spec.fill_ = '0'; spec.fill_ = '0';
break; break;
case ' ': case ' ':
spec.flags_ |= SIGN_FLAG; spec.flags_ |= SIGN_FLAG;
break; break;
case '#': case '#':
spec.flags_ |= HASH_FLAG; spec.flags_ |= HASH_FLAG;
break; break;
default: default:
--s; --s;
return; return;
}
} }
}
} }
template <typename Char> 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 *s, unsigned arg_index) {
const char *error = 0; (void)s;
Arg arg = arg_index == UINT_MAX ? const char *error = 0;
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); Arg arg = arg_index == UINT_MAX ?
if (error) next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
FMT_THROW(FormatError(!*s ? "invalid format string" : error)); if (error)
return arg; FMT_THROW(FormatError(!*s ? "invalid format string" : error));
return arg;
} }
template <typename Char> template <typename Char>
FMT_FUNC unsigned spdlog::details::fmt::internal::PrintfFormatter<Char>::parse_header( unsigned fmt::internal::PrintfFormatter<Char>::parse_header(
const Char *&s, FormatSpec &spec) { const Char *&s, FormatSpec &spec) {
unsigned arg_index = UINT_MAX; unsigned arg_index = UINT_MAX;
Char c = *s; Char c = *s;
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly // Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s). // preceded with '0' flag(s).
unsigned value = parse_nonnegative_int(s); unsigned value = parse_nonnegative_int(s);
if (*s == '$') { // value is an argument index if (*s == '$') { // value is an argument index
++s; ++s;
arg_index = value; arg_index = value;
} } else {
else { if (c == '0')
if (c == '0') spec.fill_ = '0';
spec.fill_ = '0'; if (value != 0) {
if (value != 0) { // Nonzero value means that we parsed width and don't need to
// Nonzero value means that we parsed width and don't need to // parse it or flags again, so return now.
// parse it or flags again, so return now. spec.width_ = value;
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; }
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> template <typename Char>
FMT_FUNC void spdlog::details::fmt::internal::PrintfFormatter<Char>::format( void fmt::internal::PrintfFormatter<Char>::format(
BasicWriter<Char> &writer, BasicStringRef<Char> format, BasicWriter<Char> &writer, BasicStringRef<Char> format_str,
const ArgList &args) { const ArgList &args) {
const Char *start = format.c_str(); const Char *start = format_str.c_str();
set_args(args); set_args(args);
const Char *s = start; const Char *s = start;
while (*s) { while (*s) {
Char c = *s++; Char c = *s++;
if (c != '%') continue; if (c != '%') continue;
if (*s == c) { if (*s == c) {
write(writer, start, s); write(writer, start, s);
start = ++s; start = ++s;
continue; continue;
} }
write(writer, start, s - 1); 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));
}
}
Arg arg = get_arg(s, arg_index); FormatSpec spec;
if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg)) spec.align_ = ALIGN_RIGHT;
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.
}
// Parse length and convert the argument to the required type. // Parse argument index, flags and width.
switch (*s++) { unsigned arg_index = parse_header(s, spec);
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 type. // Parse precision.
if (!*s) if (*s == '.') {
FMT_THROW(FormatError("invalid format string")); ++s;
spec.type_ = static_cast<char>(*s++); if ('0' <= *s && *s <= '9') {
if (arg.type <= Arg::LAST_INTEGER_TYPE) { spec.precision_ = parse_nonnegative_int(s);
// Normalize type. } else if (*s == '*') {
switch (spec.type_) { ++s;
case 'i': spec.precision_ = PrecisionHandler().visit(get_arg(s));
case 'u': }
spec.type_ = 'd'; }
break;
case 'c':
// TODO: handle wchar_t
CharConverter(arg).visit(arg);
break;
}
}
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. // Parse length and convert the argument to the required type.
switch (arg.type) { switch (*s++) {
case Arg::INT: case 'h':
writer.write_int(arg.int_value, spec); if (*s == 'h')
break; ArgConverter<signed char>(arg, *++s).visit(arg);
case Arg::UINT: else
writer.write_int(arg.uint_value, spec); ArgConverter<short>(arg, *s).visit(arg);
break; break;
case Arg::LONG_LONG: case 'l':
writer.write_int(arg.long_long_value, spec); if (*s == 'l')
break; ArgConverter<fmt::LongLong>(arg, *++s).visit(arg);
case Arg::ULONG_LONG: else
writer.write_int(arg.ulong_long_value, spec); ArgConverter<long>(arg, *s).visit(arg);
break; break;
case Arg::CHAR: { case 'j':
if (spec.type_ && spec.type_ != 'c') ArgConverter<intmax_t>(arg, *s).visit(arg);
writer.write_int(arg.int_value, spec); break;
typedef typename BasicWriter<Char>::CharPtr CharPtr; case 'z':
CharPtr out = CharPtr(); ArgConverter<size_t>(arg, *s).visit(arg);
if (spec.width_ > 1) { break;
Char fill = ' '; case 't':
out = writer.grow_buffer(spec.width_); ArgConverter<ptrdiff_t>(arg, *s).visit(arg);
if (spec.align_ != ALIGN_LEFT) { break;
std::fill_n(out, spec.width_ - 1, fill); case 'L':
out += spec.width_ - 1; // printf produces garbage when 'L' is omitted for long double, no
} // need to do the same.
else { break;
std::fill_n(out + 1, spec.width_ - 1, fill); default:
} --s;
} ArgConverter<int>(arg, *s).visit(arg);
else { }
out = writer.grow_buffer(1);
} // Parse type.
*out = static_cast<Char>(arg.int_value); if (!*s)
break; FMT_THROW(FormatError("invalid format string"));
} spec.type_ = static_cast<char>(*s++);
case Arg::DOUBLE: if (arg.type <= Arg::LAST_INTEGER_TYPE) {
writer.write_double(arg.double_value, spec); // Normalize type.
break; switch (spec.type_) {
case Arg::LONG_DOUBLE: case 'i': case 'u':
writer.write_double(arg.long_double_value, spec); spec.type_ = 'd';
break; break;
case Arg::CSTRING: case 'c':
arg.string.size = 0; // TODO: handle wchar_t
writer.write_str(arg.string, spec); CharConverter(arg).visit(arg);
break; break;
case Arg::STRING: }
writer.write_str(arg.string, spec); }
break;
case Arg::WSTRING: start = s;
writer.write_str(ignore_incompatible_str<Char>(arg.wstring), spec);
break; // Format argument.
case Arg::POINTER: switch (arg.type) {
if (spec.type_ && spec.type_ != 'p') case Arg::INT:
internal::report_unknown_type(spec.type_, "pointer"); writer.write_int(arg.int_value, spec);
spec.flags_ = HASH_FLAG; break;
spec.type_ = 'x'; case Arg::UINT:
writer.write_int(reinterpret_cast<uintptr_t>(arg.pointer), spec); writer.write_int(arg.uint_value, spec);
break; break;
case Arg::CUSTOM: { case Arg::LONG_LONG:
if (spec.type_) writer.write_int(arg.long_long_value, spec);
internal::report_unknown_type(spec.type_, "object"); break;
const void *s = "s"; case Arg::ULONG_LONG:
arg.custom.format(&writer, arg.custom.value, &s); writer.write_int(arg.ulong_long_value, spec);
break; break;
} case Arg::CHAR: {
default: if (spec.type_ && spec.type_ != 'c')
assert(false); writer.write_int(arg.int_value, spec);
break; 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> 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 *&format_str, const Arg &arg) {
const Char *s = format_str; const Char *s = format_str;
FormatSpec spec; FormatSpec spec;
if (*s == ':') { if (*s == ':') {
if (arg.type == Arg::CUSTOM) { if (arg.type == Arg::CUSTOM) {
arg.custom.format(this, arg.custom.value, &s); arg.custom.format(this, arg.custom.value, &s);
return s; return s;
} }
++s; ++s;
// Parse fill and alignment. // Parse fill and alignment.
if (Char c = *s) { if (Char c = *s) {
const Char *p = s + 1; const Char *p = s + 1;
spec.align_ = ALIGN_DEFAULT; spec.align_ = ALIGN_DEFAULT;
do { do {
switch (*p) { switch (*p) {
case '<': case '<':
spec.align_ = ALIGN_LEFT; spec.align_ = ALIGN_LEFT;
break; break;
case '>': case '>':
spec.align_ = ALIGN_RIGHT; 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;
break; break;
case '-': case '=':
check_sign(s, arg); spec.align_ = ALIGN_NUMERIC;
spec.flags_ |= MINUS_FLAG;
break; break;
case ' ': case '^':
check_sign(s, arg); spec.align_ = ALIGN_CENTER;
spec.flags_ |= SIGN_FLAG;
break; break;
} }
if (spec.align_ != ALIGN_DEFAULT) {
if (*s == '#') { if (p != s) {
require_numeric_argument(arg, '#'); if (c == '}') break;
spec.flags_ |= HASH_FLAG; if (c == '{')
++s; 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. // Parse sign.
if ('0' <= *s && *s <= '9') { switch (*s) {
if (*s == '0') { case '+':
require_numeric_argument(arg, '0'); check_sign(s, arg);
spec.align_ = ALIGN_NUMERIC; spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
spec.fill_ = '0'; break;
} case '-':
// Zero may be parsed again as a part of the width, but it is simpler check_sign(s, arg);
// and more efficient than checking if the next char is a digit. spec.flags_ |= MINUS_FLAG;
spec.width_ = parse_nonnegative_int(s); break;
} case ' ':
check_sign(s, arg);
spec.flags_ |= SIGN_FLAG;
break;
}
// Parse precision. if (*s == '#') {
if (*s == '.') { require_numeric_argument(arg, '#');
++s; spec.flags_ |= HASH_FLAG;
spec.precision_ = 0; ++s;
if ('0' <= *s && *s <= '9') { }
spec.precision_ = parse_nonnegative_int(s);
} // Parse width and zero flag.
else if (*s == '{') { if ('0' <= *s && *s <= '9') {
++s; if (*s == '0') {
const Arg &precision_arg = parse_arg_index(s); require_numeric_argument(arg, '0');
if (*s++ != '}') spec.align_ = ALIGN_NUMERIC;
FMT_THROW(FormatError("invalid format string")); spec.fill_ = '0';
ULongLong value = 0; }
switch (precision_arg.type) { // Zero may be parsed again as a part of the width, but it is simpler
case Arg::INT: // and more efficient than checking if the next char is a digit.
if (precision_arg.int_value < 0) spec.width_ = parse_nonnegative_int(s);
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"));
}
}
// Parse type. // Parse precision.
if (*s != '}' && *s) if (*s == '.') {
spec.type_ = static_cast<char>(*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++ != '}') // Parse type.
FMT_THROW(FormatError("missing '}' in format string")); if (*s != '}' && *s)
start_ = s; spec.type_ = static_cast<char>(*s++);
}
// Format argument. if (*s++ != '}')
internal::ArgFormatter<Char>(*this, spec, s - 1).visit(arg); FMT_THROW(FormatError("missing '}' in format string"));
return s; start_ = s;
// Format argument.
internal::ArgFormatter<Char>(*this, spec, s - 1).visit(arg);
return s;
} }
template <typename Char> template <typename Char>
FMT_FUNC void spdlog::details::fmt::BasicFormatter<Char>::format( void fmt::BasicFormatter<Char>::format(
BasicStringRef<Char> format_str, const ArgList &args) { BasicStringRef<Char> format_str, const ArgList &args) {
const Char *s = start_ = format_str.c_str(); const Char *s = start_ = format_str.c_str();
set_args(args); set_args(args);
while (*s) { while (*s) {
Char c = *s++; Char c = *s++;
if (c != '{' && c != '}') continue; if (c != '{' && c != '}') continue;
if (*s == c) { if (*s == c) {
write(writer_, start_, s); write(writer_, start_, s);
start_ = ++s; start_ = ++s;
continue; 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);
} }
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( FMT_FUNC void fmt::report_system_error(
int error_code, spdlog::details::fmt::StringRef message) FMT_NOEXCEPT(true) { int error_code, fmt::StringRef message) FMT_NOEXCEPT {
report_error(internal::format_system_error, error_code, message); report_error(internal::format_system_error, error_code, message);
} }
#ifdef _WIN32 #ifdef _WIN32
FMT_FUNC void spdlog::details::fmt::report_windows_error( FMT_FUNC void fmt::report_windows_error(
int error_code, spdlog::details::fmt::StringRef message) FMT_NOEXCEPT(true) { int error_code, fmt::StringRef message) FMT_NOEXCEPT {
report_error(internal::format_windows_error, error_code, message); report_error(internal::format_windows_error, error_code, message);
} }
#endif #endif
FMT_FUNC void spdlog::details::fmt::print(std::FILE *f, StringRef format_str, ArgList args) { FMT_FUNC void fmt::print(std::FILE *f, StringRef format_str, ArgList args) {
MemoryWriter w; MemoryWriter w;
w.write(format_str, args); w.write(format_str, args);
std::fwrite(w.data(), 1, w.size(), f); std::fwrite(w.data(), 1, w.size(), f);
} }
FMT_FUNC void spdlog::details::fmt::print(StringRef format_str, ArgList args) { FMT_FUNC void fmt::print(StringRef format_str, ArgList args) {
print(stdout, format_str, args); print(stdout, format_str, args);
} }
FMT_FUNC void spdlog::details::fmt::print(std::ostream &os, StringRef format_str, ArgList args) { FMT_FUNC void fmt::print(std::ostream &os, StringRef format_str, ArgList args) {
MemoryWriter w; MemoryWriter w;
w.write(format_str, args); w.write(format_str, args);
os.write(w.data(), w.size()); os.write(w.data(), w.size());
} }
FMT_FUNC void spdlog::details::fmt::print_colored(Color c, StringRef format, ArgList args) { FMT_FUNC void fmt::print_colored(Color c, StringRef format, ArgList args) {
char escape[] = "\x1b[30m"; char escape[] = "\x1b[30m";
escape[3] = '0' + static_cast<char>(c); escape[3] = '0' + static_cast<char>(c);
std::fputs(escape, stdout); std::fputs(escape, stdout);
print(format, args); print(format, args);
std::fputs(RESET_COLOR, stdout); std::fputs(RESET_COLOR, stdout);
} }
FMT_FUNC int spdlog::details::fmt::fprintf(std::FILE *f, StringRef format, ArgList args) { FMT_FUNC int fmt::fprintf(std::FILE *f, StringRef format, ArgList args) {
MemoryWriter w; MemoryWriter w;
printf(w, format, args); printf(w, format, args);
std::size_t size = w.size(); std::size_t size = w.size();
return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size); return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
} }
#ifndef FMT_HEADER_ONLY
// Explicit instantiations for char. // Explicit instantiations for char.
template const char *spdlog::details::fmt::BasicFormatter<char>::format( template void fmt::internal::FixedBuffer<char>::grow(std::size_t);
const char *&format_str, const spdlog::details::fmt::internal::Arg &arg);
template const char *fmt::BasicFormatter<char>::format(
const char *&format_str, const fmt::internal::Arg &arg);
template void spdlog::details::fmt::BasicFormatter<char>::format( template void fmt::BasicFormatter<char>::format(
BasicStringRef<char> format, const ArgList &args); BasicStringRef<char> format, const ArgList &args);
template void spdlog::details::fmt::internal::PrintfFormatter<char>::format( template void fmt::internal::PrintfFormatter<char>::format(
BasicWriter<char> &writer, BasicStringRef<char> format, const ArgList &args); 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, char *buffer, std::size_t size, const char *format,
unsigned width, int precision, double value); 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, char *buffer, std::size_t size, const char *format,
unsigned width, int precision, long double value); unsigned width, int precision, long double value);
// Explicit instantiations for wchar_t. // Explicit instantiations for wchar_t.
template const wchar_t *spdlog::details::fmt::BasicFormatter<wchar_t>::format( template void fmt::internal::FixedBuffer<wchar_t>::grow(std::size_t);
const wchar_t *&format_str, const spdlog::details::fmt::internal::Arg &arg);
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); 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, BasicWriter<wchar_t> &writer, BasicStringRef<wchar_t> format,
const ArgList &args); 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, wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, double value); 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, wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, long double value); unsigned width, int precision, long double value);
#endif // FMT_HEADER_ONLY
#if _MSC_VER #if _MSC_VER
# pragma warning(pop) # pragma warning(pop)
#endif #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 @@ ...@@ -27,7 +27,6 @@
#include "../common.h" #include "../common.h"
#include "../logger.h" #include "../logger.h"
// Line logger class - aggregates operator<< calls to fast ostream // Line logger class - aggregates operator<< calls to fast ostream
// and logs upon destruction // and logs upon destruction
...@@ -63,8 +62,16 @@ public: ...@@ -63,8 +62,16 @@ public:
{ {
if (_enabled) if (_enabled)
{ {
#ifndef SPDLOG_NO_NAME
_log_msg.logger_name = _callback_logger->name(); _log_msg.logger_name = _callback_logger->name();
#endif
#ifndef SPDLOG_NO_DATETIME
_log_msg.time = os::now(); _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); _callback_logger->_log_msg(_log_msg);
} }
} }
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#pragma once #pragma once
#include <thread>
#include "../common.h" #include "../common.h"
#include "./format.h" #include "./format.h"
...@@ -37,7 +38,6 @@ struct log_msg ...@@ -37,7 +38,6 @@ struct log_msg
log_msg(level::level_enum l): log_msg(level::level_enum l):
logger_name(), logger_name(),
level(l), level(l),
time(),
raw(), raw(),
formatted() {} formatted() {}
...@@ -45,7 +45,8 @@ struct log_msg ...@@ -45,7 +45,8 @@ struct log_msg
log_msg(const log_msg& other) : log_msg(const log_msg& other) :
logger_name(other.logger_name), logger_name(other.logger_name),
level(other.level), level(other.level),
time(other.time) time(other.time),
thread_id(other.thread_id)
{ {
if (other.raw.size()) if (other.raw.size())
raw << fmt::BasicStringRef<char>(other.raw.data(), other.raw.size()); raw << fmt::BasicStringRef<char>(other.raw.data(), other.raw.size());
...@@ -57,6 +58,7 @@ struct log_msg ...@@ -57,6 +58,7 @@ struct log_msg
logger_name(std::move(other.logger_name)), logger_name(std::move(other.logger_name)),
level(other.level), level(other.level),
time(std::move(other.time)), time(std::move(other.time)),
thread_id(other.thread_id),
raw(std::move(other.raw)), raw(std::move(other.raw)),
formatted(std::move(other.formatted)) formatted(std::move(other.formatted))
{ {
...@@ -71,6 +73,7 @@ struct log_msg ...@@ -71,6 +73,7 @@ struct log_msg
logger_name = std::move(other.logger_name); logger_name = std::move(other.logger_name);
level = other.level; level = other.level;
time = std::move(other.time); time = std::move(other.time);
thread_id = other.thread_id;
raw = std::move(other.raw); raw = std::move(other.raw);
formatted = std::move(other.formatted); formatted = std::move(other.formatted);
other.clear(); other.clear();
...@@ -87,6 +90,7 @@ struct log_msg ...@@ -87,6 +90,7 @@ struct log_msg
std::string logger_name; std::string logger_name;
level::level_enum level; level::level_enum level;
log_clock::time_point time; log_clock::time_point time;
size_t thread_id;
fmt::MemoryWriter raw; fmt::MemoryWriter raw;
fmt::MemoryWriter formatted; fmt::MemoryWriter formatted;
}; };
......
...@@ -32,6 +32,16 @@ ...@@ -32,6 +32,16 @@
# define WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN
# endif # endif
# include <Windows.h> # 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 #endif
#include "../common.h" #include "../common.h"
...@@ -46,7 +56,7 @@ namespace os ...@@ -46,7 +56,7 @@ namespace os
inline spdlog::log_clock::time_point now() inline spdlog::log_clock::time_point now()
{ {
#ifdef SPDLOG_CLOCK_COARSE #if defined __linux__ && defined SPDLOG_CLOCK_COARSE
timespec ts; timespec ts;
::clock_gettime(CLOCK_REALTIME_COARSE, &ts); ::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
return std::chrono::time_point<log_clock, typename log_clock::duration>( 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) ...@@ -73,7 +83,7 @@ inline std::tm localtime(const std::time_t &time_tt)
inline std::tm localtime() inline std::tm localtime()
{ {
std::time_t now_t = time(0); std::time_t now_t = time(nullptr);
return localtime(now_t); return localtime(now_t);
} }
...@@ -93,7 +103,7 @@ inline std::tm gmtime(const std::time_t &time_tt) ...@@ -93,7 +103,7 @@ inline std::tm gmtime(const std::time_t &time_tt)
inline std::tm gmtime() inline std::tm gmtime()
{ {
std::time_t now_t = time(0); std::time_t now_t = time(nullptr);
return gmtime(now_t); return gmtime(now_t);
} }
inline bool operator==(const std::tm& tm1, const std::tm& tm2) 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()) ...@@ -166,6 +176,19 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
#endif #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 } //os
} //details } //details
......
...@@ -354,7 +354,7 @@ class t_formatter :public flag_formatter ...@@ -354,7 +354,7 @@ class t_formatter :public flag_formatter
{ {
void format(details::log_msg& msg, const std::tm&) override 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 ...@@ -405,6 +405,7 @@ class full_formatter :public flag_formatter
{ {
void format(details::log_msg& msg, const std::tm& tm_time) override void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
#ifndef SPDLOG_NO_DATETIME
auto duration = msg.time.time_since_epoch(); auto duration = msg.time.time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000; auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
...@@ -421,6 +422,7 @@ class full_formatter :public flag_formatter ...@@ -421,6 +422,7 @@ class full_formatter :public flag_formatter
level::to_str(msg.level), level::to_str(msg.level),
msg.raw.str());*/ msg.raw.str());*/
// Faster (albeit uglier) way to format the line (5.6 million lines/sec under 10 threads) // 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) << '-' msg.formatted << '[' << static_cast<unsigned int>(tm_time.tm_year + 1900) << '-'
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_mon + 1), 2, '0') << '-' << fmt::pad(static_cast<unsigned int>(tm_time.tm_mon + 1), 2, '0') << '-'
...@@ -430,7 +432,16 @@ class full_formatter :public flag_formatter ...@@ -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>(tm_time.tm_sec), 2, '0') << '.'
<< fmt::pad(static_cast<unsigned int>(millis), 3, '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()); msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
} }
}; };
...@@ -478,7 +489,7 @@ inline void spdlog::pattern_formatter::handle_flag(char flag) ...@@ -478,7 +489,7 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
{ {
switch (flag) switch (flag)
{ {
// logger name // logger name
case 'n': case 'n':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::name_formatter())); _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::name_formatter()));
break; break;
...@@ -490,7 +501,7 @@ inline void spdlog::pattern_formatter::handle_flag(char flag) ...@@ -490,7 +501,7 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
case 'L': case 'L':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::short_level_formatter())); _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::short_level_formatter()));
break; break;
case('t') : case('t') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::t_formatter())); _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::t_formatter()));
break; break;
...@@ -610,8 +621,8 @@ inline void spdlog::pattern_formatter::format(details::log_msg& msg) ...@@ -610,8 +621,8 @@ inline void spdlog::pattern_formatter::format(details::log_msg& msg)
//write eol //write eol
msg.formatted << details::os::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 ...@@ -41,10 +41,17 @@ namespace spdlog
{ {
namespace details namespace details
{ {
class registry class registry
{ {
public: 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::shared_ptr<logger> get(const std::string& logger_name)
{ {
std::lock_guard<std::mutex> lock(_mutex); std::lock_guard<std::mutex> lock(_mutex);
...@@ -55,12 +62,12 @@ public: ...@@ -55,12 +62,12 @@ public:
template<class It> template<class It>
std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end) 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::shared_ptr<logger> new_logger;
std::lock_guard<std::mutex> lock(_mutex);
if (_async_mode) if (_async_mode)
new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb); new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb);
else else
...@@ -68,8 +75,9 @@ public: ...@@ -68,8 +75,9 @@ public:
if (_formatter) if (_formatter)
new_logger->set_formatter(_formatter); new_logger->set_formatter(_formatter);
new_logger->set_level(_level); new_logger->set_level(_level);
_loggers[logger_name] = new_logger; register_logger_impl(new_logger);
return new_logger; return new_logger;
} }
...@@ -103,14 +111,12 @@ public: ...@@ -103,14 +111,12 @@ public:
l.second->set_formatter(_formatter); l.second->set_formatter(_formatter);
} }
void set_pattern(const std::string& pattern) void set_pattern(const std::string& pattern)
{ {
std::lock_guard<std::mutex> lock(_mutex); std::lock_guard<std::mutex> lock(_mutex);
_formatter = std::make_shared<pattern_formatter>(pattern); _formatter = std::make_shared<pattern_formatter>(pattern);
for (auto& l : _loggers) for (auto& l : _loggers)
l.second->set_formatter(_formatter); l.second->set_formatter(_formatter);
} }
void set_level(level::level_enum log_level) void set_level(level::level_enum log_level)
...@@ -118,6 +124,7 @@ public: ...@@ -118,6 +124,7 @@ public:
std::lock_guard<std::mutex> lock(_mutex); std::lock_guard<std::mutex> lock(_mutex);
for (auto& l : _loggers) for (auto& l : _loggers)
l.second->set_level(log_level); 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) 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: ...@@ -135,7 +142,6 @@ public:
_async_mode = false; _async_mode = false;
} }
static registry& instance() static registry& instance()
{ {
static registry s_instance; static registry s_instance;
...@@ -143,6 +149,13 @@ public: ...@@ -143,6 +149,13 @@ public:
} }
private: 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() = default;
registry(const registry&) = delete; registry(const registry&) = delete;
registry& operator=(const registry&) = delete; registry& operator=(const registry&) = delete;
......
...@@ -32,6 +32,11 @@ ...@@ -32,6 +32,11 @@
#include "../sinks/stdout_sinks.h" #include "../sinks/stdout_sinks.h"
#include "../sinks/syslog_sink.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) inline std::shared_ptr<spdlog::logger> spdlog::get(const std::string& name)
{ {
return details::registry::instance().get(name); return details::registry::instance().get(name);
...@@ -54,35 +59,35 @@ inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(const std::str ...@@ -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): // 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 // Create stdout/stderr loggers
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name) 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) 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) 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) 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__ #ifdef __linux__
......
...@@ -30,21 +30,19 @@ ...@@ -30,21 +30,19 @@
#include "../details/file_helper.h" #include "../details/file_helper.h"
#include "../details/format.h" #include "../details/format.h"
namespace spdlog namespace spdlog
{ {
namespace sinks namespace sinks
{ {
/* /*
* Trivial file sink with single file as target * Trivial file sink with single file as target
*/ */
template<class Mutex> template<class Mutex>
class simple_file_sink : public base_sink<Mutex> class simple_file_sink : public base_sink < Mutex >
{ {
public: public:
explicit simple_file_sink(const std::string &filename, explicit simple_file_sink(const std::string &filename,
bool force_flush=false): bool force_flush = false) :
_file_helper(force_flush) _file_helper(force_flush)
{ {
_file_helper.open(filename); _file_helper.open(filename);
...@@ -64,14 +62,14 @@ typedef simple_file_sink<details::null_mutex> simple_file_sink_st; ...@@ -64,14 +62,14 @@ typedef simple_file_sink<details::null_mutex> simple_file_sink_st;
/* /*
* Rotating file sink based on size * Rotating file sink based on size
*/ */
template<class Mutex> template<class Mutex>
class rotating_file_sink : public base_sink<Mutex> class rotating_file_sink : public base_sink < Mutex >
{ {
public: public:
rotating_file_sink(const std::string &base_filename, const std::string &extension, rotating_file_sink(const std::string &base_filename, const std::string &extension,
std::size_t max_size, std::size_t max_files, std::size_t max_size, std::size_t max_files,
bool force_flush=false): bool force_flush = false) :
_base_filename(base_filename), _base_filename(base_filename),
_extension(extension), _extension(extension),
_max_size(max_size), _max_size(max_size),
...@@ -82,12 +80,11 @@ public: ...@@ -82,12 +80,11 @@ public:
_file_helper.open(calc_filename(_base_filename, 0, _extension)); _file_helper.open(calc_filename(_base_filename, 0, _extension));
} }
protected: protected:
void _sink_it(const details::log_msg& msg) override void _sink_it(const details::log_msg& msg) override
{ {
_current_size += msg.formatted.size(); _current_size += msg.formatted.size();
if (_current_size > _max_size) if (_current_size > _max_size)
{ {
_rotate(); _rotate();
_current_size = msg.formatted.size(); _current_size = msg.formatted.size();
...@@ -95,11 +92,10 @@ protected: ...@@ -95,11 +92,10 @@ protected:
_file_helper.write(msg); _file_helper.write(msg);
} }
private: private:
static std::string calc_filename(const std::string& filename, std::size_t index, const std::string& extension) 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) if (index)
w.write("{}.{}.{}", filename, index, extension); w.write("{}.{}.{}", filename, index, extension);
else else
...@@ -107,14 +103,12 @@ private: ...@@ -107,14 +103,12 @@ private:
return w.str(); return w.str();
} }
// Rotate files: // Rotate files:
// log.txt -> log.1.txt // log.txt -> log.1.txt
// log.1.txt -> log2.txt // log.1.txt -> log2.txt
// log.2.txt -> log3.txt // log.2.txt -> log3.txt
// log.3.txt -> delete // log.3.txt -> delete
void _rotate() void _rotate()
{ {
_file_helper.close(); _file_helper.close();
...@@ -152,60 +146,70 @@ typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st; ...@@ -152,60 +146,70 @@ typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
* Rotating file sink based on date. rotates at midnight * Rotating file sink based on date. rotates at midnight
*/ */
template<class Mutex> template<class Mutex>
class daily_file_sink:public base_sink<Mutex> class daily_file_sink :public base_sink < Mutex >
{ {
public: public:
explicit daily_file_sink(const std::string& base_filename, //create daily file sink which rotates on given time
const std::string& extension, daily_file_sink(
bool force_flush=false): const std::string& base_filename,
_base_filename(base_filename), const std::string& extension,
int rotation_hour,
int rotation_minute,
bool force_flush = false) : _base_filename(base_filename),
_extension(extension), _extension(extension),
_midnight_tp (_calc_midnight_tp() ), _rotation_h(rotation_hour),
_rotation_m(rotation_minute),
_file_helper(force_flush) _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)); _file_helper.open(calc_filename(_base_filename, _extension));
} }
protected: protected:
void _sink_it(const details::log_msg& msg) override 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)); _file_helper.open(calc_filename(_base_filename, _extension));
_midnight_tp = _calc_midnight_tp(); _rotation_tp = _next_rotation_tp();
} }
_file_helper.write(msg); _file_helper.write(msg);
} }
private: private:
// Return next midnight's time_point std::chrono::system_clock::time_point _next_rotation_tp()
static std::chrono::system_clock::time_point _calc_midnight_tp()
{ {
using namespace std::chrono; using namespace std::chrono;
auto now = system_clock::now(); auto now = system_clock::now();
time_t tnow = std::chrono::system_clock::to_time_t(now); time_t tnow = std::chrono::system_clock::to_time_t(now);
tm date = spdlog::details::os::localtime(tnow); tm date = spdlog::details::os::localtime(tnow);
date.tm_hour = date.tm_min = date.tm_sec = 0; date.tm_hour = _rotation_h;
auto midnight = std::chrono::system_clock::from_time_t(std::mktime(&date)); date.tm_min = _rotation_m;
return system_clock::time_point(midnight + hours(24)); 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 //Create filename for the form basename.YYYY-MM-DD.extension
static std::string calc_filename(const std::string& basename, const std::string& extension) static std::string calc_filename(const std::string& basename, const std::string& extension)
{ {
std::tm tm = spdlog::details::os::localtime(); std::tm tm = spdlog::details::os::localtime();
details::fmt::MemoryWriter w; fmt::MemoryWriter w;
w.write("{}.{:04d}-{:02d}-{:02d}.{}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, extension); 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(); return w.str();
} }
std::string _base_filename; std::string _base_filename;
std::string _extension; 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; details::file_helper _file_helper;
}; };
typedef daily_file_sink<std::mutex> daily_file_sink_mt; typedef daily_file_sink<std::mutex> daily_file_sink_mt;
......
...@@ -37,9 +37,14 @@ namespace sinks ...@@ -37,9 +37,14 @@ namespace sinks
template <class Mutex> template <class Mutex>
class stdout_sink : public ostream_sink<Mutex> class stdout_sink : public ostream_sink<Mutex>
{ {
using MyType = stdout_sink<Mutex>;
public: public:
stdout_sink() : ostream_sink<Mutex>(std::cout, true) {} 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; typedef stdout_sink<details::null_mutex> stdout_sink_st;
...@@ -49,8 +54,15 @@ typedef stdout_sink<std::mutex> stdout_sink_mt; ...@@ -49,8 +54,15 @@ typedef stdout_sink<std::mutex> stdout_sink_mt;
template <class Mutex> template <class Mutex>
class stderr_sink : public ostream_sink<Mutex> class stderr_sink : public ostream_sink<Mutex>
{ {
using MyType = stderr_sink<Mutex>;
public: public:
stderr_sink() : ostream_sink<Mutex>(std::cerr, true) {} 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; typedef stderr_sink<std::mutex> stderr_sink_mt;
......
...@@ -28,16 +28,12 @@ ...@@ -28,16 +28,12 @@
#pragma once #pragma once
#include "tweakme.h"
#include "common.h" #include "common.h"
#include "logger.h" #include "logger.h"
namespace spdlog namespace spdlog
{ {
// Return an existing logger or nullptr if a logger with such name doesn't exist. // Return an existing logger or nullptr if a logger with such name doesn't exist.
// Examples: // Examples:
// //
...@@ -55,7 +51,7 @@ void set_pattern(const std::string& format_string); ...@@ -55,7 +51,7 @@ void set_pattern(const std::string& format_string);
void set_formatter(formatter_ptr f); void set_formatter(formatter_ptr f);
// //
// Set global logging level for // Set global logging level for
// //
void set_level(level::level_enum log_level); void set_level(level::level_enum log_level);
...@@ -69,7 +65,7 @@ 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::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. // 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) // 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); 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 ...@@ -78,20 +74,20 @@ void set_async_mode(size_t queue_size, const async_overflow_policy overflow_poli
void set_sync_mode(); 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_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); 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_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, 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_mt(const std::string& logger_name);
std::shared_ptr<logger> stdout_logger_st(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); ...@@ -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__ #ifdef __linux__
std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0); std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0);
#endif #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); std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks);
template<class It> template<class It>
std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end); 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"); // Example: spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename", "txt");
template <typename Sink, typename... Args> template <typename Sink, typename... Args>
std::shared_ptr<spdlog::logger> create(const std::string& logger_name, const 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. // Macros to be display source file & line
// Note: using these mactors overrides the runtime log threshold of the logger. // 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: // Example:
// // spdlog::set_level(spdlog::level::debug);
// Enable debug macro, must be defined before including spdlog.h
// #define SPDLOG_DEBUG_ON
// include "spdlog/spdlog.h"
// SPDLOG_DEBUG(my_logger, "Some debug message {} {}", 1, 3.2); // SPDLOG_DEBUG(my_logger, "Some debug message {} {}", 1, 3.2);
// ///////////////////////////////////////////////////////////////////////////////
#ifdef SPDLOG_TRACE_ON #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 #else
#define SPDLOG_TRACE(logger, ...) #define SPDLOG_TRACE(logger, ...)
#endif #endif
#ifdef SPDLOG_DEBUG_ON #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 #else
#define SPDLOG_DEBUG(logger, ...) #define SPDLOG_DEBUG(logger, ...)
#endif #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