2#ifndef LOGIT_CPP_HEADER_LOGIT_CPP_LOGIT_LOGGERS_CRASHPOSIXLOGGER_HPP_INCLUDED
3#define LOGIT_CPP_HEADER_LOGIT_CPP_LOGIT_LOGGERS_CRASHPOSIXLOGGER_HPP_INCLUDED
16#if defined(__unix__) || defined(__APPLE__) || defined(__linux__)
27#if defined(__unix__) || defined(__APPLE__) || defined(__linux__)
32 class CrashPosixLogger :
public ILogger {
53 m_log_path(config.log_path),
55 if (m_capacity == 0) {
58 int flags = O_WRONLY | O_CREAT | O_APPEND;
62 m_fd = ::open(m_log_path.c_str(), flags,
static_cast<mode_t
>(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
64 if (m_fd < 0 && (flags & O_CLOEXEC) != 0) {
66 m_fd = ::open(m_log_path.c_str(), flags,
static_cast<mode_t
>(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
69 s_active_logger.store(
this, std::memory_order_release);
76 ~CrashPosixLogger()
override {
78 s_active_logger.compare_exchange_strong(
79 expected,
nullptr, std::memory_order_release, std::memory_order_relaxed);
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 {}
179 struct sigaction action;
180 std::memset(&action, 0,
sizeof(action));
181 action.sa_sigaction = &CrashPosixLogger::signal_handler;
182 sigemptyset(&action.sa_mask);
183 action.sa_flags = SA_SIGINFO | SA_RESTART;
184 sigaction(signo, &action,
nullptr);
188 static void signal_handler(
int signo, siginfo_t* info,
void* context) {
192 if (logger !=
nullptr) {
193 logger->write_snapshot(signo);
198 void write_snapshot(
int signo)
const noexcept {
203 const std::size_t capacity = m_capacity;
204 const std::size_t committed = m_committed.load(std::memory_order_acquire);
205 std::size_t available = committed;
206 if (available > capacity) {
207 available = capacity;
209 const std::size_t start_offset = committed - available;
210 const std::size_t start_index = capacity ? (start_offset % capacity) : 0;
213 std::size_t chunk = capacity - start_index;
214 if (chunk > available) {
217 safe_write(m_buffer.data() + start_index, chunk);
218 const std::size_t remaining = available - chunk;
220 safe_write(m_buffer.data(), remaining);
227 void safe_write(
const char* data, std::size_t size)
const noexcept {
229 const ssize_t written = ::write(m_fd, data, size);
231 if (written < 0 && errno == EINTR) {
236 const std::size_t advance =
static_cast<std::size_t
>(written);
242 void write_marker(
int signo)
const noexcept {
245 const char prefix[] =
"\n== CRASH SIGNAL ";
246 const char suffix[] =
" ==\n";
247 for (std::size_t i = 0; i <
sizeof(prefix) - 1 && idx <
sizeof(marker); ++i) {
248 marker[idx++] = prefix[i];
253 if (idx <
sizeof(marker)) {
260 std::size_t digit_count = 0;
261 unsigned int uvalue =
static_cast<unsigned int>(value);
263 digits[digit_count++] =
'0';
265 while (uvalue > 0 && digit_count <
sizeof(digits)) {
266 digits[digit_count++] =
static_cast<char>(
'0' + (uvalue % 10));
270 while (digit_count > 0 && idx <
sizeof(marker)) {
271 marker[idx++] = digits[--digit_count];
274 for (std::size_t i = 0; i <
sizeof(suffix) - 1 && idx <
sizeof(marker); ++i) {
275 marker[idx++] = suffix[i];
278 safe_write(marker, idx);
281 std::string m_log_path;
283 std::size_t m_capacity{0};
284 std::atomic<std::size_t> m_next_offset{0};
285 std::atomic<std::size_t> m_committed{0};
287 std::array<char, kMaxBufferSize> m_buffer{};
289 inline static std::atomic<CrashPosixLogger*> s_active_logger{
nullptr};
309 void log(
const LogRecord& record,
const std::string& message)
override {
316 return std::string();
Defines the interface for loggers used in the logging system.
Stub implementation on non-POSIX platforms.
void set_log_level(LogLevel level) override
Sets the minimal log level for this logger.
CrashPosixLogger()=default
static void install_signal_handler(int signo)
CrashPosixLogger(const Config &)
void wait() override
Waits for all asynchronous logging operations to complete.
int64_t get_int_param(const LoggerParam ¶m) const override
Retrieves an integer parameter from the logger.
void log(const LogRecord &record, const std::string &message) override
Logs a message.
double get_float_param(const LoggerParam ¶m) const override
Retrieves a floating-point parameter from the logger.
static constexpr std::size_t kDefaultBufferSize
std::string get_string_param(const LoggerParam ¶m) const override
Retrieves a string parameter from the logger.
static constexpr std::size_t kMaxBufferSize
LogLevel get_log_level() const override
Gets the minimal log level for this logger.
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.