diff --git a/example/main.cpp b/example/main.cpp index 347ff65..1dc8d47 100644 --- a/example/main.cpp +++ b/example/main.cpp @@ -4,9 +4,11 @@ int main() { std::string filename = "easylog.txt"; std::filesystem::remove(filename); - easylog::init_log(easylog::Severity::DEBUG, filename, true, 5000, 1, true); + easylog::init_log(easylog::Severity::DEBUG, filename, true, true, 500000, 1, + true); - ELOGFMT(INFO, "test {}", 42); + std::string str = "world"; + ELOGFMT(INFO, "test {} {} {}", 42, "hello", str); ELOG_INFO << 42; ELOG_INFO << 42 << " " << 4.5 << 'a' << easylog::Severity::DEBUG; diff --git a/include/easylog.hpp b/include/easylog.hpp index 538d524..ee71152 100644 --- a/include/easylog.hpp +++ b/include/easylog.hpp @@ -175,7 +175,8 @@ inline void add_appender(std::function fn) { easylog::logger::instance() += \ easylog::record_t(std::chrono::system_clock::now(), severity, \ GET_STRING(__FILE__, __LINE__)) \ - .sprintf(fmt, __VA_ARGS__); \ + .format(easylog::get_async(), \ + fmt, __VA_ARGS__); \ if (severity == easylog::Severity::CRITICAL) { \ easylog::flush(); \ std::exit(EXIT_FAILURE); \ @@ -201,7 +202,8 @@ inline void add_appender(std::function fn) { easylog::logger::instance() += \ easylog::record_t(std::chrono::system_clock::now(), severity, \ GET_STRING(__FILE__, __LINE__)) \ - .format(prefix::format(format_str, __VA_ARGS__)); \ + .format(easylog::get_async(), \ + format_str, __VA_ARGS__); \ if (severity == easylog::Severity::CRITICAL) { \ easylog::flush(); \ std::exit(EXIT_FAILURE); \ diff --git a/include/easylog/appender.hpp b/include/easylog/appender.hpp index dc4edc2..d3f1bd6 100644 --- a/include/easylog/appender.hpp +++ b/include/easylog/appender.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -77,7 +78,10 @@ class appender { void enable_console(bool b) { enable_console_ = b; } void start_thread() { - write_thd_ = std::thread([this] { + std::promise promise; + auto future = promise.get_future(); + write_thd_ = std::thread([this, promise = std::move(promise)]() mutable { + promise.set_value(); while (!stop_) { if (max_files_ > 0 && file_size_ > max_file_size_ && static_cast(-1) != file_size_) { @@ -110,6 +114,7 @@ class appender { } } }); + future.wait(); } std::string_view get_tid_buf(unsigned int tid) { @@ -140,6 +145,7 @@ class appender { template void write_record(record_t &record) { + record.gen_content(); std::lock_guard guard(get_mutex()); if constexpr (sync == true) { if (max_files_ > 0 && file_size_ > max_file_size_ && diff --git a/include/easylog/record.hpp b/include/easylog/record.hpp index af14b23..9d646e7 100644 --- a/include/easylog/record.hpp +++ b/include/easylog/record.hpp @@ -2,7 +2,10 @@ #include #include +#include #include +#include +#include #include "util/time_util.hpp" #ifdef __linux__ @@ -68,6 +71,11 @@ template constexpr inline bool has_str_v = has_str>::value; } // namespace detail +enum class FormatType { + SPRINTF, + FORMAT, +}; + enum class Severity { NONE, TRACE, @@ -170,14 +178,58 @@ class record_t { return *this; } - template - record_t &sprintf(const char *fmt, Args &&...args) { - ss_.append(fmt::sprintf(fmt, std::forward(args)...)); - return *this; + void gen_content() { + if (args_cache_) { + ss_.append(args_cache_()); + args_cache_ = {}; + } + } + + template decltype(auto) transform(T &t) { + using U = std::remove_reference_t; + + if constexpr (std::is_same_v || fixed_array_v || + string_view_v) { + return std::string(t); + } else if constexpr (string_v) { + return std::move(t); + } else { + return t; + } } - template record_t &format(String &&str) { - ss_.append(str.data()); + template + auto get_args_impl(Tuple &&tp, std::index_sequence) { + std::tuple<> t; + return std::tuple_cat(t, std::make_tuple(transform(std::get(tp)))...); + } + + template + record_t &format(bool async, const char *fmt, Args &&...args) { + size_t len = strlen(fmt); + std::string str; + str.reserve(len); + str.append(fmt); + args_cache_ = [str = std::move(str), + args1 = get_args_impl( + std::make_tuple(std::forward(args)...), + std::make_index_sequence{})]() mutable { + return std::apply( + [str = std::move(str)](auto &&...args) mutable { + if constexpr (fmt_type == FormatType::FORMAT) { + return fmt::format(str.data(), std::move(args)...); + } else { + return fmt::sprintf(str.data(), std::move(args)...); + } + }, + args1); + }; + + if (!async) { + ss_.append(args_cache_()); + args_cache_ = {}; + } + return *this; } @@ -242,6 +294,7 @@ class record_t { #else std::string ss_; #endif + std::function args_cache_; }; #define TO_STR(s) #s diff --git a/include/util/type_traits.h b/include/util/type_traits.h index 0172683..ba45560 100644 --- a/include/util/type_traits.h +++ b/include/util/type_traits.h @@ -1,12 +1,46 @@ #pragma once #include #include +#include +#include template struct remove_cvref { typedef std::remove_cv_t> type; }; template using remove_cvref_t = typename remove_cvref::type; +template +constexpr inline bool c_array_v = + std::is_array_v> &&std::extent_v> > 0; + +template struct is_array : std::false_type {}; + +template +struct is_array< + T, std::void_t().size()), + typename std::enable_if_t<(std::tuple_size::value != 0)>>> + : std::true_type {}; + +template +constexpr inline bool array_v = is_array>::value; + +template +constexpr inline bool fixed_array_v = c_array_v || array_v; + +template