This commit is contained in:
gabi
2014-10-31 01:13:27 +02:00
parent cbddc8796a
commit c7b8c762fb
25 changed files with 370 additions and 253 deletions

View File

@@ -0,0 +1,126 @@
#pragma once
// blocking_queue:
// A blocking multi-consumer/multi-producer thread safe queue.
// Has max capacity and supports timeout on push or pop operations.
#include <chrono>
#include <memory>
#include <queue>
#include <mutex>
#include <condition_variable>
namespace spdlog
{
namespace details
{
template<typename T>
class blocking_queue
{
public:
using queue_type = std::queue<T>;
using item_type = T;
using size_type = typename queue_type::size_type;
using clock = std::chrono::system_clock;
explicit blocking_queue(size_type max_size) :
_max_size(max_size),
_q(),
_mutex()
{
}
blocking_queue(const blocking_queue&) = delete;
blocking_queue& operator=(const blocking_queue&) = delete;
~blocking_queue() = default;
size_type size()
{
std::lock_guard<std::mutex> lock(_mutex);
return _q.size();
}
// Push copy of item into the back of the queue.
// If the queue is full, block the calling thread util there is room or timeout have passed.
// Return: false on timeout, true on successful push.
template<typename Duration_Rep, typename Duration_Period, typename TT>
bool push(TT&& item, const std::chrono::duration<Duration_Rep, Duration_Period>& timeout)
{
std::unique_lock<std::mutex> ul(_mutex);
if (_q.size() >= _max_size)
{
if (!_item_popped_cond.wait_until(ul, clock::now() + timeout, [this]()
{
return this->_q.size() < this->_max_size;
}))
return false;
}
_q.push(std::forward<TT>(item));
if (_q.size() <= 1)
{
ul.unlock(); //So the notified thread will have better chance to accuire the lock immediatly..
_item_pushed_cond.notify_one();
}
return true;
}
// Push copy of item into the back of the queue.
// If the queue is full, block the calling thread until there is room.
template<typename TT>
void push(TT&& item)
{
while (!push(std::forward<TT>(item), std::chrono::hours(1)));
}
// Pop a copy of the front item in the queue into the given item ref.
// If the queue is empty, block the calling thread util there is item to pop or timeout have passed.
// Return: false on timeout , true on successful pop/
template<class Duration_Rep, class Duration_Period>
bool pop(T& item, const std::chrono::duration<Duration_Rep, Duration_Period>& timeout)
{
std::unique_lock<std::mutex> ul(_mutex);
if (_q.empty())
{
if (!_item_pushed_cond.wait_until(ul, clock::now() + timeout, [this]()
{
return !this->_q.empty();
}))
return false;
}
item = std::move(_q.front());
_q.pop();
if (_q.size() >= _max_size - 1)
{
ul.unlock(); //So the notified thread will have better chance to accuire the lock immediatly..
_item_popped_cond.notify_one();
}
return true;
}
// Pop a copy of the front item in the queue into the given item ref.
// If the queue is empty, block the calling thread util there is item to pop.
void pop(T& item)
{
while (!pop(item, std::chrono::hours(1)));
}
// Clear the queue
void clear()
{
{
std::unique_lock<std::mutex> ul(_mutex);
queue_type().swap(_q);
}
_item_popped_cond.notify_all();
}
private:
size_type _max_size;
std::queue<T> _q;
std::mutex _mutex;
std::condition_variable _item_pushed_cond;
std::condition_variable _item_popped_cond;
};
}
}

View File

@@ -0,0 +1,103 @@
#pragma once
#include <string>
//Fast to int to string
//Source: http://stackoverflow.com/a/4351484/192001
//Modified version to pad zeros according to padding arg
namespace spdlog {
namespace details {
const char digit_pairs[201] = {
"00010203040506070809"
"10111213141516171819"
"20212223242526272829"
"30313233343536373839"
"40414243444546474849"
"50515253545556575859"
"60616263646566676869"
"70717273747576777879"
"80818283848586878889"
"90919293949596979899"
};
inline std::string& fast_itostr(int n, std::string& s, int padding)
{
if (n == 0)
{
s = std::string(padding, '0');
return s;
}
int sign = -(n < 0);
unsigned int val = (n^sign) - sign;
int size;
if (val >= 10000)
{
if (val >= 10000000)
{
if (val >= 1000000000)
size = 10;
else if (val >= 100000000)
size = 9;
else
size = 8;
}
else
{
if (val >= 1000000)
size = 7;
else if (val >= 100000)
size = 6;
else
size = 5;
}
}
else
{
if (val >= 100)
{
if (val >= 1000)
size = 4;
else
size = 3;
}
else
{
if (val >= 10)
size = 2;
else
size = 1;
}
}
size -= sign;
if (size < padding)
size = padding;
s.resize(size);
char* c = &s[0];
if (sign)
*c = '-';
c += size - 1;
while (val >= 100)
{
int pos = val % 100;
val /= 100;
*(short*)(c - 1) = *(short*)(digit_pairs + 2 * pos);
c -= 2;
}
while (val > 0)
{
*c-- = '0' + (val % 10);
val /= 10;
}
while (c >= s.data())
*c-- = '0';
return s;
}
}
}

View File

@@ -0,0 +1,165 @@
#pragma once
// A faster-than-ostringstream class
// uses stack_buf as the underlying buffer (upto 192 bytes before using the heap)
#include <ostream>
#include <iomanip>
#include "fast_istostr.h"
#include "stack_buf.h"
#include<iostream>
namespace spdlog
{
namespace details
{
class stack_devicebuf :public std::streambuf
{
public:
static const unsigned short stack_size = 256;
using stackbuf_t = stack_buf<stack_size>;
stack_devicebuf() = default;
~stack_devicebuf() = default;
stack_devicebuf(const stack_devicebuf& other) :std::basic_streambuf<char>(), _stackbuf(other._stackbuf)
{}
stack_devicebuf(stack_devicebuf&& other):
std::basic_streambuf<char>(),
_stackbuf(std::move(other._stackbuf))
{
other.clear();
}
stack_devicebuf& operator=(stack_devicebuf other)
{
std::swap(_stackbuf, other._stackbuf);
return *this;
}
const stackbuf_t& buf() const
{
return _stackbuf;
}
std::size_t size() const
{
return _stackbuf.size();
}
void clear()
{
_stackbuf.clear();
}
protected:
// copy the give buffer into the accumulated fast buffer
std::streamsize xsputn(const char_type* s, std::streamsize count) override
{
_stackbuf.append(s, static_cast<unsigned int>(count));
return count;
}
int_type overflow(int_type ch) override
{
if (traits_type::not_eof(ch))
{
char c = traits_type::to_char_type(ch);
xsputn(&c, 1);
}
return ch;
}
private:
stackbuf_t _stackbuf;
};
class fast_oss :public std::ostream
{
public:
fast_oss() :std::ostream(&_dev) {}
~fast_oss() = default;
fast_oss(const fast_oss& other) :std::basic_ios<char>(), std::ostream(&_dev), _dev(other._dev)
{}
fast_oss(fast_oss&& other) :std::basic_ios<char>(), std::ostream(&_dev), _dev(std::move(other._dev))
{
other.clear();
}
fast_oss& operator=(fast_oss other)
{
swap(*this, other);
return *this;
}
void swap(fast_oss& first, fast_oss& second) // nothrow
{
using std::swap;
swap(first._dev, second._dev);
}
std::string str()
{
auto& buffer = _dev.buf();
const char*data = buffer.data();
return std::string(data, data+buffer.size());
}
const stack_devicebuf::stackbuf_t& buf() const
{
return _dev.buf();
}
std::size_t size() const
{
return _dev.size();
}
void clear()
{
_dev.clear();
}
//
// The following were added because they significantly boost to perfromance
//
void putc(char c)
{
_dev.sputc(c);
}
// put int and pad with zeroes if smalled than min_width
void put_int(int n, int padding)
{
std::string s;
details::fast_itostr(n, s, padding);
_dev.sputn(s.data(), s.size());
}
void put_data(const char* p, std::size_t data_size)
{
_dev.sputn(p, data_size);
}
void put_str(const std::string& s)
{
_dev.sputn(s.data(), s.size());
}
void put_fast_oss(const fast_oss& oss)
{
auto& buffer = oss.buf();
_dev.sputn(buffer.data(), buffer.size());
}
private:
stack_devicebuf _dev;
};
}
}

View File

@@ -0,0 +1,111 @@
#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)
// Flush to file every X writes (or never if X==0)
// Throw fflog_ exception on errors
#include <cstdio>
#include <string>
#include <thread>
#include <chrono>
#include "../common.h"
namespace spdlog
{
namespace details
{
class file_helper
{
public:
static const int open_max_tries = 5;
static const int sleep_ms_bewteen_tries = 10;
explicit file_helper(const std::size_t flush_inverval):
_fd(nullptr),
_flush_inverval(flush_inverval),
_flush_countdown(flush_inverval) {};
file_helper(const file_helper&) = delete;
~file_helper()
{
close();
}
void open(const std::string& filename)
{
close();
_filename = filename;
for (int tries = 0; tries < open_max_tries; ++tries)
{
if(!os::fopen_s(&_fd, filename, "wb"))
return;
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms_bewteen_tries));
}
throw fflog_exception("Failed opening file " + filename + " for writing");
}
void close()
{
if (_fd)
{
std::fclose(_fd);
_fd = nullptr;
}
}
void write(const log_msg& msg)
{
auto& buf = msg.formatted.buf();
size_t size = buf.size();
if(std::fwrite(buf.data(), sizeof(char), size, _fd) != size)
throw fflog_exception("Failed writing to file " + _filename);
if(--_flush_countdown == 0)
{
std::fflush(_fd);
_flush_countdown = _flush_inverval;
}
}
const std::string& filename() const
{
return _filename;
}
static bool file_exists(const std::string& name)
{
FILE* file;
if (!os::fopen_s(&file, name.c_str(), "r"))
{
fclose(file);
return true;
}
else
{
return false;
}
}
private:
FILE* _fd;
std::string _filename;
const std::size_t _flush_inverval;
std::size_t _flush_countdown;
};
}
}

View File

@@ -0,0 +1,79 @@
#pragma once
#include "../common.h"
#include "../logger.h"
#include "./fast_oss.h"
// Line logger class - aggregates operator<< calls to fast ostream
// and logs upon destruction
namespace spdlog
{
namespace details
{
class line_logger
{
public:
line_logger(logger* callback_logger, level::level_enum msg_level, bool enabled):
_callback_logger(callback_logger),
_log_msg(msg_level),
_enabled(enabled)
{}
// No copy intended. Only move
line_logger(const line_logger& other) = delete;
line_logger& operator=(const line_logger&) = delete;
line_logger& operator=(line_logger&&) = delete;
line_logger(line_logger&& other) :
_callback_logger(other._callback_logger),
_log_msg(std::move(other._log_msg)),
_enabled(other._enabled)
{
other.disable();
}
//Log the log message using the callback logger
~line_logger()
{
if (_enabled)
{
_log_msg.logger_name = _callback_logger->name();
_log_msg.time = log_clock::now();
_log_msg.tm_time = details::os::localtime(log_clock::to_time_t(_log_msg.time));
_callback_logger->_log_msg(_log_msg);
}
}
template<typename T>
void write(const T& what)
{
if (_enabled)
{
_log_msg.raw << what;
}
}
template<typename T>
line_logger& operator<<(const T& what)
{
write(what);
return *this;
}
void disable()
{
_enabled = false;
}
private:
logger* _callback_logger;
log_msg _log_msg;
bool _enabled;
};
} //Namespace details
} // Namespace spdlog

View File

@@ -0,0 +1,69 @@
#pragma once
#include "../common.h"
#include "./fast_oss.h"
namespace spdlog
{
namespace details
{
struct log_msg
{
log_msg() = default;
log_msg(level::level_enum l):
logger_name(),
level(l),
time(),
tm_time(),
raw(),
formatted() {}
log_msg(const log_msg& other):
logger_name(other.logger_name),
level(other.level),
time(other.time),
tm_time(other.tm_time),
raw(other.raw),
formatted(other.formatted) {}
log_msg(log_msg&& other)
{
swap(*this, other);
}
void swap(log_msg& l, log_msg& r)
{
using std::swap;
swap(l.logger_name, r.logger_name);
swap(l.level, r.level);
swap(l.time, r.time);
swap(l.tm_time, r.tm_time);
swap(l.raw, r.raw);
swap(l.formatted, r.formatted);
}
log_msg& operator=(log_msg other)
{
swap(*this, other);
return *this;
}
void clear()
{
raw.clear();
formatted.clear();
}
std::string logger_name;
level::level_enum level;
log_clock::time_point time;
std::tm tm_time;
fast_oss raw;
fast_oss formatted;
};
}
}

View File

@@ -0,0 +1,136 @@
#pragma once
//
// Logger implementation
//
#include "./line_logger.h"
inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list) :
_name(logger_name),
_sinks(sinks_list)
{
// no support under vs2013 for member initialization for std::atomic
_level = level::INFO;
}
template<class It>
inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end) :
_name(logger_name),
_sinks(begin, end)
{}
inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter)
{
_formatter = msg_formatter;
}
inline void spdlog::logger::set_pattern(const std::string& pattern)
{
_formatter = std::make_shared<pattern_formatter>(pattern);
}
inline spdlog::formatter_ptr spdlog::logger::get_formatter() const
{
return _formatter;
}
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::log(level::level_enum lvl, const Args&... args) {
bool msg_enabled = should_log(lvl);
details::line_logger l(this, lvl, msg_enabled);
if (msg_enabled)
_variadic_log(l, args...);
return l;
}
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::log(const Args&... args) {
return log(level::ALWAYS, args...);
}
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::trace(const Args&... args)
{
return log(level::TRACE, args...);
}
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::debug(const Args&... args)
{
return log(level::DEBUG, args...);
}
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::info(const Args&... args)
{
return log(level::INFO, args...);
}
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::warn(const Args&... args)
{
return log(level::WARN, args...);
}
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::error(const Args&... args)
{
return log(level::ERR, args...);
}
template <typename... Args>
inline spdlog::details::line_logger spdlog::logger::critical(const Args&... args)
{
return log(level::CRITICAL, args...);
}
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 spdlog::level::level_enum spdlog::logger::level() const
{
return static_cast<spdlog::level::level_enum>(_level.load());
}
inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const
{
return msg_level >= _level.load();
}
inline void spdlog::logger::stop_logging()
{
set_level(level::OFF);
}
inline void spdlog::logger::_variadic_log(spdlog::details::line_logger&) {}
template <typename First, typename... Rest>
void spdlog::logger::_variadic_log(spdlog::details::line_logger& l, const First& first, const Rest&... rest)
{
l.write(first);
l.write(' ');
_variadic_log(l, rest...);
}
inline void spdlog::logger::_log_msg(details::log_msg& msg)
{
//Use default formatter if not set
if (!_formatter)
_formatter = std::make_shared<pattern_formatter>("%+");
_formatter->format(msg);
for (auto &sink : _sinks)
sink->log(msg);
}

View File

@@ -0,0 +1,17 @@
#pragma once
// null, no cost mutex
namespace spdlog {
namespace details {
struct null_mutex
{
void lock() {}
void unlock() {}
bool try_lock()
{
return true;
}
};
}
}

129
include/spdlog/details/os.h Normal file
View File

@@ -0,0 +1,129 @@
#pragma once
#include<string>
#include<cstdio>
#include<ctime>
#ifdef _WIN32
#include <Windows.h>
#endif
namespace spdlog
{
namespace details
{
namespace os
{
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(0);
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(0);
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);
}
#ifdef _WIN32
inline const char* eol()
{
return "\r\n";
}
#else
constexpr inline const char* eol()
{
return "\n";
}
#endif
#ifdef _WIN32
inline unsigned short eol_size()
{
return 2;
}
#else
constexpr inline unsigned short eol_size()
{
return 1;
}
#endif
//fopen_s on non windows for writing
inline int fopen_s(FILE** fp, const std::string& filename, const char* mode)
{
#ifdef _WIN32
return ::fopen_s(fp, filename.c_str(), mode);
#else
*fp = fopen((filename.c_str()), mode);
return *fp == nullptr;
#endif
}
//Return utc offset in minutes or -1 on failure
inline int utc_minutes_offset(const std::tm& tm = localtime())
{
#ifdef _WIN32
DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
auto rv = GetDynamicTimeZoneInformation(&tzinfo);
if (!rv)
return -1;
return -1 * (tzinfo.Bias + tzinfo.DaylightBias);
#else
return tm.tm_gmtoff / 60;
#endif
}
} //os
} //details
} //spdlog

View File

@@ -0,0 +1,534 @@
#pragma once
#include <string>
#include <chrono>
#include <memory>
#include <vector>
#include "../formatter.h"
#include "./log_msg.h"
#include "./fast_oss.h"
#include "./os.h"
namespace spdlog
{
namespace details {
class flag_formatter
{
public:
virtual void format(details::log_msg& msg) = 0;
};
///////////////////////////////////////////////////////////////////////
// name & level pattern appenders
///////////////////////////////////////////////////////////////////////
namespace {
class name_formatter :public flag_formatter
{
void format(details::log_msg& msg) override
{
msg.formatted << msg.logger_name;
}
};
}
// log level appender
class level_formatter :public flag_formatter
{
void format(details::log_msg& msg) override
{
msg.formatted << level::to_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) override
{
msg.formatted.put_str(days[msg.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) override
{
msg.formatted.put_str(full_days[msg.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) override
{
msg.formatted.put_str(months[msg.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) override
{
msg.formatted.put_str(full_months[msg.tm_time.tm_mon]);
}
};
//Date and time representation (Thu Aug 23 15:35:46 2014)
class c_formatter :public flag_formatter
{
void format(details::log_msg& msg) override
{
msg.formatted.put_str(days[msg.tm_time.tm_wday]);
msg.formatted.putc(' ');
msg.formatted.put_str(months[msg.tm_time.tm_mon]);
msg.formatted.putc(' ');
msg.formatted.put_int(msg.tm_time.tm_mday, 2);
msg.formatted.putc(' ');
msg.formatted.put_int(msg.tm_time.tm_hour, 2);
msg.formatted.putc(':');
msg.formatted.put_int(msg.tm_time.tm_min, 2);
msg.formatted.putc(':');
msg.formatted.put_int(msg.tm_time.tm_sec, 2);
}
};
// year - 2 digit
class C_formatter :public flag_formatter
{
void format(details::log_msg& msg) override
{
msg.formatted.put_int(msg.tm_time.tm_year % 100, 2);
}
};
// 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) override
{
msg.formatted.put_int(msg.tm_time.tm_mon + 1, 2);
msg.formatted.putc('/');
msg.formatted.put_int(msg.tm_time.tm_mday, 2);
msg.formatted.putc('/');
msg.formatted.put_int(msg.tm_time.tm_year % 100, 2);
}
};
// year - 4 digit
class Y_formatter :public flag_formatter
{
void format(details::log_msg& msg) override
{
msg.formatted.put_int(msg.tm_time.tm_year + 1900, 4);
}
};
// month 1-12
class m_formatter :public flag_formatter
{
void format(details::log_msg& msg) override
{
msg.formatted.put_int(msg.tm_time.tm_mon + 1, 2);
}
};
// day of month 1-31
class d_formatter :public flag_formatter
{
void format(details::log_msg& msg) override
{
msg.formatted.put_int(msg.tm_time.tm_mday, 2);
}
};
// hours in 24 format 0-23
class H_formatter :public flag_formatter
{
void format(details::log_msg& msg) override
{
msg.formatted.put_int(msg.tm_time.tm_hour, 2);
}
};
// hours in 12 format 1-12
class I_formatter :public flag_formatter
{
void format(details::log_msg& msg) override
{
msg.formatted.put_int(to12h(msg.tm_time), 2);
}
};
// ninutes 0-59
class M_formatter :public flag_formatter
{
void format(details::log_msg& msg) override
{
msg.formatted.put_int(msg.tm_time.tm_min, 2);
}
};
// seconds 0-59
class S_formatter :public flag_formatter
{
void format(details::log_msg& msg) override
{
msg.formatted.put_int(msg.tm_time.tm_sec, 2);
}
};
// milliseconds
class e_formatter :public flag_formatter
{
void format(details::log_msg& msg) override
{
auto duration = msg.time.time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
msg.formatted.put_int(static_cast<int>(millis), 3);
}
};
// AM/PM
class p_formatter :public flag_formatter
{
void format(details::log_msg& msg) override
{
msg.formatted.put_data(ampm(msg.tm_time), 2);
}
};
// 12 hour clock 02:55:02 pm
class r_formatter :public flag_formatter
{
void format(details::log_msg& msg) override
{
msg.formatted.put_int(to12h(msg.tm_time), 2);
msg.formatted.putc(':');
msg.formatted.put_int(msg.tm_time.tm_min, 2);
msg.formatted.putc(':');
msg.formatted.put_int(msg.tm_time.tm_sec, 2);
msg.formatted.putc(' ');
msg.formatted.put_data(ampm(msg.tm_time), 2);
}
};
// 24-hour HH:MM time, equivalent to %H:%M
class R_formatter :public flag_formatter
{
void format(details::log_msg& msg) override
{
msg.formatted.put_int(msg.tm_time.tm_hour, 2);
msg.formatted.putc(':');
msg.formatted.put_int(msg.tm_time.tm_min, 2);
}
};
// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
class T_formatter :public flag_formatter
{
void format(details::log_msg& msg) override
{
msg.formatted.put_int(msg.tm_time.tm_hour, 2);
msg.formatted.putc(':');
msg.formatted.put_int(msg.tm_time.tm_min, 2);
msg.formatted.putc(':');
msg.formatted.put_int(msg.tm_time.tm_sec, 2);
}
};
// ISO 8601 offset from UTC in timezone (HH:MM)
class z_formatter :public flag_formatter
{
public:
void format(log_msg& msg) override
{
std::lock_guard<std::mutex> l(_mutex);
using namespace std::chrono;
auto diff = msg.time - _last_update;
auto secs_diff = abs((duration_cast<seconds>(diff)).count());
if (secs_diff >= 2)
{
_value = get_value(msg);
_last_update = msg.time;
}
msg.formatted.put_str(_value);
}
private:
log_clock::time_point _last_update;
std::string _value;
std::string get_value(const log_msg& msg)
{
int total_minutes = os::utc_minutes_offset(msg.tm_time);
int h = total_minutes / 60;
int m = total_minutes % 60;
fast_oss oss;
oss.putc(h < 0 ? '-' : '+');
oss.put_int(h, 2);
oss.putc(':');
oss.put_int(m, 2);
return oss.str();
}
std::mutex _mutex;
};
class t_formatter :public flag_formatter
{
void format(details::log_msg& msg) override
{
msg.formatted.put_fast_oss(msg.raw);
}
};
class ch_formatter :public flag_formatter
{
public:
explicit ch_formatter(char ch) : _ch(ch)
{}
void format(details::log_msg& msg) override
{
msg.formatted.putc(_ch);
}
private:
char _ch;
};
//aggregate user chars to display as is
class aggregate_formatter :public flag_formatter
{
public:
aggregate_formatter()
{}
void add_ch(char ch)
{
_str += ch;
}
void format(details::log_msg& msg) override
{
msg.formatted.put_str(_str);
}
private:
std::string _str;
};
// Full info formatter
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %t
class full_formatter :public flag_formatter
{
void format(details::log_msg& msg) override
{
msg.formatted.putc('[');
msg.formatted.put_int(msg.tm_time.tm_year+1900, 4);
msg.formatted.putc('-');
msg.formatted.put_int(msg.tm_time.tm_mon+ 1, 2);
msg.formatted.putc('-');
msg.formatted.put_int(msg.tm_time.tm_mday, 2);
msg.formatted.putc(' ');
msg.formatted.put_int(msg.tm_time.tm_hour, 2);
msg.formatted.putc(':');
msg.formatted.put_int(msg.tm_time.tm_min, 2);
msg.formatted.putc(':');
msg.formatted.put_int(msg.tm_time.tm_sec, 2);
//millis
msg.formatted.putc('.');
auto duration = msg.time.time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
msg.formatted.put_int(static_cast<int>(millis), 3);
msg.formatted.putc(']');
msg.formatted << " [" << msg.logger_name << "] [" << level::to_str(msg.level) << "] ";
msg.formatted.put_fast_oss(msg.raw);
}
};
}
}
///////////////////////////////////////////////////////////////////////////////
// pattern_formatter inline impl
///////////////////////////////////////////////////////////////////////////////
inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern)
{
compile_pattern(pattern);
}
inline void spdlog::pattern_formatter::compile_pattern(const std::string& pattern)
{
auto end = pattern.end();
std::unique_ptr<details::aggregate_formatter> user_chars;
for (auto it = pattern.begin(); it != end; ++it)
{
if (*it == '%')
{
if (user_chars) //append user chars found so far
_formatters.push_back(std::move(user_chars));
if (++it != end)
handle_flag(*it);
else
break;
}
else // chars not following the % sign should be displayed as is
{
if (!user_chars)
user_chars = std::unique_ptr<details::aggregate_formatter>(new details::aggregate_formatter());
user_chars->add_ch(*it);
}
}
if (user_chars) //append raw chars found so far
{
_formatters.push_back(std::move(user_chars));
}
}
inline void spdlog::pattern_formatter::handle_flag(char flag)
{
switch (flag)
{
// logger name
case 'n':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::name_formatter()));
break;
case 'l':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::level_formatter()));
break;
case('t') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::t_formatter()));
break;
case('a') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::a_formatter()));
break;
case('A') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::A_formatter()));
break;
case('b') :
case('h') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::b_formatter()));
break;
case('B') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::B_formatter()));
break;
case('c') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::c_formatter()));
break;
case('C') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::C_formatter()));
break;
case('Y') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::Y_formatter()));
break;
case('D') :
case('x') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::D_formatter()));
break;
case('m') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::m_formatter()));
break;
case('d') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::d_formatter()));
break;
case('H') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::H_formatter()));
break;
case('I') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::I_formatter()));
break;
case('M') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::M_formatter()));
break;
case('S') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::S_formatter()));
break;
case('e') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::e_formatter()));
break;
case('p') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::p_formatter()));
break;
case('r') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::r_formatter()));
break;
case('R') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::R_formatter()));
break;
case('T') :
case('X') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::T_formatter()));
break;
case('z') :
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::z_formatter()));
break;
case ('+'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::full_formatter()));
break;
default: //Unkown flag appears as is
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter('%')));
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter(flag)));
break;
}
}
inline void spdlog::pattern_formatter::format(details::log_msg& msg)
{
for (auto &f : _formatters)
{
f->format(msg);
}
//write eol
msg.formatted.write(details::os::eol(), details::os::eol_size());
}

View File

@@ -0,0 +1,99 @@
#pragma once
// Loggers registy of unique name->logger pointer
// If 2 loggers with same name are added, the second will be overrun the first
// If user requests a non existing logger, nullptr will be returned
// This class is thread safe
#include <string>
#include <mutex>
#include <unordered_map>
#include "../logger.h"
#include "../common.h"
namespace spdlog {
namespace details {
class registry {
public:
std::shared_ptr<logger> get(const std::string& name)
{
std::lock_guard<std::mutex> lock(_mutex);
auto found = _loggers.find(name);
return found == _loggers.end() ? nullptr : found->second;
}
template<class It>
std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
{
std::lock_guard<std::mutex> lock(_mutex);
auto new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end);
new_logger->set_formatter(_formatter);
new_logger->set_level(_level);
_loggers[logger_name] = new_logger;
return new_logger;
}
std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks)
{
return create(logger_name, sinks.begin(), sinks.end());
}
std::shared_ptr<logger> create(const std::string& logger_name, sink_ptr sink)
{
return create(logger_name, { sink });
}
void formatter(formatter_ptr f)
{
std::lock_guard<std::mutex> lock(_mutex);
_formatter = f;
for (auto& l : _loggers)
l.second->set_formatter(_formatter);
}
void set_pattern(const std::string& pattern)
{
std::lock_guard<std::mutex> lock(_mutex);
_formatter = std::make_shared<pattern_formatter>(pattern);
for (auto& l : _loggers)
l.second->set_formatter(_formatter);
}
void set_level(level::level_enum log_level)
{
std::lock_guard<std::mutex> lock(_mutex);
for (auto& l : _loggers)
l.second->set_level(log_level);
}
void stop_all()
{
std::lock_guard<std::mutex> lock(_mutex);
_level = level::OFF;
for (auto& l : _loggers)
l.second->stop_logging();
}
static registry& instance()
{
static registry s_instance;
return s_instance;
}
private:
registry() = default;
registry(const registry&) = delete;
std::mutex _mutex;
std::unordered_map <std::string, std::shared_ptr<logger>> _loggers;
formatter_ptr _formatter;
level::level_enum _level = level::INFO;
};
}
}

View File

@@ -0,0 +1,51 @@
#pragma once
//
// Global registry functions
//
#include "registry.h"
inline std::shared_ptr<spdlog::logger> spdlog::get(const std::string& name)
{
return details::registry::instance().get(name);
}
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, spdlog::sinks_init_list sinks)
{
return details::registry::instance().create(logger_name, sinks);
}
template <typename Sink, typename... Args>
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const Args&... args)
{
sink_ptr sink = std::make_shared<Sink>(args...);
return details::registry::instance().create(logger_name, { sink });
}
template<class It>
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
{
return details::registry::instance().create(logger_name, sinks_begin, sinks_end);
}
inline void spdlog::set_formatter(spdlog::formatter_ptr f)
{
details::registry::instance().formatter(f);
}
inline void spdlog::set_pattern(const std::string& format_string)
{
return details::registry::instance().set_pattern(format_string);
}
inline void spdlog::set_level(level::level_enum log_level)
{
return details::registry::instance().set_level(log_level);
}
inline void spdlog::stop()
{
return details::registry::instance().stop_all();
}

View File

@@ -0,0 +1,111 @@
#pragma once
#include <algorithm>
#include <array>
#include <vector>
#include <cstring>
// Fast memory storage on the stack when possible or in std::vector
namespace spdlog
{
namespace details
{
template<unsigned short STACK_SIZE>
class stack_buf
{
public:
static const unsigned short stack_size = STACK_SIZE;
stack_buf() :_v(), _stack_size(0) {}
~stack_buf() = default;
stack_buf(const stack_buf& other):stack_buf(other, delegate_copy_move {})
{}
stack_buf(stack_buf&& other):stack_buf(other, delegate_copy_move {})
{
other.clear();
}
template<class T1>
stack_buf& operator=(T1&& other)
{
_stack_size = other._stack_size;
if (other.vector_used())
_v = std::forward<T1>(other)._v;
else
std::copy_n(other._stack_array.begin(), other._stack_size, _stack_array.begin());
return *this;
}
void append(const char* buf, std::size_t buf_size)
{
//If we are aleady using _v, forget about the stack
if (vector_used())
{
_v.insert(_v.end(), buf, buf + buf_size);
}
//Try use the stack
else
{
if (_stack_size + buf_size <= STACK_SIZE)
{
std::memcpy(&_stack_array[_stack_size], buf, buf_size);
_stack_size += buf_size;
}
//Not enough stack space. Copy all to _v
else
{
_v.reserve(_stack_size + buf_size);
_v.insert(_v.end(), _stack_array.begin(), _stack_array.begin() + _stack_size);
_v.insert(_v.end(), buf, buf + buf_size);
}
}
}
void clear()
{
_stack_size = 0;
_v.clear();
}
const char* data() const
{
if (vector_used())
return _v.data();
else
return _stack_array.data();
}
std::size_t size() const
{
if (vector_used())
return _v.size();
else
return _stack_size;
}
private:
struct delegate_copy_move {};
template<class T1>
stack_buf(T1&& other, delegate_copy_move)
{
_stack_size = other._stack_size;
if (other.vector_used())
_v = std::forward<T1>(other)._v;
else
std::copy_n(other._stack_array.begin(), other._stack_size, _stack_array.begin());
}
inline bool vector_used() const
{
return !(_v.empty());
}
std::vector<char> _v;
std::array<char, STACK_SIZE> _stack_array;
std::size_t _stack_size;
};
}
} //namespace spdlog { namespace details {