Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/gabime/spdlog.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgabi <galim120@bezeqint.net>2014-10-31 02:13:27 +0300
committergabi <galim120@bezeqint.net>2014-10-31 02:13:27 +0300
commitc7b8c762fbeb7db011aea264254066c01eab5ab3 (patch)
treec7ba62b8403995f10ee9e9a0eb4b0df64dc6d728 /include/spdlog
parentcbddc8796a7ff5ceb903d27fe61ccd95d1166fcc (diff)
spdlog
Diffstat (limited to 'include/spdlog')
-rw-r--r--include/spdlog/common.h56
-rw-r--r--include/spdlog/details/blocking_queue.h126
-rw-r--r--include/spdlog/details/fast_istostr.h103
-rw-r--r--include/spdlog/details/fast_oss.h165
-rw-r--r--include/spdlog/details/file_helper.h111
-rw-r--r--include/spdlog/details/line_logger.h79
-rw-r--r--include/spdlog/details/log_msg.h69
-rw-r--r--include/spdlog/details/logger_impl.h136
-rw-r--r--include/spdlog/details/null_mutex.h17
-rw-r--r--include/spdlog/details/os.h129
-rw-r--r--include/spdlog/details/pattern_formatter_impl.h534
-rw-r--r--include/spdlog/details/registry.h99
-rw-r--r--include/spdlog/details/spdlog_impl.h51
-rw-r--r--include/spdlog/details/stack_buf.h111
-rw-r--r--include/spdlog/formatter.h33
-rw-r--r--include/spdlog/logger.h91
-rw-r--r--include/spdlog/sinks/async_sink.h137
-rw-r--r--include/spdlog/sinks/base_sink.h43
-rw-r--r--include/spdlog/sinks/file_sinks.h189
-rw-r--r--include/spdlog/sinks/null_sink.h23
-rw-r--r--include/spdlog/sinks/ostream_sink.h36
-rw-r--r--include/spdlog/sinks/sink.h17
-rw-r--r--include/spdlog/sinks/stdout_sinks.h34
-rw-r--r--include/spdlog/spdlog.h84
24 files changed, 2473 insertions, 0 deletions
diff --git a/include/spdlog/common.h b/include/spdlog/common.h
new file mode 100644
index 00000000..dd0dbe64
--- /dev/null
+++ b/include/spdlog/common.h
@@ -0,0 +1,56 @@
+#pragma once
+
+#include<initializer_list>
+#include<chrono>
+
+namespace spdlog
+{
+class formatter;
+namespace sinks {
+class sink;
+}
+
+// Common types across the lib
+using log_clock = std::chrono::system_clock;
+using sink_ptr = std::shared_ptr < sinks::sink > ;
+using sinks_init_list = std::initializer_list < sink_ptr > ;
+using formatter_ptr = std::shared_ptr<spdlog::formatter>;
+
+//Log level enum
+namespace level
+{
+typedef enum
+{
+ TRACE,
+ DEBUG,
+ INFO,
+ WARN,
+ ERR,
+ CRITICAL,
+ ALWAYS,
+ OFF
+} level_enum;
+
+static const char* level_names[] { "trace", "debug", "info", "warning", "error", "critical", "", ""};
+inline const char* to_str(spdlog::level::level_enum l)
+{
+ return level_names[l];
+}
+} //level
+
+//
+// Log exception
+//
+class fflog_exception : public std::exception
+{
+public:
+ fflog_exception(const std::string& msg) :_msg(msg) {};
+ const char* what() const throw() override {
+ return _msg.c_str();
+ }
+private:
+ std::string _msg;
+
+};
+
+} //spdlog
diff --git a/include/spdlog/details/blocking_queue.h b/include/spdlog/details/blocking_queue.h
new file mode 100644
index 00000000..574a5530
--- /dev/null
+++ b/include/spdlog/details/blocking_queue.h
@@ -0,0 +1,126 @@
+#pragma once
+
+// blocking_queue:
+// A blocking multi-consumer/multi-producer thread safe queue.
+// Has max capacity and supports timeout on push or pop operations.
+
+#include <chrono>
+#include <memory>
+#include <queue>
+#include <mutex>
+#include <condition_variable>
+
+namespace spdlog
+{
+namespace details
+{
+
+template<typename T>
+class blocking_queue
+{
+public:
+ using queue_type = std::queue<T>;
+ using item_type = T;
+ using size_type = typename queue_type::size_type;
+ using clock = std::chrono::system_clock;
+
+ explicit blocking_queue(size_type max_size) :
+ _max_size(max_size),
+ _q(),
+ _mutex()
+ {
+ }
+ blocking_queue(const blocking_queue&) = delete;
+ blocking_queue& operator=(const blocking_queue&) = delete;
+ ~blocking_queue() = default;
+
+ size_type size()
+ {
+ std::lock_guard<std::mutex> lock(_mutex);
+ return _q.size();
+ }
+
+ // Push copy of item into the back of the queue.
+ // If the queue is full, block the calling thread util there is room or timeout have passed.
+ // Return: false on timeout, true on successful push.
+ template<typename Duration_Rep, typename Duration_Period, typename TT>
+ bool push(TT&& item, const std::chrono::duration<Duration_Rep, Duration_Period>& timeout)
+ {
+ std::unique_lock<std::mutex> ul(_mutex);
+ if (_q.size() >= _max_size)
+ {
+ if (!_item_popped_cond.wait_until(ul, clock::now() + timeout, [this]()
+ {
+ return this->_q.size() < this->_max_size;
+ }))
+ return false;
+ }
+ _q.push(std::forward<TT>(item));
+ if (_q.size() <= 1)
+ {
+ ul.unlock(); //So the notified thread will have better chance to accuire the lock immediatly..
+ _item_pushed_cond.notify_one();
+ }
+ return true;
+ }
+
+ // Push copy of item into the back of the queue.
+ // If the queue is full, block the calling thread until there is room.
+ template<typename TT>
+ void push(TT&& item)
+ {
+ while (!push(std::forward<TT>(item), std::chrono::hours(1)));
+ }
+
+ // Pop a copy of the front item in the queue into the given item ref.
+ // If the queue is empty, block the calling thread util there is item to pop or timeout have passed.
+ // Return: false on timeout , true on successful pop/
+ template<class Duration_Rep, class Duration_Period>
+ bool pop(T& item, const std::chrono::duration<Duration_Rep, Duration_Period>& timeout)
+ {
+ std::unique_lock<std::mutex> ul(_mutex);
+ if (_q.empty())
+ {
+ if (!_item_pushed_cond.wait_until(ul, clock::now() + timeout, [this]()
+ {
+ return !this->_q.empty();
+ }))
+ return false;
+ }
+ item = std::move(_q.front());
+ _q.pop();
+ if (_q.size() >= _max_size - 1)
+ {
+ ul.unlock(); //So the notified thread will have better chance to accuire the lock immediatly..
+ _item_popped_cond.notify_one();
+ }
+ return true;
+ }
+
+ // Pop a copy of the front item in the queue into the given item ref.
+ // If the queue is empty, block the calling thread util there is item to pop.
+ void pop(T& item)
+ {
+ while (!pop(item, std::chrono::hours(1)));
+ }
+
+ // Clear the queue
+ void clear()
+ {
+ {
+ std::unique_lock<std::mutex> ul(_mutex);
+ queue_type().swap(_q);
+ }
+ _item_popped_cond.notify_all();
+ }
+
+private:
+ size_type _max_size;
+ std::queue<T> _q;
+ std::mutex _mutex;
+ std::condition_variable _item_pushed_cond;
+ std::condition_variable _item_popped_cond;
+};
+
+}
+}
diff --git a/include/spdlog/details/fast_istostr.h b/include/spdlog/details/fast_istostr.h
new file mode 100644
index 00000000..3855d57a
--- /dev/null
+++ b/include/spdlog/details/fast_istostr.h
@@ -0,0 +1,103 @@
+#pragma once
+#include <string>
+
+//Fast to int to string
+//Source: http://stackoverflow.com/a/4351484/192001
+//Modified version to pad zeros according to padding arg
+
+namespace spdlog {
+namespace details {
+
+const char digit_pairs[201] = {
+ "00010203040506070809"
+ "10111213141516171819"
+ "20212223242526272829"
+ "30313233343536373839"
+ "40414243444546474849"
+ "50515253545556575859"
+ "60616263646566676869"
+ "70717273747576777879"
+ "80818283848586878889"
+ "90919293949596979899"
+};
+
+
+inline std::string& fast_itostr(int n, std::string& s, int padding)
+{
+ if (n == 0)
+ {
+ s = std::string(padding, '0');
+ return s;
+ }
+
+ int sign = -(n < 0);
+ unsigned int val = (n^sign) - sign;
+
+ int size;
+ if (val >= 10000)
+ {
+ if (val >= 10000000)
+ {
+ if (val >= 1000000000)
+ size = 10;
+ else if (val >= 100000000)
+ size = 9;
+ else
+ size = 8;
+ }
+ else
+ {
+ if (val >= 1000000)
+ size = 7;
+ else if (val >= 100000)
+ size = 6;
+ else
+ size = 5;
+ }
+ }
+ else
+ {
+ if (val >= 100)
+ {
+ if (val >= 1000)
+ size = 4;
+ else
+ size = 3;
+ }
+ else
+ {
+ if (val >= 10)
+ size = 2;
+ else
+ size = 1;
+ }
+ }
+ size -= sign;
+ if (size < padding)
+ size = padding;
+
+ s.resize(size);
+ char* c = &s[0];
+ if (sign)
+ *c = '-';
+
+ c += size - 1;
+ while (val >= 100)
+ {
+ int pos = val % 100;
+ val /= 100;
+ *(short*)(c - 1) = *(short*)(digit_pairs + 2 * pos);
+ c -= 2;
+ }
+ while (val > 0)
+ {
+ *c-- = '0' + (val % 10);
+ val /= 10;
+ }
+
+ while (c >= s.data())
+ *c-- = '0';
+ return s;
+}
+}
+} \ No newline at end of file
diff --git a/include/spdlog/details/fast_oss.h b/include/spdlog/details/fast_oss.h
new file mode 100644
index 00000000..3a9a07ed
--- /dev/null
+++ b/include/spdlog/details/fast_oss.h
@@ -0,0 +1,165 @@
+#pragma once
+
+// A faster-than-ostringstream class
+// uses stack_buf as the underlying buffer (upto 192 bytes before using the heap)
+
+#include <ostream>
+#include <iomanip>
+#include "fast_istostr.h"
+#include "stack_buf.h"
+#include<iostream>
+
+namespace spdlog
+{
+namespace details
+{
+
+class stack_devicebuf :public std::streambuf
+{
+public:
+ static const unsigned short stack_size = 256;
+ using stackbuf_t = stack_buf<stack_size>;
+
+ stack_devicebuf() = default;
+ ~stack_devicebuf() = default;
+
+ stack_devicebuf(const stack_devicebuf& other) :std::basic_streambuf<char>(), _stackbuf(other._stackbuf)
+ {}
+
+ stack_devicebuf(stack_devicebuf&& other):
+ std::basic_streambuf<char>(),
+ _stackbuf(std::move(other._stackbuf))
+ {
+ other.clear();
+ }
+
+ stack_devicebuf& operator=(stack_devicebuf other)
+ {
+ std::swap(_stackbuf, other._stackbuf);
+ return *this;
+ }
+
+ const stackbuf_t& buf() const
+ {
+ return _stackbuf;
+ }
+ std::size_t size() const
+ {
+ return _stackbuf.size();
+ }
+
+ void clear()
+ {
+ _stackbuf.clear();
+ }
+
+protected:
+ // copy the give buffer into the accumulated fast buffer
+ std::streamsize xsputn(const char_type* s, std::streamsize count) override
+ {
+ _stackbuf.append(s, static_cast<unsigned int>(count));
+ return count;
+ }
+
+ int_type overflow(int_type ch) override
+ {
+ if (traits_type::not_eof(ch))
+ {
+ char c = traits_type::to_char_type(ch);
+ xsputn(&c, 1);
+ }
+ return ch;
+ }
+private:
+ stackbuf_t _stackbuf;
+};
+
+
+class fast_oss :public std::ostream
+{
+public:
+ fast_oss() :std::ostream(&_dev) {}
+ ~fast_oss() = default;
+
+ fast_oss(const fast_oss& other) :std::basic_ios<char>(), std::ostream(&_dev), _dev(other._dev)
+ {}
+
+ fast_oss(fast_oss&& other) :std::basic_ios<char>(), std::ostream(&_dev), _dev(std::move(other._dev))
+ {
+ other.clear();
+ }
+
+
+ fast_oss& operator=(fast_oss other)
+ {
+ swap(*this, other);
+ return *this;
+ }
+
+ void swap(fast_oss& first, fast_oss& second) // nothrow
+ {
+ using std::swap;
+ swap(first._dev, second._dev);
+ }
+
+ std::string str()
+ {
+ auto& buffer = _dev.buf();
+ const char*data = buffer.data();
+ return std::string(data, data+buffer.size());
+ }
+
+ const stack_devicebuf::stackbuf_t& buf() const
+ {
+ return _dev.buf();
+ }
+
+
+ std::size_t size() const
+ {
+ return _dev.size();
+ }
+
+ void clear()
+ {
+ _dev.clear();
+ }
+
+ //
+ // The following were added because they significantly boost to perfromance
+ //
+ void putc(char c)
+ {
+ _dev.sputc(c);
+ }
+
+ // put int and pad with zeroes if smalled than min_width
+ void put_int(int n, int padding)
+ {
+ std::string s;
+ details::fast_itostr(n, s, padding);
+ _dev.sputn(s.data(), s.size());
+ }
+
+ void put_data(const char* p, std::size_t data_size)
+ {
+ _dev.sputn(p, data_size);
+ }
+
+ void put_str(const std::string& s)
+ {
+ _dev.sputn(s.data(), s.size());
+ }
+
+ void put_fast_oss(const fast_oss& oss)
+ {
+ auto& buffer = oss.buf();
+ _dev.sputn(buffer.data(), buffer.size());
+ }
+
+
+private:
+ stack_devicebuf _dev;
+};
+}
+}
diff --git a/include/spdlog/details/file_helper.h b/include/spdlog/details/file_helper.h
new file mode 100644
index 00000000..fd5ee4a0
--- /dev/null
+++ b/include/spdlog/details/file_helper.h
@@ -0,0 +1,111 @@
+#pragma once
+
+
+
+// Helper class for file sink
+// When failing to open a file, retry several times(5) with small delay between the tries(10 ms)
+// Flush to file every X writes (or never if X==0)
+// Throw fflog_ exception on errors
+
+
+#include <cstdio>
+#include <string>
+#include <thread>
+#include <chrono>
+#include "../common.h"
+
+
+
+namespace spdlog
+{
+namespace details
+{
+
+class file_helper
+{
+public:
+ static const int open_max_tries = 5;
+ static const int sleep_ms_bewteen_tries = 10;
+
+ explicit file_helper(const std::size_t flush_inverval):
+ _fd(nullptr),
+ _flush_inverval(flush_inverval),
+ _flush_countdown(flush_inverval) {};
+
+ file_helper(const file_helper&) = delete;
+
+ ~file_helper()
+ {
+ close();
+ }
+
+
+ void open(const std::string& filename)
+ {
+
+ close();
+
+ _filename = filename;
+ for (int tries = 0; tries < open_max_tries; ++tries)
+ {
+ if(!os::fopen_s(&_fd, filename, "wb"))
+ return;
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms_bewteen_tries));
+ }
+
+ throw fflog_exception("Failed opening file " + filename + " for writing");
+ }
+
+ void close()
+ {
+ if (_fd)
+ {
+ std::fclose(_fd);
+ _fd = nullptr;
+ }
+ }
+
+ void write(const log_msg& msg)
+ {
+ auto& buf = msg.formatted.buf();
+ size_t size = buf.size();
+ if(std::fwrite(buf.data(), sizeof(char), size, _fd) != size)
+ throw fflog_exception("Failed writing to file " + _filename);
+
+ if(--_flush_countdown == 0)
+ {
+ std::fflush(_fd);
+ _flush_countdown = _flush_inverval;
+ }
+ }
+
+ const std::string& filename() const
+ {
+ return _filename;
+ }
+
+ static bool file_exists(const std::string& name)
+ {
+ FILE* file;
+ if (!os::fopen_s(&file, name.c_str(), "r"))
+ {
+ fclose(file);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+private:
+ FILE* _fd;
+ std::string _filename;
+ const std::size_t _flush_inverval;
+ std::size_t _flush_countdown;
+
+};
+}
+}
+
diff --git a/include/spdlog/details/line_logger.h b/include/spdlog/details/line_logger.h
new file mode 100644
index 00000000..23b3b0cc
--- /dev/null
+++ b/include/spdlog/details/line_logger.h
@@ -0,0 +1,79 @@
+#pragma once
+
+#include "../common.h"
+#include "../logger.h"
+#include "./fast_oss.h"
+
+
+// Line logger class - aggregates operator<< calls to fast ostream
+// and logs upon destruction
+
+namespace spdlog
+{
+namespace details
+{
+class line_logger
+{
+public:
+ line_logger(logger* callback_logger, level::level_enum msg_level, bool enabled):
+ _callback_logger(callback_logger),
+ _log_msg(msg_level),
+ _enabled(enabled)
+ {}
+
+ // No copy intended. Only move
+ line_logger(const line_logger& other) = delete;
+ line_logger& operator=(const line_logger&) = delete;
+ line_logger& operator=(line_logger&&) = delete;
+
+
+ line_logger(line_logger&& other) :
+ _callback_logger(other._callback_logger),
+ _log_msg(std::move(other._log_msg)),
+ _enabled(other._enabled)
+ {
+ other.disable();
+ }
+
+ //Log the log message using the callback logger
+ ~line_logger()
+ {
+ if (_enabled)
+ {
+ _log_msg.logger_name = _callback_logger->name();
+ _log_msg.time = log_clock::now();
+ _log_msg.tm_time = details::os::localtime(log_clock::to_time_t(_log_msg.time));
+ _callback_logger->_log_msg(_log_msg);
+ }
+ }
+
+ template<typename T>
+ void write(const T& what)
+ {
+ if (_enabled)
+ {
+ _log_msg.raw << what;
+ }
+ }
+
+ template<typename T>
+ line_logger& operator<<(const T& what)
+ {
+ write(what);
+ return *this;
+ }
+
+ void disable()
+ {
+ _enabled = false;
+ }
+
+
+
+private:
+ logger* _callback_logger;
+ log_msg _log_msg;
+ bool _enabled;
+};
+} //Namespace details
+} // Namespace spdlog
diff --git a/include/spdlog/details/log_msg.h b/include/spdlog/details/log_msg.h
new file mode 100644
index 00000000..2ee38dc5
--- /dev/null
+++ b/include/spdlog/details/log_msg.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include "../common.h"
+#include "./fast_oss.h"
+
+namespace spdlog
+{
+namespace details
+{
+struct log_msg
+{
+ log_msg() = default;
+ log_msg(level::level_enum l):
+ logger_name(),
+ level(l),
+ time(),
+ tm_time(),
+ raw(),
+ formatted() {}
+
+ log_msg(const log_msg& other):
+ logger_name(other.logger_name),
+ level(other.level),
+ time(other.time),
+ tm_time(other.tm_time),
+ raw(other.raw),
+ formatted(other.formatted) {}
+
+ log_msg(log_msg&& other)
+ {
+ swap(*this, other);
+ }
+
+ void swap(log_msg& l, log_msg& r)
+ {
+ using std::swap;
+ swap(l.logger_name, r.logger_name);
+ swap(l.level, r.level);
+ swap(l.time, r.time);
+ swap(l.tm_time, r.tm_time);
+ swap(l.raw, r.raw);
+ swap(l.formatted, r.formatted);
+ }
+
+
+ log_msg& operator=(log_msg other)
+ {
+ swap(*this, other);
+ return *this;
+ }
+
+
+ void clear()
+ {
+ raw.clear();
+ formatted.clear();
+ }
+
+ std::string logger_name;
+ level::level_enum level;
+ log_clock::time_point time;
+ std::tm tm_time;
+ fast_oss raw;
+ fast_oss formatted;
+
+
+};
+}
+}
diff --git a/include/spdlog/details/logger_impl.h b/include/spdlog/details/logger_impl.h
new file mode 100644
index 00000000..32900093
--- /dev/null
+++ b/include/spdlog/details/logger_impl.h
@@ -0,0 +1,136 @@
+#pragma once
+//
+// Logger implementation
+//
+
+
+#include "./line_logger.h"
+
+
+inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list) :
+ _name(logger_name),
+ _sinks(sinks_list)
+{
+ // no support under vs2013 for member initialization for std::atomic
+ _level = level::INFO;
+}
+
+template<class It>
+inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end) :
+ _name(logger_name),
+ _sinks(begin, end)
+{}
+
+
+inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter)
+{
+ _formatter = msg_formatter;
+}
+
+inline void spdlog::logger::set_pattern(const std::string& pattern)
+{
+ _formatter = std::make_shared<pattern_formatter>(pattern);
+}
+
+inline spdlog::formatter_ptr spdlog::logger::get_formatter() const
+{
+ return _formatter;
+}
+
+
+template <typename... Args>
+inline spdlog::details::line_logger spdlog::logger::log(level::level_enum lvl, const Args&... args) {
+ bool msg_enabled = should_log(lvl);
+ details::line_logger l(this, lvl, msg_enabled);
+ if (msg_enabled)
+ _variadic_log(l, args...);
+ return l;
+}
+
+template <typename... Args>
+inline spdlog::details::line_logger spdlog::logger::log(const Args&... args) {
+ return log(level::ALWAYS, args...);
+}
+
+template <typename... Args>
+inline spdlog::details::line_logger spdlog::logger::trace(const Args&... args)
+{
+ return log(level::TRACE, args...);
+}
+
+template <typename... Args>
+inline spdlog::details::line_logger spdlog::logger::debug(const Args&... args)
+{
+ return log(level::DEBUG, args...);
+}
+
+template <typename... Args>
+inline spdlog::details::line_logger spdlog::logger::info(const Args&... args)
+{
+ return log(level::INFO, args...);
+}
+
+template <typename... Args>
+inline spdlog::details::line_logger spdlog::logger::warn(const Args&... args)
+{
+ return log(level::WARN, args...);
+}
+
+template <typename... Args>
+inline spdlog::details::line_logger spdlog::logger::error(const Args&... args)
+{
+ return log(level::ERR, args...);
+}
+
+template <typename... Args>
+inline spdlog::details::line_logger spdlog::logger::critical(const Args&... args)
+{
+ return log(level::CRITICAL, args...);
+}
+
+inline const std::string& spdlog::logger::name() const
+{
+ return _name;
+}
+
+inline void spdlog::logger::set_level(spdlog::level::level_enum log_level)
+{
+ _level.store(log_level);
+}
+
+inline spdlog::level::level_enum spdlog::logger::level() const
+{
+ return static_cast<spdlog::level::level_enum>(_level.load());
+}
+
+inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const
+{
+ return msg_level >= _level.load();
+}
+
+inline void spdlog::logger::stop_logging()
+{
+ set_level(level::OFF);
+}
+
+
+inline void spdlog::logger::_variadic_log(spdlog::details::line_logger&) {}
+
+template <typename First, typename... Rest>
+void spdlog::logger::_variadic_log(spdlog::details::line_logger& l, const First& first, const Rest&... rest)
+{
+ l.write(first);
+ l.write(' ');
+ _variadic_log(l, rest...);
+}
+
+inline void spdlog::logger::_log_msg(details::log_msg& msg)
+{
+ //Use default formatter if not set
+ if (!_formatter)
+ _formatter = std::make_shared<pattern_formatter>("%+");
+ _formatter->format(msg);
+ for (auto &sink : _sinks)
+ sink->log(msg);
+}
+
diff --git a/include/spdlog/details/null_mutex.h b/include/spdlog/details/null_mutex.h
new file mode 100644
index 00000000..54c354c5
--- /dev/null
+++ b/include/spdlog/details/null_mutex.h
@@ -0,0 +1,17 @@
+#pragma once
+
+// null, no cost mutex
+
+namespace spdlog {
+namespace details {
+struct null_mutex
+{
+ void lock() {}
+ void unlock() {}
+ bool try_lock()
+ {
+ return true;
+ }
+};
+}
+}
diff --git a/include/spdlog/details/os.h b/include/spdlog/details/os.h
new file mode 100644
index 00000000..ebea0547
--- /dev/null
+++ b/include/spdlog/details/os.h
@@ -0,0 +1,129 @@
+#pragma once
+
+#include<string>
+#include<cstdio>
+#include<ctime>
+#ifdef _WIN32
+#include <Windows.h>
+#endif
+
+namespace spdlog
+{
+namespace details
+{
+namespace os
+{
+
+inline std::tm localtime(const std::time_t &time_tt)
+{
+
+#ifdef _WIN32
+ std::tm tm;
+ localtime_s(&tm, &time_tt);
+#else
+ std::tm tm;
+ localtime_r(&time_tt, &tm);
+#endif
+ return tm;
+}
+
+inline std::tm localtime()
+{
+ std::time_t now_t = time(0);
+ return localtime(now_t);
+}
+
+
+inline std::tm gmtime(const std::time_t &time_tt)
+{
+
+#ifdef _WIN32
+ std::tm tm;
+ gmtime_s(&tm, &time_tt);
+#else
+ std::tm tm;
+ gmtime_r(&time_tt, &tm);
+#endif
+ return tm;
+}
+
+inline std::tm gmtime()
+{
+ std::time_t now_t = time(0);
+ return gmtime(now_t);
+}
+inline bool operator==(const std::tm& tm1, const std::tm& tm2)
+{
+ return (tm1.tm_sec == tm2.tm_sec &&
+ tm1.tm_min == tm2.tm_min &&
+ tm1.tm_hour == tm2.tm_hour &&
+ tm1.tm_mday == tm2.tm_mday &&
+ tm1.tm_mon == tm2.tm_mon &&
+ tm1.tm_year == tm2.tm_year &&
+ tm1.tm_isdst == tm2.tm_isdst);
+}
+
+inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
+{
+ return !(tm1 == tm2);
+}
+
+#ifdef _WIN32
+inline const char* eol()
+{
+ return "\r\n";
+}
+#else
+constexpr inline const char* eol()
+{
+ return "\n";
+}
+#endif
+
+#ifdef _WIN32
+inline unsigned short eol_size()
+{
+ return 2;
+}
+#else
+constexpr inline unsigned short eol_size()
+{
+ return 1;
+}
+#endif
+
+//fopen_s on non windows for writing
+inline int fopen_s(FILE** fp, const std::string& filename, const char* mode)
+{
+#ifdef _WIN32
+ return ::fopen_s(fp, filename.c_str(), mode);
+#else
+ *fp = fopen((filename.c_str()), mode);
+ return *fp == nullptr;
+#endif
+
+
+}
+
+//Return utc offset in minutes or -1 on failure
+inline int utc_minutes_offset(const std::tm& tm = localtime())
+{
+
+#ifdef _WIN32
+ DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
+ auto rv = GetDynamicTimeZoneInformation(&tzinfo);
+ if (!rv)
+ return -1;
+ return -1 * (tzinfo.Bias + tzinfo.DaylightBias);
+#else
+ return tm.tm_gmtoff / 60;
+#endif
+}
+
+
+} //os
+} //details
+} //spdlog
+
+
+
diff --git a/include/spdlog/details/pattern_formatter_impl.h b/include/spdlog/details/pattern_formatter_impl.h
new file mode 100644
index 00000000..949ecbfe
--- /dev/null
+++ b/include/spdlog/details/pattern_formatter_impl.h
@@ -0,0 +1,534 @@
+#pragma once
+
+#include <string>
+#include <chrono>
+#include <memory>
+#include <vector>
+
+#include "../formatter.h"
+#include "./log_msg.h"
+#include "./fast_oss.h"
+#include "./os.h"
+
+namespace spdlog
+{
+namespace details {
+class flag_formatter
+{
+public:
+ virtual void format(details::log_msg& msg) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////
+// name & level pattern appenders
+///////////////////////////////////////////////////////////////////////
+namespace {
+class name_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted << msg.logger_name;
+ }
+};
+}
+
+// log level appender
+class level_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted << level::to_str(msg.level);
+ }
+};
+
+///////////////////////////////////////////////////////////////////////
+// Date time pattern appenders
+///////////////////////////////////////////////////////////////////////
+
+static const char* ampm(const tm& t)
+{
+ return t.tm_hour >= 12 ? "PM" : "AM";
+}
+
+static int to12h(const tm& t)
+{
+ return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour;
+}
+
+//Abbreviated weekday name
+static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+class a_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_str(days[msg.tm_time.tm_wday]);
+ }
+};
+
+//Full weekday name
+static const std::string full_days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
+class A_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_str(full_days[msg.tm_time.tm_wday]);
+ }
+};
+
+//Abbreviated month
+static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" };
+class b_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_str(months[msg.tm_time.tm_mon]);
+ }
+};
+
+//Full month name
+static const std::string full_months[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
+class B_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_str(full_months[msg.tm_time.tm_mon]);
+ }
+};
+
+//Date and time representation (Thu Aug 23 15:35:46 2014)
+class c_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_str(days[msg.tm_time.tm_wday]);
+ msg.formatted.putc(' ');
+ msg.formatted.put_str(months[msg.tm_time.tm_mon]);
+ msg.formatted.putc(' ');
+ msg.formatted.put_int(msg.tm_time.tm_mday, 2);
+ msg.formatted.putc(' ');
+ msg.formatted.put_int(msg.tm_time.tm_hour, 2);
+ msg.formatted.putc(':');
+ msg.formatted.put_int(msg.tm_time.tm_min, 2);
+ msg.formatted.putc(':');
+ msg.formatted.put_int(msg.tm_time.tm_sec, 2);
+ }
+};
+
+
+// year - 2 digit
+class C_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_int(msg.tm_time.tm_year % 100, 2);
+ }
+};
+
+
+
+// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
+class D_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_int(msg.tm_time.tm_mon + 1, 2);
+ msg.formatted.putc('/');
+ msg.formatted.put_int(msg.tm_time.tm_mday, 2);
+ msg.formatted.putc('/');
+ msg.formatted.put_int(msg.tm_time.tm_year % 100, 2);
+ }
+};
+
+
+// year - 4 digit
+class Y_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_int(msg.tm_time.tm_year + 1900, 4);
+ }
+};
+
+// month 1-12
+class m_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_int(msg.tm_time.tm_mon + 1, 2);
+ }
+};
+
+// day of month 1-31
+class d_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_int(msg.tm_time.tm_mday, 2);
+ }
+};
+
+// hours in 24 format 0-23
+class H_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_int(msg.tm_time.tm_hour, 2);
+ }
+};
+
+// hours in 12 format 1-12
+class I_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_int(to12h(msg.tm_time), 2);
+ }
+};
+
+// ninutes 0-59
+class M_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_int(msg.tm_time.tm_min, 2);
+ }
+};
+
+// seconds 0-59
+class S_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_int(msg.tm_time.tm_sec, 2);
+ }
+};
+
+// milliseconds
+class e_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ auto duration = msg.time.time_since_epoch();
+ auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
+ msg.formatted.put_int(static_cast<int>(millis), 3);
+ }
+};
+
+// AM/PM
+class p_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_data(ampm(msg.tm_time), 2);
+ }
+};
+
+
+// 12 hour clock 02:55:02 pm
+class r_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_int(to12h(msg.tm_time), 2);
+ msg.formatted.putc(':');
+ msg.formatted.put_int(msg.tm_time.tm_min, 2);
+ msg.formatted.putc(':');
+ msg.formatted.put_int(msg.tm_time.tm_sec, 2);
+ msg.formatted.putc(' ');
+ msg.formatted.put_data(ampm(msg.tm_time), 2);
+ }
+};
+
+// 24-hour HH:MM time, equivalent to %H:%M
+class R_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_int(msg.tm_time.tm_hour, 2);
+ msg.formatted.putc(':');
+ msg.formatted.put_int(msg.tm_time.tm_min, 2);
+
+ }
+};
+
+// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
+class T_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_int(msg.tm_time.tm_hour, 2);
+ msg.formatted.putc(':');
+ msg.formatted.put_int(msg.tm_time.tm_min, 2);
+ msg.formatted.putc(':');
+ msg.formatted.put_int(msg.tm_time.tm_sec, 2);
+ }
+};
+
+// ISO 8601 offset from UTC in timezone (HH:MM)
+class z_formatter :public flag_formatter
+{
+public:
+
+ void format(log_msg& msg) override
+ {
+ std::lock_guard<std::mutex> l(_mutex);
+ using namespace std::chrono;
+ auto diff = msg.time - _last_update;
+ auto secs_diff = abs((duration_cast<seconds>(diff)).count());
+ if (secs_diff >= 2)
+ {
+ _value = get_value(msg);
+ _last_update = msg.time;
+ }
+ msg.formatted.put_str(_value);
+ }
+private:
+ log_clock::time_point _last_update;
+ std::string _value;
+ std::string get_value(const log_msg& msg)
+ {
+ int total_minutes = os::utc_minutes_offset(msg.tm_time);
+ int h = total_minutes / 60;
+ int m = total_minutes % 60;
+ fast_oss oss;
+ oss.putc(h < 0 ? '-' : '+');
+ oss.put_int(h, 2);
+ oss.putc(':');
+ oss.put_int(m, 2);
+ return oss.str();
+ }
+ std::mutex _mutex;
+};
+
+
+
+class t_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_fast_oss(msg.raw);
+ }
+};
+
+class ch_formatter :public flag_formatter
+{
+public:
+ explicit ch_formatter(char ch) : _ch(ch)
+ {}
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.putc(_ch);
+ }
+private:
+ char _ch;
+};
+
+
+//aggregate user chars to display as is
+class aggregate_formatter :public flag_formatter
+{
+public:
+ aggregate_formatter()
+ {}
+ void add_ch(char ch)
+ {
+ _str += ch;
+ }
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.put_str(_str);
+ }
+private:
+ std::string _str;
+};
+
+// Full info formatter
+// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %t
+class full_formatter :public flag_formatter
+{
+ void format(details::log_msg& msg) override
+ {
+ msg.formatted.putc('[');
+ msg.formatted.put_int(msg.tm_time.tm_year+1900, 4);
+ msg.formatted.putc('-');
+ msg.formatted.put_int(msg.tm_time.tm_mon+ 1, 2);
+ msg.formatted.putc('-');
+ msg.formatted.put_int(msg.tm_time.tm_mday, 2);
+ msg.formatted.putc(' ');
+ msg.formatted.put_int(msg.tm_time.tm_hour, 2);
+ msg.formatted.putc(':');
+ msg.formatted.put_int(msg.tm_time.tm_min, 2);
+ msg.formatted.putc(':');
+ msg.formatted.put_int(msg.tm_time.tm_sec, 2);
+ //millis
+ msg.formatted.putc('.');
+ auto duration = msg.time.time_since_epoch();
+ auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
+ msg.formatted.put_int(static_cast<int>(millis), 3);
+ msg.formatted.putc(']');
+ msg.formatted << " [" << msg.logger_name << "] [" << level::to_str(msg.level) << "] ";
+ msg.formatted.put_fast_oss(msg.raw);
+
+ }
+};
+
+}
+}
+///////////////////////////////////////////////////////////////////////////////
+// pattern_formatter inline impl
+///////////////////////////////////////////////////////////////////////////////
+inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern)
+{
+ compile_pattern(pattern);
+}
+
+inline void spdlog::pattern_formatter::compile_pattern(const std::string& pattern)
+{
+ auto end = pattern.end();
+ std::unique_ptr<details::aggregate_formatter> user_chars;
+ for (auto it = pattern.begin(); it != end; ++it)
+ {
+ if (*it == '%')
+ {
+ if (user_chars) //append user chars found so far
+ _formatters.push_back(std::move(user_chars));
+
+ if (++it != end)
+ handle_flag(*it);
+ else
+ break;
+ }
+ else // chars not following the % sign should be displayed as is
+ {
+ if (!user_chars)
+ user_chars = std::unique_ptr<details::aggregate_formatter>(new details::aggregate_formatter());
+ user_chars->add_ch(*it);
+ }
+ }
+ if (user_chars) //append raw chars found so far
+ {
+ _formatters.push_back(std::move(user_chars));
+ }
+
+}
+inline void spdlog::pattern_formatter::handle_flag(char flag)
+{
+ switch (flag)
+ {
+ // logger name
+ case 'n':
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::name_formatter()));
+ break;
+
+ case 'l':
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::level_formatter()));
+ break;
+
+ case('t') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::t_formatter()));
+ break;
+
+ case('a') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::a_formatter()));
+ break;
+
+ case('A') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::A_formatter()));
+ break;
+
+ case('b') :
+ case('h') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::b_formatter()));
+ break;
+
+ case('B') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::B_formatter()));
+ break;
+ case('c') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::c_formatter()));
+ break;
+
+ case('C') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::C_formatter()));
+ break;
+
+ case('Y') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::Y_formatter()));
+ break;
+
+ case('D') :
+ case('x') :
+
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::D_formatter()));
+ break;
+
+ case('m') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::m_formatter()));
+ break;
+
+ case('d') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::d_formatter()));
+ break;
+
+ case('H') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::H_formatter()));
+ break;
+
+ case('I') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::I_formatter()));
+ break;
+
+ case('M') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::M_formatter()));
+ break;
+
+ case('S') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::S_formatter()));
+ break;
+
+ case('e') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::e_formatter()));
+ break;
+
+ case('p') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::p_formatter()));
+ break;
+
+ case('r') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::r_formatter()));
+ break;
+
+ case('R') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::R_formatter()));
+ break;
+
+ case('T') :
+ case('X') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::T_formatter()));
+ break;
+
+ case('z') :
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::z_formatter()));
+ break;
+
+ case ('+'):
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::full_formatter()));
+ break;
+
+ default: //Unkown flag appears as is
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter('%')));
+ _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter(flag)));
+ break;
+ }
+}
+
+
+inline void spdlog::pattern_formatter::format(details::log_msg& msg)
+{
+ for (auto &f : _formatters)
+ {
+ f->format(msg);
+ }
+ //write eol
+ msg.formatted.write(details::os::eol(), details::os::eol_size());
+}
diff --git a/include/spdlog/details/registry.h b/include/spdlog/details/registry.h
new file mode 100644
index 00000000..e75a17ec
--- /dev/null
+++ b/include/spdlog/details/registry.h
@@ -0,0 +1,99 @@
+#pragma once
+// Loggers registy of unique name->logger pointer
+// If 2 loggers with same name are added, the second will be overrun the first
+// If user requests a non existing logger, nullptr will be returned
+// This class is thread safe
+
+#include <string>
+#include <mutex>
+#include <unordered_map>
+
+#include "../logger.h"
+#include "../common.h"
+
+namespace spdlog {
+namespace details {
+
+class registry {
+public:
+ std::shared_ptr<logger> get(const std::string& name)
+ {
+ std::lock_guard<std::mutex> lock(_mutex);
+ auto found = _loggers.find(name);
+ return found == _loggers.end() ? nullptr : found->second;
+ }
+
+ template<class It>
+ std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
+ {
+ std::lock_guard<std::mutex> lock(_mutex);
+ auto new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end);
+ new_logger->set_formatter(_formatter);
+ new_logger->set_level(_level);
+ _loggers[logger_name] = new_logger;
+ return new_logger;
+ }
+
+
+ std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks)
+ {
+ return create(logger_name, sinks.begin(), sinks.end());
+ }
+
+ std::shared_ptr<logger> create(const std::string& logger_name, sink_ptr sink)
+ {
+ return create(logger_name, { sink });
+ }
+
+
+ void formatter(formatter_ptr f)
+ {
+ std::lock_guard<std::mutex> lock(_mutex);
+ _formatter = f;
+ for (auto& l : _loggers)
+ l.second->set_formatter(_formatter);
+ }
+
+
+ void set_pattern(const std::string& pattern)
+ {
+ std::lock_guard<std::mutex> lock(_mutex);
+ _formatter = std::make_shared<pattern_formatter>(pattern);
+ for (auto& l : _loggers)
+ l.second->set_formatter(_formatter);
+
+ }
+
+ void set_level(level::level_enum log_level)
+ {
+ std::lock_guard<std::mutex> lock(_mutex);
+ for (auto& l : _loggers)
+ l.second->set_level(log_level);
+
+ }
+
+ void stop_all()
+ {
+ std::lock_guard<std::mutex> lock(_mutex);
+ _level = level::OFF;
+ for (auto& l : _loggers)
+ l.second->stop_logging();
+ }
+
+
+ static registry& instance()
+ {
+ static registry s_instance;
+ return s_instance;
+ }
+
+private:
+ registry() = default;
+ registry(const registry&) = delete;
+ std::mutex _mutex;
+ std::unordered_map <std::string, std::shared_ptr<logger>> _loggers;
+ formatter_ptr _formatter;
+ level::level_enum _level = level::INFO;
+};
+}
+}
diff --git a/include/spdlog/details/spdlog_impl.h b/include/spdlog/details/spdlog_impl.h
new file mode 100644
index 00000000..05c1149a
--- /dev/null
+++ b/include/spdlog/details/spdlog_impl.h
@@ -0,0 +1,51 @@
+#pragma once
+
+//
+// Global registry functions
+//
+#include "registry.h"
+
+inline std::shared_ptr<spdlog::logger> spdlog::get(const std::string& name)
+{
+ return details::registry::instance().get(name);
+}
+
+inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, spdlog::sinks_init_list sinks)
+{
+ return details::registry::instance().create(logger_name, sinks);
+}
+
+
+template <typename Sink, typename... Args>
+inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const Args&... args)
+{
+ sink_ptr sink = std::make_shared<Sink>(args...);
+ return details::registry::instance().create(logger_name, { sink });
+}
+
+
+template<class It>
+inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
+{
+ return details::registry::instance().create(logger_name, sinks_begin, sinks_end);
+}
+
+inline void spdlog::set_formatter(spdlog::formatter_ptr f)
+{
+ details::registry::instance().formatter(f);
+}
+
+inline void spdlog::set_pattern(const std::string& format_string)
+{
+ return details::registry::instance().set_pattern(format_string);
+}
+
+inline void spdlog::set_level(level::level_enum log_level)
+{
+ return details::registry::instance().set_level(log_level);
+}
+
+inline void spdlog::stop()
+{
+ return details::registry::instance().stop_all();
+}
diff --git a/include/spdlog/details/stack_buf.h b/include/spdlog/details/stack_buf.h
new file mode 100644
index 00000000..d8d2a2aa
--- /dev/null
+++ b/include/spdlog/details/stack_buf.h
@@ -0,0 +1,111 @@
+#pragma once
+
+#include <algorithm>
+#include <array>
+#include <vector>
+#include <cstring>
+
+
+// Fast memory storage on the stack when possible or in std::vector
+namespace spdlog
+{
+namespace details
+{
+
+template<unsigned short STACK_SIZE>
+class stack_buf
+{
+public:
+ static const unsigned short stack_size = STACK_SIZE;
+ stack_buf() :_v(), _stack_size(0) {}
+ ~stack_buf() = default;
+ stack_buf(const stack_buf& other):stack_buf(other, delegate_copy_move {})
+ {}
+
+ stack_buf(stack_buf&& other):stack_buf(other, delegate_copy_move {})
+ {
+ other.clear();
+ }
+ template<class T1>
+ stack_buf& operator=(T1&& other)
+ {
+ _stack_size = other._stack_size;
+ if (other.vector_used())
+ _v = std::forward<T1>(other)._v;
+ else
+ std::copy_n(other._stack_array.begin(), other._stack_size, _stack_array.begin());
+ return *this;
+ }
+
+ void append(const char* buf, std::size_t buf_size)
+ {
+ //If we are aleady using _v, forget about the stack
+ if (vector_used())
+ {
+ _v.insert(_v.end(), buf, buf + buf_size);
+ }
+ //Try use the stack
+ else
+ {
+ if (_stack_size + buf_size <= STACK_SIZE)
+ {
+ std::memcpy(&_stack_array[_stack_size], buf, buf_size);
+ _stack_size += buf_size;
+ }
+ //Not enough stack space. Copy all to _v
+ else
+ {
+ _v.reserve(_stack_size + buf_size);
+ _v.insert(_v.end(), _stack_array.begin(), _stack_array.begin() + _stack_size);
+ _v.insert(_v.end(), buf, buf + buf_size);
+ }
+ }
+ }
+
+
+ void clear()
+ {
+ _stack_size = 0;
+ _v.clear();
+ }
+
+ const char* data() const
+ {
+ if (vector_used())
+ return _v.data();
+ else
+ return _stack_array.data();
+ }
+
+ std::size_t size() const
+ {
+ if (vector_used())
+ return _v.size();
+ else
+ return _stack_size;
+ }
+
+private:
+ struct delegate_copy_move {};
+ template<class T1>
+ stack_buf(T1&& other, delegate_copy_move)
+ {
+ _stack_size = other._stack_size;
+ if (other.vector_used())
+ _v = std::forward<T1>(other)._v;
+ else
+ std::copy_n(other._stack_array.begin(), other._stack_size, _stack_array.begin());
+ }
+
+ inline bool vector_used() const
+ {
+ return !(_v.empty());
+ }
+
+ std::vector<char> _v;
+ std::array<char, STACK_SIZE> _stack_array;
+ std::size_t _stack_size;
+};
+
+}
+} //namespace spdlog { namespace details {
diff --git a/include/spdlog/formatter.h b/include/spdlog/formatter.h
new file mode 100644
index 00000000..8575c073
--- /dev/null
+++ b/include/spdlog/formatter.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include "details/log_msg.h"
+namespace spdlog
+{
+namespace details {
+class flag_formatter;
+}
+
+class formatter
+{
+public:
+ virtual ~formatter() {}
+ virtual void format(details::log_msg& msg) = 0;
+};
+
+class pattern_formatter : public formatter
+{
+
+public:
+ explicit pattern_formatter(const std::string& pattern);
+ pattern_formatter(const pattern_formatter&) = delete;
+ void format(details::log_msg& msg) override;
+private:
+ const std::string _pattern;
+ std::vector<std::unique_ptr<details::flag_formatter>> _formatters;
+ void handle_flag(char flag);
+ void compile_pattern(const std::string& pattern);
+};
+}
+
+#include "details/pattern_formatter_impl.h"
+
diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h
new file mode 100644
index 00000000..a4008dc1
--- /dev/null
+++ b/include/spdlog/logger.h
@@ -0,0 +1,91 @@
+#pragma once
+
+// Thread safe logger
+// Has name, log level, vector of std::shared sink pointers and formatter
+// Upon each log write the logger:
+// 1. Checks if its log level is enough to log the message
+// 2. Format the message using the formatter function
+// 3. Pass the formatted message to its sinks to performa the actual logging
+
+#include<vector>
+#include<memory>
+#include<atomic>
+#include <sstream>
+#include <exception>
+#include "sinks/base_sink.h"
+#include "common.h"
+
+namespace spdlog
+{
+
+namespace details
+{
+class line_logger;
+}
+
+class logger
+{
+public:
+
+ logger(const std::string& name, sinks_init_list);
+ template<class It>
+ logger(const std::string& name, const It& begin, const It& end);
+
+ void set_pattern(const std::string&);
+ void set_formatter(formatter_ptr);
+ formatter_ptr get_formatter() const;
+
+
+ logger(const logger&) = delete;
+ logger& operator=(const logger&) = delete;
+
+ void set_level(level::level_enum);
+ level::level_enum level() const;
+
+ const std::string& name() const;
+ bool should_log(level::level_enum) const;
+
+ void stop_logging();
+
+ template <typename... Args> details::line_logger log(level::level_enum lvl, const Args&... args);
+ template <typename... Args> details::line_logger log(const Args&... args);
+ template <typename... Args> details::line_logger trace(const Args&... args);
+ template <typename... Args> details::line_logger debug(const Args&... args);
+ template <typename... Args> details::line_logger info(const Args&... args);
+ template <typename... Args> details::line_logger warn(const Args&... args);
+ template <typename... Args> details::line_logger error(const Args&... args);
+ template <typename... Args> details::line_logger critical(const Args&... args);
+
+
+private:
+ friend details::line_logger;
+ std::string _name;
+ formatter_ptr _formatter;
+ std::vector<sink_ptr> _sinks;
+ std::atomic_int _level;
+ void _variadic_log(details::line_logger& l);
+ template <typename First, typename... Rest>
+ void _variadic_log(details::line_logger&l, const First& first, const Rest&... rest);
+ void _log_msg(details::log_msg& msg);
+};
+
+
+
+}
+
+//
+// Trace & debug macros
+//
+#ifdef FFLOG_ENABLE_TRACE
+#define FFLOG_TRACE(logger, ...) logger->log(spdlog::level::TRACE, __FILE__, " #", __LINE__,": " __VA_ARGS__)
+#else
+#define FFLOG_TRACE(logger, ...) {}
+#endif
+
+#ifdef FFLOG_ENABLE_DEBUG
+#define FFLOG_DEBUG(logger, ...) logger->log(spdlog::level::DEBUG, __VA_ARGS__)
+#else
+#define FFLOG_DEBUG(logger, ...) {}
+#endif
+
+#include "./details/logger_impl.h"
diff --git a/include/spdlog/sinks/async_sink.h b/include/spdlog/sinks/async_sink.h
new file mode 100644
index 00000000..35a8b6e0
--- /dev/null
+++ b/include/spdlog/sinks/async_sink.h
@@ -0,0 +1,137 @@
+#pragma once
+
+#include <thread>
+#include <chrono>
+#include <atomic>
+#include <algorithm>
+
+#include "./base_sink.h"
+#include "../logger.h"
+#include "../details/blocking_queue.h"
+#include "../details/null_mutex.h"
+#include "../details/log_msg.h"
+
+#include<iostream>
+
+namespace spdlog
+{
+namespace sinks
+{
+
+class async_sink : public base_sink<details::null_mutex>
+{
+public:
+ using q_type = details::blocking_queue<details::log_msg>;
+
+ explicit async_sink(const q_type::size_type max_queue_size);
+
+ //Stop logging and join the back thread
+ ~async_sink();
+ void add_sink(sink_ptr sink);
+ void remove_sink(sink_ptr sink_ptr);
+ q_type& q();
+ //Wait to remaining items (if any) in the queue to be written and shutdown
+ void shutdown(const std::chrono::milliseconds& timeout);
+
+
+protected:
+ void _sink_it(const details::log_msg& msg) override;
+ void _thread_loop();
+
+private:
+ std::vector<std::shared_ptr<sink>> _sinks;
+ std::atomic<bool> _active;
+ q_type _q;
+ std::thread _back_thread;
+ //Clear all remaining messages(if any), stop the _back_thread and join it
+ void _shutdown();
+ std::mutex _mutex;
+};
+}
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// async_sink class implementation
+///////////////////////////////////////////////////////////////////////////////
+inline spdlog::sinks::async_sink::async_sink(const q_type::size_type max_queue_size)
+ :_sinks(),
+ _active(true),
+ _q(max_queue_size),
+ _back_thread(&async_sink::_thread_loop, this)
+{}
+
+inline spdlog::sinks::async_sink::~async_sink()
+{
+ _shutdown();
+}
+
+inline void spdlog::sinks::async_sink::_sink_it(const details::log_msg& msg)
+{
+ if(!_active)
+ return;
+ _q.push(msg);
+}
+
+inline void spdlog::sinks::async_sink::_thread_loop()
+{
+ static std::chrono::seconds pop_timeout { 1 };
+ while (_active)
+ {
+ q_type::item_type msg;
+ if (_q.pop(msg, pop_timeout))
+ {
+ for (auto &s : _sinks)
+ {
+ s->log(msg);
+ if(!_active)
+ break;
+ }
+ }
+ }
+}
+
+inline void spdlog::sinks::async_sink::add_sink(spdlog::sink_ptr s)
+{
+ std::lock_guard<std::mutex> guard(_mutex);
+ _sinks.push_back(s);
+}
+
+
+inline void spdlog::sinks::async_sink::remove_sink(spdlog::sink_ptr s)
+{
+ std::lock_guard<std::mutex> guard(_mutex);
+ _sinks.erase(std::remove(_sinks.begin(), _sinks.end(), s), _sinks.end());
+}
+
+
+inline spdlog::sinks::async_sink::q_type& spdlog::sinks::async_sink::q()
+{
+ return _q;
+}
+
+
+inline void spdlog::sinks::async_sink::shutdown(const std::chrono::milliseconds& timeout)
+{
+ if(timeout > std::chrono::milliseconds::zero())
+ {
+ auto until = log_clock::now() + timeout;
+ while (_q.size() > 0 && log_clock::now() < until)
+ {
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
+ }
+ }
+ _shutdown();
+}
+
+
+inline void spdlog::sinks::async_sink::_shutdown()
+{
+ std::lock_guard<std::mutex> guard(_mutex);
+ if(_active)
+ {
+ _active = false;
+ if (_back_thread.joinable())
+ _back_thread.join();
+ }
+}
+
diff --git a/include/spdlog/sinks/base_sink.h b/include/spdlog/sinks/base_sink.h
new file mode 100644
index 00000000..5e91e4f5
--- /dev/null
+++ b/include/spdlog/sinks/base_sink.h
@@ -0,0 +1,43 @@
+#pragma once
+//
+// base sink templated over a mutex (either dummy or realy)
+// concrete implementation should only overrid the _sink_it method.
+// all locking is taken care of here so no locking needed by the implementors..
+//
+
+#include<string>
+#include<mutex>
+#include<atomic>
+#include "./sink.h"
+#include "../formatter.h"
+#include "../common.h"
+#include "../details/log_msg.h"
+
+
+namespace spdlog
+{
+namespace sinks
+{
+template<class Mutex>
+class base_sink:public sink
+{
+public:
+ base_sink():_mutex() {}
+ virtual ~base_sink() = default;
+
+ base_sink(const base_sink&) = delete;
+ base_sink& operator=(const base_sink&) = delete;
+
+ void log(const details::log_msg& msg) override
+ {
+ std::lock_guard<Mutex> lock(_mutex);
+ _sink_it(msg);
+ };
+
+
+protected:
+ virtual void _sink_it(const details::log_msg& msg) = 0;
+ Mutex _mutex;
+};
+}
+}
diff --git a/include/spdlog/sinks/file_sinks.h b/include/spdlog/sinks/file_sinks.h
new file mode 100644
index 00000000..ae35d834
--- /dev/null
+++ b/include/spdlog/sinks/file_sinks.h
@@ -0,0 +1,189 @@
+#pragma once
+
+
+#include <mutex>
+#include "./base_sink.h"
+
+#include "../details/null_mutex.h"
+#include "../details/file_helper.h"
+#include "../details/fast_oss.h"
+
+
+
+namespace spdlog
+{
+namespace sinks
+{
+
+/*
+* Trivial file sink with single file as target
+*/
+template<class Mutex>
+class simple_file_sink : public base_sink<Mutex>
+{
+public:
+ explicit simple_file_sink(const std::string &filename,
+ const std::size_t flush_inverval=0):
+ _file_helper(flush_inverval)
+ {
+ _file_helper.open(filename);
+ }
+protected:
+ void _sink_it(const details::log_msg& msg) override
+ {
+ _file_helper.write(msg);
+ }
+private:
+ details::file_helper _file_helper;
+};
+
+typedef simple_file_sink<std::mutex> simple_file_sink_mt;
+typedef simple_file_sink<details::null_mutex> simple_file_sink_st;
+
+/*
+ * Rotating file sink based on size
+*/
+template<class Mutex>
+class rotating_file_sink : public base_sink<Mutex>
+{
+public:
+ rotating_file_sink(const std::string &base_filename, const std::string &extension,
+ const std::size_t max_size, const std::size_t max_files,
+ const std::size_t flush_inverval=0):
+ _base_filename(base_filename),
+ _extension(extension),
+ _max_size(max_size),
+ _max_files(max_files),
+ _current_size(0),
+ _file_helper(flush_inverval)
+ {
+ _file_helper.open(calc_filename(_base_filename, 0, _extension));
+ }
+
+protected:
+ void _sink_it(const details::log_msg& msg) override
+ {
+ _current_size += msg.formatted.size();
+ if (_current_size > _max_size)
+ {
+ _rotate();
+ _current_size = msg.formatted.size();
+ }
+ _file_helper.write(msg);
+ }
+
+
+private:
+ static std::string calc_filename(const std::string& filename, std::size_t index, const std::string& extension)
+ {
+ details::fast_oss oss;
+ if (index)
+ oss << filename << "." << index << "." << extension;
+ else
+ oss << filename << "." << extension;
+ return oss.str();
+ }
+
+
+ // Rotate files:
+ // log.txt -> log.1.txt
+ // log.1.txt -> log2.txt
+ // log.2.txt -> log3.txt
+ // log.3.txt -> delete
+
+
+ void _rotate()
+ {
+ _file_helper.close();
+ for (auto i = _max_files; i > 0; --i)
+ {
+ std::string src = calc_filename(_base_filename, i - 1, _extension);
+ std::string target = calc_filename(_base_filename, i, _extension);
+
+ if (details::file_helper::file_exists(target))
+ std::remove(target.c_str());
+ if (details::file_helper::file_exists(src) && std::rename(src.c_str(), target.c_str()))
+ {
+ throw fflog_exception("rotating_file_sink: failed renaming " + src + " to " + target);
+ }
+ }
+ auto cur_name = _file_helper.filename();
+ std::remove(cur_name.c_str());
+ _file_helper.open(cur_name);
+ }
+ std::string _base_filename;
+ std::string _extension;
+ std::size_t _max_size;
+ std::size_t _max_files;
+ std::size_t _current_size;
+ details::file_helper _file_helper;
+};
+
+typedef rotating_file_sink<std::mutex> rotating_file_sink_mt;
+typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
+
+/*
+ * Rotating file sink based on date. rotates at midnight
+ */
+template<class Mutex>
+class daily_file_sink:public base_sink<Mutex>
+{
+public:
+ explicit daily_file_sink(const std::string& base_filename,
+ const std::string& extension,
+ const std::size_t flush_inverval=0):
+ _base_filename(base_filename),
+ _extension(extension),
+ _midnight_tp (_calc_midnight_tp() ),
+ _file_helper(flush_inverval)
+ {
+ _file_helper.open(calc_filename(_base_filename, _extension));
+ }
+
+protected:
+ void _sink_it(const details::log_msg& msg) override
+ {
+ if (std::chrono::system_clock::now() >= _midnight_tp)
+ {
+ _file_helper.close();
+ _file_helper.open(calc_filename(_base_filename, _extension));
+ _midnight_tp = _calc_midnight_tp();
+ }
+ _file_helper.write(msg);
+ }
+
+private:
+ // Return next midnight's time_point
+ static std::chrono::system_clock::time_point _calc_midnight_tp()
+ {
+ using namespace std::chrono;
+ auto now = system_clock::now();
+ time_t tnow = std::chrono::system_clock::to_time_t(now);
+ tm date = spdlog::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 = spdlog::details::os::localtime();
+ details::fast_oss oss;
+ oss << basename << '.';
+ oss << tm.tm_year + 1900 << '-' << std::setw(2) << std::setfill('0') << tm.tm_mon + 1 << '-' << tm.tm_mday;
+ oss << '.' << extension;
+ return oss.str();
+ }
+
+ std::string _base_filename;
+ std::string _extension;
+ std::chrono::system_clock::time_point _midnight_tp;
+ details::file_helper _file_helper;
+
+};
+
+typedef daily_file_sink<std::mutex> daily_file_sink_mt;
+typedef daily_file_sink<details::null_mutex> daily_file_sink_st;
+}
+}
diff --git a/include/spdlog/sinks/null_sink.h b/include/spdlog/sinks/null_sink.h
new file mode 100644
index 00000000..776425e6
--- /dev/null
+++ b/include/spdlog/sinks/null_sink.h
@@ -0,0 +1,23 @@
+#pragma once
+#include <mutex>
+#include "./base_sink.h"
+#include "../details/null_mutex.h"
+
+
+namespace spdlog {
+namespace sinks {
+
+template <class Mutex>
+class null_sink : public base_sink<Mutex>
+{
+protected:
+ void _sink_it(const details::log_msg&) override
+ {}
+};
+
+typedef null_sink<details::null_mutex> null_sink_st;
+typedef null_sink<std::mutex> null_sink_mt;
+
+}
+}
+
diff --git a/include/spdlog/sinks/ostream_sink.h b/include/spdlog/sinks/ostream_sink.h
new file mode 100644
index 00000000..5fa62683
--- /dev/null
+++ b/include/spdlog/sinks/ostream_sink.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <iostream>
+#include <mutex>
+#include <memory>
+
+#include "../details/null_mutex.h"
+#include "./base_sink.h"
+
+namespace spdlog
+{
+namespace sinks
+{
+template<class Mutex>
+class ostream_sink: public base_sink<Mutex>
+{
+public:
+ explicit ostream_sink(std::ostream& os) :_ostream(os) {}
+ ostream_sink(const ostream_sink&) = delete;
+ ostream_sink& operator=(const ostream_sink&) = delete;
+ virtual ~ostream_sink() = default;
+
+
+protected:
+ virtual void _sink_it(const details::log_msg& msg) override
+ {
+ auto& buf = msg.formatted.buf();
+ _ostream.write(buf.data(), buf.size());
+ }
+ std::ostream& _ostream;
+};
+
+typedef ostream_sink<std::mutex> ostream_sink_mt;
+typedef ostream_sink<details::null_mutex> ostream_sink_st;
+}
+}
diff --git a/include/spdlog/sinks/sink.h b/include/spdlog/sinks/sink.h
new file mode 100644
index 00000000..9d4a2cc5
--- /dev/null
+++ b/include/spdlog/sinks/sink.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "../details/log_msg.h"
+
+namespace spdlog
+{
+namespace sinks
+{
+class sink
+{
+public:
+ virtual ~sink() {}
+ virtual void log(const details::log_msg& msg) = 0;
+};
+}
+}
+
diff --git a/include/spdlog/sinks/stdout_sinks.h b/include/spdlog/sinks/stdout_sinks.h
new file mode 100644
index 00000000..19b0f908
--- /dev/null
+++ b/include/spdlog/sinks/stdout_sinks.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <iostream>
+#include <mutex>
+#include "./ostream_sink.h"
+#include "../details/null_mutex.h"
+
+namespace spdlog
+{
+namespace sinks
+{
+
+template <class Mutex>
+class stdout_sink : public ostream_sink<Mutex>
+{
+public:
+ stdout_sink() : ostream_sink<Mutex>(std::cout) {}
+};
+
+typedef stdout_sink<details::null_mutex> stdout_sink_st;
+typedef stdout_sink<std::mutex> stdout_sink_mt;
+
+
+template <class Mutex>
+class stderr_sink : public ostream_sink<Mutex>
+{
+public:
+ stderr_sink() : ostream_sink<Mutex>(std::cerr) {}
+};
+
+typedef stderr_sink<std::mutex> stderr_sink_mt;
+typedef stderr_sink<details::null_mutex> stderr_sink_st;
+}
+} \ No newline at end of file
diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h
new file mode 100644
index 00000000..44d13485
--- /dev/null
+++ b/include/spdlog/spdlog.h
@@ -0,0 +1,84 @@
+//
+// This is spdlog - an extremely fast and easy to use c++11 logging library
+//
+
+// example code (create multi threaded daily logger):
+// auto my_logger = spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename", "txt");
+// ..
+// auto mylog = spdlog::get("mylog");
+// mylog->info("Hello logger.", "This is message number", 1, "!!") ;
+// mylog->info() << "std streams are also supprted: " << std::hex << 255;
+
+// see example.cpp for more examples
+
+#pragma once
+
+#include "logger.h"
+#include "details/registry.h"
+
+namespace spdlog
+{
+
+// Return an existing logger or nullptr if a logger with such name doesn't exist.
+// Examples:
+//
+// spdlog::get("mylog")->info("Hello");
+// auto logger = spdlog::get("mylog");
+// logger.info("This is another message" , x, y, z);
+// logger.info() << "This is another message" << x << y << z;
+std::shared_ptr<logger> get(const std::string& name);
+
+
+// Example:
+// auto logger = spdlog::create("mylog", {sink1, sink2});
+std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks);
+
+
+// Example (create a logger with daily rotating file):
+// using namespace spdlog::sinks;
+// spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename", "txt");
+template <typename Sink, typename... Args>
+std::shared_ptr<spdlog::logger> create(const std::string& logger_name, const Args&... args);
+
+// Example:
+// using namespace spdlog::sinks;
+// std::vector<spdlog::sink_ptr> mySinks;
+// mySinks.push_back(std::make_shared<rotating_file_sink_mt>("filename", "txt", 1024 * 1024 * 5, 10));
+// mySinks.push_back(std::make_shared<stdout_sink_mt>());
+// spdlog::create("mylog", mySinks.begin(), mySinks.end());
+template<class It>
+std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end);
+
+
+// Set global formatting
+// Example:
+// spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %t");
+void set_pattern(const std::string& format_string);
+
+
+// Set global formatter object
+void set_formatter(formatter_ptr f);
+
+
+//Set global active logging level
+void set_level(level::level_enum log_level);
+
+
+//Stop all loggers
+void stop();
+
+
+//
+// Trace macro to turn on/off at compile time
+// Example: SPDLOG_TRACE(my_logger, "Some trace message");
+//
+#ifdef _DEBUG
+#define SPDLOG_TRACE(logger, ...) logger->log(__FILE__, " #", __LINE__,": " __VA_ARGS__)
+#else
+#define SPDLOG_TRACE(logger, ...) {}
+#endif
+
+
+}
+
+#include "details/spdlog_impl.h" \ No newline at end of file