Ed Tanous | 50c50c2 | 2017-05-12 16:58:06 -0700 | [diff] [blame] | 1 | /** ========================================================================== |
| 2 | * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes |
| 3 | * with no warranties. This code is yours to share, use and modify with no |
| 4 | * strings attached and no restrictions or obligations. |
| 5 | * |
| 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org |
| 7 | * ============================================================================*/ |
| 8 | |
| 9 | #include "g3log/logworker.hpp" |
| 10 | #include "g3log/logmessage.hpp" |
| 11 | #include "g3log/active.hpp" |
| 12 | #include "g3log/g3log.hpp" |
| 13 | #include "g3log/future.hpp" |
| 14 | #include "g3log/crashhandler.hpp" |
| 15 | |
| 16 | #include <iostream> |
| 17 | |
| 18 | namespace g3 { |
| 19 | |
| 20 | LogWorkerImpl::LogWorkerImpl() : _bg(kjellkod::Active::createActive()) { } |
| 21 | |
| 22 | void LogWorkerImpl::bgSave(g3::LogMessagePtr msgPtr) { |
| 23 | std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get())); |
| 24 | |
| 25 | for (auto& sink : _sinks) { |
| 26 | LogMessage msg(*(uniqueMsg)); |
| 27 | sink->send(LogMessageMover(std::move(msg))); |
| 28 | } |
| 29 | |
| 30 | if (_sinks.empty()) { |
| 31 | std::string err_msg {"g3logworker has no sinks. Message: ["}; |
| 32 | err_msg.append(uniqueMsg.get()->toString()).append({"]\n"}); |
| 33 | std::cerr << err_msg; |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | void LogWorkerImpl::bgFatal(FatalMessagePtr msgPtr) { |
| 38 | // this will be the last message. Only the active logworker can receive a FATAL call so it's |
| 39 | // safe to shutdown logging now |
| 40 | g3::internal::shutDownLogging(); |
| 41 | |
| 42 | std::string reason = msgPtr.get()->reason(); |
| 43 | const auto level = msgPtr.get()->_level; |
| 44 | const auto fatal_id = msgPtr.get()->_signal_id; |
| 45 | |
| 46 | |
| 47 | std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get())); |
| 48 | uniqueMsg->write().append("\nExiting after fatal event (").append(uniqueMsg->level()); |
| 49 | |
| 50 | |
| 51 | // Change output in case of a fatal signal (or windows exception) |
| 52 | std::string exiting = {"Fatal type: "}; |
| 53 | |
| 54 | uniqueMsg->write().append("). ").append(exiting).append(" ").append(reason) |
| 55 | .append("\nLog content flushed sucessfully to sink\n\n"); |
| 56 | |
| 57 | std::cerr << uniqueMsg->toString() << std::flush; |
| 58 | for (auto& sink : _sinks) { |
| 59 | LogMessage msg(*(uniqueMsg)); |
| 60 | sink->send(LogMessageMover(std::move(msg))); |
| 61 | } |
| 62 | |
| 63 | |
| 64 | // This clear is absolutely necessary |
| 65 | // All sinks are forced to receive the fatal message above before we continue |
| 66 | _sinks.clear(); // flush all queues |
| 67 | internal::exitWithDefaultSignalHandler(level, fatal_id); |
| 68 | |
| 69 | // should never reach this point |
| 70 | perror("g3log exited after receiving FATAL trigger. Flush message status: "); |
| 71 | } |
| 72 | |
| 73 | LogWorker::~LogWorker() { |
| 74 | g3::internal::shutDownLoggingForActiveOnly(this); |
| 75 | |
| 76 | // The sinks WILL automatically be cleared at exit of this destructor |
| 77 | // However, the waiting below ensures that all messages until this point are taken care of |
| 78 | // before any internals/LogWorkerImpl of LogWorker starts to be destroyed. |
| 79 | // i.e. this avoids a race with another thread slipping through the "shutdownLogging" and calling |
| 80 | // calling ::save or ::fatal through LOG/CHECK with lambda messages and "partly deconstructed LogWorkerImpl" |
| 81 | // |
| 82 | // Any messages put into the queue will be OK due to: |
| 83 | // *) If it is before the wait below then they will be executed |
| 84 | // *) If it is AFTER the wait below then they will be ignored and NEVER executed |
| 85 | auto bg_clear_sink_call = [this] { _impl._sinks.clear(); }; |
| 86 | auto token_cleared = g3::spawn_task(bg_clear_sink_call, _impl._bg.get()); |
| 87 | token_cleared.wait(); |
| 88 | |
| 89 | // The background worker WILL be automatically cleared at the exit of the destructor |
| 90 | // However, the explicitly clearing of the background worker (below) makes sure that there can |
| 91 | // be no thread that manages to add another sink after the call to clear the sinks above. |
| 92 | // i.e. this manages the extremely unlikely case of another thread calling |
| 93 | // addWrappedSink after the sink clear above. Normally adding of sinks should be done in main.cpp |
| 94 | // and be closely coupled with the existance of the LogWorker. Sharing this adding of sinks to |
| 95 | // other threads that do not know the state of LogWorker is considered a bug but it is dealt with |
| 96 | // nonetheless below. |
| 97 | // |
| 98 | // If sinks would already have been added after the sink clear above then this reset will deal with it |
| 99 | // without risking lambda execution with a partially deconstructed LogWorkerImpl |
| 100 | // Calling g3::spawn_task on a nullptr Active object will not crash but return |
| 101 | // a future containing an appropriate exception. |
| 102 | _impl._bg.reset(nullptr); |
| 103 | } |
| 104 | |
| 105 | void LogWorker::save(LogMessagePtr msg) { |
| 106 | _impl._bg->send([this, msg] {_impl.bgSave(msg); }); |
| 107 | } |
| 108 | |
| 109 | void LogWorker::fatal(FatalMessagePtr fatal_message) { |
| 110 | _impl._bg->send([this, fatal_message] {_impl.bgFatal(fatal_message); }); |
| 111 | } |
| 112 | |
| 113 | void LogWorker::addWrappedSink(std::shared_ptr<g3::internal::SinkWrapper> sink) { |
| 114 | auto bg_addsink_call = [this, sink] {_impl._sinks.push_back(sink);}; |
| 115 | auto token_done = g3::spawn_task(bg_addsink_call, _impl._bg.get()); |
| 116 | token_done.wait(); |
| 117 | } |
| 118 | |
| 119 | std::unique_ptr<LogWorker> LogWorker::createLogWorker() { |
| 120 | return std::unique_ptr<LogWorker>(new LogWorker); |
| 121 | } |
| 122 | |
| 123 | std::unique_ptr<FileSinkHandle>LogWorker::addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id) { |
| 124 | return addSink(std2::make_unique<g3::FileSink>(log_prefix, log_directory, default_id), &FileSink::fileWrite); |
| 125 | } |
| 126 | |
| 127 | |
| 128 | |
| 129 | |
| 130 | } // g3 |