diff --git a/c11logtest/c11logtest/c11logtest.vcxproj b/c11logtest/c11logtest/c11logtest.vcxproj
index eb84e605..a702e84b 100644
--- a/c11logtest/c11logtest/c11logtest.vcxproj
+++ b/c11logtest/c11logtest/c11logtest.vcxproj
@@ -57,6 +57,7 @@
Console
true
+ true
@@ -88,6 +89,7 @@
+
@@ -96,6 +98,7 @@
+
diff --git a/c11logtest/c11logtest/c11logtest.vcxproj.filters b/c11logtest/c11logtest/c11logtest.vcxproj.filters
index 5110b219..959b0c9d 100644
--- a/c11logtest/c11logtest/c11logtest.vcxproj.filters
+++ b/c11logtest/c11logtest/c11logtest.vcxproj.filters
@@ -90,6 +90,12 @@
Header Files\c11log
+
+ Header Files\c11log\sinks
+
+
+ Header Files\c11log\details
+
diff --git a/example/bench.cpp b/example/bench.cpp
index 615968f6..ec18ee1a 100644
--- a/example/bench.cpp
+++ b/example/bench.cpp
@@ -2,6 +2,7 @@
//
#include
#include "c11log/logger.h"
+#include "c11log/pattern_formatter.h"
#include "c11log/sinks/async_sink.h"
#include "c11log/sinks/file_sinks.h"
#include "c11log/sinks/stdout_sinks.h"
@@ -15,26 +16,27 @@ using namespace c11log;
using namespace utils;
-int main2(int argc, char* argv[])
+int main(int argc, char* argv[])
{
+ const unsigned int howmany = argc <= 1 ? 500000 : atoi(argv[1]);
- const unsigned int howmany = argc <= 1 ? 1000000 : atoi(argv[1]);
+ std::string pattern = "%Y:%m:%d %H:%M:%S.%e [%n:%l] %t";
+ auto formatter1 = std::unique_ptr(new formatters::pattern_formatter(pattern));
- logger cout_logger("example", std::make_shared());
+ logger cout_logger("bench", std::make_shared() , std::move(formatter1));
cout_logger.info() << "Hello logger";
auto nullsink = std::make_shared>();
-
-
- logger my_logger("my_logger", nullsink);
-
+ auto formatter2 = std::unique_ptr(new formatters::pattern_formatter(pattern));
+ logger my_logger("my_logger", nullsink, std::move(formatter2));
+ //logger my_logger("my_logger", nullsink);
auto start = system_clock::now();
for (unsigned int i = 1; i <= howmany; ++i)
- my_logger.info() << "Hello logger: msg #" << i << 1<<2<<3<<4<<5<<6<<7<<8<<9<<10<<11<<12<<13<<14<<15<<16<<17<<18<<19;
- //my_logger.info("Hello logger: msg #",i,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19);
+ my_logger.info() << "Hello logger: msg #" << i;
+
auto delta = system_clock::now() - start;
diff --git a/example/example.cpp b/example/example.cpp
index 23b385b1..6dc6e384 100644
--- a/example/example.cpp
+++ b/example/example.cpp
@@ -9,7 +9,7 @@
#include "c11log/sinks/file_sinks.h"
using namespace std;
-int main(int argc, char* argv[])
+int main2(int argc, char* argv[])
{
auto console = c11log::factory::stdout_logger();
@@ -30,6 +30,6 @@ int main(int argc, char* argv[])
auto sink1= std::make_shared();
auto sink2 = std::make_shared("rotating", "txt");
c11log::logger combined("combined", { sink1, sink2 });
-
+ return 0;
}
diff --git a/include/c11log/details/fast_oss.h b/include/c11log/details/fast_oss.h
index dd204576..2b67159e 100644
--- a/include/c11log/details/fast_oss.h
+++ b/include/c11log/details/fast_oss.h
@@ -5,8 +5,10 @@
#include
#include
+#include "fast_istostr.h"
#include "stack_buf.h"
+
namespace c11log
{
namespace details
@@ -15,7 +17,7 @@ namespace details
class stack_devicebuf :public std::streambuf
{
public:
- static const unsigned short stack_size = 192;
+ static const unsigned short stack_size = 256;
using stackbuf_t = stack_buf;
stack_devicebuf() = default;
@@ -103,6 +105,29 @@ public:
_dev.clear();
}
+ // The following were added because they add significant 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 min_width)
+ {
+ std::string s;
+ details::fast_itostr(n, s, min_width);
+ _dev.sputn(s.data(), s.size());
+ //sprintf_s(buf, "%d", n);
+ //_dev.sputn(buf, width);
+ }
+
+ void put_str(const std::string& str)
+ {
+ _dev.sputn(str.data(), str.size());
+ }
+
+
private:
stack_devicebuf _dev;
};
diff --git a/include/c11log/details/line_logger.h b/include/c11log/details/line_logger.h
index 64b4035e..86f04c82 100644
--- a/include/c11log/details/line_logger.h
+++ b/include/c11log/details/line_logger.h
@@ -45,6 +45,7 @@ public:
{
_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));
_log_msg.raw = _oss.str();
_callback_logger->_log_msg(_log_msg);
}
diff --git a/include/c11log/details/log_msg.h b/include/c11log/details/log_msg.h
index b8920a0f..58f2cd38 100644
--- a/include/c11log/details/log_msg.h
+++ b/include/c11log/details/log_msg.h
@@ -14,6 +14,7 @@ struct log_msg
logger_name(),
level(l),
time(),
+ tm_time(),
raw(),
formatted() {}
@@ -21,6 +22,7 @@ struct log_msg
logger_name(other.logger_name),
level(other.level),
time(other.time),
+ tm_time(other.tm_time),
raw(other.raw),
formatted(other.formatted) {}
@@ -35,6 +37,7 @@ struct log_msg
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);
}
@@ -56,9 +59,11 @@ struct log_msg
std::string logger_name;
level::level_enum level;
log_clock::time_point time;
+ std::tm tm_time;
std::string raw;
std::string formatted;
+
};
}
}
diff --git a/include/c11log/formatter.h b/include/c11log/formatter.h
index ae77201a..65e5b028 100644
--- a/include/c11log/formatter.h
+++ b/include/c11log/formatter.h
@@ -33,7 +33,7 @@ public:
void format(details::log_msg& msg) override
{
details::fast_oss oss;
- _format_time(msg.time, oss);
+ _format_time(msg, oss);
if(!msg.logger_name.empty())
oss << " [" << msg.logger_name << ':' << c11log::level::to_str(msg.level) << "] ";
@@ -44,7 +44,7 @@ public:
msg.formatted = oss.str();
}
private:
- void _format_time(const log_clock::time_point& tp, std::ostream& output);
+ void _format_time(const details::log_msg& msg, std::ostream &output);
};
@@ -53,44 +53,15 @@ private:
} //namespace c11log
// Format datetime like this: [2014-03-14 17:15:22]
-inline void c11log::formatters::default_formatter::_format_time(const log_clock::time_point& tp, std::ostream &output)
+inline void c11log::formatters::default_formatter::_format_time(const details::log_msg& msg, std::ostream &output)
{
- using namespace c11log::details::os;
- using namespace std::chrono;
-
-#ifdef _WIN32 //VS2013 doesn't support yet thread_local keyword
- __declspec(thread) static char s_cache_timestr[128];
- __declspec(thread) static int s_cache_timesize = 0;
- __declspec(thread) static std::time_t s_cache_time_t = 0;
-#else
- thread_local static char s_cache_timestr[128];
- thread_local static int s_cache_timesize = 0;
- thread_local static std::time_t s_cache_time_t = 0;
-#endif
-
- //Cache every second
- std::time_t tp_time_t = log_clock::to_time_t(tp);
- if(tp_time_t != s_cache_time_t)
- {
- auto tm_now = details::os::localtime(tp_time_t);
- std::ostringstream time_oss;
- time_oss.fill('0');
- time_oss << '[' << tm_now.tm_year + 1900 << '-';
- time_oss.width(2);
- time_oss << tm_now.tm_mon + 1 << '-';
- time_oss.width(2);
- time_oss << tm_now.tm_mday << ' ';
- time_oss.width(2);
- time_oss << tm_now.tm_hour << ':';
- time_oss.width(2);
- time_oss << tm_now.tm_min << ':';
- time_oss.width(2);
- time_oss << tm_now.tm_sec << ']';
- //Cache the resulted string and its size
- s_cache_time_t = tp_time_t;
- const std::string s = time_oss.str();
- s_cache_timesize = s.size();
- std::memcpy(s_cache_timestr, s.c_str(), s_cache_timesize);
- }
- output.write(s_cache_timestr, s_cache_timesize);
+ output.fill('0');
+ output << '[' << msg.tm_time.tm_year + 1900 << '-';
+ output.width(2);
+ output << msg.tm_time.tm_mon + 1 << '-';
+ output << msg.tm_time.tm_mday << ' ';
+ output << msg.tm_time.tm_hour << ':';
+ output << msg.tm_time.tm_min << ':';
+ output << msg.tm_time.tm_sec << ']';
}
+
diff --git a/include/c11log/pattern_formatter.h b/include/c11log/pattern_formatter.h
new file mode 100644
index 00000000..08917ab5
--- /dev/null
+++ b/include/c11log/pattern_formatter.h
@@ -0,0 +1,285 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#include "formatter.h"
+#include "details/log_msg.h"
+#include "details/fast_oss.h"
+
+
+namespace c11log
+{
+namespace details {
+class pattern_appender
+{
+public:
+ virtual void append(const details::log_msg& msg, details::fast_oss& oss) = 0;
+};
+
+// log name appender
+class name_appender :public pattern_appender
+{
+ void append(const details::log_msg& msg, details::fast_oss& oss) override
+ {
+ oss << msg.logger_name;
+ }
+};
+
+// log level appender
+class level_appender :public pattern_appender
+{
+ void append(const details::log_msg& msg, details::fast_oss& oss) override
+ {
+ oss << level::to_str(msg.level);
+ }
+};
+
+///////////////////////////////////////////////////////////////////////
+// Date time pattern appenders
+///////////////////////////////////////////////////////////////////////
+
+// year - 4 digit
+class Y_appender :public pattern_appender
+{
+ void append(const details::log_msg& msg, details::fast_oss& oss) override
+ {
+ oss.put_int(msg.tm_time.tm_year+1900, 4);
+ }
+};
+
+// year - 2 digit
+class y_appender :public pattern_appender
+{
+ void append(const details::log_msg& msg, details::fast_oss& oss) override
+ {
+ oss.put_int(msg.tm_time.tm_year, 2);
+ }
+};
+// month 1-12
+class m_appender :public pattern_appender
+{
+ void append(const details::log_msg& msg, details::fast_oss& oss) override
+ {
+ oss.put_int(msg.tm_time.tm_mon + 1, 2);
+ }
+};
+
+
+// day of month 1-31
+class d_appender :public pattern_appender
+{
+ void append(const details::log_msg& msg, details::fast_oss& oss) override
+ {
+ oss.put_int(msg.tm_time.tm_mday, 2);
+ }
+};
+
+// hours in 24 format 0-23
+class H_appender :public pattern_appender
+{
+ void append(const details::log_msg& msg, details::fast_oss& oss) override
+ {
+ oss.put_int(msg.tm_time.tm_hour, 2);
+ }
+};
+
+// hours in 12 format 1-12
+class I_appender :public pattern_appender
+{
+ void append(const details::log_msg& msg, details::fast_oss& oss) override
+ {
+ oss.put_int((msg.tm_time.tm_hour + 1) % 1, 2);
+ }
+};
+
+// ninutes 0-59
+class M_appender :public pattern_appender
+{
+ void append(const details::log_msg& msg, details::fast_oss& oss) override
+ {
+ oss.put_int(msg.tm_time.tm_min, 2);
+ }
+};
+
+// seconds 0-59
+class S_appender :public pattern_appender
+{
+ void append(const details::log_msg& msg, details::fast_oss& oss) override
+ {
+ oss.put_int(msg.tm_time.tm_sec, 2);
+ }
+};
+
+// milliseconds
+class e_appender :public pattern_appender
+{
+ void append(const details::log_msg& msg, details::fast_oss& oss) override
+ {
+ auto duration = msg.time.time_since_epoch();
+ auto millis = std::chrono::duration_cast(duration).count() % 1000;
+ oss.put_int(millis, 3);
+ }
+};
+
+
+class t_appender :public pattern_appender
+{
+ void append(const details::log_msg& msg, details::fast_oss& oss) override
+ {
+ oss.put_str(msg.raw);
+ }
+};
+
+class ch_appender :public pattern_appender
+{
+public:
+ explicit ch_appender(char ch) : _ch(ch)
+ {}
+ void append(const details::log_msg& msg, details::fast_oss& oss) override
+ {
+ oss.putc( _ch);
+ }
+private:
+ char _ch;
+};
+
+
+class str_appender :public pattern_appender
+{
+public:
+ str_appender()
+ {}
+ void add_ch(char ch)
+ {
+ _str += ch;
+ }
+ void append(const details::log_msg& msg, details::fast_oss& oss) override
+ {
+ oss << _str;
+ }
+private:
+ std::string _str;
+};
+}
+
+
+namespace formatters
+{
+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> _appenders;
+ void handle_flag(char flag);
+ void compile_pattern(const std::string& pattern);
+};
+}
+}
+
+
+c11log::formatters::pattern_formatter::pattern_formatter(const std::string& pattern)
+{
+ compile_pattern(pattern);
+}
+
+
+void c11log::formatters::pattern_formatter::compile_pattern(const std::string& pattern)
+{
+ auto end = pattern.end();
+ for (auto it = pattern.begin(); it != end; ++it)
+ {
+ if (*it == '%')
+ {
+ if (++it != end)
+ handle_flag(*it);
+ else
+ return;
+ }
+ else
+ {
+ // chars not following the % sign should be displayed as is
+ _appenders.push_back(std::unique_ptr(new details::ch_appender(*it)));
+ }
+ }
+
+}
+void c11log::formatters::pattern_formatter::handle_flag(char flag)
+{
+ switch (flag)
+ {
+ // logger name
+ case 'n':
+ _appenders.push_back(std::unique_ptr(new details::name_appender()));
+ break;
+ // message log level
+ case 'l':
+ _appenders.push_back(std::unique_ptr(new details::level_appender()));
+ break;
+ // message text
+ case('t') :
+ _appenders.push_back(std::unique_ptr(new details::t_appender()));
+ break;
+ // year
+ case('Y') :
+ _appenders.push_back(std::unique_ptr(new details::Y_appender()));
+ break;
+ // year 2 digits
+ case('y') :
+ _appenders.push_back(std::unique_ptr(new details::y_appender()));
+ break;
+ // month
+ case('m') :
+ // minute
+ _appenders.push_back(std::unique_ptr(new details::m_appender()));
+ break;
+ // day in month
+ case('d') :
+ _appenders.push_back(std::unique_ptr(new details::d_appender()));
+ break;
+ // hour (24)
+ case('H') :
+ _appenders.push_back(std::unique_ptr(new details::H_appender()));
+ break;
+ // hour (12)
+ case('I') :
+ _appenders.push_back(std::unique_ptr(new details::I_appender()));
+ break;
+ // minutes
+ case('M') :
+ _appenders.push_back(std::unique_ptr(new details::M_appender()));
+ break;
+ // seconds
+ case('S') :
+ _appenders.push_back(std::unique_ptr(new details::S_appender()));
+ break;
+ // milliseconds part
+ case('e'):
+ _appenders.push_back(std::unique_ptr(new details::e_appender()));
+ break;
+ // % sign
+ case('%') :
+ _appenders.push_back(std::unique_ptr(new details::ch_appender('%')));
+ break;
+ }
+
+}
+
+
+void c11log::formatters::pattern_formatter::format(details::log_msg& msg)
+{
+ details::fast_oss oss;
+ for (auto &appender : _appenders)
+ {
+ appender->append(msg, oss);
+ }
+ oss.write(details::os::eol(), details::os::eol_size());
+ msg.formatted = oss.str();
+}
\ No newline at end of file