Logger System 0.1.3
High-performance C++20 thread-safe logging system with asynchronous capabilities
Loading...
Searching...
No Matches
crash_safe_logger.h
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
11#pragma once
12
14#include <atomic>
15#include <csignal>
16#include <memory>
17#include <mutex>
18
20
62public:
68 static std::shared_ptr<crash_safe_logger> create(
69 std::shared_ptr<logger> underlying_logger = nullptr
70 ) {
71 if (!underlying_logger) {
72 underlying_logger = std::make_shared<logger>(true, 16384);
73 }
74
75 auto safe_logger = std::shared_ptr<crash_safe_logger>(
76 new crash_safe_logger(std::move(underlying_logger))
77 );
78
79 // Register for cleanup
80 global_instance_ = safe_logger;
81
82 return safe_logger;
83 }
84
90 std::lock_guard<std::mutex> lock(handler_mutex_);
91
93 return true; // Already installed
94 }
95
96 // Install handlers for common crash signals
97 struct sigaction sa;
98 sa.sa_handler = &crash_safe_logger::signal_handler;
99 sigemptyset(&sa.sa_mask);
100 sa.sa_flags = SA_RESTART;
101
102 bool success = true;
103 success &= (sigaction(SIGSEGV, &sa, &old_sigsegv_) == 0);
104 success &= (sigaction(SIGABRT, &sa, &old_sigabrt_) == 0);
105 success &= (sigaction(SIGTERM, &sa, &old_sigterm_) == 0);
106 success &= (sigaction(SIGINT, &sa, &old_sigint_) == 0);
107
109 return success;
110 }
111
116 std::lock_guard<std::mutex> lock(handler_mutex_);
117
118 if (!handlers_installed_) {
119 return;
120 }
121
122 sigaction(SIGSEGV, &old_sigsegv_, nullptr);
123 sigaction(SIGABRT, &old_sigabrt_, nullptr);
124 sigaction(SIGTERM, &old_sigterm_, nullptr);
125 sigaction(SIGINT, &old_sigint_, nullptr);
126
127 handlers_installed_ = false;
128 }
129
135 bool flush_with_timeout(std::chrono::milliseconds timeout) {
136 auto deadline = std::chrono::steady_clock::now() + timeout;
137
138 // Signal flush request
139 flush_requested_.store(true, std::memory_order_release);
140
141 // Try to acquire flush lock with timeout
142 while (std::chrono::steady_clock::now() < deadline) {
143 if (flush_mutex_.try_lock()) {
144 // Got lock, perform flush
145 logger_->flush();
146 flush_mutex_.unlock();
147 flush_requested_.store(false, std::memory_order_release);
148 return true;
149 }
150
151 // Wait a bit before retry
152 std::this_thread::sleep_for(std::chrono::milliseconds(10));
153 }
154
155 // Timeout occurred
156 return false;
157 }
158
166 // Set emergency flag
167 emergency_flush_active_.store(true, std::memory_order_release);
168
169 // Try immediate flush (may fail if locks are held)
170 if (flush_mutex_.try_lock()) {
171 logger_->flush();
172 flush_mutex_.unlock();
173 }
174 }
175
179 void set_min_level(log_level level) {
180 min_level_.store(level, std::memory_order_release);
181 logger_->set_min_level(level);
182 }
183
187 log_level get_min_level() const {
188 return min_level_.load(std::memory_order_acquire);
189 }
190
195 void set_auto_flush_interval(std::chrono::milliseconds interval) {
196 auto_flush_interval_ = interval;
197
198 if (interval.count() > 0 && !auto_flush_thread_.joinable()) {
200 } else if (interval.count() == 0 && auto_flush_thread_.joinable()) {
202 }
203 }
204
208 void log(log_level level, const std::string& message) {
209 // Early exit if level not enabled
210 if (level < min_level_.load(std::memory_order_acquire)) {
211 return;
212 }
213
214 logger_->log(level, message);
215 }
216
220 void log(log_level level,
221 const std::string& message,
222 const std::string& file,
223 int line,
224 const std::string& function) {
225 if (level < min_level_.load(std::memory_order_acquire)) {
226 return;
227 }
228
229 logger_->log(level, message, file, line, function);
230 }
231
236 return logger_->start();
237 }
238
244 return logger_->stop();
245 }
246
250 std::shared_ptr<logger> get_underlying_logger() {
251 return logger_;
252 }
253
258
259private:
260 explicit crash_safe_logger(std::shared_ptr<logger> logger)
261 : logger_(std::move(logger))
262 , min_level_(log_level::info)
263 , handlers_installed_(false)
265 , flush_requested_(false)
267 , stop_auto_flush_(false)
268 {
269 }
270
274 static void signal_handler(int signal) {
275 // Get global instance
276 auto instance = global_instance_.lock();
277 if (!instance) {
278 return; // Logger already destroyed
279 }
280
281 // Perform emergency flush
282 instance->emergency_flush();
283
284 // Chain to old handler if exists
285 switch (signal) {
286 case SIGSEGV:
287 if (instance->old_sigsegv_.sa_handler != SIG_DFL &&
288 instance->old_sigsegv_.sa_handler != SIG_IGN) {
289 instance->old_sigsegv_.sa_handler(signal);
290 }
291 break;
292 case SIGABRT:
293 if (instance->old_sigabrt_.sa_handler != SIG_DFL &&
294 instance->old_sigabrt_.sa_handler != SIG_IGN) {
295 instance->old_sigabrt_.sa_handler(signal);
296 }
297 break;
298 case SIGTERM:
299 case SIGINT:
300 // For these, we exit after flush
301 std::exit(signal);
302 break;
303 }
304 }
305
310 stop_auto_flush_ = false;
311 auto_flush_thread_ = std::thread([this]() {
312 while (!stop_auto_flush_.load(std::memory_order_acquire)) {
313 std::this_thread::sleep_for(auto_flush_interval_);
314
315 if (!stop_auto_flush_.load(std::memory_order_acquire)) {
316 flush_with_timeout(std::chrono::seconds(1));
317 }
318 }
319 });
320 }
321
326 if (auto_flush_thread_.joinable()) {
327 stop_auto_flush_.store(true, std::memory_order_release);
328 auto_flush_thread_.join();
329 }
330 }
331
332private:
333 std::shared_ptr<logger> logger_;
334 std::atomic<log_level> min_level_;
335
336 // Signal handler state
337 std::mutex handler_mutex_;
339 struct sigaction old_sigsegv_;
340 struct sigaction old_sigabrt_;
341 struct sigaction old_sigterm_;
342 struct sigaction old_sigint_;
343
344 // Emergency flush state
345 std::atomic<bool> emergency_flush_active_;
346 std::atomic<bool> flush_requested_;
347 std::mutex flush_mutex_;
348
349 // Auto-flush
350 std::chrono::milliseconds auto_flush_interval_;
352 std::atomic<bool> stop_auto_flush_;
353
354 // Global instance for signal handler
355 static std::weak_ptr<crash_safe_logger> global_instance_;
356};
357
358// Static member definition
359std::weak_ptr<crash_safe_logger> crash_safe_logger::global_instance_;
360
361} // namespace kcenon::logger::safety
Logger with crash recovery and emergency flush capabilities.
std::shared_ptr< logger > get_underlying_logger()
Get the underlying logger.
void log(log_level level, const std::string &message, const std::string &file, int line, const std::string &function)
Log message with source location.
bool flush_with_timeout(std::chrono::milliseconds timeout)
Flush with timeout to prevent deadlocks.
common::VoidResult start()
Start the underlying logger.
void set_min_level(log_level level)
Set minimum log level (thread-safe)
static void signal_handler(int signal)
Signal handler (async-signal-safe)
static std::weak_ptr< crash_safe_logger > global_instance_
void set_auto_flush_interval(std::chrono::milliseconds interval)
Enable auto-flush at regular intervals.
void stop_auto_flush_thread()
Stop auto-flush background thread.
void log(log_level level, const std::string &message)
Log message (delegates to underlying logger)
common::VoidResult stop()
Stop the underlying logger.
void uninstall_crash_handlers()
Remove signal handlers.
void start_auto_flush_thread()
Start auto-flush background thread.
void emergency_flush()
Emergency flush (async-signal-safe)
bool install_crash_handlers()
Install signal handlers for crash detection.
log_level get_min_level() const
Get minimum log level (thread-safe)
crash_safe_logger(std::shared_ptr< logger > logger)
static std::shared_ptr< crash_safe_logger > create(std::shared_ptr< logger > underlying_logger=nullptr)
Create crash-safe logger.
High-performance, thread-safe logging system with asynchronous capabilities.