diff --git a/astyle.sh b/astyle.sh index 0171e75d..0e2b999d 100755 --- a/astyle.sh +++ b/astyle.sh @@ -1,4 +1,5 @@ #!/bin/bash -find . -name "*\.h" -o -name "*\.cpp"|xargs astyle -A1 +find . -name "*\.h" -o -name "*\.cpp"|xargs dos2unix +find . -name "*\.h" -o -name "*\.cpp"|xargs astyle -n -A1 diff --git a/example/example.cpp b/example/example.cpp index 63acb71a..a52585a2 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -32,12 +32,12 @@ int main(int argc, char* argv[]) logger my_logger ("my_logger", {null_sink}); - std::string s(100, '0'); - const unsigned int howmany = 5000000; + std::string s(100, '0'); + const unsigned int howmany = 5000000; auto start = system_clock::now(); for(unsigned int i = 0; i < howmany ; i++) - my_logger.info() << s; - //my_logger.info() << "Hello logger " << i;; + my_logger.info() << s; + //my_logger.info() << "Hello logger " << i;; //async->shutdown(seconds(3)); auto delta = system_clock::now() - start; diff --git a/include/c11log/common_types.h b/include/c11log/common_types.h index 8fe6efcc..b6dfba4b 100644 --- a/include/c11log/common_types.h +++ b/include/c11log/common_types.h @@ -1,29 +1,29 @@ -#pragma once - -#include - -namespace c11log -{ - -typedef std::chrono::system_clock log_clock; -typedef std::pair bufpair_t; - -namespace level -{ -typedef enum -{ - DEBUG, - INFO, - WARNING, - ERROR, - FATAL, - NONE = 99 -} level_enum; - -static const char* level_names[] { "debug", "info", "warning", "error", "fatal" }; -inline const char* to_str(c11log::level::level_enum l) -{ - return level_names[l]; -} -} -} +#pragma once + +#include + +namespace c11log +{ + +typedef std::chrono::system_clock log_clock; +typedef std::pair bufpair_t; + +namespace level +{ +typedef enum +{ + DEBUG, + INFO, + WARNING, + ERROR, + FATAL, + NONE = 99 +} level_enum; + +static const char* level_names[] { "debug", "info", "warning", "error", "fatal" }; +inline const char* to_str(c11log::level::level_enum l) +{ + return level_names[l]; +} +} +} diff --git a/include/c11log/details/blocking_queue.h b/include/c11log/details/blocking_queue.h index 33e0dbaf..4630aae0 100644 --- a/include/c11log/details/blocking_queue.h +++ b/include/c11log/details/blocking_queue.h @@ -1,125 +1,125 @@ -#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 -#include -#include -#include -#include - -namespace c11log -{ -namespace details -{ - -template -class blocking_queue -{ -public: - using queue_t = std::queue; - using size_type = typename queue_t::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 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 - bool push(TT&& item, const std::chrono::duration& timeout) - { - std::unique_lock 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(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 - void push(TT&& item) - { - while (!push(std::forward(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 - bool pop(T& item, const std::chrono::duration& timeout) - { - std::unique_lock 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 ul(_mutex); - queue_t().swap(_q); - } - _item_popped_cond.notify_all(); - } - -private: - size_type _max_size; - std::queue _q; - std::mutex _mutex; - std::condition_variable _item_pushed_cond; - std::condition_variable _item_popped_cond; -}; - -} -} +#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 +#include +#include +#include +#include + +namespace c11log +{ +namespace details +{ + +template +class blocking_queue +{ +public: + using queue_t = std::queue; + using size_type = typename queue_t::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 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 + bool push(TT&& item, const std::chrono::duration& timeout) + { + std::unique_lock 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(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 + void push(TT&& item) + { + while (!push(std::forward(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 + bool pop(T& item, const std::chrono::duration& timeout) + { + std::unique_lock 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 ul(_mutex); + queue_t().swap(_q); + } + _item_popped_cond.notify_all(); + } + +private: + size_type _max_size; + std::queue _q; + std::mutex _mutex; + std::condition_variable _item_pushed_cond; + std::condition_variable _item_popped_cond; +}; + +} +} diff --git a/include/c11log/details/factory.h b/include/c11log/details/factory.h index 86227248..4445490c 100644 --- a/include/c11log/details/factory.h +++ b/include/c11log/details/factory.h @@ -1,61 +1,61 @@ -#pragma once - -#include -#include -#include -#include - -namespace c11log -{ -class logger; -namespace details -{ -class factory -{ -public: - using logger_ptr = std::shared_ptr; - using logger_map = std::unordered_map; - void add_logger(const std::string& name, logger_ptr); - logger_ptr get_logger(const std::string &name); - static c11log::details::factory& instance(); -private: - std::mutex _loggers_mutex; - logger_map _loggers; - -}; -} -} - - -inline void c11log::details::factory::add_logger(const std::string& name, logger_ptr logger_p) -{ - std::lock_guard lock(_loggers_mutex); - _loggers.insert(logger_map::value_type(name, logger_p)); -} - -inline c11log::details::factory::logger_ptr c11log::details::factory::get_logger(const std::string &name) -{ - std::lock_guard lock(_loggers_mutex); - - auto found = _loggers.find(name); - if (found != _loggers.end()) - return found->second; - else - return logger_ptr(nullptr); - /* - auto found = _loggers.find(name); - - if (found == _loggers.end()) { - auto new_logger_ptr = std::make_shared(name); - _loggers.insert(std::make_pair(name, new_logger_ptr)); - return new_logger_ptr; - } else { - return found->second; - }*/ -} - -inline c11log::details::factory & c11log::details::factory::instance() -{ - static c11log::details::factory instance; - return instance; -} +#pragma once + +#include +#include +#include +#include + +namespace c11log +{ +class logger; +namespace details +{ +class factory +{ +public: + using logger_ptr = std::shared_ptr; + using logger_map = std::unordered_map; + void add_logger(const std::string& name, logger_ptr); + logger_ptr get_logger(const std::string &name); + static c11log::details::factory& instance(); +private: + std::mutex _loggers_mutex; + logger_map _loggers; + +}; +} +} + + +inline void c11log::details::factory::add_logger(const std::string& name, logger_ptr logger_p) +{ + std::lock_guard lock(_loggers_mutex); + _loggers.insert(logger_map::value_type(name, logger_p)); +} + +inline c11log::details::factory::logger_ptr c11log::details::factory::get_logger(const std::string &name) +{ + std::lock_guard lock(_loggers_mutex); + + auto found = _loggers.find(name); + if (found != _loggers.end()) + return found->second; + else + return logger_ptr(nullptr); + /* + auto found = _loggers.find(name); + + if (found == _loggers.end()) { + auto new_logger_ptr = std::make_shared(name); + _loggers.insert(std::make_pair(name, new_logger_ptr)); + return new_logger_ptr; + } else { + return found->second; + }*/ +} + +inline c11log::details::factory & c11log::details::factory::instance() +{ + static c11log::details::factory instance; + return instance; +} diff --git a/include/c11log/details/fast_buf.h b/include/c11log/details/fast_buf.h index 8b4a49d2..6f956e1c 100644 --- a/include/c11log/details/fast_buf.h +++ b/include/c11log/details/fast_buf.h @@ -21,10 +21,10 @@ public: fast_buf():_stack_size(0) {} ~fast_buf() {}; - fast_buf(const bufpair_t& buf_to_copy):fast_buf() - { - append(buf_to_copy); - } + fast_buf(const bufpair_t& buf_to_copy):fast_buf() + { + append(buf_to_copy); + } fast_buf(const fast_buf& other) { @@ -46,7 +46,7 @@ public: } fast_buf& operator=(const fast_buf& other) = delete; - fast_buf& operator=(fast_buf&& other) = delete; + fast_buf& operator=(fast_buf&& other) = delete; void append(const char* buf, std::size_t size) { @@ -66,7 +66,7 @@ public: //Not enough stack space. Copy all to _v else { - _v.reserve(_stack_size+size); + _v.reserve(_stack_size+size); if(_stack_size) _v.insert(_v.end(), _stack_buf.begin(), _stack_buf.begin() +_stack_size); _v.insert(_v.end(), buf, buf+size); @@ -74,10 +74,10 @@ public: } } - void append(const bufpair_t &buf) - { - append(buf.first, buf.second); - } + void append(const bufpair_t &buf) + { + append(buf.first, buf.second); + } void clear() { @@ -96,7 +96,7 @@ public: private: std::vector _v; std::array _stack_buf; - std::size_t _stack_size; + std::size_t _stack_size; }; } diff --git a/include/c11log/details/fast_oss.h b/include/c11log/details/fast_oss.h index 4da16744..8804b6bb 100644 --- a/include/c11log/details/fast_oss.h +++ b/include/c11log/details/fast_oss.h @@ -1,106 +1,106 @@ -#pragma once - -// Faster than ostringstream--returns its string by ref - -#include "c11log/details/fast_buf.h" - -namespace c11log -{ -namespace details -{ - -class str_devicebuf:public std::streambuf -{ -public: - str_devicebuf() = default; - ~str_devicebuf() = default; - - str_devicebuf(const str_devicebuf& other) = delete; - str_devicebuf(str_devicebuf&& other) = delete; - str_devicebuf& operator=(const str_devicebuf&) = delete; - str_devicebuf& operator=(str_devicebuf&&) = delete; - - /* - const std::string& str_ref() const - { - return _str; - } - */ - bufpair_t buf() - { - return _fastbuf.get(); - } - - void reset_str() - { - //_str.clear(); - _fastbuf.clear(); - } - -protected: - int sync() override - { - return 0; - } - - // copy the give buffer into the accumulated string. - // reserve initially 128 bytes which should be enough for common log lines - std::streamsize xsputn(const char_type* s, std::streamsize count) override - { - /* - if(_str.capacity() < k_initial_reserve) - { - _str.reserve(k_initial_reserve); - } - _str.append(s, static_cast(count)); - */ - _fastbuf.append(s, static_cast(count)); - return count; - } - - int_type overflow(int_type ch) override - { - - bool not_eofile = traits_type::not_eof(ch); - if (not_eofile) - { - char c = traits_type::to_char_type(ch); - xsputn(&c, 1); - } - return not_eofile; - } -private: - //std::string _str; - fast_buf<192> _fastbuf; -}; - -class fast_oss:public std::ostream -{ -public: - fast_oss():std::ostream(&_dev) {} - ~fast_oss() = default; - - fast_oss(const fast_oss& other) = delete; - fast_oss(fast_oss&& other) = delete; - fast_oss& operator=(const fast_oss& other) = delete; - /* - const std::string& str_ref() const - { - return _dev.str_ref(); - } - */ - bufpair_t buf() - { - return _dev.buf(); - } - - void reset_str() - { - _dev.reset_str(); - } - -private: - str_devicebuf _dev; -}; -} -} +#pragma once + +// Faster than ostringstream--returns its string by ref + +#include "c11log/details/fast_buf.h" + +namespace c11log +{ +namespace details +{ + +class str_devicebuf:public std::streambuf +{ +public: + str_devicebuf() = default; + ~str_devicebuf() = default; + + str_devicebuf(const str_devicebuf& other) = delete; + str_devicebuf(str_devicebuf&& other) = delete; + str_devicebuf& operator=(const str_devicebuf&) = delete; + str_devicebuf& operator=(str_devicebuf&&) = delete; + + /* + const std::string& str_ref() const + { + return _str; + } + */ + bufpair_t buf() + { + return _fastbuf.get(); + } + + void reset_str() + { + //_str.clear(); + _fastbuf.clear(); + } + +protected: + int sync() override + { + return 0; + } + + // copy the give buffer into the accumulated string. + // reserve initially 128 bytes which should be enough for common log lines + std::streamsize xsputn(const char_type* s, std::streamsize count) override + { + /* + if(_str.capacity() < k_initial_reserve) + { + _str.reserve(k_initial_reserve); + } + _str.append(s, static_cast(count)); + */ + _fastbuf.append(s, static_cast(count)); + return count; + } + + int_type overflow(int_type ch) override + { + + bool not_eofile = traits_type::not_eof(ch); + if (not_eofile) + { + char c = traits_type::to_char_type(ch); + xsputn(&c, 1); + } + return not_eofile; + } +private: + //std::string _str; + fast_buf<192> _fastbuf; +}; + +class fast_oss:public std::ostream +{ +public: + fast_oss():std::ostream(&_dev) {} + ~fast_oss() = default; + + fast_oss(const fast_oss& other) = delete; + fast_oss(fast_oss&& other) = delete; + fast_oss& operator=(const fast_oss& other) = delete; + /* + const std::string& str_ref() const + { + return _dev.str_ref(); + } + */ + bufpair_t buf() + { + return _dev.buf(); + } + + void reset_str() + { + _dev.reset_str(); + } + +private: + str_devicebuf _dev; +}; +} +} diff --git a/include/c11log/details/line_logger.h b/include/c11log/details/line_logger.h index 270901a3..c6aec833 100644 --- a/include/c11log/details/line_logger.h +++ b/include/c11log/details/line_logger.h @@ -1,75 +1,75 @@ -#pragma once - -#include "../common_types.h" -#include "../logger.h" -#include "fast_oss.h" - - -// line logger class. should be used by the logger as an rvalue only. -// aggregates logging string until the end of the line and then calls the logger upon destruction - - -namespace c11log -{ -//class logger; -namespace details -{ - -class line_logger -{ -public: - line_logger(logger* callback_logger, level::level_enum msg_level, bool enabled): - _callback_logger(callback_logger), - _oss(), - _level(msg_level), - _enabled(enabled) - { - if(enabled) - { - callback_logger->_formatter->format_header(callback_logger->_logger_name, - msg_level, - log_clock::now(), - _oss); - } - } - - // 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), - // The move ctor should only be called on start of logging line, - // where no logging happened yet for this line so no need to copy the string from the other - _oss(), - _level(other._level) {}; - - - ~line_logger() - { - if (_enabled) - { - _oss << os::eol(); - _callback_logger->_log_it(_oss.buf(), _level); - } - } - - - template - line_logger&& operator<<(const T& msg) - { - if (_enabled) - _oss << msg; - return std::move(*this); - } - -private: - logger* _callback_logger; - details::fast_oss _oss; - level::level_enum _level; - bool _enabled; - -}; -} //Namespace details -} // Namespace c11log +#pragma once + +#include "../common_types.h" +#include "../logger.h" +#include "fast_oss.h" + + +// line logger class. should be used by the logger as an rvalue only. +// aggregates logging string until the end of the line and then calls the logger upon destruction + + +namespace c11log +{ +//class logger; +namespace details +{ + +class line_logger +{ +public: + line_logger(logger* callback_logger, level::level_enum msg_level, bool enabled): + _callback_logger(callback_logger), + _oss(), + _level(msg_level), + _enabled(enabled) + { + if(enabled) + { + callback_logger->_formatter->format_header(callback_logger->_logger_name, + msg_level, + log_clock::now(), + _oss); + } + } + + // 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), + // The move ctor should only be called on start of logging line, + // where no logging happened yet for this line so no need to copy the string from the other + _oss(), + _level(other._level) {}; + + + ~line_logger() + { + if (_enabled) + { + _oss << os::eol(); + _callback_logger->_log_it(_oss.buf(), _level); + } + } + + + template + line_logger&& operator<<(const T& msg) + { + if (_enabled) + _oss << msg; + return std::move(*this); + } + +private: + logger* _callback_logger; + details::fast_oss _oss; + level::level_enum _level; + bool _enabled; + +}; +} //Namespace details +} // Namespace c11log diff --git a/include/c11log/details/os.h b/include/c11log/details/os.h index 7a388c2a..8710ae47 100644 --- a/include/c11log/details/os.h +++ b/include/c11log/details/os.h @@ -1,63 +1,63 @@ -#pragma once -#include -#include -#include - -namespace c11log -{ -namespace details -{ -namespace os -{ - -inline std::tm localtime(const std::time_t &time_tt) -{ - - std::tm tm; -#ifdef _WIN32 - localtime_s(&tm, &time_tt); -#else - localtime_r(&time_tt, &tm); -#endif - return tm; -} - -inline std::tm localtime() -{ - std::time_t now_t = time(0); - return localtime(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); -} - -constexpr inline const char* eol() -{ -#ifdef _WIN32 - return "\r\n"; -#else - return "\n"; -#endif -} -} //os -} //details -} //c11log - - - +#pragma once +#include +#include +#include + +namespace c11log +{ +namespace details +{ +namespace os +{ + +inline std::tm localtime(const std::time_t &time_tt) +{ + + std::tm tm; +#ifdef _WIN32 + localtime_s(&tm, &time_tt); +#else + localtime_r(&time_tt, &tm); +#endif + return tm; +} + +inline std::tm localtime() +{ + std::time_t now_t = time(0); + return localtime(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); +} + +constexpr inline const char* eol() +{ +#ifdef _WIN32 + return "\r\n"; +#else + return "\n"; +#endif +} +} //os +} //details +} //c11log + + + diff --git a/include/c11log/formatter.h b/include/c11log/formatter.h index 37f32330..8e794c0c 100644 --- a/include/c11log/formatter.h +++ b/include/c11log/formatter.h @@ -1,97 +1,97 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common_types.h" -#include "details/os.h" -#include "details/fast_oss.h" - -namespace c11log -{ -namespace formatters -{ - -typedef std::function format_fn; - - -class formatter -{ -public: - formatter() {} - virtual ~formatter() {} - virtual void format_header(const std::string& logger_name, level::level_enum level, const log_clock::time_point& tp, std::ostream& dest) = 0; -}; - - -class default_formatter: public formatter -{ -public: - // Format: [2013-12-29 01:04:42.900] [logger_name:Info] Message body - void format_header(const std::string& logger_name, level::level_enum level, const log_clock::time_point& tp, std::ostream& dest) override - { - _format_time(tp, dest); - if(!logger_name.empty()) - dest << " [" << logger_name << ':' << c11log::level::to_str(level) << "] "; - else - dest << " [" << c11log::level::to_str(level) << "] "; - - } -private: - void _format_time(const log_clock::time_point& tp, std::ostream &dest); - -}; - - -} //namespace formatter -} //namespace c11log - -// Format datetime like this: [2014-03-14 17:15:22] -inline void c11log::formatters::default_formatter::_format_time(const log_clock::time_point& tp, std::ostream &dest) -{ - using namespace c11log::details::os; - using namespace std::chrono; - -#ifdef _WIN32 //VS2013 doesn't support yet thread_local keyword - __declspec(thread) static char s_cache_str[64]; - __declspec(thread) static size_t s_cache_size; - __declspec(thread) static std::time_t s_cache_time_t = 0; -#else - thread_local static char s_cache_str[64]; - thread_local static size_t s_cache_size; - thread_local static std::time_t s_cache_time_t = 0; -#endif - - //Cache every second - std::time_t tp_time_t = log_clock::to_time_t(tp); - if(tp_time_t != s_cache_time_t) - { - auto tm_now = details::os::localtime(tp_time_t); - details::fast_oss time_oss; - time_oss.fill('0'); - time_oss << '[' << tm_now.tm_year + 1900 << '-'; - time_oss.width(2); - time_oss << tm_now.tm_mon + 1 << '-'; - time_oss.width(2); - time_oss << tm_now.tm_mday << ' '; - time_oss.width(2); - time_oss << tm_now.tm_hour << ':'; - time_oss.width(2); - time_oss << tm_now.tm_min << ':'; - time_oss.width(2); - time_oss << tm_now.tm_sec << ']'; - //Cache the resulted string and its size - s_cache_time_t = tp_time_t; - //const std::string &s = time_oss.str_ref(); - bufpair_t buf = time_oss.buf(); - std::memcpy(s_cache_str, buf.first, buf.second); - s_cache_size = buf.second; - } - dest.write(s_cache_str, s_cache_size); -} +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common_types.h" +#include "details/os.h" +#include "details/fast_oss.h" + +namespace c11log +{ +namespace formatters +{ + +typedef std::function format_fn; + + +class formatter +{ +public: + formatter() {} + virtual ~formatter() {} + virtual void format_header(const std::string& logger_name, level::level_enum level, const log_clock::time_point& tp, std::ostream& dest) = 0; +}; + + +class default_formatter: public formatter +{ +public: + // Format: [2013-12-29 01:04:42.900] [logger_name:Info] Message body + void format_header(const std::string& logger_name, level::level_enum level, const log_clock::time_point& tp, std::ostream& dest) override + { + _format_time(tp, dest); + if(!logger_name.empty()) + dest << " [" << logger_name << ':' << c11log::level::to_str(level) << "] "; + else + dest << " [" << c11log::level::to_str(level) << "] "; + + } +private: + void _format_time(const log_clock::time_point& tp, std::ostream &dest); + +}; + + +} //namespace formatter +} //namespace c11log + +// Format datetime like this: [2014-03-14 17:15:22] +inline void c11log::formatters::default_formatter::_format_time(const log_clock::time_point& tp, std::ostream &dest) +{ + using namespace c11log::details::os; + using namespace std::chrono; + +#ifdef _WIN32 //VS2013 doesn't support yet thread_local keyword + __declspec(thread) static char s_cache_str[64]; + __declspec(thread) static size_t s_cache_size; + __declspec(thread) static std::time_t s_cache_time_t = 0; +#else + thread_local static char s_cache_str[64]; + thread_local static size_t s_cache_size; + thread_local static std::time_t s_cache_time_t = 0; +#endif + + //Cache every second + std::time_t tp_time_t = log_clock::to_time_t(tp); + if(tp_time_t != s_cache_time_t) + { + auto tm_now = details::os::localtime(tp_time_t); + details::fast_oss time_oss; + time_oss.fill('0'); + time_oss << '[' << tm_now.tm_year + 1900 << '-'; + time_oss.width(2); + time_oss << tm_now.tm_mon + 1 << '-'; + time_oss.width(2); + time_oss << tm_now.tm_mday << ' '; + time_oss.width(2); + time_oss << tm_now.tm_hour << ':'; + time_oss.width(2); + time_oss << tm_now.tm_min << ':'; + time_oss.width(2); + time_oss << tm_now.tm_sec << ']'; + //Cache the resulted string and its size + s_cache_time_t = tp_time_t; + //const std::string &s = time_oss.str_ref(); + bufpair_t buf = time_oss.buf(); + std::memcpy(s_cache_str, buf.first, buf.second); + s_cache_size = buf.second; + } + dest.write(s_cache_str, s_cache_size); +} diff --git a/include/c11log/logger.h b/include/c11log/logger.h index 9cb33167..c68e1c96 100644 --- a/include/c11log/logger.h +++ b/include/c11log/logger.h @@ -1,163 +1,163 @@ -#pragma once - -// Thread safe logger -// Has log level and vector sinks which do the actual logging -#include -#include -#include -#include -#include - -#include "common_types.h" -#include "sinks/base_sink.h" -#include "details/factory.h" -#include "c11log/details/log_msg.h" - - -//Thread safe, fast logger. -//All initialization is done in ctor only, so we get away lot of locking -namespace c11log -{ - -namespace details -{ -class line_logger; -template class fast_buf; -} - - -class logger -{ -public: - - using sink_ptr = std::shared_ptr; - using formatter_ptr = std::shared_ptr; - using sinks_vector_t = std::vector; - using sinks_init_list = std::initializer_list; - - logger(const std::string& name, formatter_ptr, sinks_init_list); - logger(const std::string& name, sinks_init_list); - logger(sinks_init_list sinks_list); - - - ~logger() = default; - - //Non copybale in anyway - logger(const logger&) = delete; - logger(logger&&) = delete; - logger& operator=(const logger&) = delete; - logger& operator=(logger&&) = delete; - - void set_level(c11log::level::level_enum); - c11log::level::level_enum get_level() const; - - const std::string& get_name() const; - bool should_log(c11log::level::level_enum) const; - - details::line_logger log(level::level_enum); - details::line_logger debug(); - details::line_logger info(); - details::line_logger warn(); - details::line_logger error(); - details::line_logger fatal(); - -private: - friend details::line_logger; - - std::string _logger_name = ""; - formatter_ptr _formatter; - sinks_vector_t _sinks; - std::atomic_int _atomic_level; - - void _log_it(const bufpair_t& buf, const level::level_enum level); - -}; - -logger& get_logger(const std::string& name); - -} - - -// -// Logger inline implementation -// -#include "details/line_logger.h" -#include "details/fast_buf.h" - - -inline c11log::logger::logger(const std::string& name, formatter_ptr f, sinks_init_list sinks_list) : - _logger_name(name), - _formatter(f), - _sinks(sinks_list) -{ - //Seems that vs2013 doesnt support atomic member initialization in ctor, so its done here - _atomic_level = level::INFO; -} - -inline c11log::logger::logger(const std::string& name, sinks_init_list sinks_list) : - logger(name, std::make_shared(), sinks_list) {} - -inline c11log::logger::logger(sinks_init_list sinks_list) : - logger("", std::make_shared(), sinks_list) {} - - -inline c11log::details::line_logger c11log::logger::log(c11log::level::level_enum msg_level) -{ - return details::line_logger(this, msg_level, msg_level >= _atomic_level); -} - -inline c11log::details::line_logger c11log::logger::debug() -{ - return log(c11log::level::DEBUG); -} -inline c11log::details::line_logger c11log::logger::info() -{ - return log(c11log::level::INFO); -} -inline c11log::details::line_logger c11log::logger::warn() -{ - return log(c11log::level::WARNING); -} -inline c11log::details::line_logger c11log::logger::error() -{ - return log(level::ERROR); -} -inline c11log::details::line_logger c11log::logger::fatal() -{ - return log(c11log::level::FATAL); -} - - -inline const std::string& c11log::logger::get_name() const -{ - return _logger_name; -} - - -inline void c11log::logger::set_level(c11log::level::level_enum level) -{ - _atomic_level.store(level); -} - -inline c11log::level::level_enum c11log::logger::get_level() const -{ - return static_cast(_atomic_level.load()); -} - -inline bool c11log::logger::should_log(c11log::level::level_enum level) const -{ - return level >= _atomic_level.load(); -} - -inline void c11log::logger::_log_it(const bufpair_t& buf, const level::level_enum level) -{ - for (auto &sink : _sinks) - sink->log(buf, level); -} - -// Static factory function - -inline c11log::logger& c11log::get_logger(const std::string& name) -{ - return *(c11log::details::factory::instance().get_logger(name)); -} +#pragma once + +// Thread safe logger +// Has log level and vector sinks which do the actual logging +#include +#include +#include +#include +#include + +#include "common_types.h" +#include "sinks/base_sink.h" +#include "details/factory.h" +#include "c11log/details/log_msg.h" + + +//Thread safe, fast logger. +//All initialization is done in ctor only, so we get away lot of locking +namespace c11log +{ + +namespace details +{ +class line_logger; +template class fast_buf; +} + + +class logger +{ +public: + + using sink_ptr = std::shared_ptr; + using formatter_ptr = std::shared_ptr; + using sinks_vector_t = std::vector; + using sinks_init_list = std::initializer_list; + + logger(const std::string& name, formatter_ptr, sinks_init_list); + logger(const std::string& name, sinks_init_list); + logger(sinks_init_list sinks_list); + + + ~logger() = default; + + //Non copybale in anyway + logger(const logger&) = delete; + logger(logger&&) = delete; + logger& operator=(const logger&) = delete; + logger& operator=(logger&&) = delete; + + void set_level(c11log::level::level_enum); + c11log::level::level_enum get_level() const; + + const std::string& get_name() const; + bool should_log(c11log::level::level_enum) const; + + details::line_logger log(level::level_enum); + details::line_logger debug(); + details::line_logger info(); + details::line_logger warn(); + details::line_logger error(); + details::line_logger fatal(); + +private: + friend details::line_logger; + + std::string _logger_name = ""; + formatter_ptr _formatter; + sinks_vector_t _sinks; + std::atomic_int _atomic_level; + + void _log_it(const bufpair_t& buf, const level::level_enum level); + +}; + +logger& get_logger(const std::string& name); + +} + + +// +// Logger inline implementation +// +#include "details/line_logger.h" +#include "details/fast_buf.h" + + +inline c11log::logger::logger(const std::string& name, formatter_ptr f, sinks_init_list sinks_list) : + _logger_name(name), + _formatter(f), + _sinks(sinks_list) +{ + //Seems that vs2013 doesnt support atomic member initialization in ctor, so its done here + _atomic_level = level::INFO; +} + +inline c11log::logger::logger(const std::string& name, sinks_init_list sinks_list) : + logger(name, std::make_shared(), sinks_list) {} + +inline c11log::logger::logger(sinks_init_list sinks_list) : + logger("", std::make_shared(), sinks_list) {} + + +inline c11log::details::line_logger c11log::logger::log(c11log::level::level_enum msg_level) +{ + return details::line_logger(this, msg_level, msg_level >= _atomic_level); +} + +inline c11log::details::line_logger c11log::logger::debug() +{ + return log(c11log::level::DEBUG); +} +inline c11log::details::line_logger c11log::logger::info() +{ + return log(c11log::level::INFO); +} +inline c11log::details::line_logger c11log::logger::warn() +{ + return log(c11log::level::WARNING); +} +inline c11log::details::line_logger c11log::logger::error() +{ + return log(level::ERROR); +} +inline c11log::details::line_logger c11log::logger::fatal() +{ + return log(c11log::level::FATAL); +} + + +inline const std::string& c11log::logger::get_name() const +{ + return _logger_name; +} + + +inline void c11log::logger::set_level(c11log::level::level_enum level) +{ + _atomic_level.store(level); +} + +inline c11log::level::level_enum c11log::logger::get_level() const +{ + return static_cast(_atomic_level.load()); +} + +inline bool c11log::logger::should_log(c11log::level::level_enum level) const +{ + return level >= _atomic_level.load(); +} + +inline void c11log::logger::_log_it(const bufpair_t& buf, const level::level_enum level) +{ + for (auto &sink : _sinks) + sink->log(buf, level); +} + +// Static factory function + +inline c11log::logger& c11log::get_logger(const std::string& name) +{ + return *(c11log::details::factory::instance().get_logger(name)); +} diff --git a/include/c11log/sinks/async_sink.h b/include/c11log/sinks/async_sink.h index d99e5b20..2277378d 100644 --- a/include/c11log/sinks/async_sink.h +++ b/include/c11log/sinks/async_sink.h @@ -1,116 +1,116 @@ -#pragma once - -#include -#include -#include - -#include "base_sink.h" -#include "../logger.h" -#include "../details/blocking_queue.h" - -namespace c11log -{ -namespace sinks -{ - -class async_sink : public base_sink -{ -public: - using size_type = c11log::details::blocking_queue::size_type; - - explicit async_sink(const size_type max_queue_size); - ~async_sink(); - void add_sink(logger::sink_ptr sink); - void remove_sink(logger::sink_ptr sink_ptr); - - //Wait to remaining items (if any) in the queue to be written and shutdown - void shutdown(const std::chrono::seconds& timeout); - - -protected: - void _sink_it(const bufpair_t& msg) override; - void _thread_loop(); - -private: - c11log::logger::sinks_vector_t _sinks; - std::atomic _active; - c11log::details::blocking_queue _q; - std::thread _back_thread; - //Clear all remaining messages(if any), stop the _back_thread and join it - void _shutdown(); -}; -} -} - -/////////////////////////////////////////////////////////////////////////////// -// async_sink class implementation -/////////////////////////////////////////////////////////////////////////////// - -inline c11log::sinks::async_sink::async_sink(const std::size_t max_queue_size) - :_sinks(), - _active(true), - _q(max_queue_size), - _back_thread(&async_sink::_thread_loop, this) -{} - -inline c11log::sinks::async_sink::~async_sink() -{ - _shutdown(); -} -inline void c11log::sinks::async_sink::_sink_it(const bufpair_t& msg) -{ - std::string s {msg.first, msg.first+msg.second}; - _q.push(s); -} - -inline void c11log::sinks::async_sink::_thread_loop() -{ - static std::chrono::seconds pop_timeout { 1 }; - std::string msg; - - while (_active) - { - if (_q.pop(msg, pop_timeout)) - { - bufpair_t buf(msg.data(), msg.size()); - for (auto &sink : _sinks) - { - sink->log(buf, static_cast(_level.load())); - if (!_active) - return; - } - } - } -} - -inline void c11log::sinks::async_sink::add_sink(logger::sink_ptr sink) -{ - _sinks.push_back(sink); -} - -inline void c11log::sinks::async_sink::remove_sink(logger::sink_ptr sink_ptr) -{ - _sinks.erase(std::remove(_sinks.begin(), _sinks.end(), sink_ptr), _sinks.end()); -} - - -inline void c11log::sinks::async_sink::shutdown(const std::chrono::seconds &timeout) -{ - auto until = std::chrono::system_clock::now() + timeout; - while (_q.size() > 0 && std::chrono::system_clock::now() < until) - { - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - } - _shutdown(); -} - -inline void c11log::sinks::async_sink::_shutdown() -{ - if(_active) - { - _active = false; - if (_back_thread.joinable()) - _back_thread.join(); - } -} - +#pragma once + +#include +#include +#include + +#include "base_sink.h" +#include "../logger.h" +#include "../details/blocking_queue.h" + +namespace c11log +{ +namespace sinks +{ + +class async_sink : public base_sink +{ +public: + using size_type = c11log::details::blocking_queue::size_type; + + explicit async_sink(const size_type max_queue_size); + ~async_sink(); + void add_sink(logger::sink_ptr sink); + void remove_sink(logger::sink_ptr sink_ptr); + + //Wait to remaining items (if any) in the queue to be written and shutdown + void shutdown(const std::chrono::seconds& timeout); + + +protected: + void _sink_it(const bufpair_t& msg) override; + void _thread_loop(); + +private: + c11log::logger::sinks_vector_t _sinks; + std::atomic _active; + c11log::details::blocking_queue _q; + std::thread _back_thread; + //Clear all remaining messages(if any), stop the _back_thread and join it + void _shutdown(); +}; +} +} + +/////////////////////////////////////////////////////////////////////////////// +// async_sink class implementation +/////////////////////////////////////////////////////////////////////////////// + +inline c11log::sinks::async_sink::async_sink(const std::size_t max_queue_size) + :_sinks(), + _active(true), + _q(max_queue_size), + _back_thread(&async_sink::_thread_loop, this) +{} + +inline c11log::sinks::async_sink::~async_sink() +{ + _shutdown(); +} +inline void c11log::sinks::async_sink::_sink_it(const bufpair_t& msg) +{ + std::string s {msg.first, msg.first+msg.second}; + _q.push(s); +} + +inline void c11log::sinks::async_sink::_thread_loop() +{ + static std::chrono::seconds pop_timeout { 1 }; + std::string msg; + + while (_active) + { + if (_q.pop(msg, pop_timeout)) + { + bufpair_t buf(msg.data(), msg.size()); + for (auto &sink : _sinks) + { + sink->log(buf, static_cast(_level.load())); + if (!_active) + return; + } + } + } +} + +inline void c11log::sinks::async_sink::add_sink(logger::sink_ptr sink) +{ + _sinks.push_back(sink); +} + +inline void c11log::sinks::async_sink::remove_sink(logger::sink_ptr sink_ptr) +{ + _sinks.erase(std::remove(_sinks.begin(), _sinks.end(), sink_ptr), _sinks.end()); +} + + +inline void c11log::sinks::async_sink::shutdown(const std::chrono::seconds &timeout) +{ + auto until = std::chrono::system_clock::now() + timeout; + while (_q.size() > 0 && std::chrono::system_clock::now() < until) + { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + _shutdown(); +} + +inline void c11log::sinks::async_sink::_shutdown() +{ + if(_active) + { + _active = false; + if (_back_thread.joinable()) + _back_thread.join(); + } +} + diff --git a/include/c11log/sinks/base_sink.h b/include/c11log/sinks/base_sink.h index a64320dc..3f1bfcca 100644 --- a/include/c11log/sinks/base_sink.h +++ b/include/c11log/sinks/base_sink.h @@ -1,51 +1,51 @@ -#pragma once - -#include -#include - -#include "../formatter.h" -#include "../common_types.h" - -namespace c11log -{ -namespace sinks -{ -class base_sink -{ -public: - base_sink() = default; - base_sink(level::level_enum l):_level(l) - { - }; - virtual ~base_sink() = default; - - base_sink(const base_sink&) = delete; - base_sink& operator=(const base_sink&) = delete; - - void log(const bufpair_t &msg, level::level_enum level) - { - if (level >= _level) - { - _sink_it(msg); - } - }; - - void set_level(level::level_enum level) - { - _level = level; - } - -protected: - virtual void _sink_it(const bufpair_t& msg) = 0; - std::atomic _level {level::INFO}; -}; - -class null_sink:public base_sink -{ -protected: - void _sink_it(const bufpair_t& ) override - { - } -}; -} -} +#pragma once + +#include +#include + +#include "../formatter.h" +#include "../common_types.h" + +namespace c11log +{ +namespace sinks +{ +class base_sink +{ +public: + base_sink() = default; + base_sink(level::level_enum l):_level(l) + { + }; + virtual ~base_sink() = default; + + base_sink(const base_sink&) = delete; + base_sink& operator=(const base_sink&) = delete; + + void log(const bufpair_t &msg, level::level_enum level) + { + if (level >= _level) + { + _sink_it(msg); + } + }; + + void set_level(level::level_enum level) + { + _level = level; + } + +protected: + virtual void _sink_it(const bufpair_t& msg) = 0; + std::atomic _level {level::INFO}; +}; + +class null_sink:public base_sink +{ +protected: + void _sink_it(const bufpair_t& ) override + { + } +}; +} +} diff --git a/include/c11log/sinks/console_sinks.h b/include/c11log/sinks/console_sinks.h index 6686e7b2..08ec2090 100644 --- a/include/c11log/sinks/console_sinks.h +++ b/include/c11log/sinks/console_sinks.h @@ -1,47 +1,47 @@ -#pragma once - -#include -#include -#include - -#include "base_sink.h" - -namespace c11log -{ -namespace sinks -{ -class console_sink: public base_sink -{ -public: - explicit console_sink(std::ostream& os):_ostream(os) {} - console_sink(const console_sink&) = delete; - console_sink& operator=(const console_sink&) = delete; - virtual ~console_sink() = default; - -protected: - virtual void _sink_it(const bufpair_t& msg) override - { - std::lock_guard lock(_mutex); - _ostream.write(msg.first, msg.second); - } - - std::ostream& _ostream; - std::mutex _mutex; -}; - - -inline std::shared_ptr& stdout_sink () -{ - static auto inst = std::make_shared(std::cout); - return inst; -} - -inline std::shared_ptr& stderr_sink () -{ - static auto inst = std::make_shared(std::cerr); - return inst; -} - - -} -} +#pragma once + +#include +#include +#include + +#include "base_sink.h" + +namespace c11log +{ +namespace sinks +{ +class console_sink: public base_sink +{ +public: + explicit console_sink(std::ostream& os):_ostream(os) {} + console_sink(const console_sink&) = delete; + console_sink& operator=(const console_sink&) = delete; + virtual ~console_sink() = default; + +protected: + virtual void _sink_it(const bufpair_t& msg) override + { + std::lock_guard lock(_mutex); + _ostream.write(msg.first, msg.second); + } + + std::ostream& _ostream; + std::mutex _mutex; +}; + + +inline std::shared_ptr& stdout_sink () +{ + static auto inst = std::make_shared(std::cout); + return inst; +} + +inline std::shared_ptr& stderr_sink () +{ + static auto inst = std::make_shared(std::cerr); + return inst; +} + + +} +} diff --git a/include/c11log/sinks/file_sinks.h b/include/c11log/sinks/file_sinks.h index 4e68e7d4..a1cb7529 100644 --- a/include/c11log/sinks/file_sinks.h +++ b/include/c11log/sinks/file_sinks.h @@ -1,180 +1,180 @@ -#pragma once - -#include -#include -#include -#include "base_sink.h" -#include "../details/flush_helper.h" - -namespace c11log -{ -namespace sinks -{ - -/* -* Thread safe, trivial file sink with single file as target -*/ -class simple_file_sink : public base_sink -{ -public: - explicit simple_file_sink(const std::string &filename, - const std::string& extension, - const std::size_t flush_every=0) - : _mutex(), - _ofstream(filename + "." + extension, std::ofstream::binary|std::ofstream::app), - _flush_helper(flush_every) - { - } -protected: - void _sink_it(const bufpair_t& msg) override - { - std::lock_guard lock(_mutex); - _flush_helper.write(_ofstream, msg); - } -private: - std::mutex _mutex; - std::ofstream _ofstream; - details::file_flush_helper _flush_helper; -}; - - -/* - * Thread safe, rotating file sink based on size -*/ -class rotating_file_sink : public base_sink -{ -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_every=0): - _base_filename(base_filename), - _extension(extension), - _max_size(max_size), - _max_files(max_files), - _current_size(0), - _mutex(), - _ofstream(_calc_filename(_base_filename, 0, _extension), std::ofstream::binary), - _flush_helper(flush_every) - { - } - -protected: - void _sink_it(const bufpair_t& msg) override - { - std::lock_guard lock(_mutex); - _current_size += msg.second; - if (_current_size > _max_size) - { - _rotate(); - _current_size = msg.second; - } - _flush_helper.write(_ofstream, msg); - } - - -private: - static std::string _calc_filename(const std::string& filename, std::size_t index, const std::string& extension) - { - std::ostringstream oss; - if (index) - oss << filename << "." << index << "." << extension; - else - oss << filename << "." << extension; - return oss.str(); - } - - - // Rotate old files: - // log.n-1.txt -> log.n.txt - // log n-2.txt -> log.n-1.txt - // ... - // log.txt -> log.1.txt - void _rotate() - { - _ofstream.close(); - //Remove oldest file - for (auto i = _max_files; i > 0; --i) - { - auto src = _calc_filename(_base_filename, i - 1, _extension); - auto target = _calc_filename(_base_filename, i, _extension); - if (i == _max_files) - std::remove(target.c_str()); - std::rename(src.c_str(), target.c_str()); - } - _ofstream.open(_calc_filename(_base_filename, 0, _extension)); - } - std::string _base_filename; - std::string _extension; - std::size_t _max_size; - std::size_t _max_files; - std::size_t _current_size; - std::mutex _mutex; - std::ofstream _ofstream; - details::file_flush_helper _flush_helper; -}; - -/* - * Thread safe, rotating file sink based on date. rotates at midnight - */ -class daily_file_sink:public base_sink -{ -public: - explicit daily_file_sink(const std::string& base_filename, - const std::string& extension, - const std::size_t flush_every=0): - _base_filename(base_filename), - _extension(extension), - _midnight_tp (_calc_midnight_tp() ), - _mutex(), - _ofstream(_calc_filename(_base_filename, _extension), std::ofstream::binary|std::ofstream::app), - _flush_helper(flush_every) - { - } - -protected: - void _sink_it(const bufpair_t& msg) override - { - std::lock_guard lock(_mutex); - if (std::chrono::system_clock::now() >= _midnight_tp) - { - _ofstream.close(); - _ofstream.open(_calc_filename(_base_filename, _extension)); - _midnight_tp = _calc_midnight_tp(); - } - _flush_helper.write(_ofstream, 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 = c11log::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 = c11log::details::os::localtime(); - std::ostringstream 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; - std::mutex _mutex; - std::ofstream _ofstream; - details::file_flush_helper _flush_helper; - -}; -} -} +#pragma once + +#include +#include +#include +#include "base_sink.h" +#include "../details/flush_helper.h" + +namespace c11log +{ +namespace sinks +{ + +/* +* Thread safe, trivial file sink with single file as target +*/ +class simple_file_sink : public base_sink +{ +public: + explicit simple_file_sink(const std::string &filename, + const std::string& extension, + const std::size_t flush_every=0) + : _mutex(), + _ofstream(filename + "." + extension, std::ofstream::binary|std::ofstream::app), + _flush_helper(flush_every) + { + } +protected: + void _sink_it(const bufpair_t& msg) override + { + std::lock_guard lock(_mutex); + _flush_helper.write(_ofstream, msg); + } +private: + std::mutex _mutex; + std::ofstream _ofstream; + details::file_flush_helper _flush_helper; +}; + + +/* + * Thread safe, rotating file sink based on size +*/ +class rotating_file_sink : public base_sink +{ +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_every=0): + _base_filename(base_filename), + _extension(extension), + _max_size(max_size), + _max_files(max_files), + _current_size(0), + _mutex(), + _ofstream(_calc_filename(_base_filename, 0, _extension), std::ofstream::binary), + _flush_helper(flush_every) + { + } + +protected: + void _sink_it(const bufpair_t& msg) override + { + std::lock_guard lock(_mutex); + _current_size += msg.second; + if (_current_size > _max_size) + { + _rotate(); + _current_size = msg.second; + } + _flush_helper.write(_ofstream, msg); + } + + +private: + static std::string _calc_filename(const std::string& filename, std::size_t index, const std::string& extension) + { + std::ostringstream oss; + if (index) + oss << filename << "." << index << "." << extension; + else + oss << filename << "." << extension; + return oss.str(); + } + + + // Rotate old files: + // log.n-1.txt -> log.n.txt + // log n-2.txt -> log.n-1.txt + // ... + // log.txt -> log.1.txt + void _rotate() + { + _ofstream.close(); + //Remove oldest file + for (auto i = _max_files; i > 0; --i) + { + auto src = _calc_filename(_base_filename, i - 1, _extension); + auto target = _calc_filename(_base_filename, i, _extension); + if (i == _max_files) + std::remove(target.c_str()); + std::rename(src.c_str(), target.c_str()); + } + _ofstream.open(_calc_filename(_base_filename, 0, _extension)); + } + std::string _base_filename; + std::string _extension; + std::size_t _max_size; + std::size_t _max_files; + std::size_t _current_size; + std::mutex _mutex; + std::ofstream _ofstream; + details::file_flush_helper _flush_helper; +}; + +/* + * Thread safe, rotating file sink based on date. rotates at midnight + */ +class daily_file_sink:public base_sink +{ +public: + explicit daily_file_sink(const std::string& base_filename, + const std::string& extension, + const std::size_t flush_every=0): + _base_filename(base_filename), + _extension(extension), + _midnight_tp (_calc_midnight_tp() ), + _mutex(), + _ofstream(_calc_filename(_base_filename, _extension), std::ofstream::binary|std::ofstream::app), + _flush_helper(flush_every) + { + } + +protected: + void _sink_it(const bufpair_t& msg) override + { + std::lock_guard lock(_mutex); + if (std::chrono::system_clock::now() >= _midnight_tp) + { + _ofstream.close(); + _ofstream.open(_calc_filename(_base_filename, _extension)); + _midnight_tp = _calc_midnight_tp(); + } + _flush_helper.write(_ofstream, 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 = c11log::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 = c11log::details::os::localtime(); + std::ostringstream 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; + std::mutex _mutex; + std::ofstream _ofstream; + details::file_flush_helper _flush_helper; + +}; +} +}