diff --git a/README.md b/README.md index ed3b6aa4..be0e60a0 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci. * Windows (vc 2013+, cygwin/mingw) * Mac OSX (clang 3.5+) * Android + * VxWorks 7 ##Features * Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below). @@ -37,6 +38,7 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci. * Console logging (colors supported). * syslog. * Windows debugger (```OutputDebugString(..)```) + * Android output (```__android_log_write(..)```) * Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface). * Severity based filtering - threshold levels can be modified in runtime as well as in compile time. diff --git a/include/spdlog/details/logger_impl.h b/include/spdlog/details/logger_impl.h index 2b27f105..17349424 100644 --- a/include/spdlog/details/logger_impl.h +++ b/include/spdlog/details/logger_impl.h @@ -197,8 +197,43 @@ inline void spdlog::logger::critical(const T& msg) log(level::critical, msg); } +inline spdlog::ostream spdlog::logger::log(level::level_enum lvl) +{ + if (!should_log(lvl)) + return ostream(); + + return ostream(this, lvl); +} + +inline spdlog::ostream spdlog::logger::trace() +{ + return log(level::trace); +} + +inline spdlog::ostream spdlog::logger::debug() +{ + return log(level::debug); +} + +inline spdlog::ostream spdlog::logger::info() +{ + return log(level::info); +} +inline spdlog::ostream spdlog::logger::warn() +{ + return log(level::warn); +} +inline spdlog::ostream spdlog::logger::error() +{ + return log(level::err); +} + +inline spdlog::ostream spdlog::logger::critical() +{ + return log(level::critical); +} // // name and level @@ -215,7 +250,15 @@ inline void spdlog::logger::set_level(spdlog::level::level_enum log_level) inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler) { - _err_handler = err_handler; + if (!err_handler) + { + _err_handler = [this](const std::string &msg) + { + this->_default_err_handler(msg); + }; + } + else + _err_handler = err_handler; } inline spdlog::log_err_handler spdlog::logger::error_handler() diff --git a/include/spdlog/details/os.h b/include/spdlog/details/os.h index b63ce667..3d36314a 100644 --- a/include/spdlog/details/os.h +++ b/include/spdlog/details/os.h @@ -306,8 +306,12 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime()) }; long int offset_seconds = helper::calculate_gmt_offset(tm); +#else +#ifdef __VXWORKS__ + long int offset_seconds = 0; #else long int offset_seconds = tm.tm_gmtoff; +#endif #endif return static_cast(offset_seconds / 60); @@ -377,7 +381,7 @@ inline std::string errno_str(int err_num) else return "Unkown error"; -#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \ +#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || defined(__VXWORKS__) || \ ((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version if (strerror_r(err_num, buf, buf_size) == 0) diff --git a/include/spdlog/details/stream_impl.h b/include/spdlog/details/stream_impl.h new file mode 100644 index 00000000..d7a7f143 --- /dev/null +++ b/include/spdlog/details/stream_impl.h @@ -0,0 +1,76 @@ +// +// Copyright(c) 2017 Benoit Leforestier. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +template +spdlog::basic_streambuf::basic_streambuf(spdlog::logger* plogger, spdlog::level::level_enum lvl) : + m_plogger(plogger), + m_log_msg(nullptr, lvl) +{ + if (m_plogger) + { + const std::string& name = m_plogger->name(); + m_log_msg.logger_name = &name; + } +} + +template +spdlog::basic_streambuf::basic_streambuf(basic_streambuf&& rhs) : + m_plogger(std::move(rhs.m_plogger)), + m_log_msg(nullptr, rhs.m_log_msg.level) +{ + rhs.m_plogger = nullptr; + if (m_plogger) + { + m_log_msg.logger_name = std::move(rhs.m_log_msg.logger_name); + m_log_msg.time = std::move(rhs.m_log_msg.time); + m_log_msg.thread_id = std::move(rhs.m_log_msg.thread_id); + m_log_msg.raw = std::move(rhs.m_log_msg.raw); + m_log_msg.formatted = std::move(rhs.m_log_msg.formatted); + } +} + +template +spdlog::basic_streambuf::~basic_streambuf() +{ + if (!m_plogger) + return; + + try + { + m_plogger->_sink_it(m_log_msg); + } + catch (const std::exception &ex) + { + m_plogger->error_handler()(ex.what()); + } + catch (...) + { + m_plogger->error_handler()("Unknown exception"); + } +} + +template +std::streamsize spdlog::basic_streambuf::xsputn(const char_type* __s, std::streamsize __n) +{ + if (!m_plogger) + return 0; + + try + { + m_log_msg.raw << fmt::BasicStringRef(__s, __n); + } + catch (const std::exception &ex) + { + m_plogger->error_handler()(ex.what()); + } + catch (...) + { + m_plogger->error_handler()("Unknown exception"); + } + + return __n; +} diff --git a/include/spdlog/fmt/bundled/format.cc b/include/spdlog/fmt/bundled/format.cc index fd8855be..2e643fa6 100644 --- a/include/spdlog/fmt/bundled/format.cc +++ b/include/spdlog/fmt/bundled/format.cc @@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include // for std::ptrdiff_t +#include #if defined(_WIN32) && defined(__MINGW32__) # include @@ -444,12 +445,12 @@ namespace fmt { typedef internal::NamedArg NamedArg; const NamedArg *named_arg = FMT_NULL; bool use_values = - args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE; + args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE_ARG; if (use_values) { for (unsigned i = 0;/*nothing*/; ++i) { internal::Arg::Type arg_type = args.type(i); switch (arg_type) { - case internal::Arg::NONE: + case internal::Arg::NONE_ARG: return; case internal::Arg::NAMED_ARG: named_arg = static_cast(args.values_[i].pointer); @@ -470,7 +471,7 @@ namespace fmt { } for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) { switch (args.args_[i].type) { - case internal::Arg::NONE: + case internal::Arg::NONE_ARG: return; case internal::Arg::NAMED_ARG: named_arg = static_cast(args.args_[i].pointer); @@ -493,7 +494,7 @@ namespace fmt { { Arg arg = args_[arg_index]; switch (arg.type) { - case Arg::NONE: + case Arg::NONE_ARG: error = "argument index out of range"; break; case Arg::NAMED_ARG: diff --git a/include/spdlog/fmt/bundled/format.h b/include/spdlog/fmt/bundled/format.h index e5e2e2ef..5dbc5684 100644 --- a/include/spdlog/fmt/bundled/format.h +++ b/include/spdlog/fmt/bundled/format.h @@ -1285,7 +1285,7 @@ struct Value enum Type { - NONE, NAMED_ARG, + NONE_ARG, NAMED_ARG, // Integer types should go first, INT, UINT, LONG_LONG, ULONG_LONG, BOOL, CHAR, LAST_INTEGER_TYPE = CHAR, // followed by floating-point types. @@ -1684,7 +1684,7 @@ class MakeArg: public Arg public: MakeArg() { - type = Arg::NONE; + type = Arg::NONE_ARG; } template @@ -1781,12 +1781,12 @@ public: { using internal::Arg; Arg arg; - bool use_values = type(MAX_PACKED_ARGS - 1) == Arg::NONE; + bool use_values = type(MAX_PACKED_ARGS - 1) == Arg::NONE_ARG; if (index < MAX_PACKED_ARGS) { Arg::Type arg_type = type(index); internal::Value &val = arg; - if (arg_type != Arg::NONE) + if (arg_type != Arg::NONE_ARG) val = use_values ? values_[index] : args_[index]; arg.type = arg_type; return arg; @@ -1795,12 +1795,12 @@ public: { // The index is greater than the number of arguments that can be stored // in values, so return a "none" argument. - arg.type = Arg::NONE; + arg.type = Arg::NONE_ARG; return arg; } for (unsigned i = MAX_PACKED_ARGS; i <= index; ++i) { - if (args_[i].type == Arg::NONE) + if (args_[i].type == Arg::NONE_ARG) return args_[i]; } return args_[index]; @@ -1961,7 +1961,7 @@ public: { switch (arg.type) { - case Arg::NONE: + case Arg::NONE_ARG: case Arg::NAMED_ARG: FMT_ASSERT(false, "invalid argument type"); break; @@ -2672,7 +2672,7 @@ static Value make(const T &value) template struct ArgArray { - typedef Arg Type[N + 1]; // +1 for the list end Arg::NONE + typedef Arg Type[N + 1]; // +1 for the list end Arg::NONE_ARG template static Arg make(const T &value) diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h index a2deb51d..095af8d5 100644 --- a/include/spdlog/logger.h +++ b/include/spdlog/logger.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -52,6 +53,14 @@ public: template void error(const T&); template void critical(const T&); + ostream log(level::level_enum lvl); + ostream trace(); + ostream debug(); + ostream info(); + ostream warn(); + ostream error(); + ostream critical(); + bool should_log(level::level_enum) const; void set_level(level::level_enum); level::level_enum level() const; @@ -71,6 +80,9 @@ public: const std::vector& sinks() const; protected: + template + friend class basic_streambuf; + virtual void _sink_it(details::log_msg&); virtual void _set_pattern(const std::string&); virtual void _set_formatter(formatter_ptr); @@ -92,3 +104,4 @@ protected: } #include +#include diff --git a/include/spdlog/stream.h b/include/spdlog/stream.h new file mode 100644 index 00000000..dd5bce21 --- /dev/null +++ b/include/spdlog/stream.h @@ -0,0 +1,80 @@ +// +// Copyright(c) 2017 Benoit Leforestier. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include + +namespace spdlog +{ + class logger; + + template > + class basic_streambuf : public std::basic_streambuf + { + public: + typedef std::basic_streambuf _Mysb; + typedef CharT char_type; + typedef Traits traits_type; + typedef typename Traits::int_type int_type; + typedef typename Traits::pos_type pos_type; + typedef typename Traits::off_type off_type; + + basic_streambuf(logger* plogger, level::level_enum lvl); + basic_streambuf(basic_streambuf&& rhs); + virtual ~basic_streambuf(); + + basic_streambuf(const basic_streambuf&) = delete; + basic_streambuf& operator=(const basic_streambuf&) = delete; + basic_streambuf& operator=(basic_streambuf&&) = delete; + + protected: + virtual std::streamsize xsputn(const char_type* __s, std::streamsize __n) override; + + private: + logger* m_plogger; + details::log_msg m_log_msg; + }; + + template > + class basic_ostream : public std::basic_ostream < CharT, Traits > + { + public: + typedef std::basic_ostream _Mybase; + typedef CharT char_type; + typedef Traits traits_type; + typedef typename Traits::int_type int_type; + typedef typename Traits::pos_type pos_type; + typedef typename Traits::off_type off_type; + + basic_ostream() : _Mybase(&m_rdbuf), m_rdbuf(nullptr, level::critical) {} + basic_ostream(spdlog::logger* plogger, level::level_enum lvl) : _Mybase(&m_rdbuf), m_rdbuf(plogger, lvl) {} + basic_ostream(basic_ostream&& rhs_stream) : _Mybase(&m_rdbuf), m_rdbuf(std::move(rhs_stream.m_rdbuf)) {} + virtual ~basic_ostream() = default; + + basic_ostream(const basic_ostream&) = delete; + basic_ostream& operator=(const basic_ostream&) = delete; + + basic_ostream& operator=(basic_ostream&& rhs_stream) { swap(rhs_stream); return *this; } + + basic_streambuf* rdbuf() const + { + basic_streambuf* pBuf = _Mybase::rdbuf(); + if (!pBuf) + return const_cast*>(&m_rdbuf); + + return reinterpret_cast*>(pBuf); + } + + private: + basic_streambuf m_rdbuf; + }; + + typedef basic_streambuf streambuf; + typedef basic_streambuf wstreambuf; + + typedef basic_ostream ostream; + typedef basic_ostream wostream; +}