diff --git a/c11log.sln b/c11log.sln new file mode 100644 index 00000000..582ead83 --- /dev/null +++ b/c11log.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.21005.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "c11log", "c11log.vcxproj", "{BBFA8622-1945-4EB0-BAF4-473BE753ED24}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test\test.vcxproj", "{429A1E1E-6F89-4941-B4A7-7464CEA22587}" + ProjectSection(ProjectDependencies) = postProject + {BBFA8622-1945-4EB0-BAF4-473BE753ED24} = {BBFA8622-1945-4EB0-BAF4-473BE753ED24} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BBFA8622-1945-4EB0-BAF4-473BE753ED24}.Debug|Win32.ActiveCfg = Debug|Win32 + {BBFA8622-1945-4EB0-BAF4-473BE753ED24}.Debug|Win32.Build.0 = Debug|Win32 + {BBFA8622-1945-4EB0-BAF4-473BE753ED24}.Release|Win32.ActiveCfg = Release|Win32 + {BBFA8622-1945-4EB0-BAF4-473BE753ED24}.Release|Win32.Build.0 = Release|Win32 + {429A1E1E-6F89-4941-B4A7-7464CEA22587}.Debug|Win32.ActiveCfg = Debug|Win32 + {429A1E1E-6F89-4941-B4A7-7464CEA22587}.Debug|Win32.Build.0 = Debug|Win32 + {429A1E1E-6F89-4941-B4A7-7464CEA22587}.Release|Win32.ActiveCfg = Release|Win32 + {429A1E1E-6F89-4941-B4A7-7464CEA22587}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/c11log.v12.suo b/c11log.v12.suo new file mode 100644 index 00000000..da81938b Binary files /dev/null and b/c11log.v12.suo differ diff --git a/include/c11log/details/blocking_queue.h b/include/c11log/details/blocking_queue.h index 0859b98b..4a9cbb15 100644 --- a/include/c11log/details/blocking_queue.h +++ b/include/c11log/details/blocking_queue.h @@ -21,17 +21,16 @@ public: 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() + explicit blocking_queue(size_type max_size) :max_size_(max_size), q_() {} blocking_queue(const blocking_queue&) = delete; - blocking_queue& operator=(const blocking_queue&) = delete; - blocking_queue& operator=(const blocking_queue&) volatile = delete; + blocking_queue& operator=(const blocking_queue&) = delete; ~blocking_queue() = default; size_type size() { - std::lock_guard lock(_mutex); - return _q.size(); + std::lock_guard lock(mutex_); + return q_.size(); } // Push copy of item into the back of the queue. @@ -40,17 +39,17 @@ public: template bool push(const T& item, const std::chrono::duration& timeout) { - std::unique_lock ul(_mutex); - if (_q.size() >= _max_size) + 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; })) + if (!item_popped_cond_.wait_until(ul, clock::now() + timeout, [this]() { return this->q_.size() < this->max_size_; })) return false; } - _q.push(item); - if (_q.size() <= 1) + q_.push(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(); + item_pushed_cond_.notify_one(); } return true; } @@ -68,18 +67,18 @@ public: template bool pop(T& item, const std::chrono::duration& timeout) { - std::unique_lock ul(_mutex); - if (_q.empty()) + std::unique_lock ul(mutex_); + if (q_.empty()) { - if (!_item_pushed_cond.wait_until(ul, clock::now() + timeout, [this]() { return !this->_q.empty(); })) + if (!item_pushed_cond_.wait_until(ul, clock::now() + timeout, [this]() { return !this->q_.empty(); })) return false; } - item = _q.front(); - _q.pop(); - if (_q.size() >= _max_size - 1) + item = 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(); + item_popped_cond_.notify_one(); } return true; } @@ -91,12 +90,19 @@ public: while (!pop(item, std::chrono::hours::max())); } + // Clear the queue + void clear() + { + T item; + while (pop(item, std::chrono::milliseconds(0))); + } + private: - size_type _max_size; - std::queue _q; - std::mutex _mutex; - std::condition_variable _item_pushed_cond; - std::condition_variable _item_popped_cond; + size_type max_size_; + std::queue q_; + std::mutex mutex_; + std::condition_variable item_pushed_cond_; + std::condition_variable item_popped_cond_; }; } } \ No newline at end of file diff --git a/include/c11log/logger.h b/include/c11log/logger.h index 3105d82a..0ca87398 100644 --- a/include/c11log/logger.h +++ b/include/c11log/logger.h @@ -1,5 +1,7 @@ #pragma once +// Thread safe logger +// Has log level and vector sinks which do the actual logging #include #include #include @@ -20,15 +22,13 @@ public: typedef std::shared_ptr sink_ptr_t; typedef std::vector sinks_vector_t; - explicit logger(const std::string& name) : _logger_name(name), - _formatter(std::make_unique()) + explicit logger(const std::string& name) : logger_name_(name), + formatter_(std::make_unique()) { - _atomic_level.store(level::INFO); + atomic_level_.store(level::INFO); } - ~logger() - { - }; + ~logger() = default; logger(const logger&) = delete; logger& operator=(const logger&) = delete; @@ -53,30 +53,32 @@ private: friend details::line_logger; - std::string _logger_name = ""; - std::unique_ptr _formatter; - sinks_vector_t _sinks; - std::mutex _mutex; - std::atomic_int _atomic_level; + std::string logger_name_ = ""; + std::unique_ptr formatter_; + sinks_vector_t sinks_; + std::mutex mutex_; + std::atomic_int atomic_level_; - void _log_it(const std::string& msg); + void log_it_(const std::string& msg); }; logger& get_logger(const std::string& name); } -/* -Logger inline impl -*/ - +// +// Logger inline impl +// inline c11log::details::line_logger c11log::logger::log(c11log::level::level_enum msg_level) { - if (msg_level >= _atomic_level.load()) { - std::lock_guard lock(_mutex); + if (msg_level >= atomic_level_.load()) + { + std::lock_guard lock(mutex_); return details::line_logger(this, msg_level); - } else { + } + else + { return details::line_logger(nullptr); } @@ -105,56 +107,57 @@ inline c11log::details::line_logger c11log::logger::fatal() inline void c11log::logger::set_name(const std::string& name) { - std::lock_guard lock(_mutex); - _logger_name = name; + std::lock_guard lock(mutex_); + logger_name_ = name; } inline const std::string& c11log::logger::get_name() { - std::lock_guard lock(_mutex); - return _logger_name; + std::lock_guard lock(mutex_); + return logger_name_; } inline void c11log::logger::add_sink(sink_ptr_t sink_ptr) { - std::lock_guard lock(_mutex); - _sinks.push_back(sink_ptr); + std::lock_guard lock(mutex_); + sinks_.push_back(sink_ptr); } inline void c11log::logger::remove_sink(sink_ptr_t sink_ptr) { - std::lock_guard lock(_mutex); - _sinks.erase(std::remove(_sinks.begin(), _sinks.end(), sink_ptr), _sinks.end()); + std::lock_guard lock(mutex_); + sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink_ptr), sinks_.end()); } inline void c11log::logger::set_formatter(std::unique_ptr formatter) { - std::lock_guard lock(_mutex); - _formatter = std::move(formatter); + std::lock_guard lock(mutex_); + formatter_ = std::move(formatter); } inline void c11log::logger::set_level(c11log::level::level_enum level) { - _atomic_level.store(level); + atomic_level_.store(level); } inline c11log::level::level_enum c11log::logger::get_level() const { - return static_cast(_atomic_level.load()); + return static_cast(atomic_level_.load()); } inline bool c11log::logger::should_log(c11log::level::level_enum level) const { - return level >= _atomic_level.load(); + return level >= atomic_level_.load(); } -inline void c11log::logger::_log_it(const std::string& msg) +inline void c11log::logger::log_it_(const std::string& msg) { - level::level_enum level = static_cast(_atomic_level.load()); - std::lock_guard lock(_mutex); - for (auto &sink : _sinks) + level::level_enum level = static_cast(atomic_level_.load()); + std::lock_guard lock(mutex_); + for (auto &sink : sinks_) sink->log(msg, 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 8f95c6c5..5d9a9138 100644 --- a/include/c11log/sinks/async_sink.h +++ b/include/c11log/sinks/async_sink.h @@ -1,55 +1,98 @@ #pragma once -#include -#include -#include -#include -#include "../logger.h" +#include +#include +#include +#include + #include "base_sink.h" +#include "../logger.h" +#include "../details/blocking_queue.h" + namespace c11log { namespace sinks { -class async_sink : base_sink { - enum class fullq_policy { - BLOCK=0, - DROP_MSG - }; +class async_sink : public base_sink { + public: - async_sink(std::size_t max_queue_size, fullq_policy q_policy) :_fullq_policy(q_policy), _back_thread(&_thread_loop) - { - - } -protected: - void _sink_it(const std::string& msg) override - { - _msgs_mutex.unlock(); - _msgs.push(msg); - } - void _thread_loop() - { - while (_active) { - _msgs_mutex.lock(); - std::string &msg = _msgs.front(); - _msgs.pop(); - _msgs_mutex.unlock(); - std::cout << "Popped: " << msg << std::endl; - } - } - -private: - c11log::logger::sinks_vector_t _sinks; - fullq_policy _fullq_policy; - std::queue _msgs; - std::thread _back_thread; - bool _active = true; - std::mutex _msgs_mutex; - - + using size_type = c11log::details::blocking_queue::size_type; + explicit async_sink(const std::size_t max_queue_size, const std::chrono::seconds& timeout = std::chrono::seconds::max()); + ~async_sink(); + void add_sink(logger::sink_ptr_t sink); + void remove_sink(logger::sink_ptr_t sink_ptr); + +protected: + void sink_it_(const std::string& msg) override; + void thread_loop_(); + +private: + c11log::logger::sinks_vector_t sinks_; + bool active_ = true; + const std::chrono::seconds timeout_; + c11log::details::blocking_queue q_; + std::thread back_thread_; + void shutdown_(); }; } } -void c11log::sinks::async_sink::_sink_it(const std::string& msg) -{ +// +// async_sink inline impl +// + +inline c11log::sinks::async_sink::async_sink(const std::size_t max_queue_size, const std::chrono::seconds& timeout) + :q_(max_queue_size), + timeout_(timeout), + back_thread_(&async_sink::thread_loop_, this) +{} + +inline c11log::sinks::async_sink::~async_sink() +{ + shutdown_(); +} +inline void c11log::sinks::async_sink::sink_it_(const std::string& msg) +{ + q_.push(msg, timeout_); +} + +inline void c11log::sinks::async_sink::thread_loop_() +{ + std::string msg; + while (active_) + { + if (q_.pop(msg, timeout_)) + { + std::lock_guard lock(mutex_); + for (auto &sink : sinks_) + { + if (active_) + sink->log(msg, _level); + else + break; + } + } + } +} + +inline void c11log::sinks::async_sink::add_sink(logger::sink_ptr_t sink) +{ + std::lock_guard lock(mutex_); + sinks_.push_back(sink); +} + +inline void c11log::sinks::async_sink::remove_sink(logger::sink_ptr_t sink_ptr) +{ + std::lock_guard lock(mutex_); + sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink_ptr), sinks_.end()); +} + +inline void c11log::sinks::async_sink::shutdown_() +{ + { + std::lock_guard lock(mutex_); + active_ = false; + } + q_.clear(); + back_thread_.join(); +} -} \ No newline at end of file diff --git a/include/c11log/sinks/base_sink.h b/include/c11log/sinks/base_sink.h index 87349972..bf444729 100644 --- a/include/c11log/sinks/base_sink.h +++ b/include/c11log/sinks/base_sink.h @@ -1,4 +1,5 @@ #pragma once + #include #include #include @@ -10,40 +11,38 @@ namespace c11log { namespace sinks { class base_sink { public: - base_sink() - {} + base_sink() = default; base_sink(level::level_enum l):_level(l) {}; - virtual ~base_sink() - {}; - + virtual ~base_sink() = default; + base_sink(const base_sink&) = delete; base_sink& operator=(const base_sink&) = delete; void log(const std::string &msg, level::level_enum level) { if (level >= _level) { - std::lock_guard lock(_mutex); + std::lock_guard lock(mutex_); if (level >= _level) - _sink_it(msg); + sink_it_(msg); } }; void set_level(level::level_enum level) { - std::lock_guard lock(_mutex); + std::lock_guard lock(mutex_); _level = level; } protected: - virtual void _sink_it(const std::string& msg) = 0; + virtual void sink_it_(const std::string& msg) = 0; level::level_enum _level = level::INFO; - std::mutex _mutex; + std::mutex mutex_; }; class null_sink:public base_sink { protected: - void _sink_it(const std::string& msg) override + void sink_it_(const std::string& msg) override {} }; } diff --git a/include/c11log/sinks/file_sinks.h b/include/c11log/sinks/file_sinks.h index bd729c42..a8edf5c0 100644 --- a/include/c11log/sinks/file_sinks.h +++ b/include/c11log/sinks/file_sinks.h @@ -1,4 +1,5 @@ #pragma once + #include #include @@ -22,7 +23,7 @@ public: _ofstream.open(oss.str(), std::ofstream::app); } protected: - void _sink_it(const std::string& msg) override + void sink_it_(const std::string& msg) override { _ofstream << msg; _ofstream.flush(); @@ -43,7 +44,7 @@ public: virtual ~rotating_file_sink_base() {} protected: - virtual void _sink_it(const std::string& msg) override + virtual void sink_it_(const std::string& msg) override { if (_should_rotate()) _rotate(); @@ -69,10 +70,10 @@ public: } protected: - virtual void _sink_it(const std::string& msg) override + virtual void sink_it_(const std::string& msg) override { _current_size += msg.length(); - rotating_file_sink_base::_sink_it(msg); + rotating_file_sink_base::sink_it_(msg); } bool _should_rotate() const override diff --git a/include/c11log/sinks/stdout_sinks.h b/include/c11log/sinks/stdout_sinks.h index 8f6b3e37..1ec0d7c5 100644 --- a/include/c11log/sinks/stdout_sinks.h +++ b/include/c11log/sinks/stdout_sinks.h @@ -1,41 +1,38 @@ +#pragma once #include #include "base_sink.h" namespace c11log { - namespace sinks +namespace sinks +{ +class ostream_sink: public base_sink +{ +public: + ostream_sink(std::ostream& os):_ostream(os) {} + virtual ~ostream_sink() = default; + +protected: + virtual void sink_it_(const std::string& msg) override { - class ostream_sink:public base_sink - { - public: - ostream_sink(std::ostream& os):_ostream(os) - {} - - virtual ~ostream_sink() - {} - - protected: - virtual void _sink_it(const std::string& msg) - { - _ostream << msg; - } - std::ostream& _ostream; - - }; - - class stdout_sink:public ostream_sink - { - public: - stdout_sink():ostream_sink(std::cout) - {} - }; - - class stderr_sink:public ostream_sink - { - public: - stderr_sink():ostream_sink(std::cerr) - {} - }; + _ostream << msg; } + + std::ostream& _ostream; +}; + +class stdout_sink:public ostream_sink +{ +public: + stdout_sink():ostream_sink(std::cout) {} +}; + +class stderr_sink:public ostream_sink +{ +public: + stderr_sink():ostream_sink(std::cerr) {} + +}; +} } \ No newline at end of file diff --git a/src/line_logger.cpp b/src/line_logger.cpp index f1b984ec..77c8708c 100644 --- a/src/line_logger.cpp +++ b/src/line_logger.cpp @@ -5,7 +5,7 @@ c11log::details::line_logger::line_logger(logger* callback_logger, level::level_ _callback_logger(callback_logger) { if (callback_logger) { - callback_logger->_formatter->format_header(callback_logger->_logger_name, + callback_logger->formatter_->format_header(callback_logger->logger_name_, msg_level, c11log::formatters::timepoint::clock::now(), _oss); @@ -16,6 +16,6 @@ c11log::details::line_logger::~line_logger() { if (_callback_logger) { _oss << '\n'; - _callback_logger->_log_it(_oss.str_ref()); + _callback_logger->log_it_(_oss.str_ref()); } } \ No newline at end of file diff --git a/test/stdafx.cpp b/test/stdafx.cpp new file mode 100644 index 00000000..ab6cf716 --- /dev/null +++ b/test/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// test.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/test/stdafx.h b/test/stdafx.h new file mode 100644 index 00000000..da4e7055 --- /dev/null +++ b/test/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include +#include +#include + +#include "utils.h" +#include "../include/c11log/logger.h" +#include "../include/c11log/sinks/async_sink.h" +#include "../include/c11log/sinks/stdout_sinks.h" +#include "../include/c11log/sinks/file_sinks.h" diff --git a/test/test.cpp b/test/test.cpp new file mode 100644 index 00000000..d6ec4839 --- /dev/null +++ b/test/test.cpp @@ -0,0 +1,24 @@ +// test.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" + + +void fn(); +int main(int argc, char* argv[]) +{ + + c11log::logger logger("test"); + + auto sink = std::make_shared(); + auto async = std::make_shared(100); + async->add_sink(sink); + logger.add_sink(async); + logger.info() << "Hello logger!"; + utils::run(std::chrono::seconds(10), fn); + return 0; +} + +static void fn() +{} + diff --git a/test/utils.h b/test/utils.h new file mode 100644 index 00000000..c9b56b18 --- /dev/null +++ b/test/utils.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace utils +{ + +template +std::string format(const T& value) +{ + static std::locale loc(""); + std::stringstream ss; + ss.imbue(loc); + ss << value; + return ss.str(); +} + +inline void run(const std::chrono::milliseconds &duration, const std::function& fn) +{ + using namespace std::chrono; + typedef steady_clock the_clock; + size_t counter = 0; + seconds print_interval(1); + auto start_time = the_clock::now(); + auto lastPrintTime = start_time; + while (true) + { + fn(); + ++counter; + auto now = the_clock::now(); + if (now - start_time >= duration) + break; + auto p = now - lastPrintTime; + if (now - lastPrintTime >= print_interval) + { + std::cout << format(counter) << " per sec" << std::endl; + counter = 0; + lastPrintTime = the_clock::now(); + } + } +} +} \ No newline at end of file