9 #include "quill/backend/ThreadUtilities.h" 11 #include "quill/Logger.h" 12 #include "quill/core/Attributes.h" 13 #include "quill/core/LogLevel.h" 14 #include "quill/core/LoggerBase.h" 15 #include "quill/core/LoggerManager.h" 16 #include "quill/core/MacroMetadata.h" 17 #include "quill/core/QuillError.h" 27 #if !defined(WIN32_LEAN_AND_MEAN) 28 #define WIN32_LEAN_AND_MEAN 31 #if !defined(NOMINMAX) 91 QUILL_NODISCARD
static LoggerBase* get_logger() noexcept
95 if (!instance().logger_name.empty())
97 logger_base = LoggerManager::instance().get_logger(instance().logger_name);
101 if (!logger_base || !logger_base->is_valid_logger())
103 logger_base = LoggerManager::instance().get_valid_logger(excluded_logger_name_substr);
109 static constexpr std::string_view excluded_logger_name_substr = {
"__csv__"};
111 std::string logger_name;
112 std::atomic<int32_t> signal_number{0};
113 std::atomic<uint32_t> lock{0};
114 std::atomic<uint32_t> backend_thread_id{0};
115 std::atomic<uint32_t> signal_handler_timeout_seconds{20};
116 std::atomic<bool> should_reraise_signal{
true};
123 #define QUILL_SIGNAL_HANDLER_LOG(logger, log_level, fmt, ...) \ 126 if (logger->template should_log_statement<log_level>()) \ 128 static constexpr quill::MacroMetadata macro_metadata{ \ 129 "SignalHandler:~", "", fmt, nullptr, log_level, quill::MacroMetadata::Event::Log}; \ 131 logger->template log_statement<false>(¯o_metadata, ##__VA_ARGS__); \ 136 template <
typename TFrontendOptions>
137 void on_signal(int32_t signal_number)
140 uint32_t
const lock = SignalHandlerContext::instance().lock.fetch_add(1);
150 std::this_thread::sleep_for(std::chrono::hours{24000});
160 SignalHandlerContext::instance().signal_number.store(signal_number);
164 alarm(SignalHandlerContext::instance().signal_handler_timeout_seconds.load());
168 uint32_t
const backend_thread_id = SignalHandlerContext::instance().backend_thread_id.load();
170 bool const should_reraise_signal = SignalHandlerContext::instance().should_reraise_signal.load();
172 if ((backend_thread_id == 0) || (current_thread_id == backend_thread_id))
175 if (signal_number == SIGINT || signal_number == SIGTERM)
177 std::exit(EXIT_SUCCESS);
180 if (should_reraise_signal)
183 std::signal(signal_number, SIG_DFL);
184 std::raise(signal_number);
190 LoggerBase* logger_base = SignalHandlerContext::instance().get_logger();
195 int32_t
const signal_desc = signal_number;
197 char const*
const signal_desc = ::strsignal(signal_number);
201 QUILL_SIGNAL_HANDLER_LOG(
logger, LogLevel::Info,
"Received signal: {} (signum: {})",
202 signal_desc, signal_number);
204 if (signal_number == SIGINT || signal_number == SIGTERM)
209 std::exit(EXIT_SUCCESS);
212 if (should_reraise_signal)
214 QUILL_SIGNAL_HANDLER_LOG(
logger, LogLevel::Critical,
215 "Program terminated unexpectedly due to signal: {} (signum: {})",
216 signal_desc, signal_number);
222 std::signal(signal_number, SIG_DFL);
223 std::raise(signal_number);
241 inline char const* get_error_message(DWORD ex_code)
245 case EXCEPTION_ACCESS_VIOLATION:
246 return "EXCEPTION_ACCESS_VIOLATION";
247 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
248 return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
249 case EXCEPTION_BREAKPOINT:
250 return "EXCEPTION_BREAKPOINT";
251 case EXCEPTION_DATATYPE_MISALIGNMENT:
252 return "EXCEPTION_DATATYPE_MISALIGNMENT";
253 case EXCEPTION_FLT_DENORMAL_OPERAND:
254 return "EXCEPTION_FLT_DENORMAL_OPERAND";
255 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
256 return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
257 case EXCEPTION_FLT_INEXACT_RESULT:
258 return "EXCEPTION_FLT_INEXACT_RESULT";
259 case EXCEPTION_FLT_INVALID_OPERATION:
260 return "EXCEPTION_FLT_INVALID_OPERATION";
261 case EXCEPTION_FLT_OVERFLOW:
262 return "EXCEPTION_FLT_OVERFLOW";
263 case EXCEPTION_FLT_STACK_CHECK:
264 return "EXCEPTION_FLT_STACK_CHECK";
265 case EXCEPTION_FLT_UNDERFLOW:
266 return "EXCEPTION_FLT_UNDERFLOW";
267 case EXCEPTION_ILLEGAL_INSTRUCTION:
268 return "EXCEPTION_ILLEGAL_INSTRUCTION";
269 case EXCEPTION_IN_PAGE_ERROR:
270 return "EXCEPTION_IN_PAGE_ERROR";
271 case EXCEPTION_INT_DIVIDE_BY_ZERO:
272 return "EXCEPTION_INT_DIVIDE_BY_ZERO";
273 case EXCEPTION_INT_OVERFLOW:
274 return "EXCEPTION_INT_OVERFLOW";
275 case EXCEPTION_INVALID_DISPOSITION:
276 return "EXCEPTION_INVALID_DISPOSITION";
277 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
278 return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
279 case EXCEPTION_PRIV_INSTRUCTION:
280 return "EXCEPTION_PRIV_INSTRUCTION";
281 case EXCEPTION_SINGLE_STEP:
282 return "EXCEPTION_SINGLE_STEP";
283 case EXCEPTION_STACK_OVERFLOW:
284 return "EXCEPTION_STACK_OVERFLOW";
286 return "Unrecognized Exception";
291 template <
typename TFrontendOptions>
292 BOOL WINAPI on_console_signal(DWORD signal)
294 uint32_t
const backend_thread_id = SignalHandlerContext::instance().backend_thread_id.load();
298 if ((backend_thread_id != 0) && (current_thread_id != backend_thread_id) &&
299 (signal == CTRL_C_EVENT || signal == CTRL_BREAK_EVENT))
302 LoggerBase* logger_base = SignalHandlerContext::instance().get_logger();
307 QUILL_SIGNAL_HANDLER_LOG(
logger, LogLevel::Info,
308 "Program interrupted by Ctrl+C or Ctrl+Break signal");
312 std::exit(EXIT_SUCCESS);
320 template <
typename TFrontendOptions>
321 LONG WINAPI on_exception(EXCEPTION_POINTERS* exception_p)
323 uint32_t
const backend_thread_id = SignalHandlerContext::instance().backend_thread_id.load();
327 if ((backend_thread_id != 0) && (current_thread_id != backend_thread_id))
330 LoggerBase* logger_base = SignalHandlerContext::instance().get_logger();
336 QUILL_SIGNAL_HANDLER_LOG(
logger, LogLevel::Info,
"Received exception: {} (Code: {})",
337 get_error_message(exception_p->ExceptionRecord->ExceptionCode),
338 exception_p->ExceptionRecord->ExceptionCode);
340 QUILL_SIGNAL_HANDLER_LOG(
logger, LogLevel::Critical,
341 "Program terminated unexpectedly due to exception: {} (Code: {})",
342 get_error_message(exception_p->ExceptionRecord->ExceptionCode),
343 exception_p->ExceptionRecord->ExceptionCode);
354 return EXCEPTION_CONTINUE_SEARCH;
358 template <
typename TFrontendOptions>
359 void init_exception_handler()
361 SetUnhandledExceptionFilter(on_exception<TFrontendOptions>);
363 if (!SetConsoleCtrlHandler(on_console_signal<TFrontendOptions>, TRUE))
365 QUILL_THROW(
QuillError{
"Failed to call SetConsoleCtrlHandler"});
374 template <
typename TFrontendOptions>
375 void init_signal_handler(std::vector<int>
const&
catchable_signals = std::vector<int>{
376 SIGTERM, SIGINT, SIGABRT, SIGFPE, SIGILL, SIGSEGV})
381 if (std::signal(catchable_signal, detail::on_signal<TFrontendOptions>) == SIG_ERR)
383 QUILL_THROW(
QuillError{
"Failed to setup signal handler for signal: " + std::to_string(catchable_signal)});
391 inline void on_alarm(int32_t signal_number)
393 if (SignalHandlerContext::instance().signal_number.load() == 0)
396 SignalHandlerContext::instance().signal_number = signal_number;
400 std::signal(SignalHandlerContext::instance().signal_number, SIG_DFL);
401 std::raise(SignalHandlerContext::instance().signal_number);
404 template <
typename TFrontendOptions>
407 for (
auto const& catchable_signal : catchable_signals)
409 if (catchable_signal == SIGALRM)
411 QUILL_THROW(
QuillError{
"SIGALRM can not be part of catchable_signals."});
415 if (std::signal(catchable_signal, on_signal<TFrontendOptions>) == SIG_ERR)
417 QUILL_THROW(
QuillError{
"Failed to setup signal handler for signal: " + std::to_string(catchable_signal)});
422 if (std::signal(SIGALRM, on_alarm) == SIG_ERR)
424 QUILL_THROW(
QuillError{
"Failed to setup signal handler for signal: SIGALRM"});
uint32_t timeout_seconds
Defines the timeout duration in seconds for the signal handler alarm.
Definition: SignalHandler.h:62
std::string logger
The logger instance that the signal handler will use to log errors when the application crashes...
Definition: SignalHandler.h:71
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:24
Thread safe logger.
Definition: Logger.h:48
Definition: SignalHandler.h:77
Definition: LoggerBase.h:36
custom exception
Definition: QuillError.h:45
std::vector< int > catchable_signals
List of signals that the backend should catch if with_signal_handler is enabled.
Definition: SignalHandler.h:53
Struct to hold options for the signal handler.
Definition: SignalHandler.h:48
QUILL_NODISCARD QUILL_EXPORT QUILL_ATTRIBUTE_USED uint32_t get_thread_id() noexcept
Returns the os assigned ID of the thread.
Definition: ThreadUtilities.h:193