diff --git a/proxy/include/proxy/logging.hpp b/proxy/include/proxy/logging.hpp index 1b701ddb14..9350ed3ae1 100644 --- a/proxy/include/proxy/logging.hpp +++ b/proxy/include/proxy/logging.hpp @@ -1,1848 +1,1860 @@ -// -// logging.hpp -// ~~~~~~~~~~~ -// -// Copyright (c) 2023 Jack (jack dot wgm at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef INCLUDE__2016_10_14__LOGGING_HPP -#define INCLUDE__2016_10_14__LOGGING_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef LOGGING_DISABLE_BOOST_ASIO_ENDPOINT -# if defined(__has_include) -# if __has_include() -# include -# include -# include -# include -# else -# define LOGGING_DISABLE_BOOST_ASIO_ENDPOINT -# endif -# else -# include -# include -# include -# include -# endif -#endif // !LOGGING_DISABLE_ASIO_ENDPOINT - - -#ifndef LOGGING_DISABLE_BOOST_POSIX_TIME -# if defined(__has_include) -# if __has_include() -# include -# else -# define LOGGING_DISABLE_BOOST_POSIX_TIME -# endif -# else -# include -# endif -#endif - -#ifndef LOGGING_DISABLE_BOOST_FILESYSTEM -# if defined(__has_include) -# if __has_include() -# include -# else -# define LOGGING_DISABLE_BOOST_FILESYSTEM -# endif -# else -# include -# endif -#endif - -#ifndef LOGGING_DISABLE_BOOST_STRING_VIEW -# if defined(__has_include) -# if __has_include() -# include -# else -# define LOGGING_DISABLE_BOOST_STRING_VIEW -# endif -# else -# include -# endif -#endif - -#ifdef WIN32 -# ifndef LOGGING_DISABLE_AUTO_UTF8 -# define LOGGING_ENABLE_AUTO_UTF8 -# endif // !LOGGING_DISABLE_WINDOWS_AUTO_UTF8 -#endif // WIN32 - - -////////////////////////////////////////////////////////////////////////// - -#if defined(_WIN32) || defined(WIN32) -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif // !WIN32_LEAN_AND_MEAN -# include -#endif // _WIN32 - -#ifdef USE_SYSTEMD_LOGGING -# if __has_include() -# include -# else -# error "systemd/sd-journal.h not found" -# endif -#endif - -////////////////////////////////////////////////////////////////////////// -#ifndef LOGGING_DISABLE_COMPRESS_LOGS -# if defined(__has_include) -# if __has_include() -# include -# ifndef LOGGING_ENABLE_COMPRESS_LOGS -# define LOGGING_ENABLE_COMPRESS_LOGS -# endif -# endif -# else -# ifdef LOGGING_ENABLE_COMPRESS_LOGS -# include -# endif -# endif -#endif - -#if defined(__cpp_lib_format) -# include -#endif - -#if !defined(__cpp_lib_format) -# ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable: 4244 4127) -# endif // _MSC_VER - -# ifdef __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wexpansion-to-defined" -# endif - -# include -# include -# include - -namespace std { - using ::fmt::format; - using ::fmt::format_to; - using ::fmt::vformat; - using ::fmt::vformat_to; - using ::fmt::make_format_args; -} - -# ifdef __clang__ -# pragma clang diagnostic pop -# endif - -# ifdef _MSC_VER -# pragma warning(pop) -# endif -#endif - - -////////////////////////////////////////////////////////////////////////// -// -// User customization function for hook log, function signature: -// -// bool logger_writer(logger_tag, -// int64_t time, const int& level, const std::string& message); -// - - -namespace util { - - namespace fs = std::filesystem; - -#ifndef LOGGING_DISABLE_BOOST_ASIO_ENDPOINT - namespace net = boost::asio; -#endif - -#ifndef LOG_APPNAME -# define LOG_APPNAME "application" -#endif - -#ifndef LOG_MAXFILE_SIZE -# define LOG_MAXFILE_SIZE (-1) -#endif // LOG_MAXFILE_SIZE - - -#ifdef LOGGING_ENABLE_COMPRESS_LOGS - -namespace logging_compress__ { - - const inline std::string LOGGING_GZ_SUFFIX = ".gz"; - const inline size_t LOGGING_GZ_BUFLEN = 65536; - - inline std::mutex& compress_lock() - { - static std::mutex lock; - return lock; - } - - inline bool do_compress_gz(const std::string& infile) - { - std::string outfile = infile + LOGGING_GZ_SUFFIX; - - gzFile out = gzopen(outfile.c_str(), "wb6f"); - if (!out) - return false; - typedef typename std::remove_pointer::type gzFileType; - std::unique_ptr gz_closer(out, &gzclose); - - FILE* in = fopen(infile.c_str(), "rb"); - if (!in) - return false; - std::unique_ptr FILE_closer(in, &fclose); - - std::unique_ptr bufs(new char[LOGGING_GZ_BUFLEN]); - char* buf = bufs.get(); - int len; - - for (;;) { - len = (int)fread(buf, 1, sizeof(buf), in); - if (ferror(in)) - return false; - - if (len == 0) - break; - - int total = 0; - int ret; - while (total < len) { - ret = gzwrite(out, buf + total, (unsigned)len - total); - if (ret <= 0) { - // detail error information see gzerror(out, &ret); - return false; - } - total += ret; - } - } - - return true; - } - -} - -#endif - -inline bool global_logging___ = true; - -namespace logger_aux__ { - - constexpr long long epoch___ = 0x19DB1DED53E8000LL; - - inline int64_t gettime() - { -#ifdef WIN32 - static std::tuple - static_start = []() -> - std::tuple - { - LARGE_INTEGER f; - QueryPerformanceFrequency(&f); - - FILETIME ft; -#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) - GetSystemTimePreciseAsFileTime(&ft); -#else - GetSystemTimeAsFileTime(&ft); -#endif - auto now = (((static_cast(ft.dwHighDateTime)) << 32) - + static_cast(ft.dwLowDateTime) - epoch___) - / 10000; - - LARGE_INTEGER start; - QueryPerformanceCounter(&start); - - return { f.QuadPart / 1000, start.QuadPart, now }; - }(); - - auto [freq, start, now] = static_start; - - LARGE_INTEGER current; - QueryPerformanceCounter(¤t); - - auto elapsed = current.QuadPart - start; - elapsed /= freq; - - return static_cast(now + elapsed); -#else - using std::chrono::system_clock; - auto now = system_clock::now() - - system_clock::time_point(std::chrono::milliseconds(0)); - - return std::chrono::duration_cast< - std::chrono::milliseconds>(now).count(); -#endif - } - - namespace internal { - template - struct Null {}; - inline Null<> localtime_r(...) { return Null<>(); } - inline Null<> localtime_s(...) { return Null<>(); } - inline Null<> gmtime_r(...) { return Null<>(); } - inline Null<> gmtime_s(...) { return Null<>(); } - } - - // Thread-safe replacement for std::localtime - inline bool localtime(std::time_t time, std::tm& tm) - { - struct LocalTime { - std::time_t time_; - std::tm tm_; - - LocalTime(std::time_t t) : time_(t) {} - - bool run() { - using namespace internal; - return handle(localtime_r(&time_, &tm_)); - } - - bool handle(std::tm* tm) { return tm != nullptr; } - - bool handle(internal::Null<>) { - using namespace internal; - return fallback(localtime_s(&tm_, &time_)); - } - - bool fallback(int res) { return res == 0; } - - bool fallback(internal::Null<>) { - using namespace internal; - std::tm* tm = std::localtime(&time_); - if (tm) tm_ = *tm; - return tm != nullptr; - } - }; - - LocalTime lt(time); - if (lt.run()) { - tm = lt.tm_; - return true; - } - - return false; - } - - inline namespace utf { - - inline uint32_t decode(uint32_t* state, uint32_t* codep, uint32_t byte) - { - static constexpr uint8_t utf8d[] = - { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf - 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df - 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef - 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff - 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2 - 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4 - 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6 - 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8 - }; - - uint32_t type = utf8d[byte]; - - *codep = (*state != 0) ? - (byte & 0x3fu) | (*codep << 6) : - (0xff >> type) & (byte); - - *state = utf8d[256 + *state * 16 + type]; - return *state; - } - - inline bool utf8_check_is_valid(std::string_view str) - { - uint32_t codepoint; - uint32_t state = 0; - uint8_t* s = (uint8_t*)str.data(); - uint8_t* end = s + str.size(); - - for (; s != end; ++s) - if (decode(&state, &codepoint, *s) == 1) - return false; - - return state == 0; - } - - inline std::optional utf8_convert(std::string_view str) - { - uint8_t* start = (uint8_t*)str.data(); - uint8_t* end = start + str.size(); - - std::wstring wstr; - uint32_t codepoint; - uint32_t state = 0; - - for (; start != end; ++start) - { - switch (decode(&state, &codepoint, *start)) - { - case 0: - if (codepoint <= 0xFFFF) [[likely]] - { - wstr.push_back(static_cast(codepoint)); - continue; - } - wstr.push_back(static_cast(0xD7C0 + (codepoint >> 10))); - wstr.push_back(static_cast(0xDC00 + (codepoint & 0x3FF))); - continue; - case 1: - return {}; - default: - ; - } - } - - if (state != 0) - return {}; - - return wstr; - } - - inline bool append(uint32_t cp, std::string& result) - { - if (!(cp <= 0x0010ffffu && !(cp >= 0xd800u && cp <= 0xdfffu))) - return false; - - if (cp < 0x80) - { - result.push_back(static_cast(cp)); - } - else if (cp < 0x800) - { - result.push_back(static_cast((cp >> 6) | 0xc0)); - result.push_back(static_cast((cp & 0x3f) | 0x80)); - } - else if (cp < 0x10000) - { - result.push_back(static_cast((cp >> 12) | 0xe0)); - result.push_back(static_cast(((cp >> 6) & 0x3f) | 0x80)); - result.push_back(static_cast((cp & 0x3f) | 0x80)); - } - else { - result.push_back(static_cast((cp >> 18) | 0xf0)); - result.push_back(static_cast(((cp >> 12) & 0x3f) | 0x80)); - result.push_back(static_cast(((cp >> 6) & 0x3f) | 0x80)); - result.push_back(static_cast((cp & 0x3f) | 0x80)); - } - - return true; - } - - inline std::optional utf16_convert(std::wstring_view wstr) - { - std::string result; - - auto end = wstr.cend(); - for (auto start = wstr.cbegin(); start != end;) - { - uint32_t cp = static_cast(0xffff & *start++); - - if (cp >= 0xdc00u && cp <= 0xdfffu) [[unlikely]] - return {}; - - if (cp >= 0xd800u && cp <= 0xdbffu) - { - if (start == end) [[unlikely]] - return {}; - - uint32_t trail = static_cast(0xffff & *start++); - if (!(trail >= 0xdc00u && trail <= 0xdfffu)) [[unlikely]] - return {}; - - cp = (cp << 10) + trail + 0xFCA02400; - } - - if (!append(cp, result)) - return {}; - } - - if (result.empty()) - return {}; - - return result; - } - -#ifdef WIN32 - inline std::optional string_wide(const std::string_view& src) - { - auto len = MultiByteToWideChar(CP_ACP, 0, - src.data(), static_cast(src.size()), NULL, 0); - if (len <= 0) - return {}; - - std::wstring ret(len, 0); - MultiByteToWideChar(CP_ACP, 0, - src.data(), static_cast(src.size()), - (LPWSTR)ret.data(), len); - - return ret; - } - - inline std::optional utf8_utf16(const std::string_view& src) - { - auto len = MultiByteToWideChar(CP_UTF8, 0, - src.data(), static_cast(src.size()), NULL, 0); - if (len <= 0) - return {}; - - std::wstring ret(len, 0); - MultiByteToWideChar(CP_UTF8, 0, - src.data(), static_cast(src.size()), - (LPWSTR)ret.data(), len); - - return ret; - } - - inline std::optional utf16_utf8(std::wstring_view utf16) - { - auto len = WideCharToMultiByte(CP_UTF8, 0, - (LPCWCH)utf16.data(), static_cast(utf16.size()), - NULL, 0, NULL, NULL); - if (len <= 0) - return {}; - - std::string ret(len, 0); - WideCharToMultiByte(CP_UTF8, 0, - (LPCWCH)utf16.data(), static_cast(utf16.size()), - (LPSTR)ret.data(), len, NULL, NULL); - - return ret; - } - -#else - inline std::optional string_wide(const std::string_view& src) - { - const char* first = src.data(); - const char* last = src.data() + src.size(); - const char* snext = nullptr; - - std::wstring result(src.size() + 1, wchar_t{ 0 }); - - wchar_t* dest = result.data(); - wchar_t* dnext = nullptr; - - using codecvt_type = std::codecvt; - std::locale sys_locale(""); - mbstate_t in_state; - - auto ret = std::use_facet(sys_locale).in( - in_state, first, last, snext, dest, dest + result.size(), dnext); - if (ret != codecvt_type::ok) - return {}; - - result.resize(static_cast(dnext - dest)); - return result; - } - - inline std::optional utf8_utf16(std::string_view utf8) - { - const char* first = &utf8[0]; - const char* last = first + utf8.size(); - const char8_t* snext = nullptr; - - std::wstring result(utf8.size(), char16_t{ 0 }); - wchar_t* dest = &result[0]; - char16_t* next = nullptr; - - using codecvt_type = std::codecvt; - - codecvt_type* cvt = new codecvt_type; - - // manages reference to codecvt facet to free memory. - std::locale loc; - loc = std::locale(loc, cvt); - - codecvt_type::state_type state{}; - - auto ret = cvt->in( - state, (char8_t*)first, (char8_t*)last, snext, - (char16_t*)dest, (char16_t*)dest + result.size(), next); - if (ret != codecvt_type::ok) - return {}; - - result.resize(static_cast((wchar_t*)next - dest)); - return result; - } - - inline std::optional utf16_utf8(std::wstring_view utf16) - { - auto* first = &utf16[0]; - auto* last = first + utf16.size(); - - std::string result((utf16.size() + 1) * 6, char{ 0 }); - char* dest = &result[0]; - char8_t* next = nullptr; - - using codecvt_type = std::codecvt; - - codecvt_type* cvt = new codecvt_type; - // manages reference to codecvt facet to free memory. - std::locale loc; - loc = std::locale(loc, cvt); - - codecvt_type::state_type state{}; - const char16_t* snext = nullptr; - auto ret = cvt->out( - state, (char16_t*)first, (char16_t*)last, snext, - (char8_t*)dest, (char8_t*)dest + result.size(), next); - if (ret != codecvt_type::ok) - return {}; - - result.resize(static_cast((char*)next - dest)); - return result; - } -#endif - - } // namespace utf - - inline std::string from_u8string(const std::string& s) - { - return s; - } - - inline std::string from_u8string(std::string&& s) - { - return s; - } - -#if defined(__cpp_lib_char8_t) - inline std::string from_u8string(const std::u8string& s) - { - return std::string(s.begin(), s.end()); - } -#endif - - ////////////////////////////////////////////////////////////////////////// - - template - Lock& lock_single() - { - static Lock lock_instance; - return lock_instance; - } - - template - Writer& writer_single(std::string log_path = "") - { - static Writer writer_instance(log_path); - return writer_instance; - } - - inline struct tm* time_to_string(char* buffer, int64_t time) - { - std::time_t rawtime = time / 1000; - thread_local struct tm ptm; - - if (!localtime(rawtime, ptm)) - return nullptr; - - if (!buffer) - return &ptm; - - std::format_to(buffer, - "{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}", - ptm.tm_year + 1900, ptm.tm_mon + 1, ptm.tm_mday, - ptm.tm_hour, ptm.tm_min, ptm.tm_sec, (int)(time % 1000) - ); - - return &ptm; - } -} - - -class auto_logger_file__ -{ - // c++11 noncopyable. - auto_logger_file__(const auto_logger_file__&) = delete; - auto_logger_file__& operator=(const auto_logger_file__&) = delete; - -public: - auto_logger_file__(std::string log_path = "") - { - if (!log_path.empty()) - m_log_path = log_path; - - m_log_path = m_log_path / (LOG_APPNAME + std::string(".log")); - - if (!global_logging___) - return; - - std::error_code ignore_ec; - if (!fs::exists(m_log_path, ignore_ec)) - fs::create_directories( - m_log_path.parent_path(), ignore_ec); - } - ~auto_logger_file__() - { - m_last_time = 0; - } - - typedef std::shared_ptr ofstream_ptr; - - void open(const char* path) - { - m_log_path = path; - - if (!global_logging___) - return; - - std::error_code ignore_ec; - if (!fs::exists(m_log_path, ignore_ec)) - fs::create_directories( - m_log_path.parent_path(), ignore_ec); - } - - std::string log_path() const - { - return m_log_path.string(); - } - - void logging(bool disable) noexcept - { - m_disable_write = disable; - } - - void write([[maybe_unused]] int64_t time, - const char* str, std::streamsize size) - { - if (m_disable_write) - return; - - bool condition = false; - auto hours = time / 1000 / 3600; - auto last_hours = m_last_time / 1000 / 3600; - - if (static_cast(m_log_size) > LOG_MAXFILE_SIZE && - LOG_MAXFILE_SIZE > 0) - condition = true; - - if (last_hours != hours && LOG_MAXFILE_SIZE < 0) - condition = true; - - while (condition) { - if (m_last_time == -1) { - m_last_time = time; - break; - } - - auto ptm = logger_aux__::time_to_string(nullptr, m_last_time); - - m_ofstream->close(); - m_ofstream.reset(); - - auto logpath = fs::path(m_log_path.parent_path()); - fs::path filename; - - if constexpr (LOG_MAXFILE_SIZE <= 0) { - auto logfile = std::format("{:04d}{:02d}{:02d}-{:02d}.log", - ptm->tm_year + 1900, - ptm->tm_mon + 1, - ptm->tm_mday, - ptm->tm_hour); - filename = logpath / logfile; - } else { - auto utc_time = std::mktime(ptm); - auto logfile = std::format("{:04d}{:02d}{:02d}-{}.log", - ptm->tm_year + 1900, - ptm->tm_mon + 1, - ptm->tm_mday, - utc_time); - filename = logpath / logfile; - } - - m_last_time = time; - - std::error_code ec; - if (!fs::copy_file(m_log_path, filename, ec)) - break; - - fs::resize_file(m_log_path, 0, ec); - m_log_size = 0; - -#ifdef LOGGING_ENABLE_COMPRESS_LOGS - auto fn = filename.string(); - std::thread th([fn]() - { - std::error_code ignore_ec; - std::mutex& m = logging_compress__::compress_lock(); - std::lock_guard lock(m); - if (!logging_compress__::do_compress_gz(fn)) - { - auto file = fn + logging_compress__::LOGGING_GZ_SUFFIX; - fs::remove(file, ignore_ec); - if (ignore_ec) - std::cerr - << "delete log failed: " << file - << ", error code: " << ignore_ec.message() - << std::endl; - return; - } - - fs::remove(fn, ignore_ec); - }); - th.detach(); -#endif - break; - } - - if (!m_ofstream) { - m_ofstream.reset(new std::ofstream); - auto& ofstream = *m_ofstream; - ofstream.open(m_log_path.string().c_str(), - std::ios_base::out | std::ios_base::app); - ofstream.sync_with_stdio(false); - } - - if (m_ofstream->is_open()) { - m_log_size += size; - m_ofstream->write(str, size); - m_ofstream->flush(); - } - } - -private: - fs::path m_log_path{"./logs"}; - ofstream_ptr m_ofstream; - int64_t m_last_time{ -1 }; - std::size_t m_log_size{ 0 }; - bool m_disable_write{ false }; -}; - -#ifndef DISABLE_LOGGER_THREAD_SAFE -#define LOGGER_LOCKS_() std::lock_guard \ - lock(logger_aux__::lock_single()) -#else -#define LOGGER_LOCKS_() ((void)0) -#endif // LOGGER_THREAD_SAFE - -#ifndef LOGGER_DBG_VIEW_ -#if defined(WIN32) && \ - (defined(LOGGER_DBG_VIEW) || \ - defined(DEBUG) || \ - defined(_DEBUG)) -#define LOGGER_DBG_VIEW_(x) \ - do { \ - ::OutputDebugStringW((x).c_str()); \ - } while (0) -#else -#define LOGGER_DBG_VIEW_(x) ((void)0) -#endif // WIN32 && LOGGER_DBG_VIEW -#endif // LOGGER_DBG_VIEW_ - -const inline int _logger_debug_id__ = 0; -const inline int _logger_info_id__ = 1; -const inline int _logger_warn_id__ = 2; -const inline int _logger_error_id__ = 3; -const inline int _logger_file_id__ = 4; - -const inline std::string _LOGGER_DEBUG_STR__ = " DEBUG "; -const inline std::string _LOGGER_INFO_STR__ = " INFO "; -const inline std::string _LOGGER_WARN_STR__ = " WARN "; -const inline std::string _LOGGER_ERR_STR__ = " ERROR "; -const inline std::string _LOGGER_FILE_STR__ = " FILE "; - -inline void logger_output_console__([[maybe_unused]] bool disable_cout, - [[maybe_unused]] const int& level, - [[maybe_unused]] const std::string& prefix, - [[maybe_unused]] const std::string& message) noexcept -{ -#if defined(WIN32) - -#if !defined(DISABLE_LOGGER_TO_CONSOLE) || !defined(DISABLE_LOGGER_TO_DBGVIEW) - std::wstring title = *logger_aux__::utf8_utf16(prefix); - std::wstring msg = *logger_aux__::utf8_utf16(message); -#endif - -#if !defined(DISABLE_LOGGER_TO_CONSOLE) - if (!disable_cout) - { - HANDLE handle_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - CONSOLE_SCREEN_BUFFER_INFO csbi; - GetConsoleScreenBufferInfo(handle_stdout, &csbi); - if (level == _logger_info_id__) - SetConsoleTextAttribute(handle_stdout, - FOREGROUND_GREEN); - else if (level == _logger_debug_id__) - SetConsoleTextAttribute(handle_stdout, - FOREGROUND_GREEN | FOREGROUND_INTENSITY); - else if (level == _logger_warn_id__) - SetConsoleTextAttribute(handle_stdout, - FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY); - else if (level == _logger_error_id__) - SetConsoleTextAttribute(handle_stdout, - FOREGROUND_RED | FOREGROUND_INTENSITY); - - WriteConsoleW(handle_stdout, - title.data(), (DWORD)title.size(), nullptr, nullptr); - SetConsoleTextAttribute(handle_stdout, - FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); - - WriteConsoleW(handle_stdout, - msg.data(), (DWORD)msg.size(), nullptr, nullptr); - SetConsoleTextAttribute(handle_stdout, csbi.wAttributes); - } -#endif - -#if !defined(DISABLE_LOGGER_TO_DBGVIEW) - LOGGER_DBG_VIEW_(title + msg); -#endif - -#elif !defined(DISABLE_LOGGER_TO_CONSOLE) - if (!disable_cout) - { - std::string out; - if (level == _logger_info_id__) - std::format_to(std::back_inserter(out), - "\033[32m{}\033[0m{}", prefix, message); - else if (level == _logger_debug_id__) - std::format_to(std::back_inserter(out), - "\033[1;32m{}\033[0m{}", prefix, message); - else if (level == _logger_warn_id__) - std::format_to(std::back_inserter(out), - "\033[1;33m{}\033[0m{}", prefix, message); - else if (level == _logger_error_id__) - std::format_to(std::back_inserter(out), - "\033[1;31m{}\033[0m{}", prefix, message); - std::cout << out; - std::cout.flush(); - } -#endif -} - -#ifdef USE_SYSTEMD_LOGGING -inline void logger_output_systemd__( - const int& level, const std::string& message) noexcept -{ - if (level == _logger_info_id__) - sd_journal_print(LOG_INFO, "%s", message.c_str()); - else if (level == _logger_debug_id__) - sd_journal_print(LOG_DEBUG, "%s", message.c_str()); - else if (level == _logger_warn_id__) - sd_journal_print(LOG_WARNING, "%s", message.c_str()); - else if (level == _logger_error_id__) - sd_journal_print(LOG_ERR, "%s", message.c_str()); -} -#endif // USE_SYSTEMD_LOGGING - -inline const std::string& logger_level_string__(const int& level) noexcept -{ - switch (level) - { - case _logger_debug_id__: - return _LOGGER_DEBUG_STR__; - case _logger_info_id__: - return _LOGGER_INFO_STR__; - case _logger_warn_id__: - return _LOGGER_WARN_STR__; - case _logger_error_id__: - return _LOGGER_ERR_STR__; - case _logger_file_id__: - return _LOGGER_FILE_STR__; - } - - BOOST_ASSERT(false && "invalid logging level!"); - return _LOGGER_DEBUG_STR__; -} - -struct logger_tag -{}; - -namespace access -{ - namespace detail - { - template - bool tag_invoke(T...) noexcept - { - return false; - } - - struct tag_invoke_t - { - template - bool operator()(Tag tag, - int64_t time, - const int& level, - const std::string& message) noexcept - { - return tag_invoke( - std::forward(tag), - time, - level, - message); - } - }; - } - - inline detail::tag_invoke_t tag_invoke{}; -} - -inline void logger_writer__(int64_t time, const int& level, - const std::string& message, - [[maybe_unused]] bool disable_cout = false) noexcept -{ - LOGGER_LOCKS_(); - static auto& logger = util::logger_aux__::writer_single< - util::auto_logger_file__>(); - char ts[64] = { 0 }; - [[maybe_unused]] auto ptm = logger_aux__::time_to_string(ts, time); - std::string prefix = ts + logger_level_string__(level); - std::string tmp = message + "\n"; - std::string whole = prefix + tmp; - - // User log hook. - if (access::tag_invoke(logger_tag(), time, level, message)) - return; - -#ifndef DISABLE_WRITE_LOGGING - logger.write(time, whole.c_str(), whole.size()); -#endif // !DISABLE_WRITE_LOGGING - logger_output_console__(disable_cout, level, prefix, tmp); -#ifdef USE_SYSTEMD_LOGGING - logger_output_systemd__(level, message); -#endif // USE_SYSTEMD_LOGGING -} - -#if defined(_WIN32) || defined(WIN32) -static LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS* info); -#endif -void signal_handler(int); - -namespace logger_aux__ { - using namespace std::chrono_literals; - - class async_logger___ - { - struct internal_message - { - int level_; - int64_t time_; - std::string message_; - bool disable_cout_; - }; - - // c++11 noncopyable. - async_logger___(const async_logger___&) = delete; - async_logger___& operator=(const async_logger___&) = delete; - - public: - async_logger___() - { - // 实现Crash handler以接管在crash时 - // 不会漏写日志. - -#if defined(_WIN32) || defined(WIN32) - m_unexpected_exception_handler = - SetUnhandledExceptionFilter(unexpectedExceptionHandling); -#endif - signal(SIGTERM, signal_handler); - signal(SIGABRT, signal_handler); - signal(SIGFPE, signal_handler); - signal(SIGSEGV, signal_handler); - signal(SIGILL, signal_handler); - } - ~async_logger___() - { - m_abort = true; - if (m_bg_thread.joinable()) - m_bg_thread.join(); - } - - public: -#if defined(_WIN32) || defined(WIN32) - LPTOP_LEVEL_EXCEPTION_FILTER oldUnhandledExceptionFilter() - { - return m_unexpected_exception_handler; - } -#endif - - void stop() - { - m_abort = true; - } - - void internal_work() - { - while (!m_abort || !m_messages.empty()) - { - std::unique_lock lock(m_bg_mutex); - - if (m_messages.empty()) - m_bg_cv.wait_for(lock, 128ms); - - while (!m_messages.empty()) - { - auto message = std::move(m_messages.front()); - m_messages.pop_front(); - - logger_writer__(message.time_, - message.level_, - message.message_, - message.disable_cout_); - } - } - } - - void post_log(const int& level, - std::string&& message, bool disable_cout = false) - { - [[maybe_unused]] static auto runthread = - &(m_bg_thread = std::thread([this]() - { - internal_work(); - })); - - auto time = logger_aux__::gettime(); - std::unique_lock lock(m_bg_mutex); - - m_messages.emplace_back( - internal_message - { - .level_ = level, - .time_ = time, - .message_ = std::move(message), - .disable_cout_ = disable_cout - } - ); - lock.unlock(); - - m_bg_cv.notify_one(); - } - - private: - std::thread m_bg_thread; - std::mutex m_bg_mutex; - std::condition_variable m_bg_cv; - std::deque m_messages; - std::atomic_bool m_abort{ false }; -#if defined(_WIN32) || defined(WIN32) - LPTOP_LEVEL_EXCEPTION_FILTER m_unexpected_exception_handler{ nullptr }; -#endif - }; -} - -inline std::shared_ptr global_logger_obj___ = - std::make_shared(); - -#if defined(_WIN32) || defined(WIN32) -static LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS* e) -{ - if (!global_logger_obj___) - return EXCEPTION_CONTINUE_SEARCH; - - auto old = global_logger_obj___->oldUnhandledExceptionFilter(); - SetUnhandledExceptionFilter(old); - - global_logger_obj___.reset(); - - return old(e); -} -#endif - -inline void signal_handler(int) -{ - global_logger_obj___.reset(); -} - -inline void init_logging(const std::string& path = "") -{ - logger_aux__::writer_single(path); -} - -inline std::string log_path() -{ - auto_logger_file__& file = - logger_aux__::writer_single(); - return file.log_path(); -} - -inline void shutdown_logging() -{ - auto& log_obj = global_logger_obj___; - if (log_obj) { - log_obj->stop(); - log_obj.reset(); - } -} - -inline void toggle_logging() -{ - global_logging___ = !global_logging___; -} - -inline void toggle_write_logging(bool disable) -{ - auto_logger_file__& file = - logger_aux__::writer_single(); - file.logging(disable); -} - -struct auto_init_async_logger -{ - auto_init_async_logger() { - init_logging(); - } - ~auto_init_async_logger() { - shutdown_logging(); - } -}; - -class logger___ -{ - // c++11 noncopyable. - logger___(const logger___&) = delete; - logger___& operator=(const logger___&) = delete; -public: - logger___(const int& level, - bool async = false, bool disable_cout = false) - : level_(level) - , async_(async) - , disable_cout_(disable_cout) - { - if (!global_logging___) - return; - } - ~logger___() - { - if (!global_logging___) - return; - - // if global_logger_obj___ is nullptr, fallback to - // synchronous operation. - if (async_ && global_logger_obj___) - global_logger_obj___->post_log( - level_, std::move(out_), disable_cout_); - else - logger_writer__(logger_aux__::gettime(), - level_, out_, disable_cout_); - } - - template - inline logger___& format_to(std::string_view fmt, Args&&... args) - { - if (!global_logging___) - return *this; - out_ += std::vformat(fmt, - std::make_format_args(std::forward(args)...)); - return *this; - } - - template - inline logger___& strcat_impl(T const& v) noexcept - { - if (!global_logging___) - return *this; - std::format_to(std::back_inserter(out_), "{}", v); - return *this; - } - - inline logger___& operator<<(bool v) - { - return strcat_impl(v); - } - inline logger___& operator<<(char v) - { - return strcat_impl(v); - } - inline logger___& operator<<(short v) - { - return strcat_impl(v); - } - inline logger___& operator<<(unsigned short v) - { - return strcat_impl(v); - } - inline logger___& operator<<(int v) - { - return strcat_impl(v); - } - inline logger___& operator<<(unsigned int v) - { - return strcat_impl(v); - } - inline logger___& operator<<(unsigned long long v) - { - return strcat_impl(v); - } - inline logger___& operator<<(long v) - { - return strcat_impl(v); - } - inline logger___& operator<<(long long v) - { - return strcat_impl(v); - } - inline logger___& operator<<(float v) - { - return strcat_impl(v); - } - inline logger___& operator<<(double v) - { - return strcat_impl(v); - } - inline logger___& operator<<(long double v) - { - return strcat_impl(v); - } - inline logger___& operator<<(unsigned long int v) - { - return strcat_impl(v); - } - inline logger___& operator<<(const std::string& v) - { -#ifdef LOGGING_ENABLE_AUTO_UTF8 - if (!logger_aux__::utf8_check_is_valid(v)) - { - auto wres = logger_aux__::string_wide(v); - if (wres) - { - auto ret = logger_aux__::utf16_utf8(*wres); - if (ret) - return strcat_impl(*ret); - } - } -#endif - return strcat_impl(v); - } - inline logger___& operator<<(const std::wstring& v) - { - return strcat_impl(*logger_aux__::utf16_utf8(v)); - } - inline logger___& operator<<(const std::u16string& v) - { - return strcat_impl(*logger_aux__::utf16_utf8( - {(const wchar_t*)v.data(), v.size()})); - } -#if (__cplusplus >= 202002L) - inline logger___& operator<<(const std::u8string& v) - { - return strcat_impl(reinterpret_cast(v.c_str())); - } -#endif - inline logger___& operator<<(const std::string_view& v) - { -#ifdef LOGGING_ENABLE_AUTO_UTF8 - if (!logger_aux__::utf8_check_is_valid(v)) - { - auto wres = logger_aux__::string_wide(v); - if (wres) - { - auto ret = logger_aux__::utf16_utf8(*wres); - if (ret) - return strcat_impl(*ret); - } - } -#endif - return strcat_impl(v); - } - inline logger___& operator<<(const boost::string_view& v) - { - std::string_view sv{v.data(), v.length()}; -#ifdef LOGGING_ENABLE_AUTO_UTF8 - if (!logger_aux__::utf8_check_is_valid(sv)) - { - auto wres = logger_aux__::string_wide(sv); - if (wres) - { - auto ret = logger_aux__::utf16_utf8(*wres); - if (ret) - return strcat_impl(*ret); - } - } -#endif - return strcat_impl(sv); - } - inline logger___& operator<<(const char* v) - { - std::string_view sv(v); -#ifdef LOGGING_ENABLE_AUTO_UTF8 - if (!logger_aux__::utf8_check_is_valid(sv)) - { - auto wres = logger_aux__::string_wide(sv); - if (wres) - { - auto ret = logger_aux__::utf16_utf8(*wres); - if (ret) - return strcat_impl(*ret); - } - } -#endif - return strcat_impl(sv); - } - inline logger___& operator<<(const wchar_t* v) - { - return strcat_impl(*logger_aux__::utf16_utf8(v)); - } - inline logger___& operator<<(const void *v) - { - if (!global_logging___) - return *this; - std::format_to(std::back_inserter(out_), "{:#010x}", (std::size_t)v); - return *this; - } - inline logger___& operator<<(const std::chrono::nanoseconds& v) - { - if (!global_logging___) - return *this; - std::format_to(std::back_inserter(out_), "{}ns", v.count()); - return *this; - } - inline logger___& operator<<(const std::chrono::microseconds& v) - { - if (!global_logging___) - return *this; - std::format_to(std::back_inserter(out_), "{}us", v.count()); - return *this; - } - inline logger___& operator<<(const std::chrono::milliseconds& v) - { - if (!global_logging___) - return *this; - std::format_to(std::back_inserter(out_), "{}ms", v.count()); - return *this; - } - inline logger___& operator<<(const std::chrono::seconds& v) - { - if (!global_logging___) - return *this; - std::format_to(std::back_inserter(out_), "{}s", v.count()); - return *this; - } - inline logger___& operator<<(const std::chrono::minutes& v) - { - if (!global_logging___) - return *this; - std::format_to(std::back_inserter(out_), "{}min", v.count()); - return *this; - } - inline logger___& operator<<(const std::chrono::hours& v) - { - if (!global_logging___) - return *this; - std::format_to(std::back_inserter(out_), "{}h", v.count()); - return *this; - } - -#ifndef LOGGING_DISABLE_BOOST_ASIO_ENDPOINT - inline logger___& operator<<(const net::ip::tcp::endpoint& v) - { - if (!global_logging___) - return *this; - if (v.address().is_v6()) - std::format_to(std::back_inserter(out_), - "[{}]:{}", v.address().to_string(), v.port()); - else - std::format_to(std::back_inserter(out_), - "{}:{}", v.address().to_string(), v.port()); - return *this; - } - inline logger___& operator<<(const net::ip::udp::endpoint& v) - { - if (!global_logging___) - return *this; - if (v.address().is_v6()) - std::format_to(std::back_inserter(out_), - "[{}]:{}", v.address().to_string(), v.port()); - else - std::format_to(std::back_inserter(out_), - "{}:{}", v.address().to_string(), v.port()); - return *this; - } -#endif - -#if (__cplusplus >= 202002L) - inline logger___& operator<<(const std::chrono::days& v) - { - if (!global_logging___) - return *this; - std::format_to(std::back_inserter(out_), "{}d", v.count()); - return *this; - } - inline logger___& operator<<(const std::chrono::weeks& v) - { - if (!global_logging___) - return *this; - std::format_to(std::back_inserter(out_), "{}weeks", v.count()); - return *this; - } - inline logger___& operator<<(const std::chrono::years& v) - { - if (!global_logging___) - return *this; - std::format_to(std::back_inserter(out_), "{}years", v.count()); - return *this; - } - inline logger___& operator<<(const std::chrono::months& v) - { - if (!global_logging___) - return *this; - std::format_to(std::back_inserter(out_), "{}months", v.count()); - return *this; - } - inline logger___& operator<<(const std::chrono::weekday& v) - { - if (!global_logging___) - return *this; - switch (v.c_encoding()) - { -#ifndef __cpp_lib_char8_t - case 0: out_ = "Sunday"; break; - case 1: out_ = "Monday"; break; - case 2: out_ = "Tuesday"; break; - case 3: out_ = "Wednesday"; break; - case 4: out_ = "Thursday"; break; - case 5: out_ = "Friday"; break; - case 6: out_ = "Saturday"; break; -#else - case 0: out_ = logger_aux__::from_u8string(u8"周日"); break; - case 1: out_ = logger_aux__::from_u8string(u8"周一"); break; - case 2: out_ = logger_aux__::from_u8string(u8"周二"); break; - case 3: out_ = logger_aux__::from_u8string(u8"周三"); break; - case 4: out_ = logger_aux__::from_u8string(u8"周四"); break; - case 5: out_ = logger_aux__::from_u8string(u8"周五"); break; - case 6: out_ = logger_aux__::from_u8string(u8"周六"); break; -#endif - } - return *this; - } - inline logger___& operator<<(const std::chrono::year& v) - { - if (!global_logging___) - return *this; -#if 0 - std::format_to(std::back_inserter(out_), - "{:04}", static_cast(v)); -#else - std::format_to(std::back_inserter(out_), - "{:04}{}", static_cast(v), - logger_aux__::from_u8string(u8"年")); -#endif - return *this; - } - inline logger___& operator<<(const std::chrono::month& v) - { - if (!global_logging___) - return *this; - switch (static_cast(v)) - { -#ifndef __cpp_lib_char8_t - case 1: out_ = "January"; break; - case 2: out_ = "February"; break; - case 3: out_ = "March"; break; - case 4: out_ = "April"; break; - case 5: out_ = "May"; break; - case 6: out_ = "June"; break; - case 7: out_ = "July"; break; - case 8: out_ = "August"; break; - case 9: out_ = "September"; break; - case 10: out_ = "October"; break; - case 11: out_ = "November"; break; - case 12: out_ = "December"; break; -#else - case 1: out_ = logger_aux__::from_u8string(u8"01月"); break; - case 2: out_ = logger_aux__::from_u8string(u8"02月"); break; - case 3: out_ = logger_aux__::from_u8string(u8"03月"); break; - case 4: out_ = logger_aux__::from_u8string(u8"04月"); break; - case 5: out_ = logger_aux__::from_u8string(u8"05月"); break; - case 6: out_ = logger_aux__::from_u8string(u8"06月"); break; - case 7: out_ = logger_aux__::from_u8string(u8"07月"); break; - case 8: out_ = logger_aux__::from_u8string(u8"08月"); break; - case 9: out_ = logger_aux__::from_u8string(u8"09月"); break; - case 10: out_ = logger_aux__::from_u8string(u8"10月"); break; - case 11: out_ = logger_aux__::from_u8string(u8"11月"); break; - case 12: out_ = logger_aux__::from_u8string(u8"12月"); break; -#endif - } - return *this; - } - inline logger___& operator<<(const std::chrono::day& v) - { - if (!global_logging___) - return *this; -#ifndef __cpp_lib_char8_t - std::format_to(std::back_inserter(out_), - "{:02}", static_cast(v)); -#else - std::format_to(std::back_inserter(out_), - "{:02}{}", static_cast(v), - logger_aux__::from_u8string(u8"日")); -#endif - return *this; - } -#endif - inline logger___& operator<<(const fs::path& p) noexcept - { - if (!global_logging___) - return *this; - auto ret = logger_aux__::utf16_utf8(p.wstring()); - if (ret) - return strcat_impl(*ret); - return strcat_impl(p.string()); - } -#ifndef LOGGING_DISABLE_BOOST_FILESYSTEM - inline logger___& operator<<(const boost::filesystem::path& p) noexcept - { - if (!global_logging___) - return *this; - auto ret = logger_aux__::utf16_utf8(p.wstring()); - if (ret) - return strcat_impl(*ret); - return strcat_impl(p.string()); - } -#endif -#ifndef LOGGING_DISABLE_BOOST_POSIX_TIME - inline logger___& operator<<(const boost::posix_time::ptime& p) noexcept - { - if (!global_logging___) - return *this; - - if (!p.is_not_a_date_time()) - { - auto date = p.date().year_month_day(); - auto time = p.time_of_day(); - - std::format_to(std::back_inserter(out_), - "{:04}", static_cast(date.year)); - std::format_to(std::back_inserter(out_), - "-{:02}", date.month.as_number()); - std::format_to(std::back_inserter(out_), - "-{:02}", date.day.as_number()); - - std::format_to(std::back_inserter(out_), - " {:02}", time.hours()); - std::format_to(std::back_inserter(out_), - ":{:02}", time.minutes()); - std::format_to(std::back_inserter(out_), - ":{:02}", time.seconds()); - - auto ms = time.total_milliseconds() % 1000; // milliseconds. - if (ms != 0) - std::format_to(std::back_inserter(out_), - ".{:03}", ms); - } - else - { - BOOST_ASSERT("Not a date time" && false); - out_ += "NOT A DATE TIME"; - } - - return *this; - } -#endif - inline logger___& operator<<(const std::thread::id& id) noexcept - { - std::ostringstream oss; - oss << id; - out_ += oss.str(); - return *this; - } - - std::string out_; - const int& level_; - bool async_; - bool disable_cout_; -}; - -class empty_logger___ -{ -public: - template - empty_logger___& operator<<(T const&/*v*/) - { - return *this; - } -}; -} // namespace util - -#undef LOG_DBG -#undef LOG_INFO -#undef LOG_WARN -#undef LOG_ERR -#undef LOG_FILE - -#undef LOG_FMT -#undef LOG_IFMT -#undef LOG_WFMT -#undef LOG_EFMT -#undef LOG_FFMT - -#undef ASYNC_LOGDBG -#undef ASYNC_LOGINFO -#undef ASYNC_LOGWARN -#undef ASYNC_LOGERR -#undef ASYNC_LOGFILE - -#undef ASYNC_LOGFMT -#undef ASYNC_LOGIFMT -#undef ASYNC_LOGWFMT -#undef ASYNC_LOGEFMT -#undef ASYNC_LOGFFMT - -#if (defined(DEBUG) || defined(_DEBUG) || \ - defined(ENABLE_LOGGER)) && !defined(DISABLE_LOGGER) - -#define LOG_DBG util::logger___(util::_logger_debug_id__) -#define LOG_INFO util::logger___(util::_logger_info_id__) -#define LOG_WARN util::logger___(util::_logger_warn_id__) -#define LOG_ERR util::logger___(util::_logger_error_id__) -#define LOG_FILE util::logger___(util::_logger_file_id__, false, true) - -#define LOG_FMT(...) util::logger___( \ - util::_logger_debug_id__).format_to(__VA_ARGS__) -#define LOG_IFMT(...) util::logger___( \ - util::_logger_info_id__).format_to(__VA_ARGS__) -#define LOG_WFMT(...) util::logger___( \ - util::_logger_warn_id__).format_to(__VA_ARGS__) -#define LOG_EFMT(...) util::logger___( \ - util::_logger_error_id__).format_to(__VA_ARGS__) -#define LOG_FFMT(...) util::logger___( \ - util::_logger_file_id__, false, true).format_to(__VA_ARGS__) - -#define ASYNC_LOGDBG util::logger___(util::_logger_debug_id__, true) -#define ASYNC_LOGINFO util::logger___(util::_logger_info_id__, true) -#define ASYNC_LOGWARN util::logger___(util::_logger_warn_id__, true) -#define ASYNC_LOGERR util::logger___(util::_logger_error_id__, true) -#define ASYNC_LOGFILE util::logger___(util::_logger_file_id__, true, true) - -#define ASYNC_LOGFMT(...) util::logger___( \ - util::_logger_debug_id__, true).format_to(__VA_ARGS__) -#define ASYNC_LOGIFMT(...) util::logger___( \ - util::_logger_info_id__, true).format_to(__VA_ARGS__) -#define ASYNC_LOGWFMT(...) util::logger___( \ - util::_logger_warn_id__, true).format_to(__VA_ARGS__) -#define ASYNC_LOGEFMT(...) util::logger___( \ - util::_logger_error_id__, true).format_to(__VA_ARGS__) -#define ASYNC_LOGFFMT(...) util::logger___( \ - util::_logger_file_id__, true, true).format_to(__VA_ARGS__) - -#define ASYNC_VLOGDBG ASYNC_LOGDBG \ - << "(" << __FILE__ << ":" << __LINE__ << "): " -#define ASYNC_VLOGINFO ASYNC_LOGINFO \ - << "(" << __FILE__ << ":" << __LINE__ << "): " -#define ASYNC_VLOGWARN ASYNC_LOGWARN \ - << "(" << __FILE__ << ":" << __LINE__ << "): " -#define ASYNC_VLOGERR ASYNC_LOGERR \ - << "(" << __FILE__ << ":" << __LINE__ << "): " -#define ASYNC_VLOGFILE ASYNC_LOGFILE \ - << "(" << __FILE__ << ":" << __LINE__ << "): " - -#define ASYNC_VLOGFMT(...) (ASYNC_LOGDBG << "(" \ - << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) -#define ASYNC_VLOGIFMT(...) (ASYNC_LOGINFO << "(" \ - << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) -#define ASYNC_VLOGWFMT(...) (ASYNC_LOGWARN << "(" \ - << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) -#define ASYNC_VLOGEFMT(...) (ASYNC_LOGERR << "(" \ - << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) -#define ASYNC_VLOGFFMT(...) (ASYNC_LOGFILE << "(" \ - << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) - -#define VLOG_DBG LOG_DBG << "(" << __FILE__ << ":" << __LINE__ << "): " -#define VLOG_INFO LOG_INFO << "(" << __FILE__ << ":" << __LINE__ << "): " -#define VLOG_WARN LOG_WARN << "(" << __FILE__ << ":" << __LINE__ << "): " -#define VLOG_ERR LOG_ERR << "(" << __FILE__ << ":" << __LINE__ << "): " -#define VLOG_FILE LOG_FILE << "(" << __FILE__ << ":" << __LINE__ << "): " - -#define VLOG_FMT(...) (LOG_DBG << "(" \ - << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) -#define VLOG_IFMT(...) (LOG_INFO << "(" \ - << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) -#define VLOG_WFMT(...) (LOG_WARN << "(" \ - << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) -#define VLOG_EFMT(...) (LOG_ERR << "(" \ - << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) -#define VLOG_FFMT(...) (LOG_FILE << "(" \ - << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) - - -#define INIT_ASYNC_LOGGING() [[maybe_unused]] \ - util::auto_init_async_logger ____init_logger____ - -#else - -#define LOG_DBG util::empty_logger___() -#define LOG_INFO util::empty_logger___() -#define LOG_WARN util::empty_logger___() -#define LOG_ERR util::empty_logger___() -#define LOG_FILE util::empty_logger___() - -#define LOG_FMT(...) util::empty_logger___() -#define LOG_IFMT(...) util::empty_logger___() -#define LOG_WFMT(...) util::empty_logger___() -#define LOG_EFMT(...) util::empty_logger___() -#define LOG_FFMT(...) util::empty_logger___() - -#define VLOG_DBG(...) util::empty_logger___() -#define VLOG_INFO(...) util::empty_logger___() -#define VLOG_WARN(...) util::empty_logger___() -#define VLOG_ERR(...) util::empty_logger___() -#define VLOG_FILE(...) util::empty_logger___() - -#define VLOG_FMT(...) util::empty_logger___() -#define VLOG_IFMT(...) util::empty_logger___() -#define VLOG_WFMT(...) util::empty_logger___() -#define VLOG_EFMT(...) util::empty_logger___() -#define VLOG_FFMT(...) util::empty_logger___() - -#define ASYNC_LOGDBG util::empty_logger___() -#define ASYNC_LOGINFO util::empty_logger___() -#define ASYNC_LOGWARN util::empty_logger___() -#define ASYNC_LOGERR util::empty_logger___() -#define ASYNC_LOGFILE util::empty_logger___() - -#define ASYNC_LOGFMT(...) util::empty_logger___() -#define ASYNC_LOGIFMT(...) util::empty_logger___() -#define ASYNC_LOGWFMT(...) util::empty_logger___() -#define ASYNC_LOGEFMT(...) util::empty_logger___() -#define ASYNC_LOGFFMT(...) util::empty_logger___() - -#define ASYNC_VLOGDBG LOG_DBG -#define ASYNC_VLOGINFO LOG_INFO -#define ASYNC_VLOGWARN LOG_WARN -#define ASYNC_VLOGERR LOG_ERR -#define ASYNC_VLOGFILE LOG_FILE - -#define ASYNC_VLOGFMT LOG_FMT -#define ASYNC_VLOGIFMT LOG_IFMT -#define ASYNC_VLOGWFMT LOG_WFMT -#define ASYNC_VLOGEFMT LOG_EFMT -#define ASYNC_VLOGFFMT LOG_FFMT - -#define INIT_ASYNC_LOGGING() void - -#endif - -#endif // INCLUDE__2016_10_14__LOGGING_HPP +// +// logging.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2023 Jack (jack dot wgm at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef INCLUDE__2016_10_14__LOGGING_HPP +#define INCLUDE__2016_10_14__LOGGING_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef LOGGING_DISABLE_BOOST_ASIO_ENDPOINT +# if defined(__has_include) +# if __has_include() +# include +# include +# include +# include +# else +# define LOGGING_DISABLE_BOOST_ASIO_ENDPOINT +# endif +# else +# include +# include +# include +# include +# endif +#endif // !LOGGING_DISABLE_ASIO_ENDPOINT + + +#ifndef LOGGING_DISABLE_BOOST_POSIX_TIME +# if defined(__has_include) +# if __has_include() +# include +# else +# define LOGGING_DISABLE_BOOST_POSIX_TIME +# endif +# else +# include +# endif +#endif + +#ifndef LOGGING_DISABLE_BOOST_FILESYSTEM +# if defined(__has_include) +# if __has_include() +# include +# else +# define LOGGING_DISABLE_BOOST_FILESYSTEM +# endif +# else +# include +# endif +#endif + +#ifndef LOGGING_DISABLE_BOOST_STRING_VIEW +# if defined(__has_include) +# if __has_include() +# include +# else +# define LOGGING_DISABLE_BOOST_STRING_VIEW +# endif +# else +# include +# endif +#endif + +#ifdef WIN32 +# ifndef LOGGING_DISABLE_AUTO_UTF8 +# define LOGGING_ENABLE_AUTO_UTF8 +# endif // !LOGGING_DISABLE_WINDOWS_AUTO_UTF8 +#endif // WIN32 + + +////////////////////////////////////////////////////////////////////////// + +#if defined(_WIN32) || defined(WIN32) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif // !WIN32_LEAN_AND_MEAN +# include +#endif // _WIN32 + +#ifdef USE_SYSTEMD_LOGGING +# if __has_include() +# include +# else +# error "systemd/sd-journal.h not found" +# endif +#endif + +////////////////////////////////////////////////////////////////////////// +#ifndef LOGGING_DISABLE_COMPRESS_LOGS +# if defined(__has_include) +# if __has_include() +# include +# ifndef LOGGING_ENABLE_COMPRESS_LOGS +# define LOGGING_ENABLE_COMPRESS_LOGS +# endif +# endif +# else +# ifdef LOGGING_ENABLE_COMPRESS_LOGS +# include +# endif +# endif +#endif + +#if defined(__cpp_lib_format) +# include +#endif + +#if !defined(__cpp_lib_format) +# ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4244 4127) +# endif // _MSC_VER + +# ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexpansion-to-defined" +# endif + +# include +# include +# include + +namespace std { + using ::fmt::format; + using ::fmt::format_to; + using ::fmt::vformat; + using ::fmt::vformat_to; + using ::fmt::make_format_args; +} + +# ifdef __clang__ +# pragma clang diagnostic pop +# endif + +# ifdef _MSC_VER +# pragma warning(pop) +# endif +#endif + + +////////////////////////////////////////////////////////////////////////// +// +// User customization function for hook log, function signature: +// +// bool logger_writer(logger_tag, +// int64_t time, const int& level, const std::string& message); +// + + +namespace util { + + namespace fs = std::filesystem; + +#ifndef LOGGING_DISABLE_BOOST_ASIO_ENDPOINT + namespace net = boost::asio; +#endif + +#ifndef LOG_APPNAME +# define LOG_APPNAME "application" +#endif + +#ifndef LOG_MAXFILE_SIZE +# define LOG_MAXFILE_SIZE (-1) +#endif // LOG_MAXFILE_SIZE + + +#ifdef LOGGING_ENABLE_COMPRESS_LOGS + +namespace logging_compress__ { + + struct closefile_deleter { + void operator()(FILE* fp) const { + fclose(fp); + } + }; + + struct closegz_deleter { + void operator()(gzFile gz) const { + gzclose(gz); + } + }; + + const inline std::string LOGGING_GZ_SUFFIX = ".gz"; + const inline size_t LOGGING_GZ_BUFLEN = 65536; + + inline std::mutex& compress_lock() + { + static std::mutex lock; + return lock; + } + + inline bool do_compress_gz(const std::string& infile) + { + std::string outfile = infile + LOGGING_GZ_SUFFIX; + + gzFile out = gzopen(outfile.c_str(), "wb6f"); + if (!out) + return false; + + using gzFileType = typename std::remove_pointer::type; + std::unique_ptr gz_closer(out); + + FILE* in = fopen(infile.c_str(), "rb"); + if (!in) + return false; + + std::unique_ptr FILE_closer(in); + std::unique_ptr bufs(new char[LOGGING_GZ_BUFLEN]); + char* buf = bufs.get(); + int len; + + for (;;) { + len = (int)fread(buf, 1, sizeof(buf), in); + if (ferror(in)) + return false; + + if (len == 0) + break; + + int total = 0; + int ret; + while (total < len) { + ret = gzwrite(out, buf + total, (unsigned)len - total); + if (ret <= 0) { + // detail error information see gzerror(out, &ret); + return false; + } + total += ret; + } + } + + return true; + } + +} + +#endif + +inline bool global_logging___ = true; + +namespace logger_aux__ { + + constexpr long long epoch___ = 0x19DB1DED53E8000LL; + + inline int64_t gettime() + { +#ifdef WIN32 + static std::tuple + static_start = []() -> + std::tuple + { + LARGE_INTEGER f; + QueryPerformanceFrequency(&f); + + FILETIME ft; +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + GetSystemTimePreciseAsFileTime(&ft); +#else + GetSystemTimeAsFileTime(&ft); +#endif + auto now = (((static_cast(ft.dwHighDateTime)) << 32) + + static_cast(ft.dwLowDateTime) - epoch___) + / 10000; + + LARGE_INTEGER start; + QueryPerformanceCounter(&start); + + return { f.QuadPart / 1000, start.QuadPart, now }; + }(); + + auto [freq, start, now] = static_start; + + LARGE_INTEGER current; + QueryPerformanceCounter(¤t); + + auto elapsed = current.QuadPart - start; + elapsed /= freq; + + return static_cast(now + elapsed); +#else + using std::chrono::system_clock; + auto now = system_clock::now() - + system_clock::time_point(std::chrono::milliseconds(0)); + + return std::chrono::duration_cast< + std::chrono::milliseconds>(now).count(); +#endif + } + + namespace internal { + template + struct Null {}; + inline Null<> localtime_r(...) { return Null<>(); } + inline Null<> localtime_s(...) { return Null<>(); } + inline Null<> gmtime_r(...) { return Null<>(); } + inline Null<> gmtime_s(...) { return Null<>(); } + } + + // Thread-safe replacement for std::localtime + inline bool localtime(std::time_t time, std::tm& tm) + { + struct LocalTime { + std::time_t time_; + std::tm tm_; + + LocalTime(std::time_t t) : time_(t) {} + + bool run() { + using namespace internal; + return handle(localtime_r(&time_, &tm_)); + } + + bool handle(std::tm* tm) { return tm != nullptr; } + + bool handle(internal::Null<>) { + using namespace internal; + return fallback(localtime_s(&tm_, &time_)); + } + + bool fallback(int res) { return res == 0; } + + bool fallback(internal::Null<>) { + using namespace internal; + std::tm* tm = std::localtime(&time_); + if (tm) tm_ = *tm; + return tm != nullptr; + } + }; + + LocalTime lt(time); + if (lt.run()) { + tm = lt.tm_; + return true; + } + + return false; + } + + inline namespace utf { + + inline uint32_t decode(uint32_t* state, uint32_t* codep, uint32_t byte) + { + static constexpr uint8_t utf8d[] = + { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df + 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef + 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff + 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2 + 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4 + 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6 + 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8 + }; + + uint32_t type = utf8d[byte]; + + *codep = (*state != 0) ? + (byte & 0x3fu) | (*codep << 6) : + (0xff >> type) & (byte); + + *state = utf8d[256 + *state * 16 + type]; + return *state; + } + + inline bool utf8_check_is_valid(std::string_view str) + { + uint32_t codepoint; + uint32_t state = 0; + uint8_t* s = (uint8_t*)str.data(); + uint8_t* end = s + str.size(); + + for (; s != end; ++s) + if (decode(&state, &codepoint, *s) == 1) + return false; + + return state == 0; + } + + inline std::optional utf8_convert(std::string_view str) + { + uint8_t* start = (uint8_t*)str.data(); + uint8_t* end = start + str.size(); + + std::wstring wstr; + uint32_t codepoint; + uint32_t state = 0; + + for (; start != end; ++start) + { + switch (decode(&state, &codepoint, *start)) + { + case 0: + if (codepoint <= 0xFFFF) [[likely]] + { + wstr.push_back(static_cast(codepoint)); + continue; + } + wstr.push_back(static_cast(0xD7C0 + (codepoint >> 10))); + wstr.push_back(static_cast(0xDC00 + (codepoint & 0x3FF))); + continue; + case 1: + return {}; + default: + ; + } + } + + if (state != 0) + return {}; + + return wstr; + } + + inline bool append(uint32_t cp, std::string& result) + { + if (!(cp <= 0x0010ffffu && !(cp >= 0xd800u && cp <= 0xdfffu))) + return false; + + if (cp < 0x80) + { + result.push_back(static_cast(cp)); + } + else if (cp < 0x800) + { + result.push_back(static_cast((cp >> 6) | 0xc0)); + result.push_back(static_cast((cp & 0x3f) | 0x80)); + } + else if (cp < 0x10000) + { + result.push_back(static_cast((cp >> 12) | 0xe0)); + result.push_back(static_cast(((cp >> 6) & 0x3f) | 0x80)); + result.push_back(static_cast((cp & 0x3f) | 0x80)); + } + else { + result.push_back(static_cast((cp >> 18) | 0xf0)); + result.push_back(static_cast(((cp >> 12) & 0x3f) | 0x80)); + result.push_back(static_cast(((cp >> 6) & 0x3f) | 0x80)); + result.push_back(static_cast((cp & 0x3f) | 0x80)); + } + + return true; + } + + inline std::optional utf16_convert(std::wstring_view wstr) + { + std::string result; + + auto end = wstr.cend(); + for (auto start = wstr.cbegin(); start != end;) + { + uint32_t cp = static_cast(0xffff & *start++); + + if (cp >= 0xdc00u && cp <= 0xdfffu) [[unlikely]] + return {}; + + if (cp >= 0xd800u && cp <= 0xdbffu) + { + if (start == end) [[unlikely]] + return {}; + + uint32_t trail = static_cast(0xffff & *start++); + if (!(trail >= 0xdc00u && trail <= 0xdfffu)) [[unlikely]] + return {}; + + cp = (cp << 10) + trail + 0xFCA02400; + } + + if (!append(cp, result)) + return {}; + } + + if (result.empty()) + return {}; + + return result; + } + +#ifdef WIN32 + inline std::optional string_wide(const std::string_view& src) + { + auto len = MultiByteToWideChar(CP_ACP, 0, + src.data(), static_cast(src.size()), NULL, 0); + if (len <= 0) + return {}; + + std::wstring ret(len, 0); + MultiByteToWideChar(CP_ACP, 0, + src.data(), static_cast(src.size()), + (LPWSTR)ret.data(), len); + + return ret; + } + + inline std::optional utf8_utf16(const std::string_view& src) + { + auto len = MultiByteToWideChar(CP_UTF8, 0, + src.data(), static_cast(src.size()), NULL, 0); + if (len <= 0) + return {}; + + std::wstring ret(len, 0); + MultiByteToWideChar(CP_UTF8, 0, + src.data(), static_cast(src.size()), + (LPWSTR)ret.data(), len); + + return ret; + } + + inline std::optional utf16_utf8(std::wstring_view utf16) + { + auto len = WideCharToMultiByte(CP_UTF8, 0, + (LPCWCH)utf16.data(), static_cast(utf16.size()), + NULL, 0, NULL, NULL); + if (len <= 0) + return {}; + + std::string ret(len, 0); + WideCharToMultiByte(CP_UTF8, 0, + (LPCWCH)utf16.data(), static_cast(utf16.size()), + (LPSTR)ret.data(), len, NULL, NULL); + + return ret; + } + +#else + inline std::optional string_wide(const std::string_view& src) + { + const char* first = src.data(); + const char* last = src.data() + src.size(); + const char* snext = nullptr; + + std::wstring result(src.size() + 1, wchar_t{ 0 }); + + wchar_t* dest = result.data(); + wchar_t* dnext = nullptr; + + using codecvt_type = std::codecvt; + std::locale sys_locale(""); + mbstate_t in_state; + + auto ret = std::use_facet(sys_locale).in( + in_state, first, last, snext, dest, dest + result.size(), dnext); + if (ret != codecvt_type::ok) + return {}; + + result.resize(static_cast(dnext - dest)); + return result; + } + + inline std::optional utf8_utf16(std::string_view utf8) + { + const char* first = &utf8[0]; + const char* last = first + utf8.size(); + const char8_t* snext = nullptr; + + std::wstring result(utf8.size(), char16_t{ 0 }); + wchar_t* dest = &result[0]; + char16_t* next = nullptr; + + using codecvt_type = std::codecvt; + + codecvt_type* cvt = new codecvt_type; + + // manages reference to codecvt facet to free memory. + std::locale loc; + loc = std::locale(loc, cvt); + + codecvt_type::state_type state{}; + + auto ret = cvt->in( + state, (char8_t*)first, (char8_t*)last, snext, + (char16_t*)dest, (char16_t*)dest + result.size(), next); + if (ret != codecvt_type::ok) + return {}; + + result.resize(static_cast((wchar_t*)next - dest)); + return result; + } + + inline std::optional utf16_utf8(std::wstring_view utf16) + { + auto* first = &utf16[0]; + auto* last = first + utf16.size(); + + std::string result((utf16.size() + 1) * 6, char{ 0 }); + char* dest = &result[0]; + char8_t* next = nullptr; + + using codecvt_type = std::codecvt; + + codecvt_type* cvt = new codecvt_type; + // manages reference to codecvt facet to free memory. + std::locale loc; + loc = std::locale(loc, cvt); + + codecvt_type::state_type state{}; + const char16_t* snext = nullptr; + auto ret = cvt->out( + state, (char16_t*)first, (char16_t*)last, snext, + (char8_t*)dest, (char8_t*)dest + result.size(), next); + if (ret != codecvt_type::ok) + return {}; + + result.resize(static_cast((char*)next - dest)); + return result; + } +#endif + + } // namespace utf + + inline std::string from_u8string(const std::string& s) + { + return s; + } + + inline std::string from_u8string(std::string&& s) + { + return s; + } + +#if defined(__cpp_lib_char8_t) + inline std::string from_u8string(const std::u8string& s) + { + return std::string(s.begin(), s.end()); + } +#endif + + ////////////////////////////////////////////////////////////////////////// + + template + Lock& lock_single() + { + static Lock lock_instance; + return lock_instance; + } + + template + Writer& writer_single(std::string log_path = "") + { + static Writer writer_instance(log_path); + return writer_instance; + } + + inline struct tm* time_to_string(char* buffer, int64_t time) + { + std::time_t rawtime = time / 1000; + thread_local struct tm ptm; + + if (!localtime(rawtime, ptm)) + return nullptr; + + if (!buffer) + return &ptm; + + std::format_to(buffer, + "{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}", + ptm.tm_year + 1900, ptm.tm_mon + 1, ptm.tm_mday, + ptm.tm_hour, ptm.tm_min, ptm.tm_sec, (int)(time % 1000) + ); + + return &ptm; + } +} + + +class auto_logger_file__ +{ + // c++11 noncopyable. + auto_logger_file__(const auto_logger_file__&) = delete; + auto_logger_file__& operator=(const auto_logger_file__&) = delete; + +public: + auto_logger_file__(std::string log_path = "") + { + if (!log_path.empty()) + m_log_path = log_path; + + m_log_path = m_log_path / (LOG_APPNAME + std::string(".log")); + + if (!global_logging___) + return; + + std::error_code ignore_ec; + if (!fs::exists(m_log_path, ignore_ec)) + fs::create_directories( + m_log_path.parent_path(), ignore_ec); + } + ~auto_logger_file__() + { + m_last_time = 0; + } + + typedef std::shared_ptr ofstream_ptr; + + void open(const char* path) + { + m_log_path = path; + + if (!global_logging___) + return; + + std::error_code ignore_ec; + if (!fs::exists(m_log_path, ignore_ec)) + fs::create_directories( + m_log_path.parent_path(), ignore_ec); + } + + std::string log_path() const + { + return m_log_path.string(); + } + + void logging(bool disable) noexcept + { + m_disable_write = disable; + } + + void write([[maybe_unused]] int64_t time, + const char* str, std::streamsize size) + { + if (m_disable_write) + return; + + bool condition = false; + auto hours = time / 1000 / 3600; + auto last_hours = m_last_time / 1000 / 3600; + + if (static_cast(m_log_size) > LOG_MAXFILE_SIZE && + LOG_MAXFILE_SIZE > 0) + condition = true; + + if (last_hours != hours && LOG_MAXFILE_SIZE < 0) + condition = true; + + while (condition) { + if (m_last_time == -1) { + m_last_time = time; + break; + } + + auto ptm = logger_aux__::time_to_string(nullptr, m_last_time); + + m_ofstream->close(); + m_ofstream.reset(); + + auto logpath = fs::path(m_log_path.parent_path()); + fs::path filename; + + if constexpr (LOG_MAXFILE_SIZE <= 0) { + auto logfile = std::format("{:04d}{:02d}{:02d}-{:02d}.log", + ptm->tm_year + 1900, + ptm->tm_mon + 1, + ptm->tm_mday, + ptm->tm_hour); + filename = logpath / logfile; + } else { + auto utc_time = std::mktime(ptm); + auto logfile = std::format("{:04d}{:02d}{:02d}-{}.log", + ptm->tm_year + 1900, + ptm->tm_mon + 1, + ptm->tm_mday, + utc_time); + filename = logpath / logfile; + } + + m_last_time = time; + + std::error_code ec; + if (!fs::copy_file(m_log_path, filename, ec)) + break; + + fs::resize_file(m_log_path, 0, ec); + m_log_size = 0; + +#ifdef LOGGING_ENABLE_COMPRESS_LOGS + auto fn = filename.string(); + std::thread th([fn]() + { + std::error_code ignore_ec; + std::mutex& m = logging_compress__::compress_lock(); + std::lock_guard lock(m); + if (!logging_compress__::do_compress_gz(fn)) + { + auto file = fn + logging_compress__::LOGGING_GZ_SUFFIX; + fs::remove(file, ignore_ec); + if (ignore_ec) + std::cerr + << "delete log failed: " << file + << ", error code: " << ignore_ec.message() + << std::endl; + return; + } + + fs::remove(fn, ignore_ec); + }); + th.detach(); +#endif + break; + } + + if (!m_ofstream) { + m_ofstream.reset(new std::ofstream); + auto& ofstream = *m_ofstream; + ofstream.open(m_log_path.string().c_str(), + std::ios_base::out | std::ios_base::app); + ofstream.sync_with_stdio(false); + } + + if (m_ofstream->is_open()) { + m_log_size += size; + m_ofstream->write(str, size); + m_ofstream->flush(); + } + } + +private: + fs::path m_log_path{"./logs"}; + ofstream_ptr m_ofstream; + int64_t m_last_time{ -1 }; + std::size_t m_log_size{ 0 }; + bool m_disable_write{ false }; +}; + +#ifndef DISABLE_LOGGER_THREAD_SAFE +#define LOGGER_LOCKS_() std::lock_guard \ + lock(logger_aux__::lock_single()) +#else +#define LOGGER_LOCKS_() ((void)0) +#endif // LOGGER_THREAD_SAFE + +#ifndef LOGGER_DBG_VIEW_ +#if defined(WIN32) && \ + (defined(LOGGER_DBG_VIEW) || \ + defined(DEBUG) || \ + defined(_DEBUG)) +#define LOGGER_DBG_VIEW_(x) \ + do { \ + ::OutputDebugStringW((x).c_str()); \ + } while (0) +#else +#define LOGGER_DBG_VIEW_(x) ((void)0) +#endif // WIN32 && LOGGER_DBG_VIEW +#endif // LOGGER_DBG_VIEW_ + +const inline int _logger_debug_id__ = 0; +const inline int _logger_info_id__ = 1; +const inline int _logger_warn_id__ = 2; +const inline int _logger_error_id__ = 3; +const inline int _logger_file_id__ = 4; + +const inline std::string _LOGGER_DEBUG_STR__ = " DEBUG "; +const inline std::string _LOGGER_INFO_STR__ = " INFO "; +const inline std::string _LOGGER_WARN_STR__ = " WARN "; +const inline std::string _LOGGER_ERR_STR__ = " ERROR "; +const inline std::string _LOGGER_FILE_STR__ = " FILE "; + +inline void logger_output_console__([[maybe_unused]] bool disable_cout, + [[maybe_unused]] const int& level, + [[maybe_unused]] const std::string& prefix, + [[maybe_unused]] const std::string& message) noexcept +{ +#if defined(WIN32) + +#if !defined(DISABLE_LOGGER_TO_CONSOLE) || !defined(DISABLE_LOGGER_TO_DBGVIEW) + std::wstring title = *logger_aux__::utf8_utf16(prefix); + std::wstring msg = *logger_aux__::utf8_utf16(message); +#endif + +#if !defined(DISABLE_LOGGER_TO_CONSOLE) + if (!disable_cout) + { + HANDLE handle_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(handle_stdout, &csbi); + if (level == _logger_info_id__) + SetConsoleTextAttribute(handle_stdout, + FOREGROUND_GREEN); + else if (level == _logger_debug_id__) + SetConsoleTextAttribute(handle_stdout, + FOREGROUND_GREEN | FOREGROUND_INTENSITY); + else if (level == _logger_warn_id__) + SetConsoleTextAttribute(handle_stdout, + FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY); + else if (level == _logger_error_id__) + SetConsoleTextAttribute(handle_stdout, + FOREGROUND_RED | FOREGROUND_INTENSITY); + + WriteConsoleW(handle_stdout, + title.data(), (DWORD)title.size(), nullptr, nullptr); + SetConsoleTextAttribute(handle_stdout, + FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); + + WriteConsoleW(handle_stdout, + msg.data(), (DWORD)msg.size(), nullptr, nullptr); + SetConsoleTextAttribute(handle_stdout, csbi.wAttributes); + } +#endif + +#if !defined(DISABLE_LOGGER_TO_DBGVIEW) + LOGGER_DBG_VIEW_(title + msg); +#endif + +#elif !defined(DISABLE_LOGGER_TO_CONSOLE) + if (!disable_cout) + { + std::string out; + if (level == _logger_info_id__) + std::format_to(std::back_inserter(out), + "\033[32m{}\033[0m{}", prefix, message); + else if (level == _logger_debug_id__) + std::format_to(std::back_inserter(out), + "\033[1;32m{}\033[0m{}", prefix, message); + else if (level == _logger_warn_id__) + std::format_to(std::back_inserter(out), + "\033[1;33m{}\033[0m{}", prefix, message); + else if (level == _logger_error_id__) + std::format_to(std::back_inserter(out), + "\033[1;31m{}\033[0m{}", prefix, message); + std::cout << out; + std::cout.flush(); + } +#endif +} + +#ifdef USE_SYSTEMD_LOGGING +inline void logger_output_systemd__( + const int& level, const std::string& message) noexcept +{ + if (level == _logger_info_id__) + sd_journal_print(LOG_INFO, "%s", message.c_str()); + else if (level == _logger_debug_id__) + sd_journal_print(LOG_DEBUG, "%s", message.c_str()); + else if (level == _logger_warn_id__) + sd_journal_print(LOG_WARNING, "%s", message.c_str()); + else if (level == _logger_error_id__) + sd_journal_print(LOG_ERR, "%s", message.c_str()); +} +#endif // USE_SYSTEMD_LOGGING + +inline const std::string& logger_level_string__(const int& level) noexcept +{ + switch (level) + { + case _logger_debug_id__: + return _LOGGER_DEBUG_STR__; + case _logger_info_id__: + return _LOGGER_INFO_STR__; + case _logger_warn_id__: + return _LOGGER_WARN_STR__; + case _logger_error_id__: + return _LOGGER_ERR_STR__; + case _logger_file_id__: + return _LOGGER_FILE_STR__; + } + + BOOST_ASSERT(false && "invalid logging level!"); + return _LOGGER_DEBUG_STR__; +} + +struct logger_tag +{}; + +namespace access +{ + namespace detail + { + template + bool tag_invoke(T...) noexcept + { + return false; + } + + struct tag_invoke_t + { + template + bool operator()(Tag tag, + int64_t time, + const int& level, + const std::string& message) noexcept + { + return tag_invoke( + std::forward(tag), + time, + level, + message); + } + }; + } + + inline detail::tag_invoke_t tag_invoke{}; +} + +inline void logger_writer__(int64_t time, const int& level, + const std::string& message, + [[maybe_unused]] bool disable_cout = false) noexcept +{ + LOGGER_LOCKS_(); + static auto& logger = util::logger_aux__::writer_single< + util::auto_logger_file__>(); + char ts[64] = { 0 }; + [[maybe_unused]] auto ptm = logger_aux__::time_to_string(ts, time); + std::string prefix = ts + logger_level_string__(level); + std::string tmp = message + "\n"; + std::string whole = prefix + tmp; + + // User log hook. + if (access::tag_invoke(logger_tag(), time, level, message)) + return; + +#ifndef DISABLE_WRITE_LOGGING + logger.write(time, whole.c_str(), whole.size()); +#endif // !DISABLE_WRITE_LOGGING + logger_output_console__(disable_cout, level, prefix, tmp); +#ifdef USE_SYSTEMD_LOGGING + logger_output_systemd__(level, message); +#endif // USE_SYSTEMD_LOGGING +} + +#if defined(_WIN32) || defined(WIN32) +static LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS* info); +#endif +void signal_handler(int); + +namespace logger_aux__ { + using namespace std::chrono_literals; + + class async_logger___ + { + struct internal_message + { + int level_; + int64_t time_; + std::string message_; + bool disable_cout_; + }; + + // c++11 noncopyable. + async_logger___(const async_logger___&) = delete; + async_logger___& operator=(const async_logger___&) = delete; + + public: + async_logger___() + { + // 实现Crash handler以接管在crash时 + // 不会漏写日志. + +#if defined(_WIN32) || defined(WIN32) + m_unexpected_exception_handler = + SetUnhandledExceptionFilter(unexpectedExceptionHandling); +#endif + signal(SIGTERM, signal_handler); + signal(SIGABRT, signal_handler); + signal(SIGFPE, signal_handler); + signal(SIGSEGV, signal_handler); + signal(SIGILL, signal_handler); + } + ~async_logger___() + { + m_abort = true; + if (m_bg_thread.joinable()) + m_bg_thread.join(); + } + + public: +#if defined(_WIN32) || defined(WIN32) + LPTOP_LEVEL_EXCEPTION_FILTER oldUnhandledExceptionFilter() + { + return m_unexpected_exception_handler; + } +#endif + + void stop() + { + m_abort = true; + } + + void internal_work() + { + while (!m_abort || !m_messages.empty()) + { + std::unique_lock lock(m_bg_mutex); + + if (m_messages.empty()) + m_bg_cv.wait_for(lock, 128ms); + + while (!m_messages.empty()) + { + auto message = std::move(m_messages.front()); + m_messages.pop_front(); + + logger_writer__(message.time_, + message.level_, + message.message_, + message.disable_cout_); + } + } + } + + void post_log(const int& level, + std::string&& message, bool disable_cout = false) + { + [[maybe_unused]] static auto runthread = + &(m_bg_thread = std::thread([this]() + { + internal_work(); + })); + + auto time = logger_aux__::gettime(); + std::unique_lock lock(m_bg_mutex); + + m_messages.emplace_back( + internal_message + { + .level_ = level, + .time_ = time, + .message_ = std::move(message), + .disable_cout_ = disable_cout + } + ); + lock.unlock(); + + m_bg_cv.notify_one(); + } + + private: + std::thread m_bg_thread; + std::mutex m_bg_mutex; + std::condition_variable m_bg_cv; + std::deque m_messages; + std::atomic_bool m_abort{ false }; +#if defined(_WIN32) || defined(WIN32) + LPTOP_LEVEL_EXCEPTION_FILTER m_unexpected_exception_handler{ nullptr }; +#endif + }; +} + +inline std::shared_ptr global_logger_obj___ = + std::make_shared(); + +#if defined(_WIN32) || defined(WIN32) +static LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS* e) +{ + if (!global_logger_obj___) + return EXCEPTION_CONTINUE_SEARCH; + + auto old = global_logger_obj___->oldUnhandledExceptionFilter(); + SetUnhandledExceptionFilter(old); + + global_logger_obj___.reset(); + + return old(e); +} +#endif + +inline void signal_handler(int) +{ + global_logger_obj___.reset(); +} + +inline void init_logging(const std::string& path = "") +{ + logger_aux__::writer_single(path); +} + +inline std::string log_path() +{ + auto_logger_file__& file = + logger_aux__::writer_single(); + return file.log_path(); +} + +inline void shutdown_logging() +{ + auto& log_obj = global_logger_obj___; + if (log_obj) { + log_obj->stop(); + log_obj.reset(); + } +} + +inline void toggle_logging() +{ + global_logging___ = !global_logging___; +} + +inline void toggle_write_logging(bool disable) +{ + auto_logger_file__& file = + logger_aux__::writer_single(); + file.logging(disable); +} + +struct auto_init_async_logger +{ + auto_init_async_logger() { + init_logging(); + } + ~auto_init_async_logger() { + shutdown_logging(); + } +}; + +class logger___ +{ + // c++11 noncopyable. + logger___(const logger___&) = delete; + logger___& operator=(const logger___&) = delete; +public: + logger___(const int& level, + bool async = false, bool disable_cout = false) + : level_(level) + , async_(async) + , disable_cout_(disable_cout) + { + if (!global_logging___) + return; + } + ~logger___() + { + if (!global_logging___) + return; + + // if global_logger_obj___ is nullptr, fallback to + // synchronous operation. + if (async_ && global_logger_obj___) + global_logger_obj___->post_log( + level_, std::move(out_), disable_cout_); + else + logger_writer__(logger_aux__::gettime(), + level_, out_, disable_cout_); + } + + template + inline logger___& format_to(std::string_view fmt, Args&&... args) + { + if (!global_logging___) + return *this; + out_ += std::vformat(fmt, + std::make_format_args(std::forward(args)...)); + return *this; + } + + template + inline logger___& strcat_impl(T const& v) noexcept + { + if (!global_logging___) + return *this; + std::format_to(std::back_inserter(out_), "{}", v); + return *this; + } + + inline logger___& operator<<(bool v) + { + return strcat_impl(v); + } + inline logger___& operator<<(char v) + { + return strcat_impl(v); + } + inline logger___& operator<<(short v) + { + return strcat_impl(v); + } + inline logger___& operator<<(unsigned short v) + { + return strcat_impl(v); + } + inline logger___& operator<<(int v) + { + return strcat_impl(v); + } + inline logger___& operator<<(unsigned int v) + { + return strcat_impl(v); + } + inline logger___& operator<<(unsigned long long v) + { + return strcat_impl(v); + } + inline logger___& operator<<(long v) + { + return strcat_impl(v); + } + inline logger___& operator<<(long long v) + { + return strcat_impl(v); + } + inline logger___& operator<<(float v) + { + return strcat_impl(v); + } + inline logger___& operator<<(double v) + { + return strcat_impl(v); + } + inline logger___& operator<<(long double v) + { + return strcat_impl(v); + } + inline logger___& operator<<(unsigned long int v) + { + return strcat_impl(v); + } + inline logger___& operator<<(const std::string& v) + { +#ifdef LOGGING_ENABLE_AUTO_UTF8 + if (!logger_aux__::utf8_check_is_valid(v)) + { + auto wres = logger_aux__::string_wide(v); + if (wres) + { + auto ret = logger_aux__::utf16_utf8(*wres); + if (ret) + return strcat_impl(*ret); + } + } +#endif + return strcat_impl(v); + } + inline logger___& operator<<(const std::wstring& v) + { + return strcat_impl(*logger_aux__::utf16_utf8(v)); + } + inline logger___& operator<<(const std::u16string& v) + { + return strcat_impl(*logger_aux__::utf16_utf8( + {(const wchar_t*)v.data(), v.size()})); + } +#if (__cplusplus >= 202002L) + inline logger___& operator<<(const std::u8string& v) + { + return strcat_impl(reinterpret_cast(v.c_str())); + } +#endif + inline logger___& operator<<(const std::string_view& v) + { +#ifdef LOGGING_ENABLE_AUTO_UTF8 + if (!logger_aux__::utf8_check_is_valid(v)) + { + auto wres = logger_aux__::string_wide(v); + if (wres) + { + auto ret = logger_aux__::utf16_utf8(*wres); + if (ret) + return strcat_impl(*ret); + } + } +#endif + return strcat_impl(v); + } + inline logger___& operator<<(const boost::string_view& v) + { + std::string_view sv{v.data(), v.length()}; +#ifdef LOGGING_ENABLE_AUTO_UTF8 + if (!logger_aux__::utf8_check_is_valid(sv)) + { + auto wres = logger_aux__::string_wide(sv); + if (wres) + { + auto ret = logger_aux__::utf16_utf8(*wres); + if (ret) + return strcat_impl(*ret); + } + } +#endif + return strcat_impl(sv); + } + inline logger___& operator<<(const char* v) + { + std::string_view sv(v); +#ifdef LOGGING_ENABLE_AUTO_UTF8 + if (!logger_aux__::utf8_check_is_valid(sv)) + { + auto wres = logger_aux__::string_wide(sv); + if (wres) + { + auto ret = logger_aux__::utf16_utf8(*wres); + if (ret) + return strcat_impl(*ret); + } + } +#endif + return strcat_impl(sv); + } + inline logger___& operator<<(const wchar_t* v) + { + return strcat_impl(*logger_aux__::utf16_utf8(v)); + } + inline logger___& operator<<(const void *v) + { + if (!global_logging___) + return *this; + std::format_to(std::back_inserter(out_), "{:#010x}", (std::size_t)v); + return *this; + } + inline logger___& operator<<(const std::chrono::nanoseconds& v) + { + if (!global_logging___) + return *this; + std::format_to(std::back_inserter(out_), "{}ns", v.count()); + return *this; + } + inline logger___& operator<<(const std::chrono::microseconds& v) + { + if (!global_logging___) + return *this; + std::format_to(std::back_inserter(out_), "{}us", v.count()); + return *this; + } + inline logger___& operator<<(const std::chrono::milliseconds& v) + { + if (!global_logging___) + return *this; + std::format_to(std::back_inserter(out_), "{}ms", v.count()); + return *this; + } + inline logger___& operator<<(const std::chrono::seconds& v) + { + if (!global_logging___) + return *this; + std::format_to(std::back_inserter(out_), "{}s", v.count()); + return *this; + } + inline logger___& operator<<(const std::chrono::minutes& v) + { + if (!global_logging___) + return *this; + std::format_to(std::back_inserter(out_), "{}min", v.count()); + return *this; + } + inline logger___& operator<<(const std::chrono::hours& v) + { + if (!global_logging___) + return *this; + std::format_to(std::back_inserter(out_), "{}h", v.count()); + return *this; + } + +#ifndef LOGGING_DISABLE_BOOST_ASIO_ENDPOINT + inline logger___& operator<<(const net::ip::tcp::endpoint& v) + { + if (!global_logging___) + return *this; + if (v.address().is_v6()) + std::format_to(std::back_inserter(out_), + "[{}]:{}", v.address().to_string(), v.port()); + else + std::format_to(std::back_inserter(out_), + "{}:{}", v.address().to_string(), v.port()); + return *this; + } + inline logger___& operator<<(const net::ip::udp::endpoint& v) + { + if (!global_logging___) + return *this; + if (v.address().is_v6()) + std::format_to(std::back_inserter(out_), + "[{}]:{}", v.address().to_string(), v.port()); + else + std::format_to(std::back_inserter(out_), + "{}:{}", v.address().to_string(), v.port()); + return *this; + } +#endif + +#if (__cplusplus >= 202002L) + inline logger___& operator<<(const std::chrono::days& v) + { + if (!global_logging___) + return *this; + std::format_to(std::back_inserter(out_), "{}d", v.count()); + return *this; + } + inline logger___& operator<<(const std::chrono::weeks& v) + { + if (!global_logging___) + return *this; + std::format_to(std::back_inserter(out_), "{}weeks", v.count()); + return *this; + } + inline logger___& operator<<(const std::chrono::years& v) + { + if (!global_logging___) + return *this; + std::format_to(std::back_inserter(out_), "{}years", v.count()); + return *this; + } + inline logger___& operator<<(const std::chrono::months& v) + { + if (!global_logging___) + return *this; + std::format_to(std::back_inserter(out_), "{}months", v.count()); + return *this; + } + inline logger___& operator<<(const std::chrono::weekday& v) + { + if (!global_logging___) + return *this; + switch (v.c_encoding()) + { +#ifndef __cpp_lib_char8_t + case 0: out_ = "Sunday"; break; + case 1: out_ = "Monday"; break; + case 2: out_ = "Tuesday"; break; + case 3: out_ = "Wednesday"; break; + case 4: out_ = "Thursday"; break; + case 5: out_ = "Friday"; break; + case 6: out_ = "Saturday"; break; +#else + case 0: out_ = logger_aux__::from_u8string(u8"周日"); break; + case 1: out_ = logger_aux__::from_u8string(u8"周一"); break; + case 2: out_ = logger_aux__::from_u8string(u8"周二"); break; + case 3: out_ = logger_aux__::from_u8string(u8"周三"); break; + case 4: out_ = logger_aux__::from_u8string(u8"周四"); break; + case 5: out_ = logger_aux__::from_u8string(u8"周五"); break; + case 6: out_ = logger_aux__::from_u8string(u8"周六"); break; +#endif + } + return *this; + } + inline logger___& operator<<(const std::chrono::year& v) + { + if (!global_logging___) + return *this; +#if 0 + std::format_to(std::back_inserter(out_), + "{:04}", static_cast(v)); +#else + std::format_to(std::back_inserter(out_), + "{:04}{}", static_cast(v), + logger_aux__::from_u8string(u8"年")); +#endif + return *this; + } + inline logger___& operator<<(const std::chrono::month& v) + { + if (!global_logging___) + return *this; + switch (static_cast(v)) + { +#ifndef __cpp_lib_char8_t + case 1: out_ = "January"; break; + case 2: out_ = "February"; break; + case 3: out_ = "March"; break; + case 4: out_ = "April"; break; + case 5: out_ = "May"; break; + case 6: out_ = "June"; break; + case 7: out_ = "July"; break; + case 8: out_ = "August"; break; + case 9: out_ = "September"; break; + case 10: out_ = "October"; break; + case 11: out_ = "November"; break; + case 12: out_ = "December"; break; +#else + case 1: out_ = logger_aux__::from_u8string(u8"01月"); break; + case 2: out_ = logger_aux__::from_u8string(u8"02月"); break; + case 3: out_ = logger_aux__::from_u8string(u8"03月"); break; + case 4: out_ = logger_aux__::from_u8string(u8"04月"); break; + case 5: out_ = logger_aux__::from_u8string(u8"05月"); break; + case 6: out_ = logger_aux__::from_u8string(u8"06月"); break; + case 7: out_ = logger_aux__::from_u8string(u8"07月"); break; + case 8: out_ = logger_aux__::from_u8string(u8"08月"); break; + case 9: out_ = logger_aux__::from_u8string(u8"09月"); break; + case 10: out_ = logger_aux__::from_u8string(u8"10月"); break; + case 11: out_ = logger_aux__::from_u8string(u8"11月"); break; + case 12: out_ = logger_aux__::from_u8string(u8"12月"); break; +#endif + } + return *this; + } + inline logger___& operator<<(const std::chrono::day& v) + { + if (!global_logging___) + return *this; +#ifndef __cpp_lib_char8_t + std::format_to(std::back_inserter(out_), + "{:02}", static_cast(v)); +#else + std::format_to(std::back_inserter(out_), + "{:02}{}", static_cast(v), + logger_aux__::from_u8string(u8"日")); +#endif + return *this; + } +#endif + inline logger___& operator<<(const fs::path& p) noexcept + { + if (!global_logging___) + return *this; + auto ret = logger_aux__::utf16_utf8(p.wstring()); + if (ret) + return strcat_impl(*ret); + return strcat_impl(p.string()); + } +#ifndef LOGGING_DISABLE_BOOST_FILESYSTEM + inline logger___& operator<<(const boost::filesystem::path& p) noexcept + { + if (!global_logging___) + return *this; + auto ret = logger_aux__::utf16_utf8(p.wstring()); + if (ret) + return strcat_impl(*ret); + return strcat_impl(p.string()); + } +#endif +#ifndef LOGGING_DISABLE_BOOST_POSIX_TIME + inline logger___& operator<<(const boost::posix_time::ptime& p) noexcept + { + if (!global_logging___) + return *this; + + if (!p.is_not_a_date_time()) + { + auto date = p.date().year_month_day(); + auto time = p.time_of_day(); + + std::format_to(std::back_inserter(out_), + "{:04}", static_cast(date.year)); + std::format_to(std::back_inserter(out_), + "-{:02}", date.month.as_number()); + std::format_to(std::back_inserter(out_), + "-{:02}", date.day.as_number()); + + std::format_to(std::back_inserter(out_), + " {:02}", time.hours()); + std::format_to(std::back_inserter(out_), + ":{:02}", time.minutes()); + std::format_to(std::back_inserter(out_), + ":{:02}", time.seconds()); + + auto ms = time.total_milliseconds() % 1000; // milliseconds. + if (ms != 0) + std::format_to(std::back_inserter(out_), + ".{:03}", ms); + } + else + { + BOOST_ASSERT("Not a date time" && false); + out_ += "NOT A DATE TIME"; + } + + return *this; + } +#endif + inline logger___& operator<<(const std::thread::id& id) noexcept + { + std::ostringstream oss; + oss << id; + out_ += oss.str(); + return *this; + } + + std::string out_; + const int& level_; + bool async_; + bool disable_cout_; +}; + +class empty_logger___ +{ +public: + template + empty_logger___& operator<<(T const&/*v*/) + { + return *this; + } +}; +} // namespace util + +#undef LOG_DBG +#undef LOG_INFO +#undef LOG_WARN +#undef LOG_ERR +#undef LOG_FILE + +#undef LOG_FMT +#undef LOG_IFMT +#undef LOG_WFMT +#undef LOG_EFMT +#undef LOG_FFMT + +#undef ASYNC_LOGDBG +#undef ASYNC_LOGINFO +#undef ASYNC_LOGWARN +#undef ASYNC_LOGERR +#undef ASYNC_LOGFILE + +#undef ASYNC_LOGFMT +#undef ASYNC_LOGIFMT +#undef ASYNC_LOGWFMT +#undef ASYNC_LOGEFMT +#undef ASYNC_LOGFFMT + +#if (defined(DEBUG) || defined(_DEBUG) || \ + defined(ENABLE_LOGGER)) && !defined(DISABLE_LOGGER) + +#define LOG_DBG util::logger___(util::_logger_debug_id__) +#define LOG_INFO util::logger___(util::_logger_info_id__) +#define LOG_WARN util::logger___(util::_logger_warn_id__) +#define LOG_ERR util::logger___(util::_logger_error_id__) +#define LOG_FILE util::logger___(util::_logger_file_id__, false, true) + +#define LOG_FMT(...) util::logger___( \ + util::_logger_debug_id__).format_to(__VA_ARGS__) +#define LOG_IFMT(...) util::logger___( \ + util::_logger_info_id__).format_to(__VA_ARGS__) +#define LOG_WFMT(...) util::logger___( \ + util::_logger_warn_id__).format_to(__VA_ARGS__) +#define LOG_EFMT(...) util::logger___( \ + util::_logger_error_id__).format_to(__VA_ARGS__) +#define LOG_FFMT(...) util::logger___( \ + util::_logger_file_id__, false, true).format_to(__VA_ARGS__) + +#define ASYNC_LOGDBG util::logger___(util::_logger_debug_id__, true) +#define ASYNC_LOGINFO util::logger___(util::_logger_info_id__, true) +#define ASYNC_LOGWARN util::logger___(util::_logger_warn_id__, true) +#define ASYNC_LOGERR util::logger___(util::_logger_error_id__, true) +#define ASYNC_LOGFILE util::logger___(util::_logger_file_id__, true, true) + +#define ASYNC_LOGFMT(...) util::logger___( \ + util::_logger_debug_id__, true).format_to(__VA_ARGS__) +#define ASYNC_LOGIFMT(...) util::logger___( \ + util::_logger_info_id__, true).format_to(__VA_ARGS__) +#define ASYNC_LOGWFMT(...) util::logger___( \ + util::_logger_warn_id__, true).format_to(__VA_ARGS__) +#define ASYNC_LOGEFMT(...) util::logger___( \ + util::_logger_error_id__, true).format_to(__VA_ARGS__) +#define ASYNC_LOGFFMT(...) util::logger___( \ + util::_logger_file_id__, true, true).format_to(__VA_ARGS__) + +#define ASYNC_VLOGDBG ASYNC_LOGDBG \ + << "(" << __FILE__ << ":" << __LINE__ << "): " +#define ASYNC_VLOGINFO ASYNC_LOGINFO \ + << "(" << __FILE__ << ":" << __LINE__ << "): " +#define ASYNC_VLOGWARN ASYNC_LOGWARN \ + << "(" << __FILE__ << ":" << __LINE__ << "): " +#define ASYNC_VLOGERR ASYNC_LOGERR \ + << "(" << __FILE__ << ":" << __LINE__ << "): " +#define ASYNC_VLOGFILE ASYNC_LOGFILE \ + << "(" << __FILE__ << ":" << __LINE__ << "): " + +#define ASYNC_VLOGFMT(...) (ASYNC_LOGDBG << "(" \ + << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) +#define ASYNC_VLOGIFMT(...) (ASYNC_LOGINFO << "(" \ + << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) +#define ASYNC_VLOGWFMT(...) (ASYNC_LOGWARN << "(" \ + << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) +#define ASYNC_VLOGEFMT(...) (ASYNC_LOGERR << "(" \ + << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) +#define ASYNC_VLOGFFMT(...) (ASYNC_LOGFILE << "(" \ + << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) + +#define VLOG_DBG LOG_DBG << "(" << __FILE__ << ":" << __LINE__ << "): " +#define VLOG_INFO LOG_INFO << "(" << __FILE__ << ":" << __LINE__ << "): " +#define VLOG_WARN LOG_WARN << "(" << __FILE__ << ":" << __LINE__ << "): " +#define VLOG_ERR LOG_ERR << "(" << __FILE__ << ":" << __LINE__ << "): " +#define VLOG_FILE LOG_FILE << "(" << __FILE__ << ":" << __LINE__ << "): " + +#define VLOG_FMT(...) (LOG_DBG << "(" \ + << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) +#define VLOG_IFMT(...) (LOG_INFO << "(" \ + << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) +#define VLOG_WFMT(...) (LOG_WARN << "(" \ + << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) +#define VLOG_EFMT(...) (LOG_ERR << "(" \ + << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) +#define VLOG_FFMT(...) (LOG_FILE << "(" \ + << __FILE__ << ":" << __LINE__ << "): ").format_to(__VA_ARGS__) + + +#define INIT_ASYNC_LOGGING() [[maybe_unused]] \ + util::auto_init_async_logger ____init_logger____ + +#else + +#define LOG_DBG util::empty_logger___() +#define LOG_INFO util::empty_logger___() +#define LOG_WARN util::empty_logger___() +#define LOG_ERR util::empty_logger___() +#define LOG_FILE util::empty_logger___() + +#define LOG_FMT(...) util::empty_logger___() +#define LOG_IFMT(...) util::empty_logger___() +#define LOG_WFMT(...) util::empty_logger___() +#define LOG_EFMT(...) util::empty_logger___() +#define LOG_FFMT(...) util::empty_logger___() + +#define VLOG_DBG(...) util::empty_logger___() +#define VLOG_INFO(...) util::empty_logger___() +#define VLOG_WARN(...) util::empty_logger___() +#define VLOG_ERR(...) util::empty_logger___() +#define VLOG_FILE(...) util::empty_logger___() + +#define VLOG_FMT(...) util::empty_logger___() +#define VLOG_IFMT(...) util::empty_logger___() +#define VLOG_WFMT(...) util::empty_logger___() +#define VLOG_EFMT(...) util::empty_logger___() +#define VLOG_FFMT(...) util::empty_logger___() + +#define ASYNC_LOGDBG util::empty_logger___() +#define ASYNC_LOGINFO util::empty_logger___() +#define ASYNC_LOGWARN util::empty_logger___() +#define ASYNC_LOGERR util::empty_logger___() +#define ASYNC_LOGFILE util::empty_logger___() + +#define ASYNC_LOGFMT(...) util::empty_logger___() +#define ASYNC_LOGIFMT(...) util::empty_logger___() +#define ASYNC_LOGWFMT(...) util::empty_logger___() +#define ASYNC_LOGEFMT(...) util::empty_logger___() +#define ASYNC_LOGFFMT(...) util::empty_logger___() + +#define ASYNC_VLOGDBG LOG_DBG +#define ASYNC_VLOGINFO LOG_INFO +#define ASYNC_VLOGWARN LOG_WARN +#define ASYNC_VLOGERR LOG_ERR +#define ASYNC_VLOGFILE LOG_FILE + +#define ASYNC_VLOGFMT LOG_FMT +#define ASYNC_VLOGIFMT LOG_IFMT +#define ASYNC_VLOGWFMT LOG_WFMT +#define ASYNC_VLOGEFMT LOG_EFMT +#define ASYNC_VLOGFFMT LOG_FFMT + +#define INIT_ASYNC_LOGGING() void + +#endif + +#endif // INCLUDE__2016_10_14__LOGGING_HPP