From e2e3df9013ca514652d8104fede59a97378cc612 Mon Sep 17 00:00:00 2001 From: gabime Date: Fri, 5 Apr 2019 16:44:17 +0300 Subject: [PATCH] static lib wip --- example/example.cpp | 11 +- include/spdlog/common.h | 7 + include/spdlog/details/async_logger_impl.h | 18 +- include/spdlog/details/fmt_helper.h | 1 + include/spdlog/details/log_msg.h | 26 +- include/spdlog/details/logger_impl.h | 435 ------------- include/spdlog/details/os.h | 369 +---------- include/spdlog/details/pattern_formatter.h | 1 - include/spdlog/details/registry.h | 1 + include/spdlog/logger.h | 724 +++++++++------------ include/spdlog/sinks/sink.h | 38 +- src/log_msg.cpp | 29 + src/logger.cpp | 187 ++++++ src/os.cpp | 406 ++++++++++++ src/sink.cpp | 31 + 15 files changed, 1029 insertions(+), 1255 deletions(-) delete mode 100644 include/spdlog/details/logger_impl.h create mode 100644 src/log_msg.cpp create mode 100644 src/logger.cpp create mode 100644 src/os.cpp create mode 100644 src/sink.cpp diff --git a/example/example.cpp b/example/example.cpp index b42c9440..e7d9559d 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -10,16 +10,15 @@ #include "spdlog/logger.h" -namespace spdlog { -class logger; -} spdlog::logger *get_logger(); - - int main(int, char *[]) { auto *l = get_logger(); - l->info("HELLO"); + l->info("HELLO {}", "World"); + l->warn("SOME WARNINNG"); + l->error("Some {}", "error"); + + } \ No newline at end of file diff --git a/include/spdlog/common.h b/include/spdlog/common.h index 71273d30..f76aedea 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -27,6 +27,13 @@ #include "spdlog/fmt/fmt.h" +#ifdef SPDLOG_HEADER_ONLY +#define SPDLOG_INLINE inline +#else +#define SPDLOG_INLINE +#endif + + // visual studio upto 2013 does not support noexcept nor constexpr #if defined(_MSC_VER) && (_MSC_VER < 1900) #define SPDLOG_NOEXCEPT throw() diff --git a/include/spdlog/details/async_logger_impl.h b/include/spdlog/details/async_logger_impl.h index aafcae65..88bd5e7c 100644 --- a/include/spdlog/details/async_logger_impl.h +++ b/include/spdlog/details/async_logger_impl.h @@ -79,7 +79,14 @@ inline void spdlog::async_logger::backend_log_(const details::log_msg &incoming_ } } } - SPDLOG_CATCH_AND_HANDLE + catch (const std::exception &ex) + { + err_handler_(ex.what()); + } + catch (...) + { + err_handler_("Unknown exception in logger"); + } if (should_flush_(incoming_log_msg)) { @@ -96,7 +103,14 @@ inline void spdlog::async_logger::backend_flush_() sink->flush(); } } - SPDLOG_CATCH_AND_HANDLE + catch (const std::exception &ex) + { + err_handler_(ex.what()); + } + catch (...) + { + err_handler_("Unknown exception in logger"); + } } inline std::shared_ptr spdlog::async_logger::clone(std::string new_name) diff --git a/include/spdlog/details/fmt_helper.h b/include/spdlog/details/fmt_helper.h index d76aac45..16035555 100644 --- a/include/spdlog/details/fmt_helper.h +++ b/include/spdlog/details/fmt_helper.h @@ -7,6 +7,7 @@ #include #include #include "spdlog/fmt/fmt.h" +#include "spdlog/common.h" // Some fmt helpers to efficiently format and pad ints and strings namespace spdlog { diff --git a/include/spdlog/details/log_msg.h b/include/spdlog/details/log_msg.h index 69422ba3..1507eac1 100644 --- a/include/spdlog/details/log_msg.h +++ b/include/spdlog/details/log_msg.h @@ -6,36 +6,16 @@ #pragma once #include "spdlog/common.h" -#include "spdlog/details/os.h" - #include -#include namespace spdlog { namespace details { struct log_msg { - log_msg(source_loc loc, const std::string *loggers_name, level::level_enum lvl, string_view_t view) - : logger_name(loggers_name) - , level(lvl) -#ifndef SPDLOG_NO_DATETIME - , time(os::now()) -#endif - -#ifndef SPDLOG_NO_THREAD_ID - , thread_id(os::thread_id()) -#endif - , source(loc) - , payload(view) - { - } - - log_msg(const std::string *loggers_name, level::level_enum lvl, string_view_t view) - : log_msg(source_loc{}, loggers_name, lvl, view) - { - } - + log_msg(source_loc loc, const std::string *loggers_name, level::level_enum lvl, string_view_t view); + + log_msg(const std::string *loggers_name, level::level_enum lvl, string_view_t view); log_msg(const log_msg &other) = default; const std::string *logger_name{nullptr}; diff --git a/include/spdlog/details/logger_impl.h b/include/spdlog/details/logger_impl.h deleted file mode 100644 index 80245ba1..00000000 --- a/include/spdlog/details/logger_impl.h +++ /dev/null @@ -1,435 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include "spdlog/details/fmt_helper.h" - -#include -#include - -//#define SPDLOG_CATCH_AND_HANDLE \ -// catch (const std::exception &ex) \ -// { \ -// err_handler_(ex.what()); \ -// } \ -// catch (...) \ -// { \ -// err_handler_("Unknown exception in logger"); \ -// } - -// create logger with given name, sinks and the default pattern formatter -// all other ctors will call this one -//template -//inline spdlog::logger::logger(std::string logger_name, It begin, It end) -// : name_(std::move(logger_name)) -// , sinks_(begin, end) -//{ -//} - -//// ctor with sinks as init list -//inline spdlog::logger::logger(std::string logger_name, sinks_init_list sinks_list) -// : logger(std::move(logger_name), sinks_list.begin(), sinks_list.end()) -//{ -//} - -// ctor with single sink -//inline spdlog::logger::logger(std::string logger_name, spdlog::sink_ptr single_sink) -// : logger(std::move(logger_name), {std::move(single_sink)}) -//{ -//} - -//inline spdlog::logger::~logger() = default; - -//inline void spdlog::logger::set_formatter(std::unique_ptr f) -//{ -// for (auto &sink : sinks_) -// { -// sink->set_formatter(f->clone()); -// } -//} - -//inline void spdlog::logger::set_pattern(std::string pattern, pattern_time_type time_type) -//{ -// auto new_formatter = details::make_unique(std::move(pattern), time_type); -// set_formatter(std::move(new_formatter)); -//} - -//template -//inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const char *fmt, const Args &... args) -//{ -// if (!should_log(lvl)) -// { -// return; -// } -// -// try -// { -// using details::fmt_helper::to_string_view; -// fmt::memory_buffer buf; -// fmt::format_to(buf, fmt, args...); -// details::log_msg log_msg(source, &name_, lvl, to_string_view(buf)); -// sink_it_(log_msg); -// } -// SPDLOG_CATCH_AND_HANDLE -//} - -//template -//inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args) -//{ -// log(source_loc{}, lvl, fmt, args...); -//} - -//inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const char *msg) -//{ -// if (!should_log(lvl)) -// { -// return; -// } -// -// try -// { -// details::log_msg log_msg(source, &name_, lvl, spdlog::string_view_t(msg)); -// sink_it_(log_msg); -// } -// SPDLOG_CATCH_AND_HANDLE -//} - -//inline void spdlog::logger::log(level::level_enum lvl, const char *msg) -//{ -// log(source_loc{}, lvl, msg); -//} - -//template -//inline void spdlog::logger::log(level::level_enum lvl, const T &msg) -//{ -// log(source_loc{}, lvl, msg); -//} -// -//template::value, T>::type *> -//inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const T &msg) -//{ -// if (!should_log(lvl)) -// { -// return; -// } -// try -// { -// details::log_msg log_msg(source, &name_, lvl, msg); -// sink_it_(log_msg); -// } -// SPDLOG_CATCH_AND_HANDLE -//} - -//template::value, T>::type *> -//inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const T &msg) -//{ -// if (!should_log(lvl)) -// { -// return; -// } -// try -// { -// using details::fmt_helper::to_string_view; -// fmt::memory_buffer buf; -// fmt::format_to(buf, "{}", msg); -// details::log_msg log_msg(source, &name_, lvl, to_string_view(buf)); -// sink_it_(log_msg); -// } -// SPDLOG_CATCH_AND_HANDLE -//} - -//template -//inline void spdlog::logger::trace(const char *fmt, const Args &... args) -//{ -// log(level::trace, fmt, args...); -//} -// -//template -//inline void spdlog::logger::debug(const char *fmt, const Args &... args) -//{ -// log(level::debug, fmt, args...); -//} -// -//template -//inline void spdlog::logger::info(const char *fmt, const Args &... args) -//{ -// log(level::info, fmt, args...); -//} -// -//template -//inline void spdlog::logger::warn(const char *fmt, const Args &... args) -//{ -// log(level::warn, fmt, args...); -//} -// -//template -//inline void spdlog::logger::error(const char *fmt, const Args &... args) -//{ -// log(level::err, fmt, args...); -//} -// -//template -//inline void spdlog::logger::critical(const char *fmt, const Args &... args) -//{ -// log(level::critical, fmt, args...); -//} -// -//template -//inline void spdlog::logger::trace(const T &msg) -//{ -// log(level::trace, msg); -//} -// -//template -//inline void spdlog::logger::debug(const T &msg) -//{ -// log(level::debug, msg); -//} -// -//template -//inline void spdlog::logger::info(const T &msg) -//{ -// log(level::info, msg); -//} -// -//template -//inline void spdlog::logger::warn(const T &msg) -//{ -// log(level::warn, msg); -//} -// -//template -//inline void spdlog::logger::error(const T &msg) -//{ -// log(level::err, msg); -//} -// -//template -//inline void spdlog::logger::critical(const T &msg) -//{ -// log(level::critical, msg); -//} - -//#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT - -//inline void wbuf_to_utf8buf(const fmt::wmemory_buffer &wbuf, fmt::memory_buffer &target) -//{ -// int wbuf_size = static_cast(wbuf.size()); -// if (wbuf_size == 0) -// { -// return; -// } -// -// auto result_size = ::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, NULL, 0, NULL, NULL); -// -// if (result_size > 0) -// { -// target.resize(result_size); -// ::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, &target.data()[0], result_size, NULL, NULL); -// } -// else -// { -// throw spdlog::spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError())); -// } -//} - -//template -//inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const wchar_t *fmt, const Args &... args) -//{ -// if (!should_log(lvl)) -// { -// return; -// } -// -// try -// { -// // format to wmemory_buffer and convert to utf8 -// using details::fmt_helper::to_string_view; -// fmt::wmemory_buffer wbuf; -// fmt::format_to(wbuf, fmt, args...); -// fmt::memory_buffer buf; -// wbuf_to_utf8buf(wbuf, buf); -// details::log_msg log_msg(source, &name_, lvl, to_string_view(buf)); -// sink_it_(log_msg); -// } -// SPDLOG_CATCH_AND_HANDLE -//} - -//template -//inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args) -//{ -// log(source_loc{}, lvl, fmt, args...); -//} -// -//template -//inline void spdlog::logger::trace(const wchar_t *fmt, const Args &... args) -//{ -// log(level::trace, fmt, args...); -//} -// -//template -//inline void spdlog::logger::debug(const wchar_t *fmt, const Args &... args) -//{ -// log(level::debug, fmt, args...); -//} -// -//template -//inline void spdlog::logger::info(const wchar_t *fmt, const Args &... args) -//{ -// log(level::info, fmt, args...); -//} -// -//template -//inline void spdlog::logger::warn(const wchar_t *fmt, const Args &... args) -//{ -// log(level::warn, fmt, args...); -//} -// -//template -//inline void spdlog::logger::error(const wchar_t *fmt, const Args &... args) -//{ -// log(level::err, fmt, args...); -//} -// -//template -//inline void spdlog::logger::critical(const wchar_t *fmt, const Args &... args) -//{ -// log(level::critical, fmt, args...); -//} -// -//#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT - -// -// name and level -// -//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 void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler) -//{ -// err_handler_ = std::move(err_handler); -//} - -//inline spdlog::log_err_handler spdlog::logger::error_handler() const -//{ -// return err_handler_; -//} - -//inline void spdlog::logger::flush() -//{ -// try -// { -// flush_(); -// } -// SPDLOG_CATCH_AND_HANDLE -//} - -//inline void spdlog::logger::flush_on(level::level_enum log_level) -//{ -// flush_level_.store(log_level); -//} -// -//inline spdlog::level::level_enum spdlog::logger::flush_level() const -//{ -// return static_cast(flush_level_.load(std::memory_order_relaxed)); -//} -// -//inline bool spdlog::logger::should_flush_(const details::log_msg &msg) -//{ -// auto flush_level = flush_level_.load(std::memory_order_relaxed); -// return (msg.level >= flush_level) && (msg.level != level::off); -//} - -//inline spdlog::level::level_enum spdlog::logger::default_level() -//{ -// return static_cast(SPDLOG_ACTIVE_LEVEL); -//} - -//inline spdlog::level::level_enum spdlog::logger::level() const -//{ -// return static_cast(level_.load(std::memory_order_relaxed)); -//} - -//inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const -//{ -// return msg_level >= level_.load(std::memory_order_relaxed); -//} - -// -// protected virtual called at end of each user log call (if enabled) by the -// line_logger -// -//inline void spdlog::logger::sink_it_(details::log_msg &msg) -//{ -//#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) -// incr_msg_counter_(msg); -//#endif -// for (auto &sink : sinks_) -// { -// if (sink->should_log(msg.level)) -// { -// sink->log(msg); -// } -// } -// -// if (should_flush_(msg)) -// { -// flush_(); -// } -//} - -//inline void spdlog::logger::flush_() -//{ -// for (auto &sink : sinks_) -// { -// sink->flush(); -// } -//} - -//inline void spdlog::logger::default_err_handler_(const std::string &msg) -//{ -// auto now = time(nullptr); -// if (now - last_err_time_ < 60) -// { -// return; -// } -// last_err_time_ = now; -// auto tm_time = details::os::localtime(now); -// char date_buf[100]; -// std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time); -// fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), msg); -//} - -//inline void spdlog::logger::incr_msg_counter_(details::log_msg &msg) -//{ -// msg.msg_id = msg_counter_.fetch_add(1, std::memory_order_relaxed); -//} -// -//inline const std::vector &spdlog::logger::sinks() const -//{ -// return sinks_; -//} - -//inline std::vector &spdlog::logger::sinks() -//{ -// return sinks_; -//} - -//inline std::shared_ptr spdlog::logger::clone(std::string logger_name) -//{ -// auto cloned = std::make_shared(std::move(logger_name), sinks_.begin(), sinks_.end()); -// cloned->set_level(this->level()); -// cloned->flush_on(this->flush_level()); -// cloned->set_error_handler(this->error_handler()); -// return cloned; -//} diff --git a/include/spdlog/details/os.h b/include/spdlog/details/os.h index 646805e6..7b1e5219 100644 --- a/include/spdlog/details/os.h +++ b/include/spdlog/details/os.h @@ -18,95 +18,19 @@ #include #include -#ifdef _WIN32 - -#ifndef NOMINMAX -#define NOMINMAX // prevent windows redefining min/max -#endif - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include // _get_osfhandle and _isatty support -#include // _get_pid support -#include - -#ifdef __MINGW32__ -#include -#endif - -#else // unix - -#include -#include - -#ifdef __linux__ -#include //Use gettid() syscall under linux to get thread id - -#elif __FreeBSD__ -#include //Use thr_self() syscall under FreeBSD to get thread id -#endif - -#endif // unix - -#ifndef __has_feature // Clang - feature checking macros. -#define __has_feature(x) 0 // Compatibility with non-clang compilers. -#endif - namespace spdlog { namespace details { namespace os { -inline spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT -{ +spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT; -#if defined __linux__ && defined SPDLOG_CLOCK_COARSE - timespec ts; - ::clock_gettime(CLOCK_REALTIME_COARSE, &ts); - return std::chrono::time_point( - std::chrono::duration_cast(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec))); +std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT; -#else - return log_clock::now(); -#endif -} -inline std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT -{ +std::tm localtime() SPDLOG_NOEXCEPT; -#ifdef _WIN32 - std::tm tm; - localtime_s(&tm, &time_tt); -#else - std::tm tm; - localtime_r(&time_tt, &tm); -#endif - return tm; -} +std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT; -inline std::tm localtime() SPDLOG_NOEXCEPT -{ - std::time_t now_t = time(nullptr); - return localtime(now_t); -} - -inline std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT -{ - -#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() SPDLOG_NOEXCEPT -{ - std::time_t now_t = time(nullptr); - return gmtime(now_t); -} +std::tm gmtime() SPDLOG_NOEXCEPT; // eol definition #if !defined(SPDLOG_EOL) @@ -121,301 +45,56 @@ SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL; // folder separator #ifdef _WIN32 -SPDLOG_CONSTEXPR static const char folder_sep = '\\'; +const char folder_sep = '\\'; #else SPDLOG_CONSTEXPR static const char folder_sep = '/'; #endif -inline void prevent_child_fd(FILE *f) -{ - -#ifdef _WIN32 -#if !defined(__cplusplus_winrt) - auto file_handle = (HANDLE)_get_osfhandle(_fileno(f)); - if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) - throw spdlog_ex("SetHandleInformation failed", errno); -#endif -#else - auto fd = fileno(f); - if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - { - throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno); - } -#endif -} +void prevent_child_fd(FILE *f); // fopen_s on non windows for writing -inline bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) -{ -#ifdef _WIN32 -#ifdef SPDLOG_WCHAR_FILENAMES - *fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); -#else - *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); -#endif -#else // unix - *fp = fopen((filename.c_str()), mode.c_str()); -#endif +bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode); -#ifdef SPDLOG_PREVENT_CHILD_FD - if (*fp != nullptr) - { - prevent_child_fd(*fp); - } -#endif - return *fp == nullptr; -} +int remove(const filename_t &filename) SPDLOG_NOEXCEPT; -inline int remove(const filename_t &filename) SPDLOG_NOEXCEPT -{ -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) - return _wremove(filename.c_str()); -#else - return std::remove(filename.c_str()); -#endif -} - -inline int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT -{ -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) - return _wrename(filename1.c_str(), filename2.c_str()); -#else - return std::rename(filename1.c_str(), filename2.c_str()); -#endif -} +int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT; // Return if file exists -inline bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT -{ -#ifdef _WIN32 -#ifdef SPDLOG_WCHAR_FILENAMES - auto attribs = GetFileAttributesW(filename.c_str()); -#else - auto attribs = GetFileAttributesA(filename.c_str()); -#endif - return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY)); -#else // common linux/unix all have the stat system call - struct stat buffer; - return (stat(filename.c_str(), &buffer) == 0); -#endif -} +bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT; // Return file size according to open FILE* object -inline size_t filesize(FILE *f) -{ - if (f == nullptr) - { - throw spdlog_ex("Failed getting file size. fd is null"); - } -#if defined(_WIN32) && !defined(__CYGWIN__) - int fd = _fileno(f); -#if _WIN64 // 64 bits - __int64 ret = _filelengthi64(fd); - if (ret >= 0) - { - return static_cast(ret); - } - -#else // windows 32 bits - long ret = _filelength(fd); - if (ret >= 0) - { - return static_cast(ret); - } -#endif - -#else // unix - int fd = fileno(f); -// 64 bits(but not in osx or cygwin, where fstat64 is deprecated) -#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__) - struct stat64 st; - if (fstat64(fd, &st) == 0) - { - return static_cast(st.st_size); - } -#else // unix 32 bits or cygwin - struct stat st; - - if (fstat(fd, &st) == 0) - { - return static_cast(st.st_size); - } -#endif -#endif - throw spdlog_ex("Failed getting file size from fd", errno); -} +size_t filesize(FILE *f); // Return utc offset in minutes or throw spdlog_ex on failure -inline int utc_minutes_offset(const std::tm &tm = details::os::localtime()) -{ - -#ifdef _WIN32 -#if _WIN32_WINNT < _WIN32_WINNT_WS08 - TIME_ZONE_INFORMATION tzinfo; - auto rv = GetTimeZoneInformation(&tzinfo); -#else - DYNAMIC_TIME_ZONE_INFORMATION tzinfo; - auto rv = GetDynamicTimeZoneInformation(&tzinfo); -#endif - if (rv == TIME_ZONE_ID_INVALID) - throw spdlog::spdlog_ex("Failed getting timezone info. ", errno); - - int offset = -tzinfo.Bias; - if (tm.tm_isdst) - { - offset -= tzinfo.DaylightBias; - } - else - { - offset -= tzinfo.StandardBias; - } - return offset; -#else - -#if defined(sun) || defined(__sun) || defined(_AIX) - // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris - struct helper - { - static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), const std::tm &gmtm = details::os::gmtime()) - { - int local_year = localtm.tm_year + (1900 - 1); - int gmt_year = gmtm.tm_year + (1900 - 1); - - long int days = ( - // difference in day of year - localtm.tm_yday - - gmtm.tm_yday - - // + intervening leap days - + ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) + - ((local_year / 100 >> 2) - (gmt_year / 100 >> 2)) - - // + difference in years * 365 */ - + (long int)(local_year - gmt_year) * 365); - - long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour); - long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min); - long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec); - - return secs; - } - }; - - auto offset_seconds = helper::calculate_gmt_offset(tm); -#else - auto offset_seconds = tm.tm_gmtoff; -#endif - - return static_cast(offset_seconds / 60); -#endif -} +int utc_minutes_offset(const std::tm &tm = details::os::localtime()); // Return current thread id as size_t // It exists because the std::this_thread::get_id() is much slower(especially // under VS 2013) -inline size_t _thread_id() SPDLOG_NOEXCEPT -{ -#ifdef _WIN32 - return static_cast(::GetCurrentThreadId()); -#elif __linux__ -#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21) -#define SYS_gettid __NR_gettid -#endif - return static_cast(syscall(SYS_gettid)); -#elif __FreeBSD__ - long tid; - thr_self(&tid); - return static_cast(tid); -#elif __APPLE__ - uint64_t tid; - pthread_threadid_np(nullptr, &tid); - return static_cast(tid); -#else // Default to standard C++11 (other Unix) - return static_cast(std::hash()(std::this_thread::get_id())); -#endif -} +size_t _thread_id() SPDLOG_NOEXCEPT; // Return current thread id as size_t (from thread local storage) -inline size_t thread_id() SPDLOG_NOEXCEPT -{ -#if defined(SPDLOG_NO_TLS) - return _thread_id(); -#else // cache thread id in tls - static thread_local const size_t tid = _thread_id(); - return tid; -#endif -} +size_t thread_id() SPDLOG_NOEXCEPT; // This is avoid msvc issue in sleep_for that happens if the clock changes. // See https://github.com/gabime/spdlog/issues/609 -inline void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT -{ -#if defined(_WIN32) - ::Sleep(milliseconds); -#else - std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); -#endif -} +void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT; -// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) -#define SPDLOG_FILENAME_T(s) L##s -inline std::string filename_to_str(const filename_t &filename) -{ - std::wstring_convert, wchar_t> c; - return c.to_bytes(filename); -} -#else -#define SPDLOG_FILENAME_T(s) s -inline std::string filename_to_str(const filename_t &filename) -{ - return filename; -} -#endif +std::string filename_to_str(const filename_t &filename); -inline int pid() -{ - -#ifdef _WIN32 - return static_cast(::GetCurrentProcessId()); -#else - return static_cast(::getpid()); -#endif -} +int pid(); // Determine if the terminal supports colors // Source: https://github.com/agauniyal/rang/ -inline bool is_color_terminal() SPDLOG_NOEXCEPT -{ -#ifdef _WIN32 - return true; -#else - static constexpr const char *Terms[] = { - "ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"}; - - const char *env_p = std::getenv("TERM"); - if (env_p == nullptr) - { - return false; - } - - static const bool result = - std::any_of(std::begin(Terms), std::end(Terms), [&](const char *term) { return std::strstr(env_p, term) != nullptr; }); - return result; -#endif -} +bool is_color_terminal() SPDLOG_NOEXCEPT; // Detrmine if the terminal attached // Source: https://github.com/agauniyal/rang/ -inline bool in_terminal(FILE *file) SPDLOG_NOEXCEPT -{ - -#ifdef _WIN32 - return _isatty(_fileno(file)) != 0; -#else - return isatty(fileno(file)) != 0; -#endif -} +bool in_terminal(FILE *file) SPDLOG_NOEXCEPT; } // namespace os } // namespace details } // namespace spdlog + +#ifdef SPDLOG_HEADER_ONLY +#include "../src/os.cpp" +#endif diff --git a/include/spdlog/details/pattern_formatter.h b/include/spdlog/details/pattern_formatter.h index c0ad86e8..c9532293 100644 --- a/include/spdlog/details/pattern_formatter.h +++ b/include/spdlog/details/pattern_formatter.h @@ -4,7 +4,6 @@ // #pragma once - #include "spdlog/details/fmt_helper.h" #include "spdlog/details/log_msg.h" #include "spdlog/details/os.h" diff --git a/include/spdlog/details/registry.h b/include/spdlog/details/registry.h index ccd53955..ab4cf3f8 100644 --- a/include/spdlog/details/registry.h +++ b/include/spdlog/details/registry.h @@ -13,6 +13,7 @@ #include "spdlog/common.h" #include "spdlog/details/periodic_worker.h" #include "spdlog/logger.h" +#include "spdlog/details/pattern_formatter.h" #ifndef SPDLOG_DISABLE_DEFAULT_LOGGER // support for the default stdout color logger diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h index 446a5076..f178281d 100644 --- a/include/spdlog/logger.h +++ b/include/spdlog/logger.h @@ -15,469 +15,353 @@ // and send to its destination. // // The use of private formatter per sink provides the opportunity to cache some -// formatted data, -// and support customize format per each sink. +// formatted data, and support for different format per sink. + #include "spdlog/common.h" -#include "spdlog/formatter.h" -#include "spdlog/sinks/sink.h" -#include "spdlog/details/fmt_helper.h" +#include "spdlog/details/log_msg.h" + +//#include "spdlog/formatter.h" +//#include "spdlog/sinks/sink.h" #include #include #include -#ifndef SPDLOG_CATCH_AND_HANDLE - -#define SPDLOG_CATCH_AND_HANDLE \ - catch (const std::exception &ex) \ - { \ - err_handler_(ex.what()); \ - } \ - catch (...) \ - { \ - err_handler_("Unknown exception in logger"); \ - } - -#endif // ! SPDLOG_CATCH_AND_HANDLE - - -namespace spdlog { - -class logger +namespace spdlog { -public: - - template - logger(std::string name, It begin, It end) - : name_(std::move(name)) - , sinks_(begin, end) - {} - - logger(std::string name, sink_ptr single_sink) - : logger(std::move(name), {std::move(single_sink)}) - {} - logger(std::string name, sinks_init_list sinks) : - logger(std::move(name), sinks.begin(), sinks.end()) - {} - - virtual ~logger() = default; - - logger(const logger &) = delete; - logger &operator=(const logger &) = delete; - - template - void log(source_loc loc, level::level_enum lvl, const char *fmt, const Args &... args) - { - if (!should_log(lvl)) + class logger + { + public: + template + logger(std::string name, It begin, It end) + : name_(std::move(name)) + , sinks_(begin, end) { - return; } - try + logger(std::string name, sink_ptr single_sink) + : logger(std::move(name), {std::move(single_sink)}) { - using details::fmt_helper::to_string_view; - fmt::memory_buffer buf; - fmt::format_to(buf, fmt, args...); - details::log_msg log_msg(loc, &name_, lvl, to_string_view(buf)); - sink_it_(log_msg); } - SPDLOG_CATCH_AND_HANDLE - } - - template - void log(level::level_enum lvl, const char *fmt, const Args &... args) - { - log(source_loc{}, lvl, fmt, args...); - } - - - void log(source_loc loc, level::level_enum lvl, const char *msg) - { - if (!should_log(lvl)) + logger(std::string name, sinks_init_list sinks) + : logger(std::move(name), sinks.begin(), sinks.end()) { - return; } - try + virtual ~logger() = default; + + logger(const logger &) = delete; + logger &operator=(const logger &) = delete; + + template + void log(source_loc loc, level::level_enum lvl, const char *fmt, const Args &... args) { - details::log_msg log_msg(loc, &name_, lvl, spdlog::string_view_t(msg)); - sink_it_(log_msg); + if (!should_log(lvl)) + { + return; + } + + try + { + fmt::memory_buffer buf; + fmt::format_to(buf, fmt, args...); + details::log_msg log_msg(loc, &name_, lvl, string_view_t(buf.data(), buf.size())); + sink_it_(log_msg); + } + catch (const std::exception &ex) + { + err_handler_(ex.what()); + } + catch (...) + { + err_handler_("Unknown exception in logger"); + } } - SPDLOG_CATCH_AND_HANDLE - } - void log(level::level_enum lvl, const char *msg) - { - log(source_loc{}, lvl, msg); - } + template + void log(level::level_enum lvl, const char *fmt, const Args &... args) + { + log(source_loc{}, lvl, fmt, args...); + } + void log(source_loc loc, level::level_enum lvl, const char *msg); + void log(level::level_enum lvl, const char *msg); - + template + void trace(const char *fmt, const Args &... args) + { + log(level::trace, fmt, args...); + } - template - void trace(const char *fmt, const Args &... args) - { - log(level::trace, fmt, args...); - } + template + void debug(const char *fmt, const Args &... args) + { + log(level::debug, fmt, args...); + } + template + void info(const char *fmt, const Args &... args) + { + log(level::info, fmt, args...); + } - template - void debug(const char *fmt, const Args &... args) - { - log(level::debug, fmt, args...); - } + template + void warn(const char *fmt, const Args &... args) + { + log(level::warn, fmt, args...); + } + template + void error(const char *fmt, const Args &... args) + { + log(level::err, fmt, args...); + } - template - void info(const char *fmt, const Args &... args) - { - log(level::info, fmt, args...); - } - - template - void warn(const char *fmt, const Args &... args) - { - log(level::warn, fmt, args...); - } - - template - void error(const char *fmt, const Args &... args) - { - log(level::err, fmt, args...); - } - - template - void critical(const char *fmt, const Args &... args) - { - log(level::critical, fmt, args...); - } - + template + void critical(const char *fmt, const Args &... args) + { + log(level::critical, fmt, args...); + } #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT #ifndef _WIN32 #error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows #else - inline void wbuf_to_utf8buf(const fmt::wmemory_buffer &wbuf, fmt::memory_buffer &target) - { - int wbuf_size = static_cast(wbuf.size()); - if (wbuf_size == 0) + inline void wbuf_to_utf8buf(const fmt::wmemory_buffer &wbuf, fmt::memory_buffer &target) { - return; - } - - auto result_size = ::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, NULL, 0, NULL, NULL); - - if (result_size > 0) - { - target.resize(result_size); - ::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, &target.data()[0], result_size, NULL, NULL); - } - else - { - throw spdlog::spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError())); - } - } - - template - void log(source_loc source, level::level_enum lvl, const wchar_t *fmt, const Args &... args) - { - if (!should_log(lvl)) - { - return; - } - - try - { - // format to wmemory_buffer and convert to utf8 - using details::fmt_helper::to_string_view; - fmt::wmemory_buffer wbuf; - fmt::format_to(wbuf, fmt, args...); - fmt::memory_buffer buf; - wbuf_to_utf8buf(wbuf, buf); - details::log_msg log_msg(source, &name_, lvl, to_string_view(buf)); - sink_it_(log_msg); - } - SPDLOG_CATCH_AND_HANDLE - } - - template - void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args) - { - log(source_loc{}, lvl, fmt, args...); - } - - template - void trace(const wchar_t *fmt, const Args &... args) - { - log(level::trace, fmt, args...); - } - - template - void debug(const wchar_t *fmt, const Args &... args) - { - log(level::debug, fmt, args...); - } - - template - void info(const wchar_t *fmt, const Args &... args) - { - log(level::info, fmt, args...); - } - - template - void warn(const wchar_t *fmt, const Args &... args) - { - log(level::warn, fmt, args...); - } - - template - void error(const wchar_t *fmt, const Args &... args) - { - log(level::err, fmt, args...); - } - - template - void critical(const wchar_t *fmt, const Args &... args) - { - log(level::critical, fmt, args...); - } -#endif // _WIN32 -#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT - - template - void log(level::level_enum lvl, const T &msg) - { - log(source_loc{}, lvl, msg); - } - - // T can be statically converted to string_view - template::value, T>::type * = nullptr> - void log(source_loc loc, level::level_enum lvl, const T &msg) - { - if (!should_log(lvl)) - { - return; - } - try - { - details::log_msg log_msg(loc, &name_, lvl, msg); - sink_it_(log_msg); - } - SPDLOG_CATCH_AND_HANDLE - } - - // T cannot be statically converted to string_view - template::value, T>::type * = nullptr> - void log(source_loc loc, level::level_enum lvl, const T &msg) - { - if (!should_log(lvl)) - { - return; - } - try - { - using details::fmt_helper::to_string_view; - fmt::memory_buffer buf; - fmt::format_to(buf, "{}", msg); - details::log_msg log_msg(loc, &name_, lvl, to_string_view(buf)); - sink_it_(log_msg); - } - SPDLOG_CATCH_AND_HANDLE - } - - template - void trace(const T &msg) - { - log(level::trace, msg); - } - - template - void debug(const T &msg) - { - log(level::debug, msg); - } - - template - void info(const T &msg) - { - log(level::info, msg); - } - - template - void warn(const T &msg) - { - log(level::warn, msg); - } - - template - void error(const T &msg) - { - log(level::err, msg); - } - - template - void critical(const T &msg) - { - log(level::critical, msg); - } - - bool should_log(level::level_enum msg_level) const - { - return msg_level >= level_.load(std::memory_order_relaxed); - } - - void set_level(level::level_enum log_level) - { - level_.store(log_level); - } - - static level::level_enum default_level() - { - return static_cast(SPDLOG_ACTIVE_LEVEL); - } - - level::level_enum level() const - { - return static_cast(level_.load(std::memory_order_relaxed)); - } - - const std::string &name() const - { - return name_; - } - - // set formatting for the sinks in this logger. - // each sink will get a seperate instance of the formatter object. - void set_formatter(std::unique_ptr f) - { - for (auto &sink : sinks_) - { - sink->set_formatter(f->clone()); - } - } - - void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local) - { - auto new_formatter = details::make_unique(std::move(pattern), time_type); - set_formatter(std::move(new_formatter)); - } - - // flush functions - void flush() - { - try - { - flush_(); - } - SPDLOG_CATCH_AND_HANDLE - } - - void flush_on(level::level_enum log_level) - { - flush_level_.store(log_level); - } - - level::level_enum flush_level() const - { - return static_cast(flush_level_.load(std::memory_order_relaxed)); - } - - // sinks - const std::vector &sinks() const - { - return sinks_; - } - - std::vector &sinks() - { - return sinks_; - } - - // error handler - void set_error_handler(log_err_handler err_handler) - { - err_handler_ = std::move(err_handler); - } - - log_err_handler error_handler() const - { - return err_handler_; - } - - // create new logger with same sinks and configuration. - virtual std::shared_ptr clone(std::string logger_name) - { - auto cloned = std::make_shared(std::move(logger_name), sinks_.begin(), sinks_.end()); - cloned->set_level(this->level()); - cloned->flush_on(this->flush_level()); - cloned->set_error_handler(this->error_handler()); - return cloned; - } - - -protected: - virtual void sink_it_(details::log_msg &msg) - { -#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) - incr_msg_counter_(msg); -#endif - for (auto &sink : sinks_) - { - if (sink->should_log(msg.level)) + int wbuf_size = static_cast(wbuf.size()); + if (wbuf_size == 0) { - sink->log(msg); + return; + } + + auto result_size = ::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, NULL, 0, NULL, NULL); + + if (result_size > 0) + { + target.resize(result_size); + ::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, &target.data()[0], result_size, NULL, NULL); + } + else + { + throw spdlog::spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError())); } } - if (should_flush_(msg)) + template + void log(source_loc source, level::level_enum lvl, const wchar_t *fmt, const Args &... args) { - flush_(); - } - } + if (!should_log(lvl)) + { + return; + } - virtual void flush_() - { - for (auto &sink : sinks_) + try + { + // format to wmemory_buffer and convert to utf8 + using details::fmt_helper::to_string_view; + fmt::wmemory_buffer wbuf; + fmt::format_to(wbuf, fmt, args...); + fmt::memory_buffer buf; + wbuf_to_utf8buf(wbuf, buf); + details::log_msg log_msg(source, &name_, lvl, to_string_view(buf)); + sink_it_(log_msg); + } + SPDLOG_CATCH_AND_HANDLE + } + + template + void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args) { - sink->flush(); + log(source_loc{}, lvl, fmt, args...); } - } - bool should_flush_(const details::log_msg &msg) - { - auto flush_level = flush_level_.load(std::memory_order_relaxed); - return (msg.level >= flush_level) && (msg.level != level::off); - } - - // default error handler. - // print the error to stderr with the max rate of 1 message/minute. - void default_err_handler_(const std::string &msg) - { - auto now = time(nullptr); - if (now - last_err_time_ < 60) + template + void trace(const wchar_t *fmt, const Args &... args) { - return; + log(level::trace, fmt, args...); } - last_err_time_ = now; - auto tm_time = details::os::localtime(now); - char date_buf[100]; - std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time); - fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), msg); - } - // increment the message count (only if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)) - void incr_msg_counter_(details::log_msg &msg) - { - msg.msg_id = msg_counter_.fetch_add(1, std::memory_order_relaxed); - } + template + void debug(const wchar_t *fmt, const Args &... args) + { + log(level::debug, fmt, args...); + } - const std::string name_; - std::vector sinks_; - spdlog::level_t level_{spdlog::logger::default_level()}; - spdlog::level_t flush_level_{level::off}; - log_err_handler err_handler_{[this](const std::string &msg) { this->default_err_handler_(msg); }}; - std::atomic last_err_time_{0}; - std::atomic msg_counter_{1}; -}; + template + void info(const wchar_t *fmt, const Args &... args) + { + log(level::info, fmt, args...); + } + + template + void warn(const wchar_t *fmt, const Args &... args) + { + log(level::warn, fmt, args...); + } + + template + void error(const wchar_t *fmt, const Args &... args) + { + log(level::err, fmt, args...); + } + + template + void critical(const wchar_t *fmt, const Args &... args) + { + log(level::critical, fmt, args...); + } +#endif // _WIN32 +#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT + + template + void log(level::level_enum lvl, const T &msg) + { + log(source_loc{}, lvl, msg); + } + + // T can be statically converted to string_view + template::value, T>::type * = nullptr> + void log(source_loc loc, level::level_enum lvl, const T &msg) + { + if (!should_log(lvl)) + { + return; + } + try + { + details::log_msg log_msg(loc, &name_, lvl, msg); + sink_it_(log_msg); + } + catch (const std::exception &ex) + { + err_handler_(ex.what()); + } + catch (...) + { + err_handler_("Unknown exception in logger"); + } + } + + // T cannot be statically converted to string_view + template::value, T>::type * = nullptr> + void log(source_loc loc, level::level_enum lvl, const T &msg) + { + if (!should_log(lvl)) + { + return; + } + try + { + using details::fmt_helper::to_string_view; + fmt::memory_buffer buf; + fmt::format_to(buf, "{}", msg); + details::log_msg log_msg(loc, &name_, lvl, to_string_view(buf)); + sink_it_(log_msg); + } + catch (const std::exception &ex) + { + err_handler_(ex.what()); + } + catch (...) + { + err_handler_("Unknown exception in logger"); + } + } + + template + void trace(const T &msg) + { + log(level::trace, msg); + } + + template + void debug(const T &msg) + { + log(level::debug, msg); + } + + template + void info(const T &msg) + { + log(level::info, msg); + } + + template + void warn(const T &msg) + { + log(level::warn, msg); + } + + template + void error(const T &msg) + { + log(level::err, msg); + } + + template + void critical(const T &msg) + { + log(level::critical, msg); + } + + bool should_log(level::level_enum msg_level) const; + + void set_level(level::level_enum log_level); + + static level::level_enum default_level(); + + level::level_enum level() const; + + const std::string &name() const; + + // set formatting for the sinks in this logger. + // each sink will get a seperate instance of the formatter object. + void set_formatter(std::unique_ptr f); + + void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local); + + // flush functions + void flush(); + void flush_on(level::level_enum log_level); + level::level_enum flush_level() const; + + // sinks + const std::vector &sinks() const; + + std::vector &sinks(); + + // error handler + void set_error_handler(log_err_handler err_handler); + + log_err_handler error_handler() const; + + // create new logger with same sinks and configuration. + virtual std::shared_ptr clone(std::string logger_name); + + protected: + virtual void sink_it_(details::log_msg &msg); + + virtual void flush_(); + bool should_flush_(const details::log_msg &msg); + + // default error handler. + // print the error to stderr with the max rate of 1 message/minute. + void default_err_handler_(const std::string &msg); + + // increment the message count (only if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)) + void incr_msg_counter_(details::log_msg &msg); + + const std::string name_; + std::vector sinks_; + spdlog::level_t level_{spdlog::logger::default_level()}; + spdlog::level_t flush_level_{level::off}; + log_err_handler err_handler_{[this](const std::string &msg) { this->default_err_handler_(msg); }}; + std::atomic last_err_time_{0}; + std::atomic msg_counter_{1}; + }; } // namespace spdlog +#ifdef SPDLOG_HEADER_ONLY +#include "../src/logger.cpp" +#endif // SPDLOG_HEADER_ONLY diff --git a/include/spdlog/sinks/sink.h b/include/spdlog/sinks/sink.h index d8325233..11c54fb6 100644 --- a/include/spdlog/sinks/sink.h +++ b/include/spdlog/sinks/sink.h @@ -6,49 +6,41 @@ #pragma once #include "spdlog/details/log_msg.h" -#include "spdlog/details/pattern_formatter.h" +//#include "spdlog/details/pattern_formatter.h" #include "spdlog/formatter.h" namespace spdlog { + namespace sinks { class sink { public: - sink() = default; - - explicit sink(std::unique_ptr formatter) - : formatter_{std::move(formatter)} - { - } + sink(); + explicit sink(std::unique_ptr formatter); virtual ~sink() = default; virtual void log(const details::log_msg &msg) = 0; virtual void flush() = 0; virtual void set_pattern(const std::string &pattern) = 0; virtual void set_formatter(std::unique_ptr sink_formatter) = 0; - bool should_log(level::level_enum msg_level) const - { - return msg_level >= level_.load(std::memory_order_relaxed); - } - - void set_level(level::level_enum log_level) - { - level_.store(log_level); - } - - level::level_enum level() const - { - return static_cast(level_.load(std::memory_order_relaxed)); - } + bool should_log(level::level_enum msg_level) const; + + void set_level(level::level_enum log_level); + + level::level_enum level() const; protected: // sink log level - default is all level_t level_{level::trace}; - // sink formatter - default is full format - std::unique_ptr formatter_{details::make_unique()}; + // sink formatter + std::unique_ptr formatter_; }; } // namespace sinks } // namespace spdlog + +#ifdef SPDLOG_HEADER_ONLY +#include "../src/sink.cpp" +#endif // SPDLOG_HEADER_ONLY diff --git a/src/log_msg.cpp b/src/log_msg.cpp new file mode 100644 index 00000000..241bcf5d --- /dev/null +++ b/src/log_msg.cpp @@ -0,0 +1,29 @@ +#include "spdlog/details/os.h" +#include "spdlog/sinks/sink.h" + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/details/log_msg.h" +#endif + + + +SPDLOG_INLINE spdlog::details::log_msg::log_msg( + spdlog::source_loc loc, const std::string *loggers_name, spdlog::level::level_enum lvl, spdlog::string_view_t view) + : logger_name(loggers_name) + , level(lvl) +#ifndef SPDLOG_NO_DATETIME + , time(os::now()) +#endif + +#ifndef SPDLOG_NO_THREAD_ID + , thread_id(os::thread_id()) +#endif + , source(loc) + , payload(view) +{ +} + +SPDLOG_INLINE spdlog::details::log_msg::log_msg(const std::string *loggers_name, spdlog::level::level_enum lvl, spdlog::string_view_t view) + : log_msg(source_loc{}, loggers_name, lvl, view) +{ +} \ No newline at end of file diff --git a/src/logger.cpp b/src/logger.cpp new file mode 100644 index 00000000..88ff4d3b --- /dev/null +++ b/src/logger.cpp @@ -0,0 +1,187 @@ +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/logger.h" +#endif + +#include "spdlog/sinks/sink.h" +#include "spdlog/details/pattern_formatter.h" + +// public methods +SPDLOG_INLINE void spdlog::logger::log(spdlog::source_loc loc, spdlog::level::level_enum lvl, const char *msg) +{ + if (!should_log(lvl)) + { + return; + } + + try + { + details::log_msg log_msg(loc, &name_, lvl, spdlog::string_view_t(msg)); + sink_it_(log_msg); + } + catch (const std::exception &ex) + { + err_handler_(ex.what()); + } + catch (...) + { + err_handler_("Unknown exception in logger"); + } +} + +SPDLOG_INLINE void spdlog::logger::log(level::level_enum lvl, const char *msg) +{ + log(source_loc{}, lvl, msg); +} + +SPDLOG_INLINE bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const +{ + return msg_level >= level_.load(std::memory_order_relaxed); +} + +SPDLOG_INLINE void spdlog::logger::set_level(spdlog::level::level_enum log_level) +{ + level_.store(log_level); +} + +SPDLOG_INLINE spdlog::level::level_enum spdlog::logger::default_level() +{ + return static_cast(SPDLOG_ACTIVE_LEVEL); +} + +SPDLOG_INLINE spdlog::level::level_enum spdlog::logger::level() const +{ + return static_cast(level_.load(std::memory_order_relaxed)); +} + +SPDLOG_INLINE const std::string &spdlog::logger::name() const +{ + return name_; +} + +// set formatting for the sinks in this logger. +// each sink will get a seperate instance of the formatter object. +SPDLOG_INLINE void spdlog::logger::set_formatter(std::unique_ptr f) +{ + for (auto &sink : sinks_) + { + sink->set_formatter(f->clone()); + } +} + +SPDLOG_INLINE void spdlog::logger::set_pattern(std::string pattern, spdlog::pattern_time_type time_type) +{ + auto new_formatter = details::make_unique(std::move(pattern), time_type); + set_formatter(std::move(new_formatter)); +} + +// flush functions +SPDLOG_INLINE void spdlog::logger::flush() +{ + try + { + flush_(); + } + catch (const std::exception &ex) + { + err_handler_(ex.what()); + } + catch (...) + { + err_handler_("Unknown exception in logger"); + } +} + +SPDLOG_INLINE void spdlog::logger::flush_on(level::level_enum log_level) +{ + flush_level_.store(log_level); +} + +SPDLOG_INLINE spdlog::level::level_enum spdlog::logger::flush_level() const +{ + return static_cast(flush_level_.load(std::memory_order_relaxed)); +} + +// sinks +SPDLOG_INLINE const std::vector &spdlog::logger::sinks() const +{ + return sinks_; +} + +SPDLOG_INLINE std::vector &spdlog::logger::sinks() +{ + return sinks_; +} + +// error handler +SPDLOG_INLINE void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler) +{ + err_handler_ = std::move(err_handler); +} + +SPDLOG_INLINE spdlog::log_err_handler spdlog::logger::error_handler() const +{ + return err_handler_; +} + +// create new logger with same sinks and configuration. +SPDLOG_INLINE std::shared_ptr spdlog::logger::clone(std::string logger_name) +{ + auto cloned = std::make_shared(std::move(logger_name), sinks_.begin(), sinks_.end()); + cloned->set_level(this->level()); + cloned->flush_on(this->flush_level()); + cloned->set_error_handler(this->error_handler()); + return cloned; +} + +// protected methods +SPDLOG_INLINE void spdlog::logger::sink_it_(spdlog::details::log_msg &msg) +{ +#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) + incr_msg_counter_(msg); +#endif + for (auto &sink : sinks_) + { + if (sink->should_log(msg.level)) + { + sink->log(msg); + } + } + + if (should_flush_(msg)) + { + flush_(); + } +} + +SPDLOG_INLINE void spdlog::logger::flush_() +{ + for (auto &sink : sinks_) + { + sink->flush(); + } +} + +SPDLOG_INLINE bool spdlog::logger::should_flush_(const spdlog::details::log_msg &msg) +{ + auto flush_level = flush_level_.load(std::memory_order_relaxed); + return (msg.level >= flush_level) && (msg.level != level::off); +} + +SPDLOG_INLINE void spdlog::logger::default_err_handler_(const std::string &msg) +{ + auto now = time(nullptr); + if (now - last_err_time_ < 60) + { + return; + } + last_err_time_ = now; + auto tm_time = details::os::localtime(now); + char date_buf[100]; + std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time); + fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), msg); +} + +SPDLOG_INLINE void spdlog::logger::incr_msg_counter_(spdlog::details::log_msg &msg) +{ + msg.msg_id = msg_counter_.fetch_add(1, std::memory_order_relaxed); +} diff --git a/src/os.cpp b/src/os.cpp new file mode 100644 index 00000000..4a8248f0 --- /dev/null +++ b/src/os.cpp @@ -0,0 +1,406 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// +#pragma once + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/details/os.h" +#include "spdlog/common.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + +#ifndef NOMINMAX +#define NOMINMAX // prevent windows redefining min/max +#endif + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include // _get_osfhandle and _isatty support +#include // _get_pid support +#include + +#ifdef __MINGW32__ +#include +#endif + +#else // unix + +#include +#include + +#ifdef __linux__ +#include //Use gettid() syscall under linux to get thread id + +#elif __FreeBSD__ +#include //Use thr_self() syscall under FreeBSD to get thread id +#endif + +#endif // unix + +#ifndef __has_feature // Clang - feature checking macros. +#define __has_feature(x) 0 // Compatibility with non-clang compilers. +#endif + +namespace spdlog { +namespace details { +namespace os { + +SPDLOG_INLINE spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT +{ + +#if defined __linux__ && defined SPDLOG_CLOCK_COARSE + timespec ts; + ::clock_gettime(CLOCK_REALTIME_COARSE, &ts); + return std::chrono::time_point( + std::chrono::duration_cast(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec))); + +#else + return log_clock::now(); +#endif +} +SPDLOG_INLINE std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT +{ + +#ifdef _WIN32 + std::tm tm; + localtime_s(&tm, &time_tt); +#else + std::tm tm; + localtime_r(&time_tt, &tm); +#endif + return tm; +} + +SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT +{ + std::time_t now_t = time(nullptr); + return localtime(now_t); +} + +SPDLOG_INLINE std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT +{ + +#ifdef _WIN32 + std::tm tm; + gmtime_s(&tm, &time_tt); +#else + std::tm tm; + gmtime_r(&time_tt, &tm); +#endif + return tm; +} + +SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT +{ + std::time_t now_t = time(nullptr); + return gmtime(now_t); +} + +SPDLOG_INLINE void prevent_child_fd(FILE *f) +{ + +#ifdef _WIN32 +#if !defined(__cplusplus_winrt) + auto file_handle = (HANDLE)_get_osfhandle(_fileno(f)); + if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) + throw spdlog_ex("SetHandleInformation failed", errno); +#endif +#else + auto fd = fileno(f); + if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) + { + throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno); + } +#endif +} + +// fopen_s on non windows for writing +SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) +{ +#ifdef _WIN32 +#ifdef SPDLOG_WCHAR_FILENAMES + *fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); +#else + *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); +#endif +#else // unix + *fp = fopen((filename.c_str()), mode.c_str()); +#endif + +#ifdef SPDLOG_PREVENT_CHILD_FD + if (*fp != nullptr) + { + prevent_child_fd(*fp); + } +#endif + return *fp == nullptr; +} + +SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT +{ +#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) + return _wremove(filename.c_str()); +#else + return std::remove(filename.c_str()); +#endif +} + +SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT +{ +#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) + return _wrename(filename1.c_str(), filename2.c_str()); +#else + return std::rename(filename1.c_str(), filename2.c_str()); +#endif +} + +// Return if file exists +SPDLOG_INLINE bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT +{ +#ifdef _WIN32 +#ifdef SPDLOG_WCHAR_FILENAMES + auto attribs = GetFileAttributesW(filename.c_str()); +#else + auto attribs = GetFileAttributesA(filename.c_str()); +#endif + return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY)); +#else // common linux/unix all have the stat system call + struct stat buffer; + return (stat(filename.c_str(), &buffer) == 0); +#endif +} + +// Return file size according to open FILE* object +SPDLOG_INLINE size_t filesize(FILE *f) +{ + if (f == nullptr) + { + throw spdlog_ex("Failed getting file size. fd is null"); + } +#if defined(_WIN32) && !defined(__CYGWIN__) + int fd = _fileno(f); +#if _WIN64 // 64 bits + __int64 ret = _filelengthi64(fd); + if (ret >= 0) + { + return static_cast(ret); + } + +#else // windows 32 bits + long ret = _filelength(fd); + if (ret >= 0) + { + return static_cast(ret); + } +#endif + +#else // unix + int fd = fileno(f); +// 64 bits(but not in osx or cygwin, where fstat64 is deprecated) +#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__) + struct stat64 st; + if (fstat64(fd, &st) == 0) + { + return static_cast(st.st_size); + } +#else // unix 32 bits or cygwin + struct stat st; + + if (fstat(fd, &st) == 0) + { + return static_cast(st.st_size); + } +#endif +#endif + throw spdlog_ex("Failed getting file size from fd", errno); +} + +// Return utc offset in minutes or throw spdlog_ex on failure +SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) +{ + +#ifdef _WIN32 +#if _WIN32_WINNT < _WIN32_WINNT_WS08 + TIME_ZONE_INFORMATION tzinfo; + auto rv = GetTimeZoneInformation(&tzinfo); +#else + DYNAMIC_TIME_ZONE_INFORMATION tzinfo; + auto rv = GetDynamicTimeZoneInformation(&tzinfo); +#endif + if (rv == TIME_ZONE_ID_INVALID) + throw spdlog::spdlog_ex("Failed getting timezone info. ", errno); + + int offset = -tzinfo.Bias; + if (tm.tm_isdst) + { + offset -= tzinfo.DaylightBias; + } + else + { + offset -= tzinfo.StandardBias; + } + return offset; +#else + +#if defined(sun) || defined(__sun) || defined(_AIX) + // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris + struct helper + { + static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), const std::tm &gmtm = details::os::gmtime()) + { + int local_year = localtm.tm_year + (1900 - 1); + int gmt_year = gmtm.tm_year + (1900 - 1); + + long int days = ( + // difference in day of year + localtm.tm_yday - + gmtm.tm_yday + + // + intervening leap days + + ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) + + ((local_year / 100 >> 2) - (gmt_year / 100 >> 2)) + + // + difference in years * 365 */ + + (long int)(local_year - gmt_year) * 365); + + long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour); + long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min); + long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec); + + return secs; + } + }; + + auto offset_seconds = helper::calculate_gmt_offset(tm); +#else + auto offset_seconds = tm.tm_gmtoff; +#endif + + return static_cast(offset_seconds / 60); +#endif +} + +// Return current thread id as size_t +// It exists because the std::this_thread::get_id() is much slower(especially +// under VS 2013) +SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT +{ +#ifdef _WIN32 + return static_cast(::GetCurrentThreadId()); +#elif __linux__ +#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21) +#define SYS_gettid __NR_gettid +#endif + return static_cast(syscall(SYS_gettid)); +#elif __FreeBSD__ + long tid; + thr_self(&tid); + return static_cast(tid); +#elif __APPLE__ + uint64_t tid; + pthread_threadid_np(nullptr, &tid); + return static_cast(tid); +#else // Default to standard C++11 (other Unix) + return static_cast(std::hash()(std::this_thread::get_id())); +#endif +} + +// Return current thread id as size_t (from thread local storage) +SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT +{ +#if defined(SPDLOG_NO_TLS) + return _thread_id(); +#else // cache thread id in tls + static thread_local const size_t tid = _thread_id(); + return tid; +#endif +} + +// This is avoid msvc issue in sleep_for that happens if the clock changes. +// See https://github.com/gabime/spdlog/issues/609 +SPDLOG_INLINE void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT +{ +#if defined(_WIN32) + ::Sleep(milliseconds); +#else + std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); +#endif +} + +// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) +#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) +#define SPDLOG_FILENAME_T(s) L##s +SPDLOG_INLINE std::string filename_to_str(const filename_t &filename) +{ + std::wstring_convert, wchar_t> c; + return c.to_bytes(filename); +} +#else +#define SPDLOG_FILENAME_T(s) s +SPDLOG_INLINE std::string filename_to_str(const filename_t &filename) +{ + return filename; +} +#endif + +SPDLOG_INLINE int pid() +{ + +#ifdef _WIN32 + return static_cast(::GetCurrentProcessId()); +#else + return static_cast(::getpid()); +#endif +} + +// Determine if the terminal supports colors +// Source: https://github.com/agauniyal/rang/ +SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT +{ +#ifdef _WIN32 + return true; +#else + static constexpr const char *Terms[] = { + "ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"}; + + const char *env_p = std::getenv("TERM"); + if (env_p == nullptr) + { + return false; + } + + static const bool result = + std::any_of(std::begin(Terms), std::end(Terms), [&](const char *term) { return std::strstr(env_p, term) != nullptr; }); + return result; +#endif +} + +// Detrmine if the terminal attached +// Source: https://github.com/agauniyal/rang/ +SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT +{ + +#ifdef _WIN32 + return _isatty(_fileno(file)) != 0; +#else + return isatty(fileno(file)) != 0; +#endif +} +} // namespace os +} // namespace details +} // namespace spdlog diff --git a/src/sink.cpp b/src/sink.cpp new file mode 100644 index 00000000..0d308748 --- /dev/null +++ b/src/sink.cpp @@ -0,0 +1,31 @@ +#include "spdlog/common.h" +#include "spdlog/details/pattern_formatter.h" + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/sinks/sink.h" +#endif + +SPDLOG_INLINE spdlog::sinks::sink::sink() + : formatter_{details::make_unique()} +{} + +SPDLOG_INLINE spdlog::sinks::sink::sink(std::unique_ptr formatter) + : formatter_{std::move(formatter)} +{} + +SPDLOG_INLINE bool spdlog::sinks::sink::should_log(spdlog::level::level_enum msg_level) const +{ + return msg_level >= level_.load(std::memory_order_relaxed); +} + +SPDLOG_INLINE void spdlog::sinks::sink::set_level(level::level_enum log_level) +{ + level_.store(log_level); +} + +SPDLOG_INLINE spdlog::level::level_enum spdlog::sinks::sink::level() const +{ + return static_cast(level_.load(std::memory_order_relaxed)); +} + +