2#ifndef LOGIT_CPP_HEADER_LOGIT_CPP_LOGIT_LOGGERS_CRASHWINDOWSLOGGER_HPP_INCLUDED
3#define LOGIT_CPP_HEADER_LOGIT_CPP_LOGIT_LOGGERS_CRASHWINDOWSLOGGER_HPP_INCLUDED
20# ifndef WIN32_LEAN_AND_MEAN
21# define WIN32_LEAN_AND_MEAN
35 class CrashWindowsLogger :
public ILogger {
56 : m_log_path(config.log_path),
58 if (m_capacity == 0) {
61 m_file = ::CreateFileA(
64 FILE_SHARE_READ | FILE_SHARE_WRITE,
67 FILE_ATTRIBUTE_NORMAL,
69 active_logger().store(
this, std::memory_order_release);
76 ~CrashWindowsLogger()
override {
78 active_logger().compare_exchange_strong(
79 expected,
nullptr, std::memory_order_release, std::memory_order_relaxed);
80 if (m_file != INVALID_HANDLE_VALUE) {
81 ::CloseHandle(m_file);
88 void log(
const LogRecord& record,
const std::string& message)
override {
89 if (!record.raw_mode &&
90 static_cast<int>(record.log_level) < m_log_level.load(std::memory_order_relaxed)) {
94 const std::size_t capacity = m_capacity;
99 const char* data = message.data();
100 std::size_t length = message.size();
102 std::size_t copy_len = length;
103 if (copy_len >= capacity) {
105 data += length - (capacity - 1);
106 copy_len = capacity - 1;
113 const std::size_t total = copy_len + 1;
114 const std::size_t start = m_next_offset.fetch_add(total, std::memory_order_acq_rel);
115 const std::size_t index = capacity ? (start % capacity) : 0;
118 std::size_t first = capacity - index;
119 if (first > copy_len) {
122 std::memcpy(m_buffer.data() + index, data, first);
123 const std::size_t remaining = copy_len - first;
125 std::memcpy(m_buffer.data(), data + first, remaining);
129 const std::size_t newline_index = capacity ? ((index + copy_len) % capacity) : 0;
130 m_buffer[newline_index] =
'\n';
132 const std::size_t desired = start + total;
133 std::size_t expected = m_committed.load(std::memory_order_relaxed);
134 while (expected < desired &&
135 !m_committed.compare_exchange_weak(
136 expected, desired, std::memory_order_release, std::memory_order_relaxed)) {
148 return std::string();
165 m_log_level.store(
static_cast<int>(level), std::memory_order_relaxed);
170 return static_cast<LogLevel>(m_log_level.load(std::memory_order_relaxed));
174 void wait()
override {}
178 ::SetUnhandledExceptionFilter(&CrashWindowsLogger::exception_filter);
182 static LONG WINAPI exception_filter(EXCEPTION_POINTERS* ex_info) {
184 if (logger !=
nullptr) {
186 if (ex_info !=
nullptr && ex_info->ExceptionRecord !=
nullptr) {
187 code = ex_info->ExceptionRecord->ExceptionCode;
189 logger->write_snapshot(code);
192 return EXCEPTION_EXECUTE_HANDLER;
195 void write_snapshot(DWORD exception_code)
const noexcept {
196 if (m_file == INVALID_HANDLE_VALUE) {
200 const std::size_t capacity = m_capacity;
201 const std::size_t committed = m_committed.load(std::memory_order_acquire);
202 std::size_t available = committed;
203 if (available > capacity) {
204 available = capacity;
206 const std::size_t start_offset = committed - available;
207 const std::size_t start_index = capacity ? (start_offset % capacity) : 0;
210 std::size_t chunk = capacity - start_index;
211 if (chunk > available) {
214 safe_write(m_buffer.data() + start_index, chunk);
215 const std::size_t remaining = available - chunk;
217 safe_write(m_buffer.data(), remaining);
221 write_marker(exception_code);
222 ::FlushFileBuffers(m_file);
225 void safe_write(
const char* data, std::size_t size)
const noexcept {
226 if (m_file == INVALID_HANDLE_VALUE) {
231 DWORD to_write =
static_cast<DWORD
>(size);
233 if (!::WriteFile(m_file, data, to_write, &written,
nullptr) || written == 0) {
241 void write_marker(DWORD exception_code)
const noexcept {
244 const char prefix[] =
"\n== CRASH EXCEPTION 0x";
245 const char suffix[] =
" ==\n";
246 for (std::size_t i = 0; i <
sizeof(prefix) - 1 && idx <
sizeof(marker); ++i) {
247 marker[idx++] = prefix[i];
250 unsigned long code = exception_code;
252 std::size_t digit_count = 0;
254 digits[digit_count++] =
'0';
256 while (code > 0 && digit_count <
sizeof(digits)) {
257 unsigned long value = code & 0xF;
258 digits[digit_count++] =
static_cast<char>((value < 10) ? (
'0' + value) : (
'A' + (value - 10)));
263 while (digit_count > 0 && idx <
sizeof(marker)) {
264 marker[idx++] = digits[--digit_count];
267 for (std::size_t i = 0; i <
sizeof(suffix) - 1 && idx <
sizeof(marker); ++i) {
268 marker[idx++] = suffix[i];
271 safe_write(marker, idx);
274 std::string m_log_path;
275 HANDLE m_file{INVALID_HANDLE_VALUE};
276 std::size_t m_capacity{0};
277 std::atomic<std::size_t> m_next_offset{0};
278 std::atomic<std::size_t> m_committed{0};
280 std::array<char, kMaxBufferSize> m_buffer{};
282 static std::atomic<CrashWindowsLogger*>& active_logger()
noexcept {
283 static std::atomic<CrashWindowsLogger*> instance(
nullptr);
305 void log(
const LogRecord& record,
const std::string& message)
override {
312 return std::string();
Defines the interface for loggers used in the logging system.
\class CrashWindowsLogger \brief Stub implementation on non-Windows platforms.
void set_log_level(LogLevel level) override
Sets the minimal log level for this logger.
CrashWindowsLogger(const Config &)
double get_float_param(const LoggerParam ¶m) const override
Retrieves a floating-point parameter from the logger.
void log(const LogRecord &record, const std::string &message) override
Logs a message.
int64_t get_int_param(const LoggerParam ¶m) const override
Retrieves an integer parameter from the logger.
CrashWindowsLogger()=default
LogLevel get_log_level() const override
Gets the minimal log level for this logger.
void wait() override
Waits for all asynchronous logging operations to complete.
static constexpr std::size_t kMaxBufferSize
static void install_exception_handler()
std::string get_string_param(const LoggerParam ¶m) const override
Retrieves a string parameter from the logger.
static constexpr std::size_t kDefaultBufferSize
Optional buffered snapshot access returns empty results by default.
The primary namespace for the LogIt++ library.
@ LOG_LVL_TRACE
Trace level logging.
LoggerParam
Enumeration for different logger parameters that can be retrieved.
@ LastFilePath
The full path of the last file written to.
Stores log metadata and content.