| /** ========================================================================== |
| * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes |
| * with no warranties. This code is yours to share, use and modify with no |
| * strings attached and no restrictions or obligations. |
| * |
| * For more information see g3log/LICENSE or refer refer to http://unlicense.org |
| * ============================================================================*/ |
| |
| #include "g3log/logworker.hpp" |
| #include "g3log/logmessage.hpp" |
| #include "g3log/active.hpp" |
| #include "g3log/g3log.hpp" |
| #include "g3log/future.hpp" |
| #include "g3log/crashhandler.hpp" |
| |
| #include <iostream> |
| |
| namespace g3 { |
| |
| LogWorkerImpl::LogWorkerImpl() : _bg(kjellkod::Active::createActive()) { } |
| |
| void LogWorkerImpl::bgSave(g3::LogMessagePtr msgPtr) { |
| std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get())); |
| |
| for (auto& sink : _sinks) { |
| LogMessage msg(*(uniqueMsg)); |
| sink->send(LogMessageMover(std::move(msg))); |
| } |
| |
| if (_sinks.empty()) { |
| std::string err_msg {"g3logworker has no sinks. Message: ["}; |
| err_msg.append(uniqueMsg.get()->toString()).append({"]\n"}); |
| std::cerr << err_msg; |
| } |
| } |
| |
| void LogWorkerImpl::bgFatal(FatalMessagePtr msgPtr) { |
| // this will be the last message. Only the active logworker can receive a FATAL call so it's |
| // safe to shutdown logging now |
| g3::internal::shutDownLogging(); |
| |
| std::string reason = msgPtr.get()->reason(); |
| const auto level = msgPtr.get()->_level; |
| const auto fatal_id = msgPtr.get()->_signal_id; |
| |
| |
| std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get())); |
| uniqueMsg->write().append("\nExiting after fatal event (").append(uniqueMsg->level()); |
| |
| |
| // Change output in case of a fatal signal (or windows exception) |
| std::string exiting = {"Fatal type: "}; |
| |
| uniqueMsg->write().append("). ").append(exiting).append(" ").append(reason) |
| .append("\nLog content flushed sucessfully to sink\n\n"); |
| |
| std::cerr << uniqueMsg->toString() << std::flush; |
| for (auto& sink : _sinks) { |
| LogMessage msg(*(uniqueMsg)); |
| sink->send(LogMessageMover(std::move(msg))); |
| } |
| |
| |
| // This clear is absolutely necessary |
| // All sinks are forced to receive the fatal message above before we continue |
| _sinks.clear(); // flush all queues |
| internal::exitWithDefaultSignalHandler(level, fatal_id); |
| |
| // should never reach this point |
| perror("g3log exited after receiving FATAL trigger. Flush message status: "); |
| } |
| |
| LogWorker::~LogWorker() { |
| g3::internal::shutDownLoggingForActiveOnly(this); |
| |
| // The sinks WILL automatically be cleared at exit of this destructor |
| // However, the waiting below ensures that all messages until this point are taken care of |
| // before any internals/LogWorkerImpl of LogWorker starts to be destroyed. |
| // i.e. this avoids a race with another thread slipping through the "shutdownLogging" and calling |
| // calling ::save or ::fatal through LOG/CHECK with lambda messages and "partly deconstructed LogWorkerImpl" |
| // |
| // Any messages put into the queue will be OK due to: |
| // *) If it is before the wait below then they will be executed |
| // *) If it is AFTER the wait below then they will be ignored and NEVER executed |
| auto bg_clear_sink_call = [this] { _impl._sinks.clear(); }; |
| auto token_cleared = g3::spawn_task(bg_clear_sink_call, _impl._bg.get()); |
| token_cleared.wait(); |
| |
| // The background worker WILL be automatically cleared at the exit of the destructor |
| // However, the explicitly clearing of the background worker (below) makes sure that there can |
| // be no thread that manages to add another sink after the call to clear the sinks above. |
| // i.e. this manages the extremely unlikely case of another thread calling |
| // addWrappedSink after the sink clear above. Normally adding of sinks should be done in main.cpp |
| // and be closely coupled with the existance of the LogWorker. Sharing this adding of sinks to |
| // other threads that do not know the state of LogWorker is considered a bug but it is dealt with |
| // nonetheless below. |
| // |
| // If sinks would already have been added after the sink clear above then this reset will deal with it |
| // without risking lambda execution with a partially deconstructed LogWorkerImpl |
| // Calling g3::spawn_task on a nullptr Active object will not crash but return |
| // a future containing an appropriate exception. |
| _impl._bg.reset(nullptr); |
| } |
| |
| void LogWorker::save(LogMessagePtr msg) { |
| _impl._bg->send([this, msg] {_impl.bgSave(msg); }); |
| } |
| |
| void LogWorker::fatal(FatalMessagePtr fatal_message) { |
| _impl._bg->send([this, fatal_message] {_impl.bgFatal(fatal_message); }); |
| } |
| |
| void LogWorker::addWrappedSink(std::shared_ptr<g3::internal::SinkWrapper> sink) { |
| auto bg_addsink_call = [this, sink] {_impl._sinks.push_back(sink);}; |
| auto token_done = g3::spawn_task(bg_addsink_call, _impl._bg.get()); |
| token_done.wait(); |
| } |
| |
| std::unique_ptr<LogWorker> LogWorker::createLogWorker() { |
| return std::unique_ptr<LogWorker>(new LogWorker); |
| } |
| |
| std::unique_ptr<FileSinkHandle>LogWorker::addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id) { |
| return addSink(std2::make_unique<g3::FileSink>(log_prefix, log_directory, default_id), &FileSink::fileWrite); |
| } |
| |
| |
| |
| |
| } // g3 |