1. renamed lib to to spitlog

2. Rotating bugfix
This commit is contained in:
gabi
2014-10-30 00:11:06 +02:00
parent cda4b9b4d5
commit cbddc8796a
29 changed files with 347 additions and 318 deletions

55
include/spitlog/common.h Normal file
View File

@@ -0,0 +1,55 @@
#pragma once
#include<initializer_list>
#include<chrono>
namespace spitlog
{
class formatter;
namespace sinks {
class sink;
}
// Common types across the lib
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<spitlog::formatter>;
//Log level enum
namespace level
{
typedef enum
{
TRACE,
DEBUG,
INFO,
WARN,
ERR,
CRITICAL,
NONE = 99
} level_enum;
static const char* level_names[] { "trace", "debug", "info", "warning", "error", "critical", "fatal" };
inline const char* to_str(spitlog::level::level_enum l)
{
return level_names[l];
}
} //level
//
// Log exception
//
class fflog_exception : public std::exception
{
public:
fflog_exception(const std::string& msg) :_msg(msg) {};
const char* what() const throw() override {
return _msg.c_str();
}
private:
std::string _msg;
};
} //spitlog

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 spitlog
{
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 spitlog {
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 spitlog
{
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,110 @@
#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 spitlog
{
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()
{
if (_fd)
std::fclose(_fd);
}
void open(const std::string& filename)
{
if (_fd)
std::fclose(_fd);
_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()
{
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 spitlog
{
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 spitlog

View File

@@ -0,0 +1,69 @@
#pragma once
#include "../common.h"
#include "./fast_oss.h"
namespace spitlog
{
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,163 @@
#pragma once
//
// Logger implementation
//
#include "./line_logger.h"
inline spitlog::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 spitlog::logger::logger(const std::string& logger_name, const It& begin, const It& end) :
_name(logger_name),
_sinks(begin, end)
{}
inline void spitlog::logger::set_formatter(spitlog::formatter_ptr msg_formatter)
{
_formatter = msg_formatter;
}
inline void spitlog::logger::set_format(const std::string& format)
{
_formatter = std::make_shared<pattern_formatter>(format);
}
inline spitlog::formatter_ptr spitlog::logger::get_formatter() const
{
return _formatter;
}
template <typename... Args>
inline spitlog::details::line_logger spitlog::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 spitlog::details::line_logger spitlog::logger::trace(const Args&... args)
{
return log(level::TRACE, args...);
}
template <typename... Args>
inline spitlog::details::line_logger spitlog::logger::debug(const Args&... args)
{
return log(level::DEBUG, args...);
}
template <typename... Args>
inline spitlog::details::line_logger spitlog::logger::info(const Args&... args)
{
return log(level::INFO, args...);
}
template <typename... Args>
inline spitlog::details::line_logger spitlog::logger::warn(const Args&... args)
{
return log(level::WARN, args...);
}
template <typename... Args>
inline spitlog::details::line_logger spitlog::logger::error(const Args&... args)
{
return log(level::ERR, args...);
}
template <typename... Args>
inline spitlog::details::line_logger spitlog::logger::critical(const Args&... args)
{
return log(level::CRITICAL, args...);
}
inline const std::string& spitlog::logger::name() const
{
return _name;
}
inline void spitlog::logger::set_level(spitlog::level::level_enum log_level)
{
_level.store(log_level);
}
inline spitlog::level::level_enum spitlog::logger::level() const
{
return static_cast<spitlog::level::level_enum>(_level.load());
}
inline bool spitlog::logger::should_log(spitlog::level::level_enum msg_level) const
{
return msg_level >= _level.load();
}
inline void spitlog::logger::_variadic_log(spitlog::details::line_logger&) {}
template <typename First, typename... Rest>
void spitlog::logger::_variadic_log(spitlog::details::line_logger& l, const First& first, const Rest&... rest)
{
l.write(first);
l.write(' ');
_variadic_log(l, rest...);
}
inline void spitlog::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);
}
//
// Global registry functions
//
#include "./registry.h"
inline std::shared_ptr<spitlog::logger> spitlog::get(const std::string& name)
{
return details::registry::instance().get(name);
}
inline std::shared_ptr<spitlog::logger> spitlog::create(const std::string& logger_name, spitlog::sinks_init_list sinks)
{
return details::registry::instance().create(logger_name, sinks);
}
template <typename Sink, typename... Args>
inline std::shared_ptr<spitlog::logger> spitlog::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<spitlog::logger> spitlog::create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
{
return details::registry::instance().create(logger_name, std::forward(sinks_begin), std::forward(sinks_end));
}
inline void spitlog::set_formatter(spitlog::formatter_ptr f)
{
details::registry::instance().formatter(f);
}
inline void spitlog::set_format(const std::string& format_string)
{
return details::registry::instance().set_format(format_string);
}

View File

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

View File

@@ -0,0 +1,129 @@
#pragma once
#include<string>
#include<cstdio>
#include<ctime>
#ifdef _WIN32
#include <Windows.h>
#endif
namespace spitlog
{
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
} //spitlog

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 spitlog
{
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 spitlog::pattern_formatter::pattern_formatter(const std::string& pattern)
{
compile_pattern(pattern);
}
inline void spitlog::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 spitlog::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 spitlog::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,80 @@
#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 spitlog {
namespace details {
class registry {
public:
std::shared_ptr<logger> get(const std::string& name)
{
std::lock_guard<std::mutex> l(_mutex);
auto found = _loggers.find(name);
return found == _loggers.end() ? nullptr : found->second;
}
std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks)
{
std::lock_guard<std::mutex> l(_mutex);
auto new_logger = std::make_shared<logger>(logger_name, sinks);
new_logger->set_formatter(_formatter);
_loggers[logger_name] = new_logger;
return new_logger;
}
std::shared_ptr<logger> create(const std::string& logger_name, sink_ptr sink)
{
return create(logger_name, { sink });
}
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> l(_mutex);
auto new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end);
new_logger->set_formatter(_formatter);
_loggers[logger_name] = new_logger;
return new_logger;
}
void formatter(formatter_ptr f)
{
_formatter = f;
}
formatter_ptr formatter()
{
return _formatter;
}
void set_format(const std::string& format_string)
{
_formatter = std::make_shared<pattern_formatter>(format_string);
}
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;
};
}
}

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 spitlog
{
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 spitlog { namespace details {

View File

@@ -0,0 +1,33 @@
#pragma once
#include "details/log_msg.h"
namespace spitlog
{
namespace details {
class flag_formatter;
}
class formatter
{
public:
virtual ~formatter() {}
virtual void format(details::log_msg& msg) = 0;
};
class pattern_formatter : public formatter
{
public:
explicit pattern_formatter(const std::string& pattern);
pattern_formatter(const pattern_formatter&) = delete;
void format(details::log_msg& msg) override;
private:
const std::string _pattern;
std::vector<std::unique_ptr<details::flag_formatter>> _formatters;
void handle_flag(char flag);
void compile_pattern(const std::string& pattern);
};
}
#include "./details/pattern_formatter_impl.h"

103
include/spitlog/logger.h Normal file
View File

@@ -0,0 +1,103 @@
#pragma once
// Thread safe logger
// Has name, log level, vector of std::shared sink pointers and formatter
// Upon each log write the logger:
// 1. Checks if its log level is enough to log the message
// 2. Format the message using the formatter function
// 3. Pass the formatted message to its sinks to performa the actual logging
#include<vector>
#include<memory>
#include<atomic>
#include <sstream>
#include <exception>
#include "sinks/base_sink.h"
#include "common.h"
namespace spitlog
{
namespace details
{
class line_logger;
}
class logger
{
public:
logger(const std::string& name, sinks_init_list);
template<class It>
logger(const std::string& name, const It& begin, const It& end);
void set_format(const std::string&);
void set_formatter(formatter_ptr);
formatter_ptr get_formatter() const;
logger(const logger&) = delete;
logger& operator=(const logger&) = delete;
void set_level(level::level_enum);
level::level_enum level() const;
const std::string& name() const;
bool should_log(level::level_enum) const;
template <typename... Args> details::line_logger log(level::level_enum lvl, const Args&... args);
template <typename... Args> details::line_logger trace(const Args&... args);
template <typename... Args> details::line_logger debug(const Args&... args);
template <typename... Args> details::line_logger info(const Args&... args);
template <typename... Args> details::line_logger warn(const Args&... args);
template <typename... Args> details::line_logger error(const Args&... args);
template <typename... Args> details::line_logger critical(const Args&... args);
private:
friend details::line_logger;
std::string _name;
formatter_ptr _formatter;
std::vector<sink_ptr> _sinks;
std::atomic_int _level;
void _variadic_log(details::line_logger& l);
template <typename First, typename... Rest>
void _variadic_log(details::line_logger&l, const First& first, const Rest&... rest);
void _log_msg(details::log_msg& msg);
};
//
// Registry functions for easy loggers creation and retrieval
// example
// auto console_logger = spitlog::create("my_logger", spitlog::sinks<stdout_sink_mt>);
// auto same_logger = spitlog::get("my_logger");
// auto file_logger = c11
//
std::shared_ptr<logger> get(const std::string& name);
std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks);
template <typename Sink, typename... Args>
std::shared_ptr<spitlog::logger> create(const std::string& logger_name, const Args&... args);
template<class It>
std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end);
void set_formatter(formatter_ptr f);
void set_format(const std::string& format_string);
}
//
// Trace & debug macros
//
#ifdef FFLOG_ENABLE_TRACE
#define FFLOG_TRACE(logger, ...) logger->log(spitlog::level::TRACE, __FILE__, " #", __LINE__,": " __VA_ARGS__)
#else
#define FFLOG_TRACE(logger, ...) {}
#endif
#ifdef FFLOG_ENABLE_DEBUG
#define FFLOG_DEBUG(logger, ...) logger->log(spitlog::level::DEBUG, __VA_ARGS__)
#else
#define FFLOG_DEBUG(logger, ...) {}
#endif
#include "./details/logger_impl.h"

View File

@@ -0,0 +1,137 @@
#pragma once
#include <thread>
#include <chrono>
#include <atomic>
#include <algorithm>
#include "./base_sink.h"
#include "../logger.h"
#include "../details/blocking_queue.h"
#include "../details/null_mutex.h"
#include "../details/log_msg.h"
#include<iostream>
namespace spitlog
{
namespace sinks
{
class async_sink : public base_sink<details::null_mutex>
{
public:
using q_type = details::blocking_queue<details::log_msg>;
explicit async_sink(const q_type::size_type max_queue_size);
//Stop logging and join the back thread
~async_sink();
void add_sink(sink_ptr sink);
void remove_sink(sink_ptr sink_ptr);
q_type& q();
//Wait to remaining items (if any) in the queue to be written and shutdown
void shutdown(const std::chrono::milliseconds& timeout);
protected:
void _sink_it(const details::log_msg& msg) override;
void _thread_loop();
private:
std::vector<std::shared_ptr<sink>> _sinks;
std::atomic<bool> _active;
q_type _q;
std::thread _back_thread;
//Clear all remaining messages(if any), stop the _back_thread and join it
void _shutdown();
std::mutex _mutex;
};
}
}
///////////////////////////////////////////////////////////////////////////////
// async_sink class implementation
///////////////////////////////////////////////////////////////////////////////
inline spitlog::sinks::async_sink::async_sink(const q_type::size_type max_queue_size)
:_sinks(),
_active(true),
_q(max_queue_size),
_back_thread(&async_sink::_thread_loop, this)
{}
inline spitlog::sinks::async_sink::~async_sink()
{
_shutdown();
}
inline void spitlog::sinks::async_sink::_sink_it(const details::log_msg& msg)
{
if(!_active)
return;
_q.push(msg);
}
inline void spitlog::sinks::async_sink::_thread_loop()
{
static std::chrono::seconds pop_timeout { 1 };
while (_active)
{
q_type::item_type msg;
if (_q.pop(msg, pop_timeout))
{
for (auto &s : _sinks)
{
s->log(msg);
if(!_active)
break;
}
}
}
}
inline void spitlog::sinks::async_sink::add_sink(spitlog::sink_ptr s)
{
std::lock_guard<std::mutex> guard(_mutex);
_sinks.push_back(s);
}
inline void spitlog::sinks::async_sink::remove_sink(spitlog::sink_ptr s)
{
std::lock_guard<std::mutex> guard(_mutex);
_sinks.erase(std::remove(_sinks.begin(), _sinks.end(), s), _sinks.end());
}
inline spitlog::sinks::async_sink::q_type& spitlog::sinks::async_sink::q()
{
return _q;
}
inline void spitlog::sinks::async_sink::shutdown(const std::chrono::milliseconds& timeout)
{
if(timeout > std::chrono::milliseconds::zero())
{
auto until = log_clock::now() + timeout;
while (_q.size() > 0 && log_clock::now() < until)
{
std::this_thread::sleep_for(std::chrono::milliseconds(2));
}
}
_shutdown();
}
inline void spitlog::sinks::async_sink::_shutdown()
{
std::lock_guard<std::mutex> guard(_mutex);
if(_active)
{
_active = false;
if (_back_thread.joinable())
_back_thread.join();
}
}

View File

@@ -0,0 +1,43 @@
#pragma once
//
// base sink templated over a mutex (either dummy or realy)
// concrete implementation should only overrid the _sink_it method.
// all locking is taken care of here so no locking needed by the implementors..
//
#include<string>
#include<mutex>
#include<atomic>
#include "./sink.h"
#include "../formatter.h"
#include "../common.h"
#include "../details/log_msg.h"
namespace spitlog
{
namespace sinks
{
template<class Mutex>
class base_sink:public sink
{
public:
base_sink():_mutex() {}
virtual ~base_sink() = default;
base_sink(const base_sink&) = delete;
base_sink& operator=(const base_sink&) = delete;
void log(const details::log_msg& msg) override
{
std::lock_guard<Mutex> lock(_mutex);
_sink_it(msg);
};
protected:
virtual void _sink_it(const details::log_msg& msg) = 0;
Mutex _mutex;
};
}
}

View File

@@ -0,0 +1,189 @@
#pragma once
#include <mutex>
#include "./base_sink.h"
#include "../details/null_mutex.h"
#include "../details/file_helper.h"
#include "../details/fast_oss.h"
namespace spitlog
{
namespace sinks
{
/*
* Trivial file sink with single file as target
*/
template<class Mutex>
class simple_file_sink : public base_sink<Mutex>
{
public:
explicit simple_file_sink(const std::string &filename,
const std::size_t flush_inverval=0):
_file_helper(flush_inverval)
{
_file_helper.open(filename);
}
protected:
void _sink_it(const details::log_msg& msg) override
{
_file_helper.write(msg);
}
private:
details::file_helper _file_helper;
};
typedef simple_file_sink<std::mutex> simple_file_sink_mt;
typedef simple_file_sink<details::null_mutex> simple_file_sink_st;
/*
* Rotating file sink based on size
*/
template<class Mutex>
class rotating_file_sink : public base_sink<Mutex>
{
public:
rotating_file_sink(const std::string &base_filename, const std::string &extension,
const std::size_t max_size, const std::size_t max_files,
const std::size_t flush_inverval=0):
_base_filename(base_filename),
_extension(extension),
_max_size(max_size),
_max_files(max_files),
_current_size(0),
_file_helper(flush_inverval)
{
_file_helper.open(calc_filename(_base_filename, 0, _extension));
}
protected:
void _sink_it(const details::log_msg& msg) override
{
_current_size += msg.formatted.size();
if (_current_size > _max_size)
{
_rotate();
_current_size = msg.formatted.size();
}
_file_helper.write(msg);
}
private:
static std::string calc_filename(const std::string& filename, std::size_t index, const std::string& extension)
{
details::fast_oss oss;
if (index)
oss << filename << "." << index << "." << extension;
else
oss << filename << "." << extension;
return oss.str();
}
// Rotate files:
// log.txt -> log.1.txt
// log.1.txt -> log2.txt
// log.2.txt -> log3.txt
// log.3.txt -> delete
void _rotate()
{
_file_helper.close();
for (auto i = _max_files; i > 0; --i)
{
std::string src = calc_filename(_base_filename, i - 1, _extension);
std::string target = calc_filename(_base_filename, i, _extension);
if (details::file_helper::file_exists(target))
std::remove(target.c_str());
if (details::file_helper::file_exists(src) && std::rename(src.c_str(), target.c_str()))
{
throw fflog_exception("rotating_file_sink: failed renaming " + src + " to " + target);
}
}
auto cur_name = _file_helper.filename();
std::remove(cur_name.c_str());
_file_helper.open(cur_name);
}
std::string _base_filename;
std::string _extension;
std::size_t _max_size;
std::size_t _max_files;
std::size_t _current_size;
details::file_helper _file_helper;
};
typedef rotating_file_sink<std::mutex> rotating_file_sink_mt;
typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
/*
* Rotating file sink based on date. rotates at midnight
*/
template<class Mutex>
class daily_file_sink:public base_sink<Mutex>
{
public:
explicit daily_file_sink(const std::string& base_filename,
const std::string& extension,
const std::size_t flush_inverval=0):
_base_filename(base_filename),
_extension(extension),
_midnight_tp (_calc_midnight_tp() ),
_file_helper(flush_inverval)
{
_file_helper.open(calc_filename(_base_filename, _extension));
}
protected:
void _sink_it(const details::log_msg& msg) override
{
if (std::chrono::system_clock::now() >= _midnight_tp)
{
_file_helper.close();
_file_helper.open(calc_filename(_base_filename, _extension));
_midnight_tp = _calc_midnight_tp();
}
_file_helper.write(msg);
}
private:
// Return next midnight's time_point
static std::chrono::system_clock::time_point _calc_midnight_tp()
{
using namespace std::chrono;
auto now = system_clock::now();
time_t tnow = std::chrono::system_clock::to_time_t(now);
tm date = spitlog::details::os::localtime(tnow);
date.tm_hour = date.tm_min = date.tm_sec = 0;
auto midnight = std::chrono::system_clock::from_time_t(std::mktime(&date));
return system_clock::time_point(midnight + hours(24));
}
//Create filename for the form basename.YYYY-MM-DD.extension
static std::string calc_filename(const std::string& basename, const std::string& extension)
{
std::tm tm = spitlog::details::os::localtime();
fast_oss oss;
oss << basename << '.';
oss << tm.tm_year + 1900 << '-' << std::setw(2) << std::setfill('0') << tm.tm_mon + 1 << '-' << tm.tm_mday;
oss << '.' << extension;
return oss.str();
}
std::string _base_filename;
std::string _extension;
std::chrono::system_clock::time_point _midnight_tp;
details::file_helper _file_helper;
};
typedef daily_file_sink<std::mutex> daily_file_sink_mt;
typedef daily_file_sink<details::null_mutex> daily_file_sink_st;
}
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include <mutex>
#include "./base_sink.h"
#include "../details/null_mutex.h"
namespace spitlog {
namespace sinks {
template <class Mutex>
class null_sink : public base_sink<Mutex>
{
protected:
void _sink_it(const details::log_msg&) override
{}
};
typedef null_sink<details::null_mutex> null_sink_st;
typedef null_sink<std::mutex> null_sink_mt;
}
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include <iostream>
#include <mutex>
#include <memory>
#include "../details/null_mutex.h"
#include "./base_sink.h"
namespace spitlog
{
namespace sinks
{
template<class Mutex>
class ostream_sink: public base_sink<Mutex>
{
public:
explicit ostream_sink(std::ostream& os) :_ostream(os) {}
ostream_sink(const ostream_sink&) = delete;
ostream_sink& operator=(const ostream_sink&) = delete;
virtual ~ostream_sink() = default;
protected:
virtual void _sink_it(const details::log_msg& msg) override
{
auto& buf = msg.formatted.buf();
_ostream.write(buf.data(), buf.size());
}
std::ostream& _ostream;
};
typedef ostream_sink<std::mutex> ostream_sink_mt;
typedef ostream_sink<details::null_mutex> ostream_sink_st;
}
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include "../details/log_msg.h"
namespace spitlog
{
namespace sinks
{
class sink
{
public:
virtual ~sink() {}
virtual void log(const details::log_msg& msg) = 0;
};
}
}

View File

@@ -0,0 +1,34 @@
#pragma once
#include <iostream>
#include <mutex>
#include "./ostream_sink.h"
#include "../details/null_mutex.h"
namespace spitlog
{
namespace sinks
{
template <class Mutex>
class stdout_sink : public ostream_sink<Mutex>
{
public:
stdout_sink() : ostream_sink<Mutex>(std::cout) {}
};
typedef stdout_sink<details::null_mutex> stdout_sink_st;
typedef stdout_sink<std::mutex> stdout_sink_mt;
template <class Mutex>
class stderr_sink : public ostream_sink<Mutex>
{
public:
stderr_sink() : ostream_sink<Mutex>(std::cerr) {}
};
typedef stderr_sink<std::mutex> stderr_sink_mt;
typedef stderr_sink<details::null_mutex> stderr_sink_st;
}
}