blob: 86a437bcf780ac172cec54a0764f53f12354c47c [file] [log] [blame]
Ed Tanous50c50c22017-05-12 16:58:06 -07001/** ==========================================================================
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 * Filename:g3log.cpp Framework for Logging and Design By Contract
10 * Created: 2011 by Kjell Hedström
11 *
12 * PUBLIC DOMAIN and Not copywrited since it was built on public-domain software and at least in "spirit" influenced
13 * from the following sources
14 * 1. kjellkod.cc ;)
15 * 2. Dr.Dobbs, Petru Marginean: http://drdobbs.com/article/printableArticle.jhtml?articleId=201804215&dept_url=/cpp/
16 * 3. Dr.Dobbs, Michael Schulze: http://drdobbs.com/article/printableArticle.jhtml?articleId=225700666&dept_url=/cpp/
17 * 4. Google 'glog': http://google-glog.googlecode.com/svn/trunk/doc/glog.html
18 * 5. Various Q&A at StackOverflow
19 * ********************************************* */
20
21#include "g3log/g3log.hpp"
22#include "g3log/std2_make_unique.hpp"
23#include "g3log/logworker.hpp"
24#include "g3log/crashhandler.hpp"
25#include "g3log/logmessage.hpp"
26#include "g3log/loglevels.hpp"
27
28
29#include <mutex>
30#include <memory>
31#include <iostream>
32#include <thread>
33#include <atomic>
34#include <cstdlib>
35#include <sstream>
36
37namespace {
38 std::once_flag g_initialize_flag;
39 g3::LogWorker *g_logger_instance = nullptr; // instantiated and OWNED somewhere else (main)
40 std::mutex g_logging_init_mutex;
41
42 std::unique_ptr<g3::LogMessage> g_first_unintialized_msg = {nullptr};
43 std::once_flag g_set_first_uninitialized_flag;
44 std::once_flag g_save_first_unintialized_flag;
45 const std::function<void(void)> g_pre_fatal_hook_that_does_nothing = [] { /*does nothing */};
46 std::function<void(void)> g_fatal_pre_logging_hook;
47
48
49 std::atomic<size_t> g_fatal_hook_recursive_counter = {0};
50}
51
52
53
54
55
56namespace g3 {
57 // signalhandler and internal clock is only needed to install once
58 // for unit testing purposes the initializeLogging might be called
59 // several times...
60 // for all other practical use, it shouldn't!
61
62 void initializeLogging(LogWorker *bgworker) {
63 std::call_once(g_initialize_flag, [] {
64 installCrashHandler();
65 });
66 std::lock_guard<std::mutex> lock(g_logging_init_mutex);
67 if (internal::isLoggingInitialized() || nullptr == bgworker) {
68 std::ostringstream exitMsg;
69 exitMsg << __FILE__ "->" << __FUNCTION__ << ":" << __LINE__ << std::endl;
70 exitMsg << "\tFatal exit due to illegal initialization of g3::LogWorker\n";
71 exitMsg << "\t(due to multiple initializations? : " << std::boolalpha << internal::isLoggingInitialized();
72 exitMsg << ", due to nullptr == bgworker? : " << std::boolalpha << (nullptr == bgworker) << ")";
73 std::cerr << exitMsg.str() << std::endl;
74 std::exit(EXIT_FAILURE);
75 }
76
77 // Save the first uninitialized message, if any
78 std::call_once(g_save_first_unintialized_flag, [&bgworker] {
79 if (g_first_unintialized_msg) {
80 bgworker->save(LogMessagePtr {std::move(g_first_unintialized_msg)});
81 }
82 });
83
84 g_logger_instance = bgworker;
85 // by default the pre fatal logging hook does nothing
86 // if it WOULD do something it would happen in
87 setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing);
88 // recurvise crash counter re-set to zero
89 g_fatal_hook_recursive_counter.store(0);
90 }
91
92
93 /**
94 * default does nothing, @ref ::g_pre_fatal_hook_that_does_nothing
95 * It will be called just before sending the fatal message, @ref pushFatalmessageToLogger
96 * It will be reset to do nothing in ::initializeLogging(...)
97 * so please call this function, if you ever need to, after initializeLogging(...)
98 */
99 void setFatalPreLoggingHook(std::function<void(void)> pre_fatal_hook) {
100 static std::mutex m;
101 std::lock_guard<std::mutex> lock(m);
102 g_fatal_pre_logging_hook = pre_fatal_hook;
103 }
104
105
106
107
108 // By default this function pointer goes to \ref pushFatalMessageToLogger;
109 std::function<void(FatalMessagePtr) > g_fatal_to_g3logworker_function_ptr = internal::pushFatalMessageToLogger;
110
111 /** REPLACE fatalCallToLogger for fatalCallForUnitTest
112 * This function switches the function pointer so that only
113 * 'unitTest' mock-fatal calls are made.
114 * */
115 void setFatalExitHandler(std::function<void(FatalMessagePtr) > fatal_call) {
116 g_fatal_to_g3logworker_function_ptr = fatal_call;
117 }
118
119
120 namespace internal {
121
122 bool isLoggingInitialized() {
123 return g_logger_instance != nullptr;
124 }
125
126 /**
127 * Shutdown the logging by making the pointer to the background logger to nullptr. The object is not deleted
128 * that is the responsibility of its owner. *
129 */
130 void shutDownLogging() {
131 std::lock_guard<std::mutex> lock(g_logging_init_mutex);
132 g_logger_instance = nullptr;
133
134 }
135
136 /** Same as the Shutdown above but called by the destructor of the LogWorker, thus ensuring that no further
137 * LOG(...) calls can happen to a non-existing LogWorker.
138 * @param active MUST BE the LogWorker initialized for logging. If it is not then this call is just ignored
139 * and the logging continues to be active.
140 * @return true if the correct worker was given,. and shutDownLogging was called
141 */
142 bool shutDownLoggingForActiveOnly(LogWorker *active) {
143 if (isLoggingInitialized() && nullptr != active && (active != g_logger_instance)) {
144 LOG(WARNING) << "\n\t\tAttempted to shut down logging, but the ID of the Logger is not the one that is active."
145 << "\n\t\tHaving multiple instances of the g3::LogWorker is likely a BUG"
146 << "\n\t\tEither way, this call to shutDownLogging was ignored"
147 << "\n\t\tTry g3::internal::shutDownLogging() instead";
148 return false;
149 }
150 shutDownLogging();
151 return true;
152 }
153
154
155
156
157 /** explicits copy of all input. This is makes it possibly to use g3log across dynamically loaded libraries
158 * i.e. (dlopen + dlsym) */
159 void saveMessage(const char *entry, const char *file, int line, const char *function, const LEVELS &level,
160 const char *boolean_expression, int fatal_signal, const char *stack_trace) {
161 LEVELS msgLevel {level};
162 LogMessagePtr message {std2::make_unique<LogMessage>(file, line, function, msgLevel)};
163 message.get()->write().append(entry);
164 message.get()->setExpression(boolean_expression);
165
166
167 if (internal::wasFatal(level)) {
168 auto fatalhook = g_fatal_pre_logging_hook;
169 // In case the fatal_pre logging actually will cause a crash in its turn
170 // let's not do recursive crashing!
171 setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing);
172 ++g_fatal_hook_recursive_counter; // thread safe counter
173 // "benign" race here. If two threads crashes, with recursive crashes
174 // then it's possible that the "other" fatal stack trace will be shown
175 // that's OK since it was anyhow the first crash detected
176 static const std::string first_stack_trace = stack_trace;
177 fatalhook();
178 message.get()->write().append(stack_trace);
179
180 if (g_fatal_hook_recursive_counter.load() > 1) {
181 message.get()->write()
182 .append("\n\n\nWARNING\n"
183 "A recursive crash detected. It is likely the hook set with 'setFatalPreLoggingHook(...)' is responsible\n\n")
184 .append("---First crash stacktrace: ").append(first_stack_trace).append("\n---End of first stacktrace\n");
185 }
186 FatalMessagePtr fatal_message { std2::make_unique<FatalMessage>(*(message._move_only.get()), fatal_signal) };
187 // At destruction, flushes fatal message to g3LogWorker
188 // either we will stay here until the background worker has received the fatal
189 // message, flushed the crash message to the sinks and exits with the same fatal signal
190 //..... OR it's in unit-test mode then we throw a std::runtime_error (and never hit sleep)
191 fatalCall(fatal_message);
192 } else {
193 pushMessageToLogger(message);
194 }
195 }
196
197 /**
198 * save the message to the logger. In case of called before the logger is instantiated
199 * the first message will be saved. Any following subsequent unitnialized log calls
200 * will be ignored.
201 *
202 * The first initialized log entry will also save the first uninitialized log message, if any
203 * @param log_entry to save to logger
204 */
205 void pushMessageToLogger(LogMessagePtr incoming) { // todo rename to Push SavedMessage To Worker
206 // Uninitialized messages are ignored but does not CHECK/crash the logger
207 if (!internal::isLoggingInitialized()) {
208 std::call_once(g_set_first_uninitialized_flag, [&] {
209 g_first_unintialized_msg = incoming.release();
210 std::string err = {"LOGGER NOT INITIALIZED:\n\t\t"};
211 err.append(g_first_unintialized_msg->message());
212 std::string &str = g_first_unintialized_msg->write();
213 str.clear();
214 str.append(err); // replace content
215 std::cerr << str << std::endl;
216 });
217 return;
218 }
219
220 // logger is initialized
221 g_logger_instance->save(incoming);
222 }
223
224 /** Fatal call saved to logger. This will trigger SIGABRT or other fatal signal
225 * to exit the program. After saving the fatal message the calling thread
226 * will sleep forever (i.e. until the background thread catches up, saves the fatal
227 * message and kills the software with the fatal signal.
228 */
229 void pushFatalMessageToLogger(FatalMessagePtr message) {
230 if (!isLoggingInitialized()) {
231 std::ostringstream error;
232 error << "FATAL CALL but logger is NOT initialized\n"
233 << "CAUSE: " << message.get()->reason()
234 << "\nMessage: \n" << message.get()->toString() << std::flush;
235 std::cerr << error.str() << std::flush;
236 internal::exitWithDefaultSignalHandler(message.get()->_level, message.get()->_signal_id);
237 }
238 g_logger_instance->fatal(message);
239 while (shouldBlockForFatalHandling()) {
240 std::this_thread::sleep_for(std::chrono::seconds(1));
241 }
242 }
243
244 /** The default, initial, handling to send a 'fatal' event to g3logworker
245 * the caller will stay here, eternally, until the software is aborted
246 * ... in the case of unit testing it is the given "Mock" fatalCall that will
247 * define the behaviour.
248 */
249 void fatalCall(FatalMessagePtr message) {
250 g_fatal_to_g3logworker_function_ptr(FatalMessagePtr {std::move(message)});
251 }
252
253
254 } // internal
255} // g3
256
257
258