From 681e40ce46c568f66bcbfb33ad34c39c35f75ab4 Mon Sep 17 00:00:00 2001 From: gab Date: Sat, 25 Jan 2014 11:09:04 +0200 Subject: [PATCH] Initial import --- ReadMe.txt | 37 +++++ c11log.vcxproj | 115 ++++++++++++++++ c11log.vcxproj.filters | 102 ++++++++++++++ include/c11log/details/blocking_queue.h | 64 +++++++++ include/c11log/details/factory.h | 22 +++ include/c11log/details/fast_oss.h | 76 +++++++++++ include/c11log/details/line_logger.h | 30 ++++ include/c11log/details/message.h | 31 +++++ include/c11log/details/null_mutex.h | 17 +++ include/c11log/details/os.h | 17 +++ include/c11log/formatters/formatters.h | 38 ++++++ include/c11log/level.h | 24 ++++ include/c11log/log_exception.h | 46 +++++++ include/c11log/logger.h | 161 ++++++++++++++++++++++ include/c11log/sinks/async_sink.h | 55 ++++++++ include/c11log/sinks/base_sink.h | 50 +++++++ include/c11log/sinks/file_sinks.h | 174 ++++++++++++++++++++++++ include/c11log/sinks/stdout_sinks.h | 41 ++++++ src/factory.cpp | 23 ++++ src/formatters.cpp | 36 +++++ src/line_logger.cpp | 21 +++ src/logger.cpp | 51 +++++++ src/os.cpp | 24 ++++ stdafx.cpp | 8 ++ stdafx.h | 16 +++ targetver.h | 8 ++ 26 files changed, 1287 insertions(+) create mode 100644 ReadMe.txt create mode 100644 c11log.vcxproj create mode 100644 c11log.vcxproj.filters create mode 100644 include/c11log/details/blocking_queue.h create mode 100644 include/c11log/details/factory.h create mode 100644 include/c11log/details/fast_oss.h create mode 100644 include/c11log/details/line_logger.h create mode 100644 include/c11log/details/message.h create mode 100644 include/c11log/details/null_mutex.h create mode 100644 include/c11log/details/os.h create mode 100644 include/c11log/formatters/formatters.h create mode 100644 include/c11log/level.h create mode 100644 include/c11log/log_exception.h create mode 100644 include/c11log/logger.h create mode 100644 include/c11log/sinks/async_sink.h create mode 100644 include/c11log/sinks/base_sink.h create mode 100644 include/c11log/sinks/file_sinks.h create mode 100644 include/c11log/sinks/stdout_sinks.h create mode 100644 src/factory.cpp create mode 100644 src/formatters.cpp create mode 100644 src/line_logger.cpp create mode 100644 src/logger.cpp create mode 100644 src/os.cpp create mode 100644 stdafx.cpp create mode 100644 stdafx.h create mode 100644 targetver.h diff --git a/ReadMe.txt b/ReadMe.txt new file mode 100644 index 00000000..9866d009 --- /dev/null +++ b/ReadMe.txt @@ -0,0 +1,37 @@ +======================================================================== + STATIC LIBRARY : c11log Project Overview +======================================================================== + +AppWizard has created this c11log library project for you. + +This file contains a summary of what you will find in each of the files that +make up your c11log application. + + +c11log.vcxproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +c11log.vcxproj.filters + This is the filters file for VC++ projects generated using an Application Wizard. + It contains information about the association between the files in your project + and the filters. This association is used in the IDE to show grouping of files with + similar extensions under a specific node (for e.g. ".cpp" files are associated with the + "Source Files" filter). + + +///////////////////////////////////////////////////////////////////////////// + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named c11log.pch and a precompiled types file named StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/c11log.vcxproj b/c11log.vcxproj new file mode 100644 index 00000000..6584ec34 --- /dev/null +++ b/c11log.vcxproj @@ -0,0 +1,115 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {BBFA8622-1945-4EB0-BAF4-473BE753ED24} + Win32Proj + c11log + + + + StaticLibrary + true + v120 + Unicode + + + StaticLibrary + false + v120 + true + Unicode + + + + + + + + + + + + + $(ProjectDir)build\ + $(ProjectName)-debug + + + $(ProjectDir)build\ + $(ProjectName) + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + $(ProjectDir);$(ProjectDir)\include;%(AdditionalIncludeDirectories) + + + Windows + true + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + $(ProjectDir);$(ProjectDir)\include;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + + + + + + \ No newline at end of file diff --git a/c11log.vcxproj.filters b/c11log.vcxproj.filters new file mode 100644 index 00000000..22e1df4f --- /dev/null +++ b/c11log.vcxproj.filters @@ -0,0 +1,102 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {3f3d8a51-d799-43e2-bd6e-3638f1cd4f54} + + + {d087c87d-8703-46a1-aa23-4509cf253e87} + + + {5af50a0f-d174-41cd-833c-ead8ba06199a} + + + {64fe6898-a191-4d60-9363-25ecfd196f30} + + + + + + + + Header Files + + + Header Files + + + Header Files\c11log + + + Header Files\c11log + + + Header Files\c11log\details + + + Header Files\c11log\formatters + + + Header Files\c11log\sinks + + + Header Files\c11log\sinks + + + Header Files\c11log\sinks + + + Header Files\c11log + + + Header Files\c11log\details + + + Header Files\c11log\details + + + Header Files\c11log\details + + + Header Files\c11log\details + + + Header Files\c11log\details + + + Header Files\c11log\sinks + + + Header Files\c11log\details + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/include/c11log/details/blocking_queue.h b/include/c11log/details/blocking_queue.h new file mode 100644 index 00000000..c49d3096 --- /dev/null +++ b/include/c11log/details/blocking_queue.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace c11log { +namespace details { + +template +class blocking_queue { +public: + explicit blocking_queue(std::size_t max_size) :_max_size(max_size), _q() + {} + blocking_queue(const blocking_queue&) = delete; + blocking_queue& operator=(const blocking_queue&) = delete; + ~blocking_queue() = default; + + std::size_t size() + { + std::lock_guard lock(_mutex); + return _q.size(); + } + bool push(const T& item, const std::chrono::milliseconds& timeout) + { + std::unique_lock ul(_mutex); + if (_q.size() >= _max_size) { + if (_item_popped_cond.wait_for(ul, timeout) == std::cv_status::timeout || _q.size() >= _max_size) + return false; + } + + _q.push(item); + if (_q.size() <= 1) + _item_pushed_cond.notify_all(); + + return true; + } + bool pop(T& item, const std::chrono::milliseconds& timeout) + { + std::unique_lock ul(_mutex); + if (_q.empty()) { + if (_item_pushed_cond.wait_for(ul, timeout) == std::cv_status::timeout || _q.empty()) + return false; + } + item = _q.front(); + _q.pop(); + if (_q.size() >= _max_size - 1) + _item_popped_cond.notify_all(); + + return true; + } +private: + std::size_t _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/details/factory.h b/include/c11log/details/factory.h new file mode 100644 index 00000000..f57883dd --- /dev/null +++ b/include/c11log/details/factory.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include +#include + +namespace c11log { +class logger; +namespace details { +class factory { +public: + typedef std::shared_ptr logger_ptr; + typedef std::unordered_map logger_map; + logger_ptr get_logger(const std::string &name); + static c11log::details::factory& instance(); +private: + logger_map _loggers; + std::mutex _loggers_mutex; +}; +} +} diff --git a/include/c11log/details/fast_oss.h b/include/c11log/details/fast_oss.h new file mode 100644 index 00000000..1a5f9cd1 --- /dev/null +++ b/include/c11log/details/fast_oss.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include +namespace c11log { +namespace details { + +class str_devicebuf:public std::streambuf { +public: + str_devicebuf() + { + _str.reserve(128); + } + + const std::string& str_ref() + { + return _str; + } + + void clear() + { + _str.clear(); + } + +protected: + virtual int sync() override + { + return 0; + } + + virtual std::streamsize xsputn(const char_type* s, std::streamsize count) override + { + _str.append(s, static_cast(count)); + return count; + } + + virtual int_type overflow(int_type ch) override + { + if (ch != traits_type::eof()) + _str.append((char*)&ch, 1); + return 1; + } +private: + std::string _str; +}; + +class fast_oss:public std::ostream { +public: + fast_oss():std::ostream(&_dev) + {} + ~fast_oss() + {} + + const std::string& str_ref() const + { + auto mydevice = static_cast(rdbuf()); + return mydevice->str_ref(); + } + + const std::string str() const + { + auto mydevice = static_cast(rdbuf()); + return mydevice->str_ref(); + } + + void clear() + { + auto mydevice = static_cast(rdbuf()); + mydevice->clear(); + } +private: + str_devicebuf _dev; + +}; +} +} \ No newline at end of file diff --git a/include/c11log/details/line_logger.h b/include/c11log/details/line_logger.h new file mode 100644 index 00000000..42c24a9b --- /dev/null +++ b/include/c11log/details/line_logger.h @@ -0,0 +1,30 @@ +#pragma once + +#include "../level.h" +#include "fast_oss.h" + +namespace c11log { +class logger; +namespace details { + +class line_logger { +public: + c11log::details::line_logger::line_logger(logger* callback_logger, level::level_enum msg_level); + c11log::details::line_logger::line_logger(logger* callback_logger):_callback_logger(nullptr) {}; + ~line_logger(); + + template + line_logger& operator<<(const T& msg) + { + if (_callback_logger) + _oss << msg; + return *this; + } + +private: + logger* _callback_logger; + details::fast_oss _oss; + +}; +} //Namespace details +} // Namespace c11log \ No newline at end of file diff --git a/include/c11log/details/message.h b/include/c11log/details/message.h new file mode 100644 index 00000000..5e68f0af --- /dev/null +++ b/include/c11log/details/message.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include "../level.h" + +namespace c11log { +namespace details { +struct message { + message(const std::string& logger_name, + level::level_enum log_level, + const std::chrono::system_clock::time_point time_p + , const std::string& msg) : + logger_name(logger_name), + log_level(log_level), + time_p(time_p), + msg(msg) {} + + ~message() = default; + message(const message& other) = default; + message& operator=(const message& rhs) = default; + + + std::string logger_name; + level::level_enum log_level; + std::chrono::system_clock::time_point time_p; + std::string msg; +}; + +} +} diff --git a/include/c11log/details/null_mutex.h b/include/c11log/details/null_mutex.h new file mode 100644 index 00000000..2ef24f34 --- /dev/null +++ b/include/c11log/details/null_mutex.h @@ -0,0 +1,17 @@ +#pragma once + +namespace c11log{ + namespace details{ + struct null_mutex + { + void lock() + {} + void unlock() + {} + bool try_lock() + { + return true; + } + }; + } +} \ No newline at end of file diff --git a/include/c11log/details/os.h b/include/c11log/details/os.h new file mode 100644 index 00000000..e8088ec1 --- /dev/null +++ b/include/c11log/details/os.h @@ -0,0 +1,17 @@ +#pragma once +#include +#include +#include + +namespace c11log +{ + namespace details + { + namespace os + { + std::tm localtime(const std::time_t &time_t); + std::tm localtime(); + + } + } +} \ No newline at end of file diff --git a/include/c11log/formatters/formatters.h b/include/c11log/formatters/formatters.h new file mode 100644 index 00000000..49c1d662 --- /dev/null +++ b/include/c11log/formatters/formatters.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include +#include + +#include "../level.h" +#include "../details/os.h" + +namespace c11log { +namespace formatters { +typedef std::chrono::system_clock::time_point timepoint; +typedef std::function format_fn; +void format_time(const timepoint& tp, std::ostream &dest); +void format_time(std::ostream &dest); +std::string to_hex(const unsigned char* buf, std::size_t size); + +class formatter { +public: + formatter() {} + virtual ~formatter() {} + virtual void format_header(const std::string& logger_name, level::level_enum level, const timepoint& 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 timepoint& tp, std::ostream& dest) override + { + format_time(tp, dest); + dest << " [" << logger_name << ":" << c11log::level::to_str(level) << "] "; + } + +}; +} //namespace formatter +} //namespace c11log diff --git a/include/c11log/level.h b/include/c11log/level.h new file mode 100644 index 00000000..a97bb3fa --- /dev/null +++ b/include/c11log/level.h @@ -0,0 +1,24 @@ +#pragma once + +namespace c11log +{ + namespace level + { + typedef enum + { + DEBUG, + INFO, + WARNING, + ERROR, + FATAL, + NONE = 99 + } level_enum; + const char* to_str(level_enum l); + } +} + +static const char* level_names[] { "Debug", "Info", "Warning", "Error", "Fatal" }; +inline const char* c11log::level::to_str(c11log::level::level_enum l) +{ + return level_names[l]; +} \ No newline at end of file diff --git a/include/c11log/log_exception.h b/include/c11log/log_exception.h new file mode 100644 index 00000000..5bf7aaf3 --- /dev/null +++ b/include/c11log/log_exception.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include + +namespace c11log +{ + class log_exception :public std::exception + { + public: + log_exception() : _oss(), _msg() + {} + + virtual ~log_exception() + {} + + explicit log_exception(const std::string& msg) :_oss(msg, std::ostringstream::ate), _msg(msg) + {} + + log_exception(const log_exception &other) :_oss(other._oss.str()), _msg(other._msg) + {} + + log_exception& operator=(const log_exception& other) + { + _oss.str(other._oss.str()); + _msg = other._msg; + return *this; + } + + virtual const char* what() const throw () override + { + return _msg.c_str(); + } + + template + log_exception& operator<<(const T& what) + { + _oss << what; + _msg = _oss.str(); + return *this; + } + + private: + std::ostringstream _oss; + std::string _msg; + }; +} \ No newline at end of file diff --git a/include/c11log/logger.h b/include/c11log/logger.h new file mode 100644 index 00000000..3105d82a --- /dev/null +++ b/include/c11log/logger.h @@ -0,0 +1,161 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "level.h" +#include "sinks/base_sink.h" +#include "details/line_logger.h" +#include "details/factory.h" + + +namespace c11log { + +class logger { +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()) + { + _atomic_level.store(level::INFO); + } + + ~logger() + { + }; + + logger(const logger&) = delete; + logger& operator=(const logger&) = delete; + + void set_name(const std::string& name); + const std::string& get_name(); + void add_sink(sink_ptr_t sink_ptr); + void remove_sink(sink_ptr_t sink_ptr); + void set_formatter(std::unique_ptr formatter); + void set_level(c11log::level::level_enum level); + c11log::level::level_enum get_level() const; + bool should_log(c11log::level::level_enum level) const; + + details::line_logger log(level::level_enum level); + 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 = ""; + std::unique_ptr _formatter; + sinks_vector_t _sinks; + std::mutex _mutex; + std::atomic_int _atomic_level; + + void _log_it(const std::string& msg); + +}; + +logger& get_logger(const std::string& name); +} + +/* +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); + return details::line_logger(this, msg_level); + } else { + return details::line_logger(nullptr); + } + +} + +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 void c11log::logger::set_name(const std::string& 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; +} + +inline void c11log::logger::add_sink(sink_ptr_t 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()); +} + +inline void c11log::logger::set_formatter(std::unique_ptr 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); +} + +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 std::string& msg) +{ + level::level_enum level = static_cast(_atomic_level.load()); + std::lock_guard lock(_mutex); + for (auto &sink : _sinks) + sink->log(msg, level); +} + +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 new file mode 100644 index 00000000..8f95c6c5 --- /dev/null +++ b/include/c11log/sinks/async_sink.h @@ -0,0 +1,55 @@ +#pragma once +#include +#include +#include +#include + +#include "../logger.h" +#include "base_sink.h" +namespace c11log { +namespace sinks { +class async_sink : base_sink { + enum class fullq_policy { + BLOCK=0, + DROP_MSG + }; +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; + + + +}; +} +} + +void c11log::sinks::async_sink::_sink_it(const std::string& msg) +{ + +} \ No newline at end of file diff --git a/include/c11log/sinks/base_sink.h b/include/c11log/sinks/base_sink.h new file mode 100644 index 00000000..87349972 --- /dev/null +++ b/include/c11log/sinks/base_sink.h @@ -0,0 +1,50 @@ +#pragma once +#include +#include +#include + +#include "../formatters/formatters.h" +#include "../level.h" + +namespace c11log { +namespace sinks { +class base_sink { +public: + base_sink() + {} + base_sink(level::level_enum l):_level(l) + {}; + virtual ~base_sink() + {}; + + 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); + if (level >= _level) + _sink_it(msg); + } + }; + + void set_level(level::level_enum level) + { + std::lock_guard lock(_mutex); + _level = level; + } + +protected: + virtual void _sink_it(const std::string& msg) = 0; + level::level_enum _level = level::INFO; + std::mutex _mutex; +}; + +class null_sink:public base_sink { +protected: + void _sink_it(const std::string& msg) override + {} +}; +} +} \ No newline at end of file diff --git a/include/c11log/sinks/file_sinks.h b/include/c11log/sinks/file_sinks.h new file mode 100644 index 00000000..bd729c42 --- /dev/null +++ b/include/c11log/sinks/file_sinks.h @@ -0,0 +1,174 @@ +#pragma once +#include +#include + +#include "../logger.h" +#include "../log_exception.h" +#include "../details/os.h" + +#include "base_sink.h" + +namespace c11log { +namespace sinks { +/* +* Trivial file sink with single file as target +*/ +class simple_file_sink:base_sink { +public: + simple_file_sink(const std::string &filename, const std::string& extension = "txt") + { + std::ostringstream oss; + oss << filename << "." << extension; + _ofstream.open(oss.str(), std::ofstream::app); + } +protected: + void _sink_it(const std::string& msg) override + { + _ofstream << msg; + _ofstream.flush(); + } + + std::ofstream _ofstream; +}; + + +/* + * Rotating file sinks. Close and open new file at some point + */ +namespace details { +class rotating_file_sink_base:public base_sink { +public: + rotating_file_sink_base() + {} + virtual ~rotating_file_sink_base() + {} +protected: + virtual void _sink_it(const std::string& msg) override + { + if (_should_rotate()) + _rotate(); + _ofstream << msg; + _ofstream.flush(); + } + virtual bool _should_rotate() const = 0; + virtual void _rotate() = 0; + std::ofstream _ofstream; +}; +} +class rotating_file_sink:public details::rotating_file_sink_base { +public: + rotating_file_sink(const std::string &base_filename, const std::string &extension, size_t max_size, size_t max_files): + _base_filename(base_filename), + _extension(extension), + _max_size(max_size), + _max_files(max_files), + _current_size(0), + _index(0) + { + _ofstream.open(_calc_filename(_base_filename, 0, _extension)); + } + +protected: + virtual void _sink_it(const std::string& msg) override + { + _current_size += msg.length(); + rotating_file_sink_base::_sink_it(msg); + } + + bool _should_rotate() const override + { + return _current_size >= _max_size; + } + + // 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() override + { + _ofstream.close(); + _current_size = 0; + //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)); + } +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(); + } + std::string _base_filename; + std::string _extension; + std::size_t _max_size; + std::size_t _max_files; + std::size_t _current_size; + std::size_t _index; + +}; + +/* + * File sink that closes the log file at midnight and opens new one + */ +class midnight_file_sink:public details::rotating_file_sink_base { +public: + midnight_file_sink(const std::string& base_filename, const std::string& extension): + _base_filename(base_filename), + _extension(extension), + _midnight_tp { _calc_midnight_tp() } + + { + _ofstream.open(_calc_filename(_base_filename, _extension)); + } + +protected: + bool _should_rotate() const override + { + return std::chrono::system_clock::now() >= _midnight_tp; + } + void _rotate() override + { + _midnight_tp = _calc_midnight_tp(); + _ofstream.close(); + _ofstream.open(_calc_filename(_base_filename, _extension)); + + } + +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)); + } + + static std::string _calc_filename(const std::string& basename, const std::string& extension) + { + std::ostringstream oss; + std::tm now_tm = c11log::details::os::localtime(); + oss << basename << std::put_time(&now_tm, ".%Y-%m-%d.") << extension; + return oss.str(); + } + std::string _base_filename; + std::string _extension; + std::chrono::system_clock::time_point _midnight_tp; +}; +} +} \ No newline at end of file diff --git a/include/c11log/sinks/stdout_sinks.h b/include/c11log/sinks/stdout_sinks.h new file mode 100644 index 00000000..8f6b3e37 --- /dev/null +++ b/include/c11log/sinks/stdout_sinks.h @@ -0,0 +1,41 @@ + +#include +#include "base_sink.h" + +namespace c11log +{ + namespace sinks + { + 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) + {} + }; + } +} \ No newline at end of file diff --git a/src/factory.cpp b/src/factory.cpp new file mode 100644 index 00000000..2a96ac96 --- /dev/null +++ b/src/factory.cpp @@ -0,0 +1,23 @@ +#include "stdafx.h" +#include "c11log/details/factory.h" +#include "c11log/logger.h" + +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()) { + 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; + } +} + +c11log::details::factory & c11log::details::factory::instance() +{ + static c11log::details::factory instance; + return instance; +} diff --git a/src/formatters.cpp b/src/formatters.cpp new file mode 100644 index 00000000..ff384ac9 --- /dev/null +++ b/src/formatters.cpp @@ -0,0 +1,36 @@ +#include "stdafx.h" +#include "c11log/formatters/formatters.h" +#include "c11log/level.h" + +void c11log::formatters::format_time(const c11log::formatters::timepoint& tp, std::ostream &dest) +{ + std::tm tm = details::os::localtime(std::chrono::system_clock::to_time_t(tp)); + //get ms + auto duration = tp.time_since_epoch(); + int millis = static_cast(std::chrono::duration_cast(duration).count() % 1000); + //std::put_time(&tm, "[ %Y-%m-%d %H:%M:%S ]") - seems too slow + char buf[64]; + sprintf(buf, "[%d-%02d-%02d %02d:%02d:%02d.%03d]", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, millis); + dest << buf; +} + +void c11log::formatters::format_time(std::ostream& dest) +{ + return format_time(std::chrono::system_clock::now(), dest); +} + + +static const char _hex_chars[17] = "0123456789ABCDEF"; + +std::string c11log::formatters::to_hex(const unsigned char* buf, std::size_t size) +{ + std::ostringstream oss; + + for (std::size_t i = 0; i < size; i++) { + oss << _hex_chars[buf[i] >> 4]; + oss << _hex_chars[buf[i] & 0x0F]; + } + return oss.str(); +} \ No newline at end of file diff --git a/src/line_logger.cpp b/src/line_logger.cpp new file mode 100644 index 00000000..f1b984ec --- /dev/null +++ b/src/line_logger.cpp @@ -0,0 +1,21 @@ +#include "stdafx.h" +#include "c11log/logger.h" + +c11log::details::line_logger::line_logger(logger* callback_logger, level::level_enum msg_level) : + _callback_logger(callback_logger) +{ + if (callback_logger) { + callback_logger->_formatter->format_header(callback_logger->_logger_name, + msg_level, + c11log::formatters::timepoint::clock::now(), + _oss); + } +} + +c11log::details::line_logger::~line_logger() +{ + if (_callback_logger) { + _oss << '\n'; + _callback_logger->_log_it(_oss.str_ref()); + } +} \ No newline at end of file diff --git a/src/logger.cpp b/src/logger.cpp new file mode 100644 index 00000000..4bf022c6 --- /dev/null +++ b/src/logger.cpp @@ -0,0 +1,51 @@ +#include "stdafx.h" +#include + +#include "c11log/logger.h" + +void c11log::logger::set_name(const std::string& name) +{ + std::lock_guard lock(_mutex); + _logger_name = name; +} + +const std::string& c11log::logger::get_name() +{ + std::lock_guard lock(_mutex); + return _logger_name; +} + +void c11log::logger::add_sink(sink_ptr_t sink_ptr) +{ + std::lock_guard lock(_mutex); + _sinks.push_back(sink_ptr); +} + +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()); +} + +void c11log::logger::set_formatter(std::unique_ptr formatter) +{ + std::lock_guard lock(_mutex); + _formatter = std::move(formatter); +} + +void c11log::logger::set_level(c11log::level::level_enum level) +{ + std::lock_guard lock(_mutex); + _level = level; +} + +bool c11log::logger::should_log(c11log::level::level_enum level) +{ + std::lock_guard lock(_mutex); + return level >= _level; +} + +c11log::logger& c11log::get_logger(const std::string& name) +{ + return *(c11log::details::factory::instance().get_logger(name)); +} \ No newline at end of file diff --git a/src/os.cpp b/src/os.cpp new file mode 100644 index 00000000..63956a9b --- /dev/null +++ b/src/os.cpp @@ -0,0 +1,24 @@ +#include "stdafx.h" + +#include "c11log/details/os.h" + +namespace c11log { +namespace details { +namespace os { +std::tm localtime(const std::time_t &time_t) +{ +#ifdef _MSC_VER + std::tm tm; + localtime_s(&tm, &time_t); + return tm; +#endif +} + +std::tm localtime() +{ + std::time_t now_t = time(0); + return localtime(now_t); +} +} +} +} \ No newline at end of file diff --git a/stdafx.cpp b/stdafx.cpp new file mode 100644 index 00000000..7cce5dbb --- /dev/null +++ b/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// c11log.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/stdafx.h b/stdafx.h new file mode 100644 index 00000000..ae597376 --- /dev/null +++ b/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 "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/targetver.h b/targetver.h new file mode 100644 index 00000000..90e767bf --- /dev/null +++ b/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include