Files

848 lines
17 KiB
C++
Raw Permalink Normal View History

2020-12-05 13:08:24 +01:00
#include "util/logs.hpp"
2020-03-07 13:31:10 +03:00
#include "Utilities/File.h"
#include "Utilities/mutex.h"
2017-11-19 21:59:23 +03:00
#include "Utilities/Thread.h"
#include "Utilities/StrFmt.h"
#include <cstring>
2018-09-19 14:14:04 +03:00
#include <cstdarg>
2016-04-27 01:27:24 +03:00
#include <string>
2017-05-13 21:30:37 +03:00
#include <unordered_map>
2017-11-19 21:59:23 +03:00
#include <thread>
#include <chrono>
2018-09-18 13:07:33 +03:00
#include <cstring>
#include <cerrno>
2023-07-09 10:35:18 +03:00
#include <regex>
2017-11-19 21:59:23 +03:00
using namespace std::literals::chrono_literals;
2016-04-27 01:27:24 +03:00
2017-02-22 12:52:03 +03:00
#ifdef _WIN32
2020-12-22 11:42:57 +03:00
#ifndef NOMINMAX
2017-08-23 20:44:31 +03:00
#define NOMINMAX
2020-12-22 11:42:57 +03:00
#endif
2017-02-22 12:52:03 +03:00
#include <Windows.h>
#else
2017-08-23 20:44:31 +03:00
#include <sys/mman.h>
2018-03-17 00:34:16 +03:00
#include <sys/stat.h>
2017-02-22 12:52:03 +03:00
#endif
2017-08-29 17:11:36 +03:00
#include <zlib.h>
static std::string default_string()
2017-05-13 21:30:37 +03:00
{
if (thread_ctrl::is_main())
{
return {};
}
return fmt::format("TID: %u", thread_ctrl::get_tid());
2017-05-13 21:30:37 +03:00
}
2016-04-27 01:27:24 +03:00
// Thread-specific log prefix provider
thread_local std::string(*g_tls_log_prefix)() = &default_string;
2014-06-17 17:44:03 +02:00
// Another thread-specific callback
thread_local void(*g_tls_log_control)(const char* fmt, u64 progress) = [](const char*, u64){};
2016-08-03 23:51:05 +03:00
template<>
void fmt_class_string<logs::level>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto lev)
{
switch (lev)
{
case logs::level::always: return "Nothing";
case logs::level::fatal: return "Fatal";
case logs::level::error: return "Error";
case logs::level::todo: return "TODO";
case logs::level::success: return "Success";
case logs::level::warning: return "Warning";
case logs::level::notice: return "Notice";
case logs::level::trace: return "Trace";
}
return unknown;
});
}
2016-05-13 17:01:48 +03:00
namespace logs
2014-06-17 17:44:03 +02:00
{
2021-05-19 14:30:39 +03:00
static_assert(std::is_empty_v<message> && sizeof(message) == 1);
static_assert(sizeof(channel) == alignof(channel));
static_assert(uchar(level::always) == 0);
static_assert(uchar(level::fatal) == 1);
static_assert(uchar(level::trace) == 7);
static_assert((offsetof(channel, fatal) & 7) == 1);
static_assert((offsetof(channel, trace) & 7) == 7);
2017-11-19 21:59:23 +03:00
// Memory-mapped buffer size
constexpr u64 s_log_size = 32 * 1024 * 1024;
2022-11-11 18:35:52 +02:00
static_assert(s_log_size * s_log_size > s_log_size && (s_log_size & (s_log_size - 1)) == 0); // Assert on an overflowing value
2017-08-23 20:44:31 +03:00
2016-04-27 01:27:24 +03:00
class file_writer
{
2021-04-03 19:38:02 +03:00
std::thread m_writer{};
fs::file m_fout{};
fs::file m_fout2{};
u64 m_max_size{};
2016-04-27 01:27:24 +03:00
2021-04-03 19:38:02 +03:00
std::unique_ptr<uchar[]> m_fptr{};
2017-11-21 21:45:02 +03:00
z_stream m_zs{};
2021-04-03 19:38:02 +03:00
shared_mutex m_m{};
2017-08-23 20:44:31 +03:00
2025-12-15 21:30:40 +02:00
atomic_t<u64, 128> m_buf{0}; // MSB (39 bits): push begin, LSB (25 bis): push size
atomic_t<u64, 128> m_out{0}; // Amount of bytes written to file
2017-11-19 21:59:23 +03:00
2021-04-03 19:38:02 +03:00
uchar m_zout[65536]{};
2017-11-21 21:45:02 +03:00
// Write buffered logs immediately
bool flush(u64 bufv);
2016-04-27 01:27:24 +03:00
public:
2020-03-06 22:29:16 +03:00
file_writer(const std::string& name, u64 max_size);
2016-04-27 01:27:24 +03:00
2017-08-23 20:44:31 +03:00
virtual ~file_writer();
2016-04-27 01:27:24 +03:00
// Append raw data
2020-12-18 10:39:54 +03:00
void log(const char* text, usz size);
2022-11-11 18:35:52 +02:00
// Ensure written to disk
void sync();
// Close file handle after flushing to disk
void close_prematurely();
2016-04-27 01:27:24 +03:00
};
2020-03-06 22:29:16 +03:00
struct file_listener final : file_writer, public listener
2017-09-16 22:10:55 +03:00
{
2020-03-06 22:29:16 +03:00
file_listener(const std::string& path, u64 max_size);
~file_listener() override = default;
2025-05-06 09:15:05 +02:00
void log(u64 stamp, const message& msg, std::string_view prefix, std::string_view text) override;
2022-11-11 18:35:52 +02:00
void sync() override
{
file_writer::sync();
}
void close_prematurely() override
{
file_writer::close_prematurely();
}
2017-09-16 22:10:55 +03:00
};
2020-03-06 22:29:16 +03:00
struct root_listener final : public listener
2016-04-27 01:27:24 +03:00
{
2020-03-06 22:29:16 +03:00
root_listener() = default;
2016-04-27 01:27:24 +03:00
2020-03-06 22:29:16 +03:00
~root_listener() override = default;
2017-08-23 20:44:31 +03:00
2016-04-27 01:27:24 +03:00
// Encode level, current thread name, channel name and write log message
2025-05-06 09:15:05 +02:00
void log(u64, const message&, std::string_view, std::string_view) override
2020-03-06 22:29:16 +03:00
{
// Do nothing
}
2017-09-16 22:10:55 +03:00
// Channel registry
2021-04-03 19:38:02 +03:00
std::unordered_multimap<std::string, channel*> channels{};
2017-09-16 22:10:55 +03:00
// Messages for delayed listener initialization
2021-04-03 19:38:02 +03:00
std::vector<stored_message> messages{};
2016-04-27 01:27:24 +03:00
};
2020-03-06 22:29:16 +03:00
static root_listener* get_logger()
2014-06-17 17:44:03 +02:00
{
2016-02-02 00:55:43 +03:00
// Use magic static
2020-03-06 22:29:16 +03:00
static root_listener logger{};
2016-07-21 01:00:31 +03:00
return &logger;
2014-06-17 17:44:03 +02:00
}
2016-01-13 00:57:16 +03:00
2017-02-22 12:52:03 +03:00
static u64 get_stamp()
{
static struct time_initializer
{
#ifdef _WIN32
LARGE_INTEGER freq;
LARGE_INTEGER start;
time_initializer()
{
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&start);
}
#else
steady_clock::time_point start = steady_clock::now();
2017-02-22 12:52:03 +03:00
#endif
u64 get() const
{
#ifdef _WIN32
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
const LONGLONG diff = now.QuadPart - start.QuadPart;
return diff / freq.QuadPart * 1'000'000 + diff % freq.QuadPart * 1'000'000 / freq.QuadPart;
#else
return (steady_clock::now() - start).count() / 1000;
2017-02-22 12:52:03 +03:00
#endif
}
} timebase{};
return timebase.get();
}
2017-05-13 21:30:37 +03:00
// Channel registry mutex
2020-01-31 12:01:17 +03:00
static shared_mutex g_mutex;
2017-05-13 21:30:37 +03:00
2017-08-21 00:58:25 +03:00
// Must be set to true in main()
2020-01-31 12:01:17 +03:00
static atomic_t<bool> g_init{false};
2017-08-21 00:58:25 +03:00
2017-05-13 21:30:37 +03:00
void reset()
{
std::lock_guard lock(g_mutex);
2017-05-13 21:30:37 +03:00
2017-09-16 22:10:55 +03:00
for (auto&& pair : get_logger()->channels)
2017-05-13 21:30:37 +03:00
{
2026-03-04 19:31:50 +01:00
pair.second->enabled.release(level::_default);
2017-05-13 21:30:37 +03:00
}
}
2020-01-31 15:18:25 +03:00
void silence()
{
std::lock_guard lock(g_mutex);
for (auto&& pair : get_logger()->channels)
{
pair.second->enabled.release(level::always);
2020-01-31 15:18:25 +03:00
}
}
2017-05-13 21:30:37 +03:00
void set_level(const std::string& ch_name, level value)
{
std::lock_guard lock(g_mutex);
2017-05-13 21:30:37 +03:00
2023-07-09 10:35:18 +03:00
if (ch_name.find_first_of(".+*?^$()[]{}|\\") != umax)
{
const std::regex ex(ch_name);
// RegEx pattern
for (auto& channel_pair : get_logger()->channels)
{
std::smatch sm;
if (std::regex_match(channel_pair.first, sm, ex))
{
channel_pair.second->enabled.release(value);
}
}
return;
}
2020-01-31 12:01:17 +03:00
auto found = get_logger()->channels.equal_range(ch_name);
while (found.first != found.second)
{
found.first->second->enabled.release(value);
2020-01-31 12:01:17 +03:00
found.first++;
}
2017-05-13 21:30:37 +03:00
}
2017-08-21 00:58:25 +03:00
2020-01-31 12:09:34 +03:00
level get_level(const std::string& ch_name)
{
std::lock_guard lock(g_mutex);
2025-05-06 09:15:05 +02:00
const auto found = get_logger()->channels.equal_range(ch_name);
2020-01-31 12:09:34 +03:00
if (found.first != found.second)
{
return found.first->second->enabled.observe();
2020-01-31 12:09:34 +03:00
}
return level::always;
2020-01-31 12:09:34 +03:00
}
2021-11-28 09:30:41 +02:00
void set_channel_levels(const std::map<std::string, logs::level, std::less<>>& map)
2020-03-28 15:28:23 +01:00
{
for (auto&& pair : map)
{
logs::set_level(pair.first, pair.second);
}
}
2026-03-04 19:31:50 +01:00
std::set<std::string> get_channels()
2020-01-31 15:04:40 +03:00
{
2026-03-04 19:31:50 +01:00
std::set<std::string> result;
2020-01-31 15:04:40 +03:00
std::lock_guard lock(g_mutex);
for (auto&& p : get_logger()->channels)
{
2026-03-04 19:31:50 +01:00
if (!p.first.empty())
2020-01-31 15:04:40 +03:00
{
2026-03-04 19:31:50 +01:00
result.insert(p.first);
2020-01-31 15:04:40 +03:00
}
}
return result;
}
2017-08-21 00:58:25 +03:00
// Must be called in main() to stop accumulating messages in g_messages
2020-03-06 22:29:16 +03:00
void set_init(std::initializer_list<stored_message> init_msg)
2017-08-21 00:58:25 +03:00
{
if (!g_init)
{
std::lock_guard lock(g_mutex);
2020-03-06 22:29:16 +03:00
// Prepend main messages
for (const auto& msg : init_msg)
{
get_logger()->broadcast(msg);
}
// Send initial messages
for (const auto& msg : get_logger()->messages)
{
get_logger()->broadcast(msg);
}
// Clear it
2017-09-16 22:10:55 +03:00
get_logger()->messages.clear();
2017-08-21 00:58:25 +03:00
g_init = true;
}
}
2014-06-17 17:44:03 +02:00
}
2017-01-25 02:22:19 +03:00
logs::listener::~listener()
{
2020-02-29 17:19:53 +03:00
// Shut up all channels on exit
if (auto logger = get_logger())
{
if (logger == this)
{
return;
}
for (auto&& pair : logger->channels)
{
pair.second->enabled.release(level::always);
2020-02-29 17:19:53 +03:00
}
}
2017-01-25 02:22:19 +03:00
}
2016-07-21 01:00:31 +03:00
void logs::listener::add(logs::listener* _new)
{
// Get first (main) listener
listener* lis = get_logger();
std::lock_guard lock(g_mutex);
2017-08-21 00:58:25 +03:00
2016-07-21 01:00:31 +03:00
// Install new listener at the end of linked list
2020-03-07 12:29:23 +03:00
listener* null = nullptr;
while (lis->m_next || !lis->m_next.compare_exchange(null, _new))
2016-07-21 01:00:31 +03:00
{
lis = lis->m_next;
2020-03-07 12:29:23 +03:00
null = nullptr;
2016-07-21 01:00:31 +03:00
}
2020-03-06 22:29:16 +03:00
}
2017-08-21 00:58:25 +03:00
2020-03-06 22:29:16 +03:00
void logs::listener::broadcast(const logs::stored_message& msg) const
{
for (auto lis = m_next.load(); lis; lis = lis->m_next)
2017-08-21 00:58:25 +03:00
{
2020-03-06 22:29:16 +03:00
lis->log(msg.stamp, msg.m, msg.prefix, msg.text);
2017-08-21 00:58:25 +03:00
}
2016-07-21 01:00:31 +03:00
}
2022-11-11 18:35:52 +02:00
void logs::listener::sync()
{
}
void logs::listener::close_prematurely()
{
}
2022-11-11 18:35:52 +02:00
void logs::listener::sync_all()
{
for (listener* lis = get_logger(); lis; lis = lis->m_next)
{
lis->sync();
}
}
2026-04-09 23:51:34 +02:00
void logs::listener::shutdown_all()
{
std::lock_guard lock(g_mutex);
for (listener* lis = get_logger()->m_next.exchange(nullptr); lis;)
{
lis = lis->m_next.exchange(nullptr);
}
}
void logs::listener::close_all_prematurely()
{
for (listener* lis = get_logger(); lis; lis = lis->m_next)
{
lis->close_prematurely();
}
}
logs::registerer::registerer(channel& _ch)
2020-01-31 12:01:17 +03:00
{
std::lock_guard lock(g_mutex);
get_logger()->channels.emplace(_ch.name, &_ch);
2020-01-31 12:01:17 +03:00
}
2018-09-19 14:14:04 +03:00
void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, ...) const
2014-06-17 17:44:03 +02:00
{
2017-02-22 12:52:03 +03:00
// Get timestamp
const u64 stamp = get_stamp();
// Notify start operation
g_tls_log_control(fmt, 0);
2018-09-19 14:14:04 +03:00
// Get text, extract va_args
2025-06-01 18:43:05 +02:00
thread_local std::string text;
thread_local std::vector<u64> args;
2018-09-19 14:14:04 +03:00
static constexpr fmt_type_info empty_sup{};
2020-12-18 10:39:54 +03:00
usz args_count = 0;
for (auto v = sup; v && v->fmt_string; v++)
2018-09-19 14:14:04 +03:00
args_count++;
2025-06-01 18:43:05 +02:00
text.clear();
2018-09-19 14:14:04 +03:00
args.resize(args_count);
va_list c_args;
va_start(c_args, sup);
for (u64& arg : args)
arg = va_arg(c_args, u64);
va_end(c_args);
fmt::raw_append(text, fmt, sup ? sup : &empty_sup, args.data());
2017-05-13 21:30:37 +03:00
std::string prefix = g_tls_log_prefix();
2016-07-21 01:00:31 +03:00
// Get first (main) listener
listener* lis = get_logger();
2017-08-21 00:58:25 +03:00
if (!g_init)
{
std::lock_guard lock(g_mutex);
2017-08-21 00:58:25 +03:00
if (!g_init)
{
while (lis)
{
lis->log(stamp, *this, prefix, text);
lis = lis->m_next;
}
// Store message additionally
2017-09-16 22:10:55 +03:00
get_logger()->messages.emplace_back(stored_message{*this, stamp, std::move(prefix), text});
2017-08-21 00:58:25 +03:00
}
}
2016-07-21 01:00:31 +03:00
// Send message to all listeners
while (lis)
{
2017-02-22 12:52:03 +03:00
lis->log(stamp, *this, prefix, text);
2016-07-21 01:00:31 +03:00
lis = lis->m_next;
}
// Notify end operation
g_tls_log_control(fmt, -1);
2014-06-17 17:44:03 +02:00
}
2020-03-06 22:29:16 +03:00
logs::file_writer::file_writer(const std::string& name, u64 max_size)
: m_max_size(max_size)
2014-06-17 17:44:03 +02:00
{
2024-10-14 20:06:17 +02:00
if (name.empty() || !max_size)
2014-06-17 17:44:03 +02:00
{
2024-10-14 20:06:17 +02:00
return;
}
2017-08-30 17:15:35 +03:00
2024-10-14 20:06:17 +02:00
// Initialize ringbuffer
m_fptr = std::make_unique<uchar[]>(s_log_size);
2017-11-20 01:01:29 +03:00
2024-10-14 20:06:17 +02:00
// Actual log file (allowed to fail)
if (!m_fout.open(name, fs::rewrite))
{
fprintf(stderr, "Log file open failed: %s (error %d)\n", name.c_str(), errno);
}
// Compressed log, make it inaccessible (foolproof)
if (m_fout2.open(name + ".gz", fs::rewrite + fs::unread))
{
#ifndef _MSC_VER
2020-02-04 21:37:00 +03:00
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
2024-10-14 20:06:17 +02:00
if (deflateInit2(&m_zs, 9, Z_DEFLATED, 16 + 15, 9, Z_DEFAULT_STRATEGY) != Z_OK)
#ifndef _MSC_VER
2020-02-04 21:37:00 +03:00
#pragma GCC diagnostic pop
#endif
{
2024-10-14 20:06:17 +02:00
m_fout2.close();
}
2024-10-14 20:06:17 +02:00
}
if (!m_fout2)
{
fprintf(stderr, "Log file open failed: %s.gz (error %d)\n", name.c_str(), errno);
}
2018-03-17 00:34:16 +03:00
#ifdef _WIN32
2024-10-14 20:06:17 +02:00
// Autodelete compressed log file
FILE_DISPOSITION_INFO disp{};
disp.DeleteFileW = true;
SetFileInformationByHandle(m_fout2.get_handle(), FileDispositionInfo, &disp, sizeof(disp));
2018-03-17 00:34:16 +03:00
#endif
2017-11-19 21:59:23 +03:00
m_writer = std::thread([this]()
{
2024-01-27 20:33:54 +01:00
thread_base::set_name("Log Writer");
2021-01-25 21:49:16 +03:00
thread_ctrl::scoped_priority low_prio(-1);
2017-11-19 21:59:23 +03:00
while (true)
{
const u64 bufv = m_buf;
2022-11-11 18:35:52 +02:00
if (bufv % s_log_size)
2017-11-19 21:59:23 +03:00
{
// Wait if threads are writing logs
std::this_thread::yield();
continue;
}
2017-11-21 21:45:02 +03:00
if (!flush(bufv))
2017-11-19 21:59:23 +03:00
{
if (m_out == umax)
2017-11-19 21:59:23 +03:00
{
break;
}
std::this_thread::sleep_for(10ms);
}
}
});
2016-01-13 00:57:16 +03:00
}
2014-06-17 17:44:03 +02:00
2017-08-23 20:44:31 +03:00
logs::file_writer::~file_writer()
{
2017-11-23 18:37:08 +03:00
if (!m_fptr)
{
return;
}
2017-11-19 21:59:23 +03:00
// Stop writer thread
2022-11-11 18:35:52 +02:00
file_writer::sync();
2017-11-19 21:59:23 +03:00
m_out = -1;
m_writer.join();
2017-08-29 17:11:36 +03:00
2017-11-21 21:45:02 +03:00
if (m_fout2)
{
m_zs.avail_in = 0;
m_zs.next_in = nullptr;
do
{
m_zs.avail_out = sizeof(m_zout);
m_zs.next_out = m_zout;
if (deflate(&m_zs, Z_FINISH) == Z_STREAM_ERROR || m_fout2.write(m_zout, sizeof(m_zout) - m_zs.avail_out) != sizeof(m_zout) - m_zs.avail_out)
{
break;
}
}
while (m_zs.avail_out == 0);
deflateEnd(&m_zs);
}
2017-08-23 20:44:31 +03:00
#ifdef _WIN32
// Cancel compressed log file auto-deletion
2019-11-08 00:18:16 +03:00
FILE_DISPOSITION_INFO disp;
disp.DeleteFileW = false;
SetFileInformationByHandle(m_fout2.get_handle(), FileDispositionInfo, &disp, sizeof(disp));
2017-08-23 20:44:31 +03:00
#else
2018-03-17 00:34:16 +03:00
// Restore compressed log file permissions
::fchmod(m_fout2.get_handle(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
2017-08-23 20:44:31 +03:00
#endif
}
2017-11-21 21:45:02 +03:00
bool logs::file_writer::flush(u64 bufv)
{
std::lock_guard lock(m_m);
2017-11-21 21:45:02 +03:00
2022-11-11 18:35:52 +02:00
const u64 read_pos = m_out;
const u64 out_index = read_pos % s_log_size;
const u64 pushed = (bufv / s_log_size) % s_log_size;
2022-11-29 21:56:18 +02:00
const u64 end = std::min<u64>(out_index <= pushed ? read_pos - out_index + pushed : ((read_pos + s_log_size) & ~(s_log_size - 1)), m_max_size);
2017-11-21 21:45:02 +03:00
2022-11-11 18:35:52 +02:00
if (end > read_pos)
2017-11-21 21:45:02 +03:00
{
// Avoid writing too big fragments
2022-11-11 18:35:52 +02:00
const u64 size = std::min<u64>(end - read_pos, sizeof(m_zout) / 2);
2017-11-21 21:45:02 +03:00
// Write uncompressed
2022-11-11 18:35:52 +02:00
if (m_fout && m_fout.write(m_fptr.get() + out_index, size) != size)
2017-11-21 21:45:02 +03:00
{
m_fout.close();
}
// Write compressed
2022-11-11 18:35:52 +02:00
if (m_fout2)
2017-11-21 21:45:02 +03:00
{
2019-10-25 13:32:21 +03:00
m_zs.avail_in = static_cast<uInt>(size);
2022-11-11 18:35:52 +02:00
m_zs.next_in = m_fptr.get() + out_index;
2017-11-21 21:45:02 +03:00
do
{
m_zs.avail_out = sizeof(m_zout);
m_zs.next_out = m_zout;
if (deflate(&m_zs, Z_NO_FLUSH) == Z_STREAM_ERROR || m_fout2.write(m_zout, sizeof(m_zout) - m_zs.avail_out) != sizeof(m_zout) - m_zs.avail_out)
{
deflateEnd(&m_zs);
m_fout2.close();
break;
}
}
while (m_zs.avail_out == 0);
}
m_out += size;
return true;
}
return false;
}
2020-12-18 10:39:54 +03:00
void logs::file_writer::log(const char* text, usz size)
2016-01-13 00:57:16 +03:00
{
2017-11-23 18:37:08 +03:00
if (!m_fptr)
{
return;
}
2017-11-21 21:45:02 +03:00
// TODO: write bigger fragment directly in blocking manner
2022-11-11 18:35:52 +02:00
while (size && size < s_log_size)
{
2022-11-11 18:35:52 +02:00
const auto [bufv, pos] = m_buf.fetch_op([&](u64& v) -> uchar*
2017-11-19 21:59:23 +03:00
{
2022-11-11 18:35:52 +02:00
const u64 out = m_out % s_log_size;
const u64 v1 = (v / s_log_size) % s_log_size;
const u64 v2 = v % s_log_size;
2022-11-29 21:56:18 +02:00
if (v1 + v2 + size >= (out <= v1 ? out + s_log_size : out)) [[unlikely]]
2017-11-19 21:59:23 +03:00
{
return nullptr;
}
2017-08-23 20:44:31 +03:00
2017-11-19 21:59:23 +03:00
v += size;
return m_fptr.get() + (v1 + v2) % s_log_size;
2017-11-19 21:59:23 +03:00
});
2020-02-05 10:00:08 +03:00
if (!pos) [[unlikely]]
2017-11-19 21:59:23 +03:00
{
2022-11-11 18:35:52 +02:00
if (m_out >= m_max_size || (!m_fout && !m_fout2))
{
// Logging is inactive
return;
}
if ((bufv % s_log_size) + size >= s_log_size || bufv % s_log_size)
2017-11-21 21:45:02 +03:00
{
// Concurrency limit reached
std::this_thread::yield();
}
2022-11-11 18:35:52 +02:00
else if (!m_m.is_free())
{
// Wait for another flush call to complete
m_m.lock_unlock();
}
2017-11-21 21:45:02 +03:00
else
{
// Queue is full, need to write out
flush(bufv);
}
2022-11-11 18:35:52 +02:00
2017-11-19 21:59:23 +03:00
continue;
}
2022-11-11 18:35:52 +02:00
if (pos - m_fptr.get() + size > s_log_size)
2017-11-19 21:59:23 +03:00
{
2022-11-11 18:35:52 +02:00
const auto frag = s_log_size - (pos - m_fptr.get());
2017-11-19 21:59:23 +03:00
std::memcpy(pos, text, frag);
std::memcpy(m_fptr.get(), text + frag, size - frag);
2017-11-19 21:59:23 +03:00
}
else
{
std::memcpy(pos, text, size);
}
2022-11-11 18:35:52 +02:00
m_buf += (size * s_log_size) - size;
2017-11-19 21:59:23 +03:00
break;
}
2016-01-13 00:57:16 +03:00
}
2022-11-11 18:35:52 +02:00
void logs::file_writer::sync()
{
2022-11-29 21:56:18 +02:00
if (!m_fptr)
2022-11-11 18:35:52 +02:00
{
return;
}
// Wait for the writer thread
2022-11-29 21:56:18 +02:00
while ((m_out % s_log_size) * s_log_size != m_buf % (s_log_size * s_log_size))
2022-11-11 18:35:52 +02:00
{
if (m_out >= m_max_size)
{
break;
}
std::this_thread::yield();
}
if (thread_ctrl::get_current())
{
return;
}
2022-11-11 18:35:52 +02:00
// Ensure written to disk
if (m_fout)
{
m_fout.sync();
}
if (m_fout2)
{
m_fout2.sync();
}
}
void logs::file_writer::close_prematurely()
{
if (!m_fptr)
{
return;
}
// Ensure written to disk
sync();
std::lock_guard lock(m_m);
if (m_fout2)
{
m_zs.avail_in = 0;
m_zs.next_in = nullptr;
do
{
m_zs.avail_out = sizeof(m_zout);
m_zs.next_out = m_zout;
if (deflate(&m_zs, Z_FINISH) == Z_STREAM_ERROR || m_fout2.write(m_zout, sizeof(m_zout) - m_zs.avail_out) != sizeof(m_zout) - m_zs.avail_out)
{
break;
}
}
while (m_zs.avail_out == 0);
deflateEnd(&m_zs);
#ifdef _WIN32
// Cancel compressed log file auto-deletion
FILE_DISPOSITION_INFO disp;
disp.DeleteFileW = false;
SetFileInformationByHandle(m_fout2.get_handle(), FileDispositionInfo, &disp, sizeof(disp));
#else
// Restore compressed log file permissions
::fchmod(m_fout2.get_handle(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
#endif
m_fout2.close();
}
if (m_fout)
{
m_fout.close();
}
}
2020-03-06 22:29:16 +03:00
logs::file_listener::file_listener(const std::string& path, u64 max_size)
: file_writer(path, max_size)
2017-08-21 00:58:25 +03:00
, listener()
{
// Write UTF-8 BOM
2020-03-06 22:29:16 +03:00
file_writer::log("\xEF\xBB\xBF", 3);
2017-08-21 00:58:25 +03:00
}
2025-05-06 09:15:05 +02:00
void logs::file_listener::log(u64 stamp, const logs::message& msg, std::string_view prefix, std::string_view _text)
2016-01-13 00:57:16 +03:00
{
2020-03-09 19:18:39 +03:00
/*constinit thread_local*/ std::string text;
text.reserve(50000);
2016-01-13 00:57:16 +03:00
// Used character: U+00B7 (Middle Dot)
2021-05-19 14:30:39 +03:00
switch (msg)
2014-06-17 17:44:03 +02:00
{
2020-02-04 21:37:00 +03:00
case level::always: text = reinterpret_cast<const char*>(u8"·A "); break;
case level::fatal: text = reinterpret_cast<const char*>(u8"·F "); break;
case level::error: text = reinterpret_cast<const char*>(u8"·E "); break;
case level::todo: text = reinterpret_cast<const char*>(u8"·U "); break;
case level::success: text = reinterpret_cast<const char*>(u8"·S "); break;
case level::warning: text = reinterpret_cast<const char*>(u8"·W "); break;
case level::notice: text = reinterpret_cast<const char*>(u8"·! "); break;
case level::trace: text = reinterpret_cast<const char*>(u8"·T "); break;
2014-06-17 17:44:03 +02:00
}
// Print microsecond timestamp
2017-02-22 12:52:03 +03:00
const u64 hours = stamp / 3600'000'000;
const u64 mins = (stamp % 3600'000'000) / 60'000'000;
const u64 secs = (stamp % 60'000'000) / 1'000'000;
const u64 frac = (stamp % 1'000'000);
fmt::append(text, "%u:%02u:%02u.%06u ", hours, mins, secs, frac);
2016-01-13 00:57:16 +03:00
2021-05-19 14:30:39 +03:00
if (stamp == 0)
{
// Workaround for first special messages to keep backward compatibility
text.clear();
}
if (!prefix.empty())
2014-06-17 17:44:03 +02:00
{
2016-08-05 19:49:45 +03:00
text += "{";
text += prefix;
text += "} ";
2014-06-17 17:44:03 +02:00
}
2021-05-21 00:02:38 +03:00
if (stamp && msg->name && '\0' != *msg->name)
2014-06-17 17:44:03 +02:00
{
2021-05-19 14:30:39 +03:00
text += msg->name;
text += msg == level::todo ? " TODO: " : ": ";
2014-06-17 17:44:03 +02:00
}
2021-05-19 14:30:39 +03:00
else if (msg == level::todo)
2014-06-17 17:44:03 +02:00
{
2016-07-21 01:00:31 +03:00
text += "TODO: ";
2014-06-17 17:44:03 +02:00
}
2016-08-05 19:49:45 +03:00
text += _text;
2016-07-21 01:00:31 +03:00
text += '\n';
2014-06-17 17:44:03 +02:00
2020-03-06 22:29:16 +03:00
file_writer::log(text.data(), text.size());
}
std::unique_ptr<logs::listener> logs::make_file_listener(const std::string& path, u64 max_size)
{
std::unique_ptr<logs::listener> result = std::make_unique<logs::file_listener>(path, max_size);
// Register file listener
result->add(result.get());
return result;
}