11#ifndef CPPTRAIL_ASYNC_LOGGER_H
12#define CPPTRAIL_ASYNC_LOGGER_H
15#include <condition_variable>
38 : std::runtime_error(
"LoggerOverflowError") {
52 template<
typename char_t>
70 std::numeric_limits<std::size_t>::max(),
82 std::size_t nMaxEntryCount,
83 bool bThrowOnOverflow,
84 std::size_t nWorkerCount
213 std::mutex m_oQueueMutex;
217 std::
size_t m_nMaxEntryCount;
219 bool m_bThrowOnOverflow;
226 std::mutex m_oWorkerMutex;
228 std::condition_variable m_oWorkerCv;
230 std::vector<std::thread> m_vWorkers;
232 std::
size_t m_nWorkerCount;
234 std::atomic_bool m_bWorkerEngineOn{
false};
241 std::mutex m_oKillerMutex;
243 std::vector<std::thread> m_oKillerThread;
245 std::condition_variable m_oKillerCv;
276 void waitForKiller();
289#if __cplusplus >= 202002L
302 template<
typename char_t>
304 const std::size_t nMaxEntryCount,
305 const bool bThrowOnOverflow,
306 const std::size_t nWorkerCount
307 ) : m_nMaxEntryCount(nMaxEntryCount),
308 m_bThrowOnOverflow(bThrowOnOverflow),
309 m_nWorkerCount(nWorkerCount) {
311 this->m_vWorkers.reserve(nWorkerCount);
312 this->m_oKillerThread.reserve(1);
315 template<
typename char_t>
320 if (!m_bWorkerEngineOn.load())
return;
323 std::unique_lock<std::mutex> oQueueLock(this->m_oQueueMutex);
326 if (this->m_qEntryQueue.size() + 1 > this->m_nMaxEntryCount) {
328 if (this->m_bThrowOnOverflow)
335 this->m_qEntryQueue.push_back(std::move(oMessage));
338 this->m_oWorkerCv.notify_one();
342 template<
typename char_t>
344 if (!this->m_bWorkerEngineOn.load()) {
346 this->m_vWorkers.clear();
347 for (std::size_t i = 0; i < this->m_nWorkerCount; i++) {
348 this->m_vWorkers.emplace_back([
this] {
353 this->m_bWorkerEngineOn.store(
true);
357 template<
typename char_t>
358 void BasicAsyncLoggerImpl<char_t>::stopWorkers() {
359 if (this->m_bWorkerEngineOn.load()) {
361 this->m_bWorkerEngineOn.store(
false);
362 this->m_oWorkerCv.notify_all();
364 for (
auto &oWorker: this->m_vWorkers) {
365 if (oWorker.joinable())
368 this->m_vWorkers.clear();
372 template<
typename char_t>
373 void BasicAsyncLoggerImpl<char_t>::waitForKiller() {
375 if (!this->m_oKillerThread.empty()) {
376 auto &oKiller = this->m_oKillerThread.front();
377 if (oKiller.joinable())
379 this->m_oKillerThread.clear();
383 template<
typename char_t>
386 this->m_oKillerMutex.lock();
387 this->waitForKiller();
390 std::unique_lock<std::mutex> oWorkerLock(this->m_oWorkerMutex);
394 this->m_oKillerMutex.unlock();
397 this->waitForKiller();
400 const auto nStatus = this->serviceStatus();
405 this->serviceStart();
409 this->startWorkers();
412 template<
typename char_t>
415 this->m_oKillerMutex.lock();
416 this->waitForKiller();
419 std::unique_lock<std::mutex> oWorkerLock(this->m_oWorkerMutex);
423 this->m_oKillerMutex.unlock();
429 const auto nStatus = this->serviceStatus();
438 template<
typename char_t>
441 std::unique_lock<std::mutex> oKillerLock(this->m_oKillerMutex);
444 this->waitForKiller();
447 std::unique_lock<std::mutex> oWorkerLock(this->m_oWorkerMutex);
450 this->m_oKillerThread.emplace_back(
453 std::unique_lock<std::mutex> oKillerWorkerLock(this->m_oWorkerMutex);
459 const auto nStatus = this->serviceStatus();
470 template<
typename char_t>
473 std::unique_lock<std::mutex> oKillerLock(this->m_oKillerMutex);
475 this->waitForKiller();
478 template<
typename char_t>
481 auto nStatus = this->serviceStatus();
496 template<
typename char_t>
501 std::unique_lock<std::mutex> oQueueLock(this->m_oQueueMutex);
505 this->m_oWorkerCv.wait(oQueueLock, [
this] {
507 if (!this->m_qEntryQueue.empty())
return true;
509 return !this->m_bWorkerEngineOn.load();
513 if (!this->m_qEntryQueue.empty()) {
515 message_type oEntry = std::move(this->m_qEntryQueue.front());
516 this->m_qEntryQueue.pop_front();
519 this->m_oQueueMutex.unlock();
520 this->work(std::move(oEntry));
521 this->m_oQueueMutex.lock();
525 const bool bEmpty = this->m_qEntryQueue.empty();
526 const bool bEngine = this->m_bWorkerEngineOn.load();
527 bContinue = bEngine || !bEmpty;
Core logging interfaces and high-level handle abstractions.
An asynchronous logger template utilizing background worker threads and object recycling....
Definition async_logger.h:53
void start() final
Starts the background worker pool and the underlying service.
Definition async_logger.h:384
void join() final
Blocks until all background activity (workers and killer threads) has ceased.
Definition async_logger.h:471
void log(message_type oMessage) final
Enqueues a log message for asynchronous processing.
Definition async_logger.h:316
typename BasicLoggerImpl< char_t >::string_type string_type
Alias for the basic_string type associated with this logger's encoding.
Definition async_logger.h:59
virtual void serviceStart()=0
Performs the initialization sequence for the sink.
~BasicAsyncLoggerImpl() override=default
Destructor.
virtual void work(message_type oMessage)=0
Abstract method for the actual I/O work.
virtual void serviceStop()=0
Performs the shutdown sequence for the sink.
typename BasicLoggerImpl< char_t >::message_type message_type
Alias for the BasicMessage type associated to the logger.
Definition async_logger.h:62
BasicAsyncLoggerImpl()
Default constructor.
Definition async_logger.h:69
void stop() final
Synchronously shuts down the worker pool and the service.
Definition async_logger.h:413
Status status() final
Retrieves the aggregated operational status of the asynchronous logger.
Definition async_logger.h:479
virtual Status serviceStatus()=0
Reports the current operational status of the underlying sink.
void signalStop() final
Initiates a shutdown sequence on a separate background "killer" thread.
Definition async_logger.h:439
typename BasicLoggerImpl< char_t >::char_type char_type
Alias for the underlying character type.
Definition async_logger.h:56
Abstract template interface defining the mandatory contract for all loggers.
Definition base_logger.h:28
char_t char_type
Alias for the underlying character type used by this logger.
Definition base_logger.h:31
std::basic_string< char_type > string_type
Alias for the basic_string type associated with this logger's encoding.
Definition base_logger.h:34
Container for log message implementations.
Definition message.h:76
Exception thrown when the logger queue exceeds its maximum allowed size. This exception is specifical...
Definition async_logger.h:32
LoggerOverflowError()
Constructs a new LoggerOverflowError object.
Definition async_logger.h:37
Root namespace for the CppTrail logging library.
Definition async_logger.h:24
Status
Represents the current operational state of a Logger implementation.
Definition def.h:31
@ TRASCENDENT
The logger operates outside standard lifecycle management.
@ STOPPED
The logger has been gracefully shut down.
@ BROKEN
The logger encountered a fatal error (e.g., IO failure).
@ RUNNING
The logger is active and processing entries.