Maze Builder Docs 8.2.1
Loading...
Searching...
No Matches
async_logger.h
Go to the documentation of this file.
1#ifndef ASYNC_LOGGER_H
2#define ASYNC_LOGGER_H
3
4#include <condition_variable>
5#include <deque>
6#include <functional>
7#include <mutex>
8#include <stop_token>
9#include <string>
10#include <string_view>
11#include <thread>
12#include <utility>
13#include <vector>
14
15#include <fmt/format.h>
16#include <fmt/ranges.h>
17
20namespace mazes
21{
23 class async_logger final
24 {
25 public:
27 using sink_type = std::function<void(std::string_view)>;
28
30 : sink_([](std::string_view msg)
31 {
32 fmt::print("{}\n", msg);
33 }),
34 worker_([this](const std::stop_token& st)
35 {
36 run(st);
37 })
38 {
39 }
40
41 explicit async_logger(sink_type sink)
42 : sink_(std::move(sink)), worker_([this](const std::stop_token& st)
43 {
44 run(st);
45 })
46 {
47 }
48
49 ~async_logger()
50 {
51 stop();
52 }
53
54 async_logger(const async_logger&) = delete;
55 async_logger& operator=(const async_logger&) = delete;
56 async_logger(async_logger&&) = delete;
57 async_logger& operator=(async_logger&&) = delete;
58
63 template <typename... FormatArgs>
64 void log(fmt::format_string<FormatArgs...> fmt_s, FormatArgs&&... args)
65 {
66 enqueue(fmt::format(fmt_s, std::forward<FormatArgs>(args)...));
67 }
68
71 void log_message(std::string msg)
72 {
73 enqueue(std::move(msg));
74 }
75
77 void flush()
78 {
79 std::unique_lock lock(mtx_);
80 drained_cv_.wait(lock, [this]()
81 {
82 return pending_ == 0;
83 });
84 }
85
87 void stop() noexcept
88 {
89 bool should_join = false;
90 {
91 std::lock_guard lock(mtx_);
92 if (!stopped_)
93 {
94 stopped_ = true;
95 should_join = true;
96 }
97 }
98
99 if (should_join)
100 {
101 worker_.request_stop();
102 cv_.notify_all();
103 if (worker_.joinable())
104 {
105 worker_.join();
106 }
107 }
108 }
109
113 {
114 std::lock_guard lock(mtx_);
115 sink_ = std::move(sink);
116 }
117
118 private:
121 void enqueue(std::string msg)
122 {
123 std::lock_guard lock(mtx_);
124 if (stopped_)
125 {
126 return;
127 }
128
129 queue_.emplace_back(std::move(msg));
130 ++pending_;
131 cv_.notify_one();
132 }
133
136 void run(const std::stop_token& st)
137 {
138 for (;;)
139 {
140 std::string message;
141 sink_type sink;
142
143 {
144 std::unique_lock lock(mtx_);
145 cv_.wait(lock, [this, &st]()
146 {
147 return st.stop_requested() || !queue_.empty();
148 });
149
150 if (st.stop_requested() && queue_.empty())
151 {
152 break;
153 }
154
155 message = std::move(queue_.front());
156 queue_.pop_front();
157 sink = sink_;
158 }
159
160 try
161 {
162 if (sink)
163 {
164 sink(message);
165 }
166 }
167 catch (...)
168 {
169 }
170
171 {
172 std::lock_guard lock(mtx_);
173 if (pending_ > 0)
174 {
175 --pending_;
176 }
177 if (pending_ == 0)
178 {
179 drained_cv_.notify_all();
180 }
181 }
182 }
183
184 std::lock_guard lock(mtx_);
185 if (pending_ == 0)
186 {
187 drained_cv_.notify_all();
188 }
189 }
190
191 mutable std::mutex mtx_;
192 std::condition_variable cv_;
193 std::condition_variable drained_cv_;
194 std::deque<std::string> queue_;
195 sink_type sink_;
196 std::jthread worker_;
197 std::size_t pending_{0};
198 bool stopped_{false};
199 };
200
204 {
205 static async_logger logger{};
206 return logger;
207 }
208
212 {
213 // Drain already-queued messages to the current sink before rerouting output.
215 global_async_logger().set_sink(std::move(sink));
216 }
217
219 inline void reset_printer_sink()
220 {
221 // Preserve message ordering and avoid redirecting queued messages mid-flight.
223 global_async_logger().set_sink([](std::string_view msg)
224 {
225 fmt::print("{}\n", msg);
226 });
227 }
228
230 inline void flush_printer()
231 {
233 }
234
238 template <typename... Args>
239 void printer(Args&&... args)
240 {
241 std::vector<std::string> parts = {fmt::format("{}", std::forward<Args>(args))...};
242 global_async_logger().log("{}", fmt::join(parts, ", "));
243 }
244} // namespace mazes
245
246#endif // ASYNC_LOGGER_H
void set_printer_sink(async_logger::sink_type sink)
Set the sink function for the global async logger.
Definition async_logger.h:211
void flush_printer()
Flush the global async logger, blocking until all pending messages are processed.
Definition async_logger.h:230
void printer(Args &&... args)
Log a message using the global async logger.
Definition async_logger.h:239
async_logger & global_async_logger()
Get the global async logger instance.
Definition async_logger.h:203
void reset_printer_sink()
Flush the global async logger, blocking until all pending messages are processed.
Definition async_logger.h:219
Command-line argument handler with JSON support.
Definition args.h:19
Asynchronous logger class.
Definition async_logger.h:24
void log_message(std::string msg)
Log a message.
Definition async_logger.h:71
std::function< void(std::string_view)> sink_type
Type alias for the sink function.
Definition async_logger.h:27
void stop() noexcept
Stop the logger, blocking until all pending messages are processed.
Definition async_logger.h:87
void set_sink(sink_type sink)
Set the sink function for the logger.
Definition async_logger.h:112
void flush()
Flush the logger, blocking until all pending messages are processed.
Definition async_logger.h:77
void log(fmt::format_string< FormatArgs... > fmt_s, FormatArgs &&... args)
Log a formatted message.
Definition async_logger.h:64