@@ -0,0 +1 @@ | |||
../spdlog/include/spdlog/ |
@@ -1,77 +0,0 @@ | |||
// | |||
// Copyright(c) 2015 Gabi Melman. | |||
// Distributed under the MIT License (http://opensource.org/licenses/MIT) | |||
// | |||
#pragma once | |||
// Very fast asynchronous logger (millions of logs per second on an average desktop) | |||
// Uses pre allocated lockfree queue for maximum throughput even under large number of threads. | |||
// Creates a single back thread to pop messages from the queue and log them. | |||
// | |||
// Upon each log write the logger: | |||
// 1. Checks if its log level is enough to log the message | |||
// 2. Push a new copy of the message to a queue (or block the caller until space is available in the queue) | |||
// 3. will throw spdlog_ex upon log exceptions | |||
// Upon destruction, logs all remaining messages in the queue before destructing.. | |||
#include <spdlog/common.h> | |||
#include <spdlog/logger.h> | |||
#include <chrono> | |||
#include <functional> | |||
#include <string> | |||
#include <memory> | |||
namespace spdlog | |||
{ | |||
namespace details | |||
{ | |||
class async_log_helper; | |||
} | |||
class async_logger :public logger | |||
{ | |||
public: | |||
template<class It> | |||
async_logger(const std::string& name, | |||
const It& begin, | |||
const It& end, | |||
size_t queue_size, | |||
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, | |||
const std::function<void()>& worker_warmup_cb = nullptr, | |||
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), | |||
const std::function<void()>& worker_teardown_cb = nullptr); | |||
async_logger(const std::string& logger_name, | |||
sinks_init_list sinks, | |||
size_t queue_size, | |||
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, | |||
const std::function<void()>& worker_warmup_cb = nullptr, | |||
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), | |||
const std::function<void()>& worker_teardown_cb = nullptr); | |||
async_logger(const std::string& logger_name, | |||
sink_ptr single_sink, | |||
size_t queue_size, | |||
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, | |||
const std::function<void()>& worker_warmup_cb = nullptr, | |||
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), | |||
const std::function<void()>& worker_teardown_cb = nullptr); | |||
//Wait for the queue to be empty, and flush synchronously | |||
//Warning: this can potentialy last forever as we wait it to complete | |||
void flush() override; | |||
protected: | |||
void _sink_it(details::log_msg& msg) override; | |||
void _set_formatter(spdlog::formatter_ptr msg_formatter) override; | |||
void _set_pattern(const std::string& pattern) override; | |||
private: | |||
std::unique_ptr<details::async_log_helper> _async_log_helper; | |||
}; | |||
} | |||
#include <spdlog/details/async_logger_impl.h> |
@@ -1,143 +0,0 @@ | |||
// | |||
// Copyright(c) 2015 Gabi Melman. | |||
// Distributed under the MIT License (http://opensource.org/licenses/MIT) | |||
// | |||
#pragma once | |||
#include <string> | |||
#include <initializer_list> | |||
#include <chrono> | |||
#include <memory> | |||
#include <atomic> | |||
#include <exception> | |||
#include<functional> | |||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) | |||
#include <codecvt> | |||
#include <locale> | |||
#endif | |||
#include <spdlog/details/null_mutex.h> | |||
//visual studio upto 2013 does not support noexcept nor constexpr | |||
#if defined(_MSC_VER) && (_MSC_VER < 1900) | |||
#define SPDLOG_NOEXCEPT throw() | |||
#define SPDLOG_CONSTEXPR | |||
#else | |||
#define SPDLOG_NOEXCEPT noexcept | |||
#define SPDLOG_CONSTEXPR constexpr | |||
#endif | |||
#if defined(__GNUC__) || defined(__clang__) | |||
#define DEPRECATED __attribute__((deprecated)) | |||
#elif defined(_MSC_VER) | |||
#define DEPRECATED __declspec(deprecated) | |||
#else | |||
#define DEPRECATED | |||
#endif | |||
#include <spdlog/fmt/fmt.h> | |||
namespace spdlog | |||
{ | |||
class formatter; | |||
namespace sinks | |||
{ | |||
class sink; | |||
} | |||
using log_clock = std::chrono::system_clock; | |||
using sink_ptr = std::shared_ptr < sinks::sink >; | |||
using sinks_init_list = std::initializer_list < sink_ptr >; | |||
using formatter_ptr = std::shared_ptr<spdlog::formatter>; | |||
#if defined(SPDLOG_NO_ATOMIC_LEVELS) | |||
using level_t = details::null_atomic_int; | |||
#else | |||
using level_t = std::atomic_int; | |||
#endif | |||
using log_err_handler = std::function<void(const std::string &err_msg)>; | |||
//Log level enum | |||
namespace level | |||
{ | |||
typedef enum | |||
{ | |||
trace = 0, | |||
debug = 1, | |||
info = 2, | |||
warn = 3, | |||
err = 4, | |||
critical = 5, | |||
off = 6 | |||
} level_enum; | |||
static const char* level_names[] { "trace", "debug", "info", "warning", "error", "critical", "off" }; | |||
static const char* short_level_names[] { "T", "D", "I", "W", "E", "C", "O" }; | |||
inline const char* to_str(spdlog::level::level_enum l) | |||
{ | |||
return level_names[l]; | |||
} | |||
inline const char* to_short_str(spdlog::level::level_enum l) | |||
{ | |||
return short_level_names[l]; | |||
} | |||
} //level | |||
// | |||
// Async overflow policy - block by default. | |||
// | |||
enum class async_overflow_policy | |||
{ | |||
block_retry, // Block / yield / sleep until message can be enqueued | |||
discard_log_msg // Discard the message it enqueue fails | |||
}; | |||
// | |||
// Log exception | |||
// | |||
namespace details | |||
{ | |||
namespace os | |||
{ | |||
std::string errno_str(int err_num); | |||
} | |||
} | |||
class spdlog_ex: public std::exception | |||
{ | |||
public: | |||
spdlog_ex(const std::string& msg):_msg(msg) | |||
{} | |||
spdlog_ex(const std::string& msg, int last_errno) | |||
{ | |||
_msg = msg + ": " + details::os::errno_str(last_errno); | |||
} | |||
const char* what() const SPDLOG_NOEXCEPT override | |||
{ | |||
return _msg.c_str(); | |||
} | |||
private: | |||
std::string _msg; | |||
}; | |||
// | |||
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) | |||
// | |||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) | |||
using filename_t = std::wstring; | |||
#else | |||
using filename_t = std::string; | |||
#endif | |||
} //spdlog |
@@ -1,390 +0,0 @@ | |||
// | |||
// Copyright(c) 2015 Gabi Melman. | |||
// Distributed under the MIT License (http://opensource.org/licenses/MIT) | |||
// | |||
// async log helper : | |||
// Process logs asynchronously using a back thread. | |||
// | |||
// If the internal queue of log messages reaches its max size, | |||
// then the client call will block until there is more room. | |||
// | |||
// If the back thread throws during logging, a spdlog::spdlog_ex exception | |||
// will be thrown in client's thread when tries to log the next message | |||
#pragma once | |||
#include <spdlog/common.h> | |||
#include <spdlog/sinks/sink.h> | |||
#include <spdlog/details/mpmc_bounded_q.h> | |||
#include <spdlog/details/log_msg.h> | |||
#include <spdlog/details/os.h> | |||
#include <spdlog/formatter.h> | |||
#include <chrono> | |||
#include <exception> | |||
#include <functional> | |||
#include <memory> | |||
#include <string> | |||
#include <thread> | |||
#include <utility> | |||
#include <vector> | |||
namespace spdlog | |||
{ | |||
namespace details | |||
{ | |||
class async_log_helper | |||
{ | |||
// Async msg to move to/from the queue | |||
// Movable only. should never be copied | |||
enum class async_msg_type | |||
{ | |||
log, | |||
flush, | |||
terminate | |||
}; | |||
struct async_msg | |||
{ | |||
std::string logger_name; | |||
level::level_enum level; | |||
log_clock::time_point time; | |||
size_t thread_id; | |||
std::string txt; | |||
async_msg_type msg_type; | |||
async_msg() = default; | |||
~async_msg() = default; | |||
async_msg(async_msg&& other) SPDLOG_NOEXCEPT: | |||
logger_name(std::move(other.logger_name)), | |||
level(std::move(other.level)), | |||
time(std::move(other.time)), | |||
txt(std::move(other.txt)), | |||
msg_type(std::move(other.msg_type)) | |||
{} | |||
async_msg(async_msg_type m_type) :msg_type(m_type) | |||
{}; | |||
async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT | |||
{ | |||
logger_name = std::move(other.logger_name); | |||
level = other.level; | |||
time = std::move(other.time); | |||
thread_id = other.thread_id; | |||
txt = std::move(other.txt); | |||
msg_type = other.msg_type; | |||
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) : | |||
level(m.level), | |||
time(m.time), | |||
thread_id(m.thread_id), | |||
txt(m.raw.data(), m.raw.size()), | |||
msg_type(async_msg_type::log) | |||
{ | |||
#ifndef SPDLOG_NO_NAME | |||
logger_name = *m.logger_name; | |||
#endif | |||
} | |||
// copy into log_msg | |||
void fill_log_msg(log_msg &msg) | |||
{ | |||
msg.logger_name = &logger_name; | |||
msg.level = level; | |||
msg.time = time; | |||
msg.thread_id = thread_id; | |||
msg.raw << txt; | |||
} | |||
}; | |||
public: | |||
using item_type = async_msg; | |||
using q_type = details::mpmc_bounded_queue<item_type>; | |||
using clock = std::chrono::steady_clock; | |||
async_log_helper(formatter_ptr formatter, | |||
const std::vector<sink_ptr>& sinks, | |||
size_t queue_size, | |||
const log_err_handler err_handler, | |||
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, | |||
const std::function<void()>& worker_warmup_cb = nullptr, | |||
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), | |||
const std::function<void()>& worker_teardown_cb = nullptr); | |||
void log(const details::log_msg& msg); | |||
// stop logging and join the back thread | |||
~async_log_helper(); | |||
void set_formatter(formatter_ptr); | |||
void flush(bool wait_for_q); | |||
private: | |||
formatter_ptr _formatter; | |||
std::vector<std::shared_ptr<sinks::sink>> _sinks; | |||
// queue of messages to log | |||
q_type _q; | |||
log_err_handler _err_handler; | |||
bool _flush_requested; | |||
bool _terminate_requested; | |||
// overflow policy | |||
const async_overflow_policy _overflow_policy; | |||
// worker thread warmup callback - one can set thread priority, affinity, etc | |||
const std::function<void()> _worker_warmup_cb; | |||
// auto periodic sink flush parameter | |||
const std::chrono::milliseconds _flush_interval_ms; | |||
// worker thread teardown callback | |||
const std::function<void()> _worker_teardown_cb; | |||
// worker thread | |||
std::thread _worker_thread; | |||
void push_msg(async_msg&& new_msg); | |||
// worker thread main loop | |||
void worker_loop(); | |||
// pop next message from the queue and process it. will set the last_pop to the pop time | |||
// return false if termination of the queue is required | |||
bool process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush); | |||
void handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush); | |||
// sleep,yield or return immediatly using the time passed since last message as a hint | |||
static void sleep_or_yield(const spdlog::log_clock::time_point& now, const log_clock::time_point& last_op_time); | |||
// wait until the queue is empty | |||
void wait_empty_q(); | |||
}; | |||
} | |||
} | |||
/////////////////////////////////////////////////////////////////////////////// | |||
// async_sink class implementation | |||
/////////////////////////////////////////////////////////////////////////////// | |||
inline spdlog::details::async_log_helper::async_log_helper( | |||
formatter_ptr formatter, | |||
const std::vector<sink_ptr>& sinks, | |||
size_t queue_size, | |||
log_err_handler err_handler, | |||
const async_overflow_policy overflow_policy, | |||
const std::function<void()>& worker_warmup_cb, | |||
const std::chrono::milliseconds& flush_interval_ms, | |||
const std::function<void()>& worker_teardown_cb): | |||
_formatter(formatter), | |||
_sinks(sinks), | |||
_q(queue_size), | |||
_err_handler(err_handler), | |||
_flush_requested(false), | |||
_terminate_requested(false), | |||
_overflow_policy(overflow_policy), | |||
_worker_warmup_cb(worker_warmup_cb), | |||
_flush_interval_ms(flush_interval_ms), | |||
_worker_teardown_cb(worker_teardown_cb), | |||
_worker_thread(&async_log_helper::worker_loop, this) | |||
{} | |||
// Send to the worker thread termination message(level=off) | |||
// and wait for it to finish gracefully | |||
inline spdlog::details::async_log_helper::~async_log_helper() | |||
{ | |||
try | |||
{ | |||
push_msg(async_msg(async_msg_type::terminate)); | |||
_worker_thread.join(); | |||
} | |||
catch (...) // don't crash in destructor | |||
{} | |||
} | |||
//Try to push and block until succeeded (if the policy is not to discard when the queue is full) | |||
inline void spdlog::details::async_log_helper::log(const details::log_msg& msg) | |||
{ | |||
push_msg(async_msg(msg)); | |||
} | |||
inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg) | |||
{ | |||
if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg) | |||
{ | |||
auto last_op_time = details::os::now(); | |||
auto now = last_op_time; | |||
do | |||
{ | |||
now = details::os::now(); | |||
sleep_or_yield(now, last_op_time); | |||
} | |||
while (!_q.enqueue(std::move(new_msg))); | |||
} | |||
} | |||
// optionally wait for the queue be empty and request flush from the sinks | |||
inline void spdlog::details::async_log_helper::flush(bool wait_for_q) | |||
{ | |||
push_msg(async_msg(async_msg_type::flush)); | |||
if(wait_for_q) | |||
wait_empty_q(); //return only make after the above flush message was processed | |||
} | |||
inline void spdlog::details::async_log_helper::worker_loop() | |||
{ | |||
try | |||
{ | |||
if (_worker_warmup_cb) _worker_warmup_cb(); | |||
auto last_pop = details::os::now(); | |||
auto last_flush = last_pop; | |||
while(process_next_msg(last_pop, last_flush)); | |||
if (_worker_teardown_cb) _worker_teardown_cb(); | |||
} | |||
catch (const std::exception &ex) | |||
{ | |||
_err_handler(ex.what()); | |||
} | |||
catch (...) | |||
{ | |||
_err_handler("Unknown exception"); | |||
} | |||
} | |||
// process next message in the queue | |||
// return true if this thread should still be active (while no terminate msg was received) | |||
inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush) | |||
{ | |||
async_msg incoming_async_msg; | |||
if (_q.dequeue(incoming_async_msg)) | |||
{ | |||
last_pop = details::os::now(); | |||
switch (incoming_async_msg.msg_type) | |||
{ | |||
case async_msg_type::flush: | |||
_flush_requested = true; | |||
break; | |||
case async_msg_type::terminate: | |||
_flush_requested = true; | |||
_terminate_requested = true; | |||
break; | |||
default: | |||
log_msg incoming_log_msg; | |||
incoming_async_msg.fill_log_msg(incoming_log_msg); | |||
_formatter->format(incoming_log_msg); | |||
for (auto &s : _sinks) | |||
{ | |||
if(s->should_log( incoming_log_msg.level)) | |||
{ | |||
s->log(incoming_log_msg); | |||
} | |||
} | |||
} | |||
return true; | |||
} | |||
// Handle empty queue.. | |||
// This is the only place where the queue can terminate or flush to avoid losing messages already in the queue | |||
else | |||
{ | |||
auto now = details::os::now(); | |||
handle_flush_interval(now, last_flush); | |||
sleep_or_yield(now, last_pop); | |||
return !_terminate_requested; | |||
} | |||
} | |||
// flush all sinks if _flush_interval_ms has expired | |||
inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush) | |||
{ | |||
auto should_flush = _flush_requested || (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms); | |||
if (should_flush) | |||
{ | |||
for (auto &s : _sinks) | |||
s->flush(); | |||
now = last_flush = details::os::now(); | |||
_flush_requested = false; | |||
} | |||
} | |||
inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter) | |||
{ | |||
_formatter = msg_formatter; | |||
} | |||
// spin, yield or sleep. use the time passed since last message as a hint | |||
inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_clock::time_point& now, const spdlog::log_clock::time_point& last_op_time) | |||
{ | |||
using namespace std::this_thread; | |||
using std::chrono::milliseconds; | |||
using std::chrono::microseconds; | |||
auto time_since_op = now - last_op_time; | |||
// spin upto 50 micros | |||
if (time_since_op <= microseconds(50)) | |||
return; | |||
// yield upto 150 micros | |||
if (time_since_op <= microseconds(100)) | |||
return yield(); | |||
// sleep for 20 ms upto 200 ms | |||
if (time_since_op <= milliseconds(200)) | |||
return sleep_for(milliseconds(20)); | |||
// sleep for 200 ms | |||
return sleep_for(milliseconds(200)); | |||
} | |||
// wait for the queue to be empty | |||
inline void spdlog::details::async_log_helper::wait_empty_q() | |||
{ | |||
auto last_op = details::os::now(); | |||
while (_q.approx_size() > 0) | |||
{ | |||
sleep_or_yield(details::os::now(), last_op); | |||
} | |||
} | |||
@@ -1,89 +0,0 @@ | |||
// | |||
// Copyright(c) 2015 Gabi Melman. | |||
// Distributed under the MIT License (http://opensource.org/licenses/MIT) | |||
// | |||
#pragma once | |||
// Async Logger implementation | |||
// Use an async_sink (queue per logger) to perform the logging in a worker thread | |||
#include <spdlog/details/async_log_helper.h> | |||
#include <spdlog/async_logger.h> | |||
#include <string> | |||
#include <functional> | |||
#include <chrono> | |||
#include <memory> | |||
template<class It> | |||
inline spdlog::async_logger::async_logger(const std::string& logger_name, | |||
const It& begin, | |||
const It& end, | |||
size_t queue_size, | |||
const async_overflow_policy overflow_policy, | |||
const std::function<void()>& worker_warmup_cb, | |||
const std::chrono::milliseconds& flush_interval_ms, | |||
const std::function<void()>& worker_teardown_cb) : | |||
logger(logger_name, begin, end), | |||
_async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, _err_handler, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb)) | |||
{ | |||
} | |||
inline spdlog::async_logger::async_logger(const std::string& logger_name, | |||
sinks_init_list sinks, | |||
size_t queue_size, | |||
const async_overflow_policy overflow_policy, | |||
const std::function<void()>& worker_warmup_cb, | |||
const std::chrono::milliseconds& flush_interval_ms, | |||
const std::function<void()>& worker_teardown_cb) : | |||
async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {} | |||
inline spdlog::async_logger::async_logger(const std::string& logger_name, | |||
sink_ptr single_sink, | |||
size_t queue_size, | |||
const async_overflow_policy overflow_policy, | |||
const std::function<void()>& worker_warmup_cb, | |||
const std::chrono::milliseconds& flush_interval_ms, | |||
const std::function<void()>& worker_teardown_cb) : | |||
async_logger(logger_name, | |||
{ | |||
single_sink | |||
}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {} | |||
inline void spdlog::async_logger::flush() | |||
{ | |||
_async_log_helper->flush(true); | |||
} | |||
inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter) | |||
{ | |||
_formatter = msg_formatter; | |||
_async_log_helper->set_formatter(_formatter); | |||
} | |||
inline void spdlog::async_logger::_set_pattern(const std::string& pattern) | |||
{ | |||
_formatter = std::make_shared<pattern_formatter>(pattern); | |||
_async_log_helper->set_formatter(_formatter); | |||
} | |||
inline void spdlog::async_logger::_sink_it(details::log_msg& msg) | |||
{ | |||
try | |||
{ | |||
_async_log_helper->log(msg); | |||
if (_should_flush_on(msg)) | |||
_async_log_helper->flush(false); // do async flush | |||
} | |||
catch (const std::exception &ex) | |||
{ | |||
_err_handler(ex.what()); | |||
} | |||
catch (...) | |||
{ | |||
_err_handler("Unknown exception"); | |||
} | |||
} |
@@ -1,118 +0,0 @@ | |||
// | |||
// Copyright(c) 2015 Gabi Melman. | |||
// Distributed under the MIT License (http://opensource.org/licenses/MIT) | |||
// | |||
#pragma once | |||
// Helper class for file sink | |||
// When failing to open a file, retry several times(5) with small delay between the tries(10 ms) | |||
// Can be set to auto flush on every line | |||
// Throw spdlog_ex exception on errors | |||
#include <spdlog/details/os.h> | |||
#include <spdlog/details/log_msg.h> | |||
#include <chrono> | |||
#include <cstdio> | |||
#include <string> | |||
#include <thread> | |||
#include <cerrno> | |||
namespace spdlog | |||
{ | |||
namespace details | |||
{ | |||
class file_helper | |||
{ | |||
public: | |||
const int open_tries = 5; | |||
const int open_interval = 10; | |||
explicit file_helper() : | |||
_fd(nullptr) | |||
{} | |||
file_helper(const file_helper&) = delete; | |||
file_helper& operator=(const file_helper&) = delete; | |||
~file_helper() | |||
{ | |||
close(); | |||
} | |||
void open(const filename_t& fname, bool truncate = false) | |||
{ | |||
close(); | |||
auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab"); | |||
_filename = fname; | |||
for (int tries = 0; tries < open_tries; ++tries) | |||
{ | |||
if (!os::fopen_s(&_fd, fname, mode)) | |||
return; | |||
std::this_thread::sleep_for(std::chrono::milliseconds(open_interval)); | |||
} | |||
throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno); | |||
} | |||
void reopen(bool truncate) | |||
{ | |||
if (_filename.empty()) | |||
throw spdlog_ex("Failed re opening file - was not opened before"); | |||
open(_filename, truncate); | |||
} | |||
void flush() | |||
{ | |||
std::fflush(_fd); | |||
} | |||
void close() | |||
{ | |||
if (_fd) | |||
{ | |||
std::fclose(_fd); | |||
_fd = nullptr; | |||
} | |||
} | |||
void write(const log_msg& msg) | |||
{ | |||
size_t msg_size = msg.formatted.size(); | |||
auto data = msg.formatted.data(); | |||
if (std::fwrite(data, 1, msg_size, _fd) != msg_size) | |||
throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno); | |||
} | |||
size_t size() | |||
{ | |||
if (!_fd) | |||
throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename)); | |||
return os::filesize(_fd); | |||
} | |||
const filename_t& filename() const | |||
{ | |||
return _filename; | |||
} | |||
static bool file_exists(const filename_t& name) | |||
{ | |||
return os::file_exists(name); | |||
} | |||
private: | |||
FILE* _fd; | |||
filename_t _filename; | |||
}; | |||
} | |||
} |
@@ -1,46 +0,0 @@ | |||
// | |||
// Copyright(c) 2015 Gabi Melman. | |||
// Distributed under the MIT License (http://opensource.org/licenses/MIT) | |||
// | |||
#pragma once | |||
#include <spdlog/common.h> | |||
#include <spdlog/details/os.h> | |||
#include <string> | |||
#include <utility> | |||
namespace spdlog | |||
{ | |||
namespace details | |||
{ | |||
struct log_msg | |||
{ | |||
log_msg() = default; | |||
log_msg(const std::string *loggers_name, level::level_enum lvl) : logger_name(loggers_name), level(lvl) | |||
{ | |||
#ifndef SPDLOG_NO_DATETIME | |||
time = os::now(); | |||
#endif | |||
#ifndef SPDLOG_NO_THREAD_ID | |||
thread_id = os::thread_id(); | |||
#endif | |||
} | |||
log_msg(const log_msg& other) = delete; | |||
log_msg& operator=(log_msg&& other) = delete; | |||
log_msg(log_msg&& other) = delete; | |||
const std::string *logger_name; | |||
level::level_enum level; | |||
log_clock::time_point time; | |||
size_t thread_id; | |||
fmt::MemoryWriter raw; | |||
fmt::MemoryWriter formatted; | |||
}; | |||
} | |||
} |
@@ -1,293 +0,0 @@ | |||
// | |||
// Copyright(c) 2015 Gabi Melman. | |||
// Distributed under the MIT License (http://opensource.org/licenses/MIT) | |||
// | |||
#pragma once | |||
#include <spdlog/logger.h> | |||
#include <spdlog/sinks/stdout_sinks.h> | |||
#include <memory> | |||
#include <string> | |||
// create logger with given name, sinks and the default pattern formatter | |||
// all other ctors will call this one | |||
template<class It> | |||
inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end): | |||
_name(logger_name), | |||
_sinks(begin, end), | |||
_formatter(std::make_shared<pattern_formatter>("%+")) | |||
{ | |||
_level = level::info; | |||
_flush_level = level::off; | |||
_last_err_time = 0; | |||
_err_handler = [this](const std::string &msg) | |||
{ | |||
this->_default_err_handler(msg); | |||
}; | |||
} | |||
// ctor with sinks as init list | |||
inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list): | |||
logger(logger_name, sinks_list.begin(), sinks_list.end()) | |||
{} | |||
// ctor with single sink | |||
inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink): | |||
logger(logger_name, | |||
{ | |||
single_sink | |||
}) | |||
{} | |||
inline spdlog::logger::~logger() = default; | |||
inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter) | |||
{ | |||
_set_formatter(msg_formatter); | |||
} | |||
inline void spdlog::logger::set_pattern(const std::string& pattern) | |||
{ | |||
_set_pattern(pattern); | |||
} | |||
template <typename... Args> | |||
inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args) | |||
{ | |||
if (!should_log(lvl)) return; | |||
try | |||
{ | |||
details::log_msg log_msg(&_name, lvl); | |||
log_msg.raw.write(fmt, args...); | |||
_sink_it(log_msg); | |||
} | |||
catch (const std::exception &ex) | |||
{ | |||
_err_handler(ex.what()); | |||
} | |||
catch (...) | |||
{ | |||
_err_handler("Unknown exception"); | |||
} | |||
} | |||
template <typename... Args> | |||
inline void spdlog::logger::log(level::level_enum lvl, const char* msg) | |||
{ | |||
if (!should_log(lvl)) return; | |||
try | |||
{ | |||
details::log_msg log_msg(&_name, lvl); | |||
log_msg.raw << msg; | |||
_sink_it(log_msg); | |||
} | |||
catch (const std::exception &ex) | |||
{ | |||
_err_handler(ex.what()); | |||
} | |||
catch (...) | |||
{ | |||
_err_handler("Unknown exception"); | |||
} | |||
} | |||
template<typename T> | |||
inline void spdlog::logger::log(level::level_enum lvl, const T& msg) | |||
{ | |||
if (!should_log(lvl)) return; | |||
try | |||
{ | |||
details::log_msg log_msg(&_name, lvl); | |||
log_msg.raw << msg; | |||
_sink_it(log_msg); | |||
} | |||
catch (const std::exception &ex) | |||
{ | |||
_err_handler(ex.what()); | |||
} | |||
catch (...) | |||
{ | |||
_err_handler("Unknown exception"); | |||
} | |||
} | |||
template <typename... Args> | |||
inline void spdlog::logger::trace(const char* fmt, const Args&... args) | |||
{ | |||
log(level::trace, fmt, args...); | |||
} | |||
template <typename... Args> | |||
inline void spdlog::logger::debug(const char* fmt, const Args&... args) | |||
{ | |||
log(level::debug, fmt, args...); | |||
} | |||
template <typename... Args> | |||
inline void spdlog::logger::info(const char* fmt, const Args&... args) | |||
{ | |||
log(level::info, fmt, args...); | |||
} | |||
template <typename... Args> | |||
inline void spdlog::logger::warn(const char* fmt, const Args&... args) | |||
{ | |||
log(level::warn, fmt, args...); | |||
} | |||
template <typename... Args> | |||
inline void spdlog::logger::error(const char* fmt, const Args&... args) | |||
{ | |||
log(level::err, fmt, args...); | |||
} | |||
template <typename... Args> | |||
inline void spdlog::logger::critical(const char* fmt, const Args&... args) | |||
{ | |||
log(level::critical, fmt, args...); | |||
} | |||
template<typename T> | |||
inline void spdlog::logger::trace(const T& msg) | |||
{ | |||
log(level::trace, msg); | |||
} | |||
template<typename T> | |||
inline void spdlog::logger::debug(const T& msg) | |||
{ | |||
log(level::debug, msg); | |||
} | |||
template<typename T> | |||
inline void spdlog::logger::info(const T& msg) | |||
{ | |||
log(level::info, msg); | |||
} | |||
template<typename T> | |||
inline void spdlog::logger::warn(const T& msg) | |||
{ | |||
log(level::warn, msg); | |||
} | |||
template<typename T> | |||
inline void spdlog::logger::error(const T& msg) | |||
{ | |||
log(level::err, msg); | |||
} | |||
template<typename T> | |||
inline void spdlog::logger::critical(const T& msg) | |||
{ | |||
log(level::critical, msg); | |||
} | |||
// | |||
// name and level | |||
// | |||
inline const std::string& spdlog::logger::name() const | |||
{ | |||
return _name; | |||
} | |||
inline void spdlog::logger::set_level(spdlog::level::level_enum log_level) | |||
{ | |||
_level.store(log_level); | |||
} | |||
inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler) | |||
{ | |||
_err_handler = err_handler; | |||
} | |||
inline spdlog::log_err_handler spdlog::logger::error_handler() | |||
{ | |||
return _err_handler; | |||
} | |||
inline void spdlog::logger::flush_on(level::level_enum log_level) | |||
{ | |||
_flush_level.store(log_level); | |||
} | |||
inline spdlog::level::level_enum spdlog::logger::level() const | |||
{ | |||
return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed)); | |||
} | |||
inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const | |||
{ | |||
return msg_level >= _level.load(std::memory_order_relaxed); | |||
} | |||
// | |||
// protected virtual called at end of each user log call (if enabled) by the line_logger | |||
// | |||
inline void spdlog::logger::_sink_it(details::log_msg& msg) | |||
{ | |||
_formatter->format(msg); | |||
for (auto &sink : _sinks) | |||
{ | |||
if( sink->should_log( msg.level)) | |||
{ | |||
sink->log(msg); | |||
} | |||
} | |||
if(_should_flush_on(msg)) | |||
flush(); | |||
} | |||
inline void spdlog::logger::_set_pattern(const std::string& pattern) | |||
{ | |||
_formatter = std::make_shared<pattern_formatter>(pattern); | |||
} | |||
inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter) | |||
{ | |||
_formatter = msg_formatter; | |||
} | |||
inline void spdlog::logger::flush() | |||
{ | |||
for (auto& sink : _sinks) | |||
sink->flush(); | |||
} | |||
inline void spdlog::logger::_default_err_handler(const std::string &msg) | |||
{ | |||
auto now = time(nullptr); | |||
if (now - _last_err_time < 60) | |||
return; | |||
auto tm_time = details::os::localtime(now); | |||
char date_buf[100]; | |||
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time); | |||
details::log_msg err_msg; | |||
err_msg.formatted.write("[*** LOG ERROR ***] [{}] [{}] [{}]{}", name(), msg, date_buf, details::os::eol); | |||
sinks::stderr_sink_mt::instance()->log(err_msg); | |||
_last_err_time = now; | |||
} | |||
inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg) | |||
{ | |||
const auto flush_level = _flush_level.load(std::memory_order_relaxed); | |||
return (msg.level >= flush_level) && (msg.level != level::off); | |||
} |
@@ -1,172 +0,0 @@ | |||
/* | |||
A modified version of Bounded MPMC queue by Dmitry Vyukov. | |||
Original code from: | |||
http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue | |||
licensed by Dmitry Vyukov under the terms below: | |||
Simplified BSD license | |||
Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. | |||
Redistribution and use in source and binary forms, with or without modification, | |||
are permitted provided that the following conditions are met: | |||
1. Redistributions of source code must retain the above copyright notice, this list of | |||
conditions and the following disclaimer. | |||
2. Redistributions in binary form must reproduce the above copyright notice, this list | |||
of conditions and the following disclaimer in the documentation and/or other materials | |||
provided with the distribution. | |||
THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED | |||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT | |||
SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, | |||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE | |||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
The views and conclusions contained in the software and documentation are those of the authors and | |||
should not be interpreted as representing official policies, either expressed or implied, of Dmitry Vyukov. | |||
*/ | |||
/* | |||
The code in its current form adds the license below: | |||
Copyright(c) 2015 Gabi Melman. | |||
Distributed under the MIT License (http://opensource.org/licenses/MIT) | |||
*/ | |||
#pragma once | |||
#include <spdlog/common.h> | |||
#include <atomic> | |||
#include <utility> | |||
namespace spdlog | |||
{ | |||
namespace details | |||
{ | |||
template<typename T> | |||
class mpmc_bounded_queue | |||
{ | |||
public: | |||
using item_type = T; | |||
mpmc_bounded_queue(size_t buffer_size) | |||
:max_size_(buffer_size), | |||
buffer_(new cell_t [buffer_size]), | |||
buffer_mask_(buffer_size - 1) | |||
{ | |||
//queue size must be power of two | |||
if(!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0))) | |||
throw spdlog_ex("async logger queue size must be power of two"); | |||
for (size_t i = 0; i != buffer_size; i += 1) | |||
buffer_[i].sequence_.store(i, std::memory_order_relaxed); | |||
enqueue_pos_.store(0, std::memory_order_relaxed); | |||
dequeue_pos_.store(0, std::memory_order_relaxed); | |||
} | |||
~mpmc_bounded_queue() | |||
{ | |||
delete [] buffer_; | |||
} | |||
bool enqueue(T&& data) | |||
{ | |||
cell_t* cell; | |||
size_t pos = enqueue_pos_.load(std::memory_order_relaxed); | |||
for (;;) | |||
{ | |||
cell = &buffer_[pos & buffer_mask_]; | |||
size_t seq = cell->sequence_.load(std::memory_order_acquire); | |||
intptr_t dif = (intptr_t)seq - (intptr_t)pos; | |||
if (dif == 0) | |||
{ | |||
if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) | |||
break; | |||
} | |||
else if (dif < 0) | |||
{ | |||
return false; | |||
} | |||
else | |||
{ | |||
pos = enqueue_pos_.load(std::memory_order_relaxed); | |||
} | |||
} | |||
cell->data_ = std::move(data); | |||
cell->sequence_.store(pos + 1, std::memory_order_release); | |||
return true; | |||
} | |||
bool dequeue(T& data) | |||
{ | |||
cell_t* cell; | |||
size_t pos = dequeue_pos_.load(std::memory_order_relaxed); | |||
for (;;) | |||
{ | |||
cell = &buffer_[pos & buffer_mask_]; | |||
size_t seq = | |||
cell->sequence_.load(std::memory_order_acquire); | |||
intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1); | |||
if (dif == 0) | |||
{ | |||
if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) | |||
break; | |||
} | |||
else if (dif < 0) | |||
return false; | |||
else | |||
pos = dequeue_pos_.load(std::memory_order_relaxed); | |||
} | |||
data = std::move(cell->data_); | |||
cell->sequence_.store(pos + buffer_mask_ + 1, std::memory_order_release); | |||
return true; | |||
} | |||
size_t approx_size() | |||
{ | |||
size_t first_pos = dequeue_pos_.load(std::memory_order_relaxed); | |||
size_t last_pos = enqueue_pos_.load(std::memory_order_relaxed); | |||
if (last_pos <= first_pos) | |||
return 0; | |||
auto size = last_pos - first_pos; | |||
return size < max_size_ ? size : max_size_; | |||
} | |||
private: | |||
struct cell_t | |||
{ | |||
std::atomic<size_t> sequence_; | |||
T data_; | |||
}; | |||
size_t const max_size_; | |||
static size_t const cacheline_size = 64; | |||
typedef char cacheline_pad_t [cacheline_size]; | |||
cacheline_pad_t pad0_; | |||
cell_t* const buffer_; | |||
size_t const buffer_mask_; | |||
cacheline_pad_t pad1_; | |||
std::atomic<size_t> enqueue_pos_; | |||
cacheline_pad_t pad2_; | |||
std::atomic<size_t> dequeue_pos_; | |||
cacheline_pad_t pad3_; | |||
mpmc_bounded_queue(mpmc_bounded_queue const&) = delete; | |||
void operator= (mpmc_bounded_queue const&) = delete; | |||
}; | |||
} // ns details | |||
} // ns spdlog |
@@ -1,45 +0,0 @@ | |||
// | |||
// Copyright(c) 2015 Gabi Melman. | |||
// Distributed under the MIT License (http://opensource.org/licenses/MIT) | |||
// | |||
#pragma once | |||
#include <atomic> | |||
// null, no cost dummy "mutex" and dummy "atomic" int | |||
namespace spdlog | |||
{ | |||
namespace details | |||
{ | |||
struct null_mutex | |||
{ | |||
void lock() {} | |||
void unlock() {} | |||
bool try_lock() | |||
{ | |||
return true; | |||
} | |||
}; | |||
struct null_atomic_int | |||
{ | |||
int value; | |||
null_atomic_int() = default; | |||
null_atomic_int(int val):value(val) | |||
{} | |||
int load(std::memory_order) const | |||
{ | |||
return value; | |||
} | |||
void store(int val) | |||
{ | |||
value = val; | |||
} | |||
}; | |||
} | |||
} |
@@ -1,361 +0,0 @@ | |||
// | |||
// Copyright(c) 2015 Gabi Melman. | |||
// Distributed under the MIT License (http://opensource.org/licenses/MIT) | |||
// | |||
#pragma once | |||
#include <spdlog/common.h> | |||
#include <cstdio> | |||
#include <ctime> | |||
#include <functional> | |||
#include <string> | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <sys/stat.h> | |||
#ifdef _WIN32 | |||
#ifndef NOMINMAX | |||
#define NOMINMAX //prevent windows redefining min/max | |||
#endif | |||
#ifndef WIN32_LEAN_AND_MEAN | |||
#define WIN32_LEAN_AND_MEAN | |||
#endif | |||
#include <windows.h> | |||
#ifdef __MINGW32__ | |||
#include <share.h> | |||
#endif | |||
#include <sys/types.h> | |||
#include <io.h> | |||
#elif __linux__ | |||
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id | |||
#include <unistd.h> | |||
#include <chrono> | |||
#elif __FreeBSD__ | |||
#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id | |||
#else | |||
#include <thread> | |||
#endif | |||
namespace spdlog | |||
{ | |||
namespace details | |||
{ | |||
namespace os | |||
{ | |||
inline spdlog::log_clock::time_point now() | |||
{ | |||
#if defined __linux__ && defined SPDLOG_CLOCK_COARSE | |||
timespec ts; | |||
::clock_gettime(CLOCK_REALTIME_COARSE, &ts); | |||
return std::chrono::time_point<log_clock, typename log_clock::duration>( | |||
std::chrono::duration_cast<typename log_clock::duration>( | |||
std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec))); | |||
#else | |||
return log_clock::now(); | |||
#endif | |||
} | |||
inline std::tm localtime(const std::time_t &time_tt) | |||
{ | |||
#ifdef _WIN32 | |||
std::tm tm; | |||
localtime_s(&tm, &time_tt); | |||
#else | |||
std::tm tm; | |||
localtime_r(&time_tt, &tm); | |||
#endif | |||
return tm; | |||
} | |||
inline std::tm localtime() | |||
{ | |||
std::time_t now_t = time(nullptr); | |||
return localtime(now_t); | |||
} | |||
inline std::tm gmtime(const std::time_t &time_tt) | |||
{ | |||
#ifdef _WIN32 | |||
std::tm tm; | |||
gmtime_s(&tm, &time_tt); | |||
#else | |||
std::tm tm; | |||
gmtime_r(&time_tt, &tm); | |||
#endif | |||
return tm; | |||
} | |||
inline std::tm gmtime() | |||
{ | |||
std::time_t now_t = time(nullptr); | |||
return gmtime(now_t); | |||
} | |||
inline bool operator==(const std::tm& tm1, const std::tm& tm2) | |||
{ | |||
return (tm1.tm_sec == tm2.tm_sec && | |||
tm1.tm_min == tm2.tm_min && | |||
tm1.tm_hour == tm2.tm_hour && | |||
tm1.tm_mday == tm2.tm_mday && | |||
tm1.tm_mon == tm2.tm_mon && | |||
tm1.tm_year == tm2.tm_year && | |||
tm1.tm_isdst == tm2.tm_isdst); | |||
} | |||
inline bool operator!=(const std::tm& tm1, const std::tm& tm2) | |||
{ | |||
return !(tm1 == tm2); | |||
} | |||
// eol definition | |||
#if !defined (SPDLOG_EOL) | |||
#ifdef _WIN32 | |||
#define SPDLOG_EOL "\r\n" | |||
#else | |||
#define SPDLOG_EOL "\n" | |||
#endif | |||
#endif | |||
SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL; | |||
SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1; | |||
//fopen_s on non windows for writing | |||
inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode) | |||
{ | |||
#ifdef _WIN32 | |||
#ifdef SPDLOG_WCHAR_FILENAMES | |||
*fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); | |||
#else | |||
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); | |||
#endif | |||
return *fp == nullptr; | |||
#else | |||
*fp = fopen((filename.c_str()), mode.c_str()); | |||
return *fp == nullptr; | |||
#endif | |||
} | |||
inline int remove(const filename_t &filename) | |||
{ | |||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) | |||
return _wremove(filename.c_str()); | |||
#else | |||
return std::remove(filename.c_str()); | |||
#endif | |||
} | |||
inline int rename(const filename_t& filename1, const filename_t& filename2) | |||
{ | |||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) | |||
return _wrename(filename1.c_str(), filename2.c_str()); | |||
#else | |||
return std::rename(filename1.c_str(), filename2.c_str()); | |||
#endif | |||
} | |||
//Return if file exists | |||
inline bool file_exists(const filename_t& filename) | |||
{ | |||
#ifdef _WIN32 | |||
#ifdef SPDLOG_WCHAR_FILENAMES | |||
auto attribs = GetFileAttributesW(filename.c_str()); | |||
#else | |||
auto attribs = GetFileAttributesA(filename.c_str()); | |||
#endif | |||
return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY)); | |||
#else //common linux/unix all have the stat system call | |||
struct stat buffer; | |||
return (stat (filename.c_str(), &buffer) == 0); | |||
#endif | |||
} | |||
//Return file size according to open FILE* object | |||
inline size_t filesize(FILE *f) | |||
{ | |||
if (f == nullptr) | |||
throw spdlog_ex("Failed getting file size. fd is null"); | |||
#ifdef _WIN32 | |||
int fd = _fileno(f); | |||
#if _WIN64 //64 bits | |||
struct _stat64 st; | |||
if (_fstat64(fd, &st) == 0) | |||
return st.st_size; | |||
#else //windows 32 bits | |||
long ret = _filelength(fd); | |||
if (ret >= 0) | |||
return static_cast<size_t>(ret); | |||
#endif | |||
#else // unix | |||
int fd = fileno(f); | |||
//64 bits(but not in osx, where fstat64 is deprecated) | |||
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) | |||
struct stat64 st; | |||
if (fstat64(fd, &st) == 0) | |||
return st.st_size; | |||
#else // unix 32 bits or osx | |||
struct stat st; | |||
if (fstat(fd, &st) == 0) | |||
return st.st_size; | |||
#endif | |||
#endif | |||
throw spdlog_ex("Failed getting file size from fd", errno); | |||
} | |||
//Return utc offset in minutes or throw spdlog_ex on failure | |||
inline int utc_minutes_offset(const std::tm& tm = details::os::localtime()) | |||
{ | |||
#ifdef _WIN32 | |||
#if _WIN32_WINNT < _WIN32_WINNT_WS08 | |||
TIME_ZONE_INFORMATION tzinfo; | |||
auto rv = GetTimeZoneInformation(&tzinfo); | |||
#else | |||
DYNAMIC_TIME_ZONE_INFORMATION tzinfo; | |||
auto rv = GetDynamicTimeZoneInformation(&tzinfo); | |||
#endif | |||
if (rv == TIME_ZONE_ID_INVALID) | |||
throw spdlog::spdlog_ex("Failed getting timezone info. ", errno); | |||
int offset = -tzinfo.Bias; | |||
if (tm.tm_isdst) | |||
offset -= tzinfo.DaylightBias; | |||
else | |||
offset -= tzinfo.StandardBias; | |||
return offset; | |||
#else | |||
#if defined(sun) || defined(__sun) | |||
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris | |||
struct helper | |||
{ | |||
static long int calculate_gmt_offset(const std::tm & localtm = details::os::localtime(), const std::tm & gmtm = details::os::gmtime()) | |||
{ | |||
int local_year = localtm.tm_year + (1900 - 1); | |||
int gmt_year = gmtm.tm_year + (1900 - 1); | |||
long int days = ( | |||
// difference in day of year | |||
localtm.tm_yday - gmtm.tm_yday | |||
// + intervening leap days | |||
+ ((local_year >> 2) - (gmt_year >> 2)) | |||
- (local_year / 100 - gmt_year / 100) | |||
+ ((local_year / 100 >> 2) - (gmt_year / 100 >> 2)) | |||
// + difference in years * 365 */ | |||
+ (long int)(local_year - gmt_year) * 365 | |||
); | |||
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour); | |||
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min); | |||
long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec); | |||
return secs; | |||
} | |||
}; | |||
long int offset_seconds = helper::calculate_gmt_offset(tm); | |||
#else | |||
long int offset_seconds = tm.tm_gmtoff; | |||
#endif | |||
return static_cast<int>(offset_seconds / 60); | |||
#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__ | |||
# if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21) | |||
# define SYS_gettid __NR_gettid | |||
# endif | |||
return static_cast<size_t>(syscall(SYS_gettid)); | |||
#elif __FreeBSD__ | |||
long tid; | |||
thr_self(&tid); | |||
return static_cast<size_t>(tid); | |||
#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 | |||
} | |||
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) | |||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) | |||
#define SPDLOG_FILENAME_T(s) L ## s | |||
inline std::string filename_to_str(const filename_t& filename) | |||
{ | |||
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c; | |||
return c.to_bytes(filename); | |||
} | |||
#else | |||
#define SPDLOG_FILENAME_T(s) s | |||
inline std::string filename_to_str(const filename_t& filename) | |||
{ | |||
return filename; | |||
} | |||
#endif | |||
// Return errno string (thread safe) | |||
inline std::string errno_str(int err_num) | |||
{ | |||
char buf[256]; | |||
SPDLOG_CONSTEXPR auto buf_size = sizeof(buf); | |||
#ifdef _WIN32 | |||
if(strerror_s(buf, buf_size, err_num) == 0) | |||
return std::string(buf); | |||
else | |||
return "Unkown error"; | |||
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || \ | |||
((_POSIX_C_SOURCE >= 200112L) && ! _GNU_SOURCE) // posix version | |||
if (strerror_r(err_num, buf, buf_size) == 0) | |||
return std::string(buf); | |||
else | |||
return "Unkown error"; | |||
#else // gnu version (might not use the given buf, so its retval pointer must be used) | |||
return std::string(strerror_r(err_num, buf, buf_size)); | |||
#endif | |||
} | |||
} //os | |||
} //details | |||
} //spdlog |
@@ -1,636 +0,0 @@ | |||
// | |||
// Copyright(c) 2015 Gabi Melman. | |||
// Distributed under the MIT License (http://opensource.org/licenses/MIT) | |||
// | |||
#pragma once | |||
#include <spdlog/formatter.h> | |||
#include <spdlog/details/log_msg.h> | |||
#include <spdlog/details/os.h> | |||
#include <spdlog/fmt/fmt.h> | |||
#include <chrono> | |||
#include <ctime> | |||
#include <memory> | |||
#include <mutex> | |||
#include <string> | |||
#include <thread> | |||
#include <utility> | |||
#include <vector> | |||
namespace spdlog | |||
{ | |||
namespace details | |||
{ | |||
class flag_formatter | |||
{ | |||
public: | |||
virtual ~flag_formatter() | |||
{} | |||
virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0; | |||
}; | |||
/////////////////////////////////////////////////////////////////////// | |||
// name & level pattern appenders | |||
/////////////////////////////////////////////////////////////////////// | |||
namespace | |||
{ | |||
class name_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm&) override | |||
{ | |||
msg.formatted << *msg.logger_name; | |||
} | |||
}; | |||
} | |||
// log level appender | |||
class level_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm&) override | |||
{ | |||
msg.formatted << level::to_str(msg.level); | |||
} | |||
}; | |||
// short log level appender | |||
class short_level_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm&) override | |||
{ | |||
msg.formatted << level::to_short_str(msg.level); | |||
} | |||
}; | |||
/////////////////////////////////////////////////////////////////////// | |||
// Date time pattern appenders | |||
/////////////////////////////////////////////////////////////////////// | |||
static const char* ampm(const tm& t) | |||
{ | |||
return t.tm_hour >= 12 ? "PM" : "AM"; | |||
} | |||
static int to12h(const tm& t) | |||
{ | |||
return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour; | |||
} | |||
//Abbreviated weekday name | |||
static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; | |||
class a_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
msg.formatted << days[tm_time.tm_wday]; | |||
} | |||
}; | |||
//Full weekday name | |||
static const std::string full_days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; | |||
class A_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
msg.formatted << full_days[tm_time.tm_wday]; | |||
} | |||
}; | |||
//Abbreviated month | |||
static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" }; | |||
class b_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
msg.formatted << months[tm_time.tm_mon]; | |||
} | |||
}; | |||
//Full month name | |||
static const std::string full_months[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; | |||
class B_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
msg.formatted << full_months[tm_time.tm_mon]; | |||
} | |||
}; | |||
//write 2 ints seperated by sep with padding of 2 | |||
static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, char sep) | |||
{ | |||
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0'); | |||
return w; | |||
} | |||
//write 3 ints seperated by sep with padding of 2 | |||
static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v3, char sep) | |||
{ | |||
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0'); | |||
return w; | |||
} | |||
//Date and time representation (Thu Aug 23 15:35:46 2014) | |||
class c_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
msg.formatted << days[tm_time.tm_wday] << ' ' << months[tm_time.tm_mon] << ' ' << tm_time.tm_mday << ' '; | |||
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << tm_time.tm_year + 1900; | |||
} | |||
}; | |||
// year - 2 digit | |||
class C_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
msg.formatted << fmt::pad(tm_time.tm_year % 100, 2, '0'); | |||
} | |||
}; | |||
// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01 | |||
class D_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
pad_n_join(msg.formatted, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_year % 100, '/'); | |||
} | |||
}; | |||
// year - 4 digit | |||
class Y_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
msg.formatted << tm_time.tm_year + 1900; | |||
} | |||
}; | |||
// month 1-12 | |||
class m_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
msg.formatted << fmt::pad(tm_time.tm_mon + 1, 2, '0'); | |||
} | |||
}; | |||
// day of month 1-31 | |||
class d_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
msg.formatted << fmt::pad(tm_time.tm_mday, 2, '0'); | |||
} | |||
}; | |||
// hours in 24 format 0-23 | |||
class H_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
msg.formatted << fmt::pad(tm_time.tm_hour, 2, '0'); | |||
} | |||
}; | |||
// hours in 12 format 1-12 | |||
class I_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
msg.formatted << fmt::pad(to12h(tm_time), 2, '0'); | |||
} | |||
}; | |||
// minutes 0-59 | |||
class M_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
msg.formatted << fmt::pad(tm_time.tm_min, 2, '0'); | |||
} | |||
}; | |||
// seconds 0-59 | |||
class S_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
msg.formatted << fmt::pad(tm_time.tm_sec, 2, '0'); | |||
} | |||
}; | |||
// milliseconds | |||
class e_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm&) override | |||
{ | |||
auto duration = msg.time.time_since_epoch(); | |||
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000; | |||
msg.formatted << fmt::pad(static_cast<int>(millis), 3, '0'); | |||
} | |||
}; | |||
// microseconds | |||
class f_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm&) override | |||
{ | |||
auto duration = msg.time.time_since_epoch(); | |||
auto micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count() % 1000000; | |||
msg.formatted << fmt::pad(static_cast<int>(micros), 6, '0'); | |||
} | |||
}; | |||
// nanoseconds | |||
class F_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm&) override | |||
{ | |||
auto duration = msg.time.time_since_epoch(); | |||
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % 1000000000; | |||
msg.formatted << fmt::pad(static_cast<int>(ns), 9, '0'); | |||
} | |||
}; | |||
// AM/PM | |||
class p_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
msg.formatted << ampm(tm_time); | |||
} | |||
}; | |||
// 12 hour clock 02:55:02 pm | |||
class r_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
pad_n_join(msg.formatted, to12h(tm_time), tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << ampm(tm_time); | |||
} | |||
}; | |||
// 24-hour HH:MM time, equivalent to %H:%M | |||
class R_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, ':'); | |||
} | |||
}; | |||
// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S | |||
class T_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':'); | |||
} | |||
}; | |||
// ISO 8601 offset from UTC in timezone (+-HH:MM) | |||
class z_formatter:public flag_formatter | |||
{ | |||
public: | |||
const std::chrono::seconds cache_refresh = std::chrono::seconds(5); | |||
z_formatter():_last_update(std::chrono::seconds(0)) | |||
{} | |||
z_formatter(const z_formatter&) = delete; | |||
z_formatter& operator=(const z_formatter&) = delete; | |||
void format(details::log_msg& msg, const std::tm& tm_time) override | |||
{ | |||
#ifdef _WIN32 | |||
int total_minutes = get_cached_offset(msg, tm_time); | |||
#else | |||
// No need to chache under gcc, | |||
// it is very fast (already stored in tm.tm_gmtoff) | |||
int total_minutes = os::utc_minutes_offset(tm_time); | |||
#endif | |||
bool is_negative = total_minutes < 0; | |||
char sign; | |||
if (is_negative) | |||
{ | |||
total_minutes = -total_minutes; | |||
sign = '-'; | |||
} | |||
else | |||
{ | |||
sign = '+'; | |||
} | |||
int h = total_minutes / 60; | |||
int m = total_minutes % 60; | |||
msg.formatted << sign; | |||
pad_n_join(msg.formatted, h, m, ':'); | |||
} | |||
private: | |||
log_clock::time_point _last_update; | |||
int _offset_minutes; | |||
std::mutex _mutex; | |||
int get_cached_offset(const log_msg& msg, const std::tm& tm_time) | |||
{ | |||
using namespace std::chrono; | |||
std::lock_guard<std::mutex> l(_mutex); | |||
if (msg.time - _last_update >= cache_refresh) | |||
{ | |||
_offset_minutes = os::utc_minutes_offset(tm_time); | |||
_last_update = msg.time; | |||
} | |||
return _offset_minutes; | |||
} | |||
}; | |||
//Thread id | |||
class t_formatter:public flag_formatter | |||
{ | |||
void format(details::log_msg& msg, const std::tm&) override | |||
{ | |||
msg.formatted << msg.thread_id; | |||
} | |||
}; | |||