quill
Backend.h
1 
7 #pragma once
8 
9 #include "quill/backend/BackendManager.h"
10 #include "quill/backend/BackendOptions.h"
11 #include "quill/backend/SignalHandler.h"
12 #include "quill/core/Attributes.h"
13 #include "quill/core/QuillError.h"
14 
15 #include <atomic>
16 #include <csignal>
17 #include <cstdint>
18 #include <cstdlib>
19 #include <mutex>
20 
21 QUILL_BEGIN_NAMESPACE
22 
24 constexpr uint32_t VersionMajor{11};
25 constexpr uint32_t VersionMinor{1};
26 constexpr uint32_t VersionPatch{0};
27 constexpr uint32_t Version{VersionMajor * 10000 + VersionMinor * 100 + VersionPatch};
28 
29 class Backend
30 {
31 public:
36  QUILL_ATTRIBUTE_COLD static void start(BackendOptions const& options = BackendOptions{})
37  {
38  std::call_once(detail::BackendManager::instance().get_start_once_flag(),
39  [options]()
40  {
41  // Ensure SignalHandlerContext singleton is constructed before atexit
42  // registration so it is alive when the atexit handler runs during shutdown.
43  (void)detail::SignalHandlerContext::instance();
44 
45  // Run the backend worker thread, we wait here until the thread enters the main loop
46  detail::BackendManager::instance().start_backend_thread(options);
47 
48  // Set up an exit handler to call stop when the main application exits.
49  // always call stop on destruction to log everything. std::atexit seems to be
50  // working better with dll on windows compared to using ~LogManagerSingleton().
51  if (!detail::BackendManager::instance().is_atexit_registered())
52  {
53  detail::BackendManager::instance().set_atexit_registered();
54  std::atexit([]() { Backend::stop(); });
55  }
56  });
57  }
58 
80  template <typename TFrontendOptions>
81  QUILL_ATTRIBUTE_COLD static void start(BackendOptions const& backend_options,
82  SignalHandlerOptions const& signal_handler_options)
83  {
84  std::call_once(detail::BackendManager::instance().get_start_once_flag(),
85  [backend_options, signal_handler_options]()
86  {
87 #if defined(_WIN32)
88  detail::init_exception_handler<TFrontendOptions>();
89 #else
90  // We do not want signal handler to run in the backend worker thread
91  // Block signals in the main thread so when we spawn the backend worker thread it inherits
92  // the master
93  sigset_t set, oldset;
94  sigfillset(&set);
95  sigprocmask(SIG_SETMASK, &set, &oldset);
96  detail::init_signal_handler<TFrontendOptions>(signal_handler_options.catchable_signals);
97 #endif
98 
99  // Run the backend worker thread, we wait here until the thread enters the main loop
100  detail::BackendManager::instance().start_backend_thread(backend_options);
101 
102  detail::SignalHandlerContext::instance().logger_name = signal_handler_options.logger_name;
103 
104  detail::SignalHandlerContext::instance().excluded_logger_substrings =
105  signal_handler_options.excluded_logger_substrings;
106 
107  detail::SignalHandlerContext::instance().signal_handler_timeout_seconds.store(
108  signal_handler_options.timeout_seconds);
109 
110  // We need to update the signal handler with some backend thread details
111  detail::SignalHandlerContext::instance().backend_thread_id.store(
112  detail::BackendManager::instance().get_backend_thread_id());
113 
114 #if defined(_WIN32)
115  // nothing to do
116 #else
117  // Unblock signals in the main thread so subsequent threads do not inherit the blocked mask
118  sigprocmask(SIG_SETMASK, &oldset, nullptr);
119 #endif
120 
121  // Set up an exit handler to call stop when the main application exits.
122  // always call stop on destruction to log everything. std::atexit seems to be
123  // working better with dll on windows compared to using ~LogManagerSingleton().
124  if (!detail::BackendManager::instance().is_atexit_registered())
125  {
126  detail::BackendManager::instance().set_atexit_registered();
127  std::atexit([]() { Backend::stop(); });
128  }
129  });
130  }
131 
139  QUILL_ATTRIBUTE_COLD static void stop()
140  {
141  detail::SignalHandlerContext::instance().backend_thread_id.store(0);
142  detail::BackendManager::instance().stop_backend_thread();
143  detail::deinit_signal_handler();
144  }
145 
153  static void notify() noexcept { detail::BackendManager::instance().notify_backend_thread(); }
154 
159  QUILL_NODISCARD static bool is_running() noexcept
160  {
161  return detail::BackendManager::instance().is_backend_thread_running();
162  }
163 
168  QUILL_NODISCARD static uint32_t get_thread_id() noexcept
169  {
170  return detail::BackendManager::instance().get_backend_thread_id();
171  }
172 
183  QUILL_NODISCARD static uint64_t convert_rdtsc_to_epoch_time(uint64_t rdtsc_value) noexcept
184  {
185  return detail::BackendManager::instance().convert_rdtsc_to_epoch_time(rdtsc_value);
186  }
187 
226  QUILL_ATTRIBUTE_COLD static ManualBackendWorker* acquire_manual_backend_worker()
227  {
228  ManualBackendWorker* manual_backend_worker{nullptr};
229 
230  std::call_once(
231  detail::BackendManager::instance().get_start_once_flag(), [&manual_backend_worker]() mutable
232  { manual_backend_worker = detail::BackendManager::instance().get_manual_backend_worker(); });
233 
234  if (!manual_backend_worker)
235  {
236  QUILL_THROW(
237  QuillError{"acquire_manual_backend_worker() can only be called once per process. "
238  "Additionally, it should not be "
239  "called when start() has already been invoked"});
240  }
241 
242  return manual_backend_worker;
243  }
244 };
245 
246 QUILL_END_NAMESPACE
static QUILL_NODISCARD uint32_t get_thread_id() noexcept
Retrieves the ID of the backend thread.
Definition: Backend.h:168
static QUILL_NODISCARD bool is_running() noexcept
Checks if the backend is currently running.
Definition: Backend.h:159
static QUILL_ATTRIBUTE_COLD void start(BackendOptions const &backend_options, SignalHandlerOptions const &signal_handler_options)
Starts the backend thread and initialises a signal handler.
Definition: Backend.h:81
uint32_t timeout_seconds
Defines the timeout duration in seconds for the signal handler alarm.
Definition: SignalHandler.h:65
std::vector< std::string > excluded_logger_substrings
List of substrings used to exclude loggers during automatic logger selection.
Definition: SignalHandler.h:87
static void notify() noexcept
Notifies the backend thread to wake up.
Definition: Backend.h:153
static QUILL_NODISCARD uint64_t convert_rdtsc_to_epoch_time(uint64_t rdtsc_value) noexcept
Converts an rdtsc value to epoch time.
Definition: Backend.h:183
static QUILL_ATTRIBUTE_COLD void stop()
Stops the backend thread.
Definition: Backend.h:139
static QUILL_ATTRIBUTE_COLD ManualBackendWorker * acquire_manual_backend_worker()
This feature is designed for advanced users who need to run the backend worker on their own thread...
Definition: Backend.h:226
Definition: Backend.h:29
This class can be used when you want to run the backend worker on your own thread.
Definition: ManualBackendWorker.h:19
static QUILL_ATTRIBUTE_COLD void start(BackendOptions const &options=BackendOptions{})
Starts the backend thread.
Definition: Backend.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:56
Struct to hold options for the signal handler.
Definition: SignalHandler.h:50
std::string logger_name
The name of the logger instance that the signal handler will use to log errors when the application c...
Definition: SignalHandler.h:77
Configuration options for the backend.
Definition: BackendOptions.h:30