Skip to content

Instantly share code, notes, and snippets.

@Jackarain
Last active August 16, 2022 14:47
Show Gist options
  • Save Jackarain/513b046867a168b61c83c87d198327f4 to your computer and use it in GitHub Desktop.
Save Jackarain/513b046867a168b61c83c87d198327f4 to your computer and use it in GitHub Desktop.
一个现代c++实现的日志库
//
// Copyright (C) 2019 Jack.
//
// Author: jack
// Email: jack.wgm at gmail dot com
//
#pragma once
#include <version>
#include <codecvt>
#include <clocale>
#include <fstream>
#include <chrono>
#include <list>
#include <mutex>
#include <memory>
#include <string>
#include <tuple>
#include <version>
#include <thread>
#include <functional>
#include <filesystem>
#include <system_error>
#include <boost/asio/thread_pool.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/asio/ip/basic_endpoint.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ip/udp.hpp>
namespace net = boost::asio;
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/nowide/convert.hpp>
#include <boost/utility/string_view.hpp>
//////////////////////////////////////////////////////////////////////////
#if defined(_WIN32) || defined(WIN32)
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif // !WIN32_LEAN_AND_MEAN
# include <mmsystem.h>
# include <windows.h>
# pragma comment(lib, "Winmm.lib")
#endif // _WIN32
#ifdef USE_SYSTEMD_LOGGING
#if __has_include(<systemd/sd-journal.h>)
# include <systemd/sd-journal.h>
#else
#error "systemd/sd-journal.h not found"
#endif
#endif
//////////////////////////////////////////////////////////////////////////
#if defined(__has_include)
# if __has_include(<zlib.h>)
# include <zlib.h>
# ifndef LOGGING_COMPRESS_LOGS
# define LOGGING_COMPRESS_LOGS
# endif
# endif
#else
# ifdef LOGGING_COMPRESS_LOGS
# include <zlib.h>
# endif
#endif
#if defined(__cpp_lib_format)
# include <format>
#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 <fmt/ostream.h>
#include <fmt/printf.h>
#include <fmt/format.h>
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
//////////////////////////////////////////////////////////////////////////
namespace util {
#ifndef LOG_APPNAME
# define LOG_APPNAME "application"
#endif
#ifndef LOG_MAXFILE_SIZE
# define LOG_MAXFILE_SIZE (-1)
#endif // LOG_MAXFILE_SIZE
#ifdef LOGGING_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<gzFile>::type gzFileType;
std::unique_ptr<gzFileType, decltype(&gzclose)> gz_closer(out, &gzclose);
FILE* in = fopen(infile.c_str(), "rb");
if (!in)
return false;
std::unique_ptr<FILE, decltype(&fclose)> FILE_closer(in, &fclose);
std::unique_ptr<char[]> 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) {
return false; // detail error information see gzerror(out, &ret);
}
total += ret;
}
}
return true;
}
}
#endif
namespace logger_aux__ {
inline int64_t gettime()
{
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();
}
namespace internal {
template <typename T = void>
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 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(const std::string& str)
{
uint32_t codepoint;
uint32_t state = 0;
uint8_t* s = (uint8_t*)str.c_str();
uint8_t* end = s + str.size();
for (; s != end; ++s)
if (decode(&state, &codepoint, *s) == 1)
return false;
return state == 0;
}
inline std::string from_u8string(const std::string& s)
{
return s;
}
inline std::string from_u8string(std::string&& s)
{
return std::move(s);
}
#if defined(__cpp_lib_char8_t)
inline std::string from_u8string(const std::u8string& s)
{
return std::string(s.begin(), s.end());
}
#endif
#if 0
inline bool utf8_check_is_valid(const std::string& str)
{
int c, i, ix, n, j;
for (i = 0, ix = static_cast<int>(str.size()); i < ix; i++)
{
c = (unsigned char)str[i];
//if (c==0x09 || c==0x0a || c==0x0d || (0x20 <= c && c <= 0x7e) ) n = 0; // is_printable_ascii
if (0x00 <= c && c <= 0x7f) n = 0; // 0bbbbbbb
else if ((c & 0xE0) == 0xC0) n = 1; // 110bbbbb
else if (c == 0xed && i < (ix - 1) && ((unsigned char)str[i + 1] & 0xa0) == 0xa0)
return false; // U+d800 to U+dfff
else if ((c & 0xF0) == 0xE0) n = 2; // 1110bbbb
else if ((c & 0xF8) == 0xF0) n = 3; // 11110bbb
//else if (($c & 0xFC) == 0xF8) n=4; // 111110bb //byte 5, unnecessary in 4 byte UTF-8
//else if (($c & 0xFE) == 0xFC) n=5; // 1111110b //byte 6, unnecessary in 4 byte UTF-8
else return false;
for (j = 0; j < n && i < ix; j++) // n bytes matching 10bbbbbb follow ?
{
if ((++i == ix) || (((unsigned char)str[i] & 0xC0) != 0x80))
return false;
}
}
return true;
}
#endif
inline bool wide_string(const std::wstring& src, std::string& str)
{
std::locale sys_locale("");
const wchar_t* data_from = src.c_str();
const wchar_t* data_from_end = src.c_str() + src.size();
const wchar_t* data_from_next = 0;
int wchar_size = 4;
std::unique_ptr<char> buffer(new char[(src.size() + 1) * wchar_size]);
char* data_to = buffer.get();
char* data_to_end = data_to + (src.size() + 1) * wchar_size;
char* data_to_next = 0;
memset(data_to, 0, (src.size() + 1) * wchar_size);
typedef std::codecvt<wchar_t, char, mbstate_t> convert_facet;
mbstate_t out_state;
auto result = std::use_facet<convert_facet>(sys_locale).out(
out_state, data_from, data_from_end, data_from_next,
data_to, data_to_end, data_to_next);
if (result == convert_facet::ok)
{
str = data_to;
return true;
}
return false;
}
inline bool string_wide(const std::string& src, std::wstring& wstr)
{
std::locale sys_locale("");
const char* data_from = src.c_str();
const char* data_from_end = src.c_str() + src.size();
const char* data_from_next = 0;
std::vector<wchar_t> buffer(src.size() + 1, 0);
wchar_t* data_to = buffer.data();
wchar_t* data_to_end = data_to + src.size() + 1;
wchar_t* data_to_next = 0;
wmemset(data_to, 0, src.size() + 1);
typedef std::codecvt<wchar_t, char, mbstate_t> convert_facet;
mbstate_t in_state;
auto result = std::use_facet<convert_facet>(sys_locale).in(
in_state, data_from, data_from_end, data_from_next,
data_to, data_to_end, data_to_next);
if (result == convert_facet::ok)
{
wstr = data_to;
return true;
}
return false;
}
inline std::string string_utf8(const std::string& str)
{
if (!logger_aux__::utf8_check_is_valid(str))
{
std::wstring wres;
if (logger_aux__::string_wide(str, wres))
return boost::nowide::narrow(wres);
}
return str;
}
template <class Lock>
Lock& lock_single()
{
static Lock lock_instance;
return lock_instance;
}
template <class Writer>
Writer& writer_single()
{
static Writer writer_instance;
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::sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
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__()
{
m_log_path = m_log_path / (LOG_APPNAME + std::string(".log"));
std::error_code ignore_ec;
if (!std::filesystem::exists(m_log_path, ignore_ec))
std::filesystem::create_directories(m_log_path.parent_path(), ignore_ec);
}
~auto_logger_file__()
{
}
typedef std::shared_ptr<std::ofstream> ofstream_ptr;
void open(const char* path)
{
m_log_path = path;
std::error_code ignore_ec;
if (!std::filesystem::exists(m_log_path, ignore_ec))
std::filesystem::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<int>(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 = std::filesystem::path(m_log_path.parent_path());
std::filesystem::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 (!std::filesystem::copy_file(m_log_path, filename, ec))
break;
std::filesystem::resize_file(m_log_path, 0, ec);
m_log_size = 0;
#ifdef LOGGING_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;
std::filesystem::remove(file, ignore_ec);
if (ignore_ec)
std::cerr
<< "delete log failed: " << file
<< ", error code: " << ignore_ec.message()
<< std::endl;
return;
}
std::filesystem::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:
std::filesystem::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<std::mutex>())
#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__ = "WARNING";
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 = boost::nowide::widen(prefix);
std::wstring msg = boost::nowide::widen(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__;
}
inline void logger_writer__(int64_t time, const int& level,
const std::string& message, [[maybe_unused]] bool disable_cout = false) noexcept
{
LOGGER_LOCKS_();
char ts[64] = { 0 };
[[maybe_unused]] auto ptm = logger_aux__::time_to_string(ts, time);
std::string prefix = ts + std::string(" [") + logger_level_string__(level) + std::string("]: ");
std::string tmp = message + "\n";
std::string whole = prefix + tmp;
#ifndef DISABLE_WRITE_LOGGING
util::logger_aux__::writer_single<util::auto_logger_file__>().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
}
namespace logger_aux__ {
class logger_internal
{
// c++11 noncopyable.
logger_internal(const logger_internal&) = delete;
logger_internal& operator=(const logger_internal&) = delete;
public:
logger_internal()
{
}
~logger_internal()
{
m_io_thread.join();
}
public:
void stop()
{
m_io_thread.stop();
}
void post_log(const int& level,
std::string&& message, bool disable_cout = false)
{
net::post(m_io_thread,
[time = logger_aux__::gettime(), level, message = std::move(message), disable_cout]()
{
logger_writer__(time, level, message, disable_cout);
});
}
private:
net::thread_pool m_io_thread{ 1 };
};
}
inline bool global_logging___ = true;
inline std::shared_ptr<logger_aux__::logger_internal> global_logger_obj___;
inline void init_logging(bool use_async = true, const std::string& path = "")
{
auto_logger_file__& file = logger_aux__::writer_single<util::auto_logger_file__>();
if (!path.empty())
file.open(path.c_str());
auto& log_obj = global_logger_obj___;
if (use_async && !log_obj) {
log_obj.reset(new logger_aux__::logger_internal());
}
}
inline std::string log_path()
{
auto_logger_file__& file = logger_aux__::writer_single<util::auto_logger_file__>();
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<util::auto_logger_file__>();
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 disable_cout = false)
: level_(level)
, disable_cout_(disable_cout)
{
if (!global_logging___)
return;
}
~logger___()
{
if (!global_logging___)
return;
std::string message = logger_aux__::string_utf8(out_);
if (global_logger_obj___)
global_logger_obj___->post_log(level_, std::move(message), disable_cout_);
else
logger_writer__(logger_aux__::gettime(), level_, message, disable_cout_);
}
template <class... Args>
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>(args)...));
return *this;
}
template <class T>
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)
{
return strcat_impl(v);
}
inline logger___& operator<<(const std::wstring& v)
{
return strcat_impl(boost::nowide::narrow(v));
}
inline logger___& operator<<(const std::string_view& v)
{
return strcat_impl(v);
}
inline logger___& operator<<(const boost::string_view& v)
{
return strcat_impl(std::string_view{v.data(), v.length()});
}
inline logger___& operator<<(const char* v)
{
return strcat_impl(v);
}
inline logger___& operator<<(const wchar_t* v)
{
return strcat_impl(boost::nowide::narrow(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;
}
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;
}
#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())
{
#if 0
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<int>(v));
#else
std::format_to(std::back_inserter(out_), "{:04}{}", static_cast<int>(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<unsigned int>(v))
{
#if 0
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;
#if 0
std::format_to(std::back_inserter(out_), "{:02}", static_cast<int>(v));
#else
std::format_to(std::back_inserter(out_), "{:02}{}", static_cast<unsigned int>(v), logger_aux__::from_u8string(u8"日"));
#endif
return *this;
}
#endif
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<unsigned int>(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;
}
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 disable_cout_;
};
class empty_logger___
{
public:
template <class T>
empty_logger___& operator<<(T const&/*v*/)
{
return *this;
}
};
} // namespace util
#if (defined(DEBUG) || defined(_DEBUG) || defined(ENABLE_LOGGER)) && !defined(DISABLE_LOGGER)
#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
#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__, 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__, true).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
#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
#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 LOG_DBG
#define VLOG_INFO LOG_INFO
#define VLOG_WARN LOG_WARN
#define VLOG_ERR LOG_ERR
#define VLOG_FILE LOG_FILE
#define VLOG_FMT LOG_FMT
#define VLOG_IFMT LOG_IFMT
#define VLOG_WFMT LOG_WFMT
#define VLOG_EFMT LOG_EFMT
#define VLOG_FFMT LOG_FFMT
#define INIT_ASYNC_LOGGING() void
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment