1 #ifndef _WIN32
2 // Disable all warnings from gcc/clang:
3 #pragma GCC diagnostic push
4 #pragma GCC diagnostic ignored "-Wpragmas"
5 
6 #pragma GCC diagnostic ignored "-Wc++98-compat"
7 #pragma GCC diagnostic ignored "-Wc++98-compat-pedantic"
8 #pragma GCC diagnostic ignored "-Wexit-time-destructors"
9 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
10 #pragma GCC diagnostic ignored "-Wglobal-constructors"
11 #pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
12 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
13 #pragma GCC diagnostic ignored "-Wpadded"
14 #pragma GCC diagnostic ignored "-Wsign-compare"
15 #pragma GCC diagnostic ignored "-Wsign-conversion"
16 #pragma GCC diagnostic ignored "-Wunknown-pragmas"
17 #pragma GCC diagnostic ignored "-Wunused-macros"
18 #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
19 #else
20 #ifdef _MSC_VER
21 #pragma warning(push)
22 #pragma warning(disable:4018)
23 #endif // _MSC_VER
24 #endif
25 
26 #include "loguru.hpp"
27 
28 #ifndef LOGURU_HAS_BEEN_IMPLEMENTED
29 #define LOGURU_HAS_BEEN_IMPLEMENTED
30 
31 #define LOGURU_PREAMBLE_WIDTH (53 + LOGURU_THREADNAME_WIDTH + LOGURU_FILENAME_WIDTH)
32 
33 #undef min
34 #undef max
35 
36 #include <algorithm>
37 #include <atomic>
38 #include <chrono>
39 #include <cstdarg>
40 #include <cstdio>
41 #include <cstdlib>
42 #include <cstring>
43 #include <mutex>
44 #include <regex>
45 #include <string>
46 #include <thread>
47 #include <vector>
48 
49 #ifdef _WIN32
50 	#include <direct.h>
51 
52 	#define localtime_r(a, b) localtime_s(b, a) // No localtime_r with MSVC, but arguments are swapped for localtime_s
53 #else
54 	#include <signal.h>
55 	#include <sys/stat.h> // mkdir
56 	#include <unistd.h>   // STDERR_FILENO
57 #endif
58 
59 #ifdef __linux__
60 	#include <linux/limits.h> // PATH_MAX
61 #elif !defined(_WIN32)
62 	#include <limits.h> // PATH_MAX
63 #endif
64 
65 #ifndef PATH_MAX
66 	#define PATH_MAX 1024
67 #endif
68 
69 #ifdef __APPLE__
70 	#include "TargetConditionals.h"
71 #endif
72 
73 // TODO: use defined(_POSIX_VERSION) for some of these things?
74 
75 #if defined(_WIN32) || defined(__CYGWIN__)
76 	#define LOGURU_PTHREADS    0
77 	#define LOGURU_WINTHREADS  1
78 	#ifndef LOGURU_STACKTRACES
79 		#define LOGURU_STACKTRACES 0
80 	#endif
81 #elif defined(__rtems__) || defined(__ANDROID__)
82 	#define LOGURU_PTHREADS    1
83 	#define LOGURU_WINTHREADS  0
84 	#ifndef LOGURU_STACKTRACES
85 		#define LOGURU_STACKTRACES 0
86 	#endif
87 #else
88 	#define LOGURU_PTHREADS    1
89 	#define LOGURU_WINTHREADS  0
90 	#ifndef LOGURU_STACKTRACES
91 		#define LOGURU_STACKTRACES 1
92 	#endif
93 #endif
94 
95 #if LOGURU_STACKTRACES
96 	#include <cxxabi.h>    // for __cxa_demangle
97 	#include <dlfcn.h>     // for dladdr
98 	#include <execinfo.h>  // for backtrace
99 #endif // LOGURU_STACKTRACES
100 
101 #if LOGURU_PTHREADS
102 	#include <pthread.h>
103 	#if defined(__FreeBSD__)
104 		#include <pthread_np.h>
105 		#include <sys/thr.h>
106 	#elif (defined(__OpenBSD__)||defined(__DragonFly__))
107 		#include <pthread_np.h>
108 	#endif
109 
110 	#ifdef __linux__
111 		/* On Linux, the default thread name is the same as the name of the binary.
112 		   Additionally, all new threads inherit the name of the thread it got forked from.
113 		   For this reason, Loguru use the pthread Thread Local Storage
114 		   for storing thread names on Linux. */
115 		#define LOGURU_PTLS_NAMES 1
116 	#endif
117 #endif
118 
119 #if LOGURU_WINTHREADS
120 	#ifndef _WIN32_WINNT
121 		#define _WIN32_WINNT 0x0502
122 	#endif
123 	#define WIN32_LEAN_AND_MEAN
124 	#define NOMINMAX
125 	#include <windows.h>
126 #endif
127 
128 #ifndef LOGURU_PTLS_NAMES
129    #define LOGURU_PTLS_NAMES 0
130 #endif
131 
132 
133 namespace loguru
134 {
135 	using namespace std::chrono;
136 
137 #if LOGURU_WITH_FILEABS
138 	struct FileAbs
139 	{
140 		char path[PATH_MAX];
141 		char mode_str[4];
142 		Verbosity verbosity;
143 		struct stat st;
144 		FILE* fp;
145 		bool is_reopening = false; // to prevent recursive call in file_reopen.
146 		decltype(steady_clock::now()) last_check_time = steady_clock::now();
147 	};
148 #else
149 	typedef FILE* FileAbs;
150 #endif
151 
152 	struct Callback
153 	{
154 		std::string     id;
155 		log_handler_t   callback;
156 		void*           user_data;
157 		Verbosity       verbosity; // Does not change!
158 		close_handler_t close;
159 		flush_handler_t flush;
160 		unsigned        indentation;
161 	};
162 
163 	using CallbackVec = std::vector<Callback>;
164 
165 	using StringPair     = std::pair<std::string, std::string>;
166 	using StringPairList = std::vector<StringPair>;
167 
168 	const auto s_start_time = steady_clock::now();
169 
170 	Verbosity g_stderr_verbosity  = Verbosity_0;
171 	bool      g_colorlogtostderr  = true;
172 	unsigned  g_flush_interval_ms = 0;
173 	bool      g_preamble          = true;
174 
175 	Verbosity g_internal_verbosity = Verbosity_0;
176 
177 	// Preamble details
178 	bool      g_preamble_date     = true;
179 	bool      g_preamble_time     = true;
180 	bool      g_preamble_uptime   = true;
181 	bool      g_preamble_thread   = true;
182 	bool      g_preamble_file     = true;
183 	bool      g_preamble_verbose  = true;
184 	bool      g_preamble_pipe     = true;
185 
186 	static std::recursive_mutex  s_mutex;
187 	static Verbosity             s_max_out_verbosity = Verbosity_OFF;
188 	static std::string           s_argv0_filename;
189 	static std::string           s_arguments;
190 	static char                  s_current_dir[PATH_MAX];
191 	static CallbackVec           s_callbacks;
192 	static fatal_handler_t       s_fatal_handler   = nullptr;
193 	static verbosity_to_name_t   s_verbosity_to_name_callback = nullptr;
194 	static name_to_verbosity_t   s_name_to_verbosity_callback = nullptr;
195 	static StringPairList        s_user_stack_cleanups;
196 	static bool                  s_strip_file_path = true;
197 	static std::atomic<unsigned> s_stderr_indentation { 0 };
198 
199 	// For periodic flushing:
200 	static std::thread* s_flush_thread   = nullptr;
201 	static bool         s_needs_flushing = false;
202 
__anon6d705b3f0102()203 	static const bool s_terminal_has_color = [](){
204 		#ifdef _WIN32
205 			#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
206 			#define ENABLE_VIRTUAL_TERMINAL_PROCESSING  0x0004
207 			#endif
208 
209 			HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
210 			if (hOut != INVALID_HANDLE_VALUE) {
211 				DWORD dwMode = 0;
212 				GetConsoleMode(hOut, &dwMode);
213 				dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
214 				return SetConsoleMode(hOut, dwMode) != 0;
215 			}
216 			return false;
217 		#else
218 			if (const char* term = getenv("TERM")) {
219 				return 0 == strcmp(term, "cygwin")
220 					|| 0 == strcmp(term, "linux")
221 					|| 0 == strcmp(term, "rxvt-unicode-256color")
222 					|| 0 == strcmp(term, "screen")
223 					|| 0 == strcmp(term, "screen-256color")
224 					|| 0 == strcmp(term, "screen.xterm-256color")
225 					|| 0 == strcmp(term, "tmux-256color")
226 					|| 0 == strcmp(term, "xterm")
227 					|| 0 == strcmp(term, "xterm-256color")
228 					|| 0 == strcmp(term, "xterm-termite")
229 					|| 0 == strcmp(term, "xterm-color");
230 			} else {
231 				return false;
232 			}
233 		#endif
234 	}();
235 
236 	static void print_preamble_header(char* out_buff, size_t out_buff_size);
237 
238 	#if LOGURU_PTLS_NAMES
239 		static pthread_once_t s_pthread_key_once = PTHREAD_ONCE_INIT;
240 		static pthread_key_t  s_pthread_key_name;
241 
make_pthread_key_name()242 		void make_pthread_key_name()
243 		{
244 			(void)pthread_key_create(&s_pthread_key_name, free);
245 		}
246 	#endif
247 
248 	// ------------------------------------------------------------------------------
249 	// Colors
250 
terminal_has_color()251 	bool terminal_has_color() { return s_terminal_has_color; }
252 
253 	// Colors
254 
255 #ifdef _WIN32
256 #define VTSEQ(ID) ("\x1b[1;" #ID "m")
257 #else
258 #define VTSEQ(ID) ("\x1b[" #ID "m")
259 #endif
260 
terminal_black()261 	const char* terminal_black()      { return s_terminal_has_color ? VTSEQ(30) : ""; }
terminal_red()262 	const char* terminal_red()        { return s_terminal_has_color ? VTSEQ(31) : ""; }
terminal_green()263 	const char* terminal_green()      { return s_terminal_has_color ? VTSEQ(32) : ""; }
terminal_yellow()264 	const char* terminal_yellow()     { return s_terminal_has_color ? VTSEQ(33) : ""; }
terminal_blue()265 	const char* terminal_blue()       { return s_terminal_has_color ? VTSEQ(34) : ""; }
terminal_purple()266 	const char* terminal_purple()     { return s_terminal_has_color ? VTSEQ(35) : ""; }
terminal_cyan()267 	const char* terminal_cyan()       { return s_terminal_has_color ? VTSEQ(36) : ""; }
terminal_light_gray()268 	const char* terminal_light_gray() { return s_terminal_has_color ? VTSEQ(37) : ""; }
terminal_white()269 	const char* terminal_white()      { return s_terminal_has_color ? VTSEQ(37) : ""; }
terminal_light_red()270 	const char* terminal_light_red()  { return s_terminal_has_color ? VTSEQ(91) : ""; }
terminal_dim()271 	const char* terminal_dim()        { return s_terminal_has_color ? VTSEQ(2)  : ""; }
272 
273 	// Formating
terminal_bold()274 	const char* terminal_bold()       { return s_terminal_has_color ? VTSEQ(1) : ""; }
terminal_underline()275 	const char* terminal_underline()  { return s_terminal_has_color ? VTSEQ(4) : ""; }
276 
277 	// You should end each line with this!
terminal_reset()278 	const char* terminal_reset()      { return s_terminal_has_color ? VTSEQ(0) : ""; }
279 
280 	// ------------------------------------------------------------------------------
281 #if LOGURU_WITH_FILEABS
282 	void file_reopen(void* user_data);
to_file(void * user_data)283 	inline FILE* to_file(void* user_data) { return reinterpret_cast<FileAbs*>(user_data)->fp; }
284 #else
to_file(void * user_data)285 	inline FILE* to_file(void* user_data) { return reinterpret_cast<FILE*>(user_data); }
286 #endif
287 
file_log(void * user_data,const Message & message)288 	void file_log(void* user_data, const Message& message)
289 	{
290 #if LOGURU_WITH_FILEABS
291 		FileAbs* file_abs = reinterpret_cast<FileAbs*>(user_data);
292 		if (file_abs->is_reopening) {
293 			return;
294 		}
295 		// It is better checking file change every minute/hour/day,
296 		// instead of doing this every time we log.
297 		// Here check_interval is set to zero to enable checking every time;
298 		const auto check_interval = seconds(0);
299 		if (duration_cast<seconds>(steady_clock::now() - file_abs->last_check_time) > check_interval) {
300 			file_abs->last_check_time = steady_clock::now();
301 			file_reopen(user_data);
302 		}
303 		FILE* file = to_file(user_data);
304 		if (!file) {
305 			return;
306 		}
307 #else
308 		FILE* file = to_file(user_data);
309 #endif
310 		fprintf(file, "%s%s%s%s\n",
311 			message.preamble, message.indentation, message.prefix, message.message);
312 		if (g_flush_interval_ms == 0) {
313 			fflush(file);
314 		}
315 	}
316 
file_close(void * user_data)317 	void file_close(void* user_data)
318 	{
319 		FILE* file = to_file(user_data);
320 		if (file) {
321 			fclose(file);
322 		}
323 #if LOGURU_WITH_FILEABS
324 		delete reinterpret_cast<FileAbs*>(user_data);
325 #endif
326 	}
327 
file_flush(void * user_data)328 	void file_flush(void* user_data)
329 	{
330 		FILE* file = to_file(user_data);
331 		fflush(file);
332 	}
333 
334 #if LOGURU_WITH_FILEABS
file_reopen(void * user_data)335 	void file_reopen(void* user_data)
336 	{
337 		FileAbs * file_abs = reinterpret_cast<FileAbs*>(user_data);
338 		struct stat st;
339 		int ret;
340 		if (!file_abs->fp || (ret = stat(file_abs->path, &st)) == -1 || (st.st_ino != file_abs->st.st_ino)) {
341 			file_abs->is_reopening = true;
342 			if (file_abs->fp) {
343 				fclose(file_abs->fp);
344 			}
345 			if (!file_abs->fp) {
346 				VLOG_F(g_internal_verbosity, "Reopening file '" LOGURU_FMT(s) "' due to previous error", file_abs->path);
347 			}
348 			else if (ret < 0) {
349 				const auto why = errno_as_text();
350 				VLOG_F(g_internal_verbosity, "Reopening file '" LOGURU_FMT(s) "' due to '" LOGURU_FMT(s) "'", file_abs->path, why.c_str());
351 			} else {
352 				VLOG_F(g_internal_verbosity, "Reopening file '" LOGURU_FMT(s) "' due to file changed", file_abs->path);
353 			}
354 			// try reopen current file.
355 			if (!create_directories(file_abs->path)) {
356 				LOG_F(ERROR, "Failed to create directories to '" LOGURU_FMT(s) "'", file_abs->path);
357 			}
358 			file_abs->fp = fopen(file_abs->path, file_abs->mode_str);
359 			if (!file_abs->fp) {
360 				LOG_F(ERROR, "Failed to open '" LOGURU_FMT(s) "'", file_abs->path);
361 			} else {
362 				stat(file_abs->path, &file_abs->st);
363 			}
364 			file_abs->is_reopening = false;
365 		}
366 	}
367 #endif
368 	// ------------------------------------------------------------------------------
369 
370 	// Helpers:
371 
~Text()372 	Text::~Text() { free(_str); }
373 
374 #if LOGURU_USE_FMTLIB
vtextprintf(const char * format,fmt::format_args args)375 	Text vtextprintf(const char* format, fmt::format_args args)
376 	{
377 		return Text(STRDUP(fmt::vformat(format, args).c_str()));
378 	}
379 #else
380 	LOGURU_PRINTF_LIKE(1, 0)
vtextprintf(const char * format,va_list vlist)381 	static Text vtextprintf(const char* format, va_list vlist)
382 	{
383 #ifdef _WIN32
384 		int bytes_needed = _vscprintf(format, vlist);
385 		CHECK_F(bytes_needed >= 0, "Bad string format: '%s'", format);
386 		char* buff = (char*)malloc(bytes_needed+1);
387 		vsnprintf(buff, bytes_needed+1, format, vlist);
388 		return Text(buff);
389 #else
390 		char* buff = nullptr;
391 		int result = vasprintf(&buff, format, vlist);
392 		CHECK_F(result >= 0, "Bad string format: '" LOGURU_FMT(s) "'", format);
393 		return Text(buff);
394 #endif
395 	}
396 
textprintf(const char * format,...)397 	Text textprintf(const char* format, ...)
398 	{
399 		va_list vlist;
400 		va_start(vlist, format);
401 		auto result = vtextprintf(format, vlist);
402 		va_end(vlist);
403 		return result;
404 	}
405 #endif
406 
407 	// Overloaded for variadic template matching.
textprintf()408 	Text textprintf()
409 	{
410 		return Text(static_cast<char*>(calloc(1, 1)));
411 	}
412 
indentation(unsigned depth)413 	static const char* indentation(unsigned depth)
414 	{
415 		static const char buff[] =
416 		".   .   .   .   .   .   .   .   .   .   " ".   .   .   .   .   .   .   .   .   .   "
417 		".   .   .   .   .   .   .   .   .   .   " ".   .   .   .   .   .   .   .   .   .   "
418 		".   .   .   .   .   .   .   .   .   .   " ".   .   .   .   .   .   .   .   .   .   "
419 		".   .   .   .   .   .   .   .   .   .   " ".   .   .   .   .   .   .   .   .   .   "
420 		".   .   .   .   .   .   .   .   .   .   " ".   .   .   .   .   .   .   .   .   .   ";
421 		static const size_t INDENTATION_WIDTH = 4;
422 		static const size_t NUM_INDENTATIONS = (sizeof(buff) - 1) / INDENTATION_WIDTH;
423 		depth = std::min<unsigned>(depth, NUM_INDENTATIONS);
424 		return buff + INDENTATION_WIDTH * (NUM_INDENTATIONS - depth);
425 	}
426 
parse_args(int & argc,char * argv[],const char * verbosity_flag)427 	static void parse_args(int& argc, char* argv[], const char* verbosity_flag)
428 	{
429 		int arg_dest = 1;
430 		int out_argc = argc;
431 
432 		for (int arg_it = 1; arg_it < argc; ++arg_it) {
433 			auto cmd = argv[arg_it];
434 			auto arg_len = strlen(verbosity_flag);
435 			if (strncmp(cmd, verbosity_flag, arg_len) == 0 && !std::isalpha(cmd[arg_len], std::locale(""))) {
436 				out_argc -= 1;
437 				auto value_str = cmd + arg_len;
438 				if (value_str[0] == '\0') {
439 					// Value in separate argument
440 					arg_it += 1;
441 					CHECK_LT_F(arg_it, argc, "Missing verbosiy level after " LOGURU_FMT(s) "", verbosity_flag);
442 					value_str = argv[arg_it];
443 					out_argc -= 1;
444 				}
445 				if (*value_str == '=') { value_str += 1; }
446 
447 				auto req_verbosity = get_verbosity_from_name(value_str);
448 				if (req_verbosity != Verbosity_INVALID) {
449 					g_stderr_verbosity = req_verbosity;
450 				} else {
451 					char* end = 0;
452 					g_stderr_verbosity = static_cast<int>(strtol(value_str, &end, 10));
453 					CHECK_F(end && *end == '\0',
454 						"Invalid verbosity. Expected integer, INFO, WARNING, ERROR or OFF, got '" LOGURU_FMT(s) "'", value_str);
455 				}
456 			} else {
457 				argv[arg_dest++] = argv[arg_it];
458 			}
459 		}
460 
461 		argc = out_argc;
462 		argv[argc] = nullptr;
463 	}
464 
now_ns()465 	static long long now_ns()
466 	{
467 		return duration_cast<nanoseconds>(high_resolution_clock::now().time_since_epoch()).count();
468 	}
469 
470 	// Returns the part of the path after the last / or \ (if any).
filename(const char * path)471 	const char* filename(const char* path)
472 	{
473 		for (auto ptr = path; *ptr; ++ptr) {
474 			if (*ptr == '/' || *ptr == '\\') {
475 				path = ptr + 1;
476 			}
477 		}
478 		return path;
479 	}
480 
481 	// ------------------------------------------------------------------------------
482 
on_atexit()483 	static void on_atexit()
484 	{
485 		VLOG_F(g_internal_verbosity, "atexit");
486 		flush();
487 	}
488 
489 	static void install_signal_handlers(bool unsafe_signal_handler);
490 
write_hex_digit(std::string & out,unsigned num)491 	static void write_hex_digit(std::string& out, unsigned num)
492 	{
493 		DCHECK_LT_F(num, 16u);
494 		if (num < 10u) { out.push_back(char('0' + num)); }
495 		else { out.push_back(char('A' + num - 10)); }
496 	}
497 
write_hex_byte(std::string & out,uint8_t n)498 	static void write_hex_byte(std::string& out, uint8_t n)
499 	{
500 		write_hex_digit(out, n >> 4u);
501 		write_hex_digit(out, n & 0x0f);
502 	}
503 
escape(std::string & out,const std::string & str)504 	static void escape(std::string& out, const std::string& str)
505 	{
506 		for (char c : str) {
507 			/**/ if (c == '\a') { out += "\\a";  }
508 			else if (c == '\b') { out += "\\b";  }
509 			else if (c == '\f') { out += "\\f";  }
510 			else if (c == '\n') { out += "\\n";  }
511 			else if (c == '\r') { out += "\\r";  }
512 			else if (c == '\t') { out += "\\t";  }
513 			else if (c == '\v') { out += "\\v";  }
514 			else if (c == '\\') { out += "\\\\"; }
515 			else if (c == '\'') { out += "\\\'"; }
516 			else if (c == '\"') { out += "\\\""; }
517 			else if (c == ' ')  { out += "\\ ";  }
518 			else if (0 <= c && c < 0x20) { // ASCI control character:
519 			// else if (c < 0x20 || c != (c & 127)) { // ASCII control character or UTF-8:
520 				out += "\\x";
521 				write_hex_byte(out, static_cast<uint8_t>(c));
522 			} else { out += c; }
523 		}
524 	}
525 
errno_as_text()526 	Text errno_as_text()
527 	{
528 		char buff[256];
529 	#if defined(__GLIBC__) && defined(_GNU_SOURCE)
530 		// GNU Version
531 		return Text(STRDUP(strerror_r(errno, buff, sizeof(buff))));
532 	#elif defined(__APPLE__) || _POSIX_C_SOURCE >= 200112L
533 		// XSI Version
534 		strerror_r(errno, buff, sizeof(buff));
535 		return Text(strdup(buff));
536 	#elif defined(_WIN32)
537 		strerror_s(buff, sizeof(buff), errno);
538 		return Text(STRDUP(buff));
539 	#else
540 		// Not thread-safe.
541 		return Text(STRDUP(strerror(errno)));
542 	#endif
543 	}
544 
init(int & argc,char * argv[],const Options & options)545 	void init(int& argc, char* argv[], const Options& options)
546 	{
547 		CHECK_GT_F(argc,       0,       "Expected proper argc/argv");
548 		CHECK_EQ_F(argv[argc], nullptr, "Expected proper argc/argv");
549 
550 		s_argv0_filename = filename(argv[0]);
551 
552 		#ifdef _WIN32
553 			#define getcwd _getcwd
554 		#endif
555 
556 		if (!getcwd(s_current_dir, sizeof(s_current_dir))) {
557 			const auto error_text = errno_as_text();
558 			LOG_F(WARNING, "Failed to get current working directory: " LOGURU_FMT(s) "", error_text.c_str());
559 		}
560 
561 		s_arguments = "";
562 		for (int i = 0; i < argc; ++i) {
563 			escape(s_arguments, argv[i]);
564 			if (i + 1 < argc) {
565 				s_arguments += " ";
566 			}
567 		}
568 
569 		if (options.verbosity_flag) {
570 			parse_args(argc, argv, options.verbosity_flag);
571 		}
572 
573 		if (const auto main_thread_name = options.main_thread_name) {
574 			#if LOGURU_PTLS_NAMES || LOGURU_WINTHREADS
575 				set_thread_name(main_thread_name);
576 			#elif LOGURU_PTHREADS
577 				char old_thread_name[16] = {0};
578 				auto this_thread = pthread_self();
579 				#if defined(__APPLE__) || defined(__linux__)
580 					pthread_getname_np(this_thread, old_thread_name, sizeof(old_thread_name));
581 				#endif
582 				if (old_thread_name[0] == 0) {
583 					#ifdef __APPLE__
584 						pthread_setname_np(main_thread_name);
585 					#elif defined(__FreeBSD__) || (defined(__OpenBSD__)||defined(__DragonFly__))
586 						pthread_set_name_np(this_thread, main_thread_name);
587 					#elif defined(__linux__)
588 						pthread_setname_np(this_thread, main_thread_name);
589 					#endif
590 				}
591 			#endif // LOGURU_PTHREADS
592 		}
593 
594 		if (g_stderr_verbosity >= Verbosity_INFO) {
595 			if (g_preamble) {
596 				char preamble_explain[LOGURU_PREAMBLE_WIDTH];
597 				print_preamble_header(preamble_explain, sizeof(preamble_explain));
598 				if (g_colorlogtostderr && s_terminal_has_color) {
599 					fprintf(stderr, "%s%s%s\n", terminal_reset(), terminal_dim(), preamble_explain);
600 				} else {
601 					fprintf(stderr, "%s\n", preamble_explain);
602 				}
603 			}
604 			fflush(stderr);
605 		}
606 		VLOG_F(g_internal_verbosity, "arguments: " LOGURU_FMT(s) "", s_arguments.c_str());
607 		if (strlen(s_current_dir) != 0)
608 		{
609 			VLOG_F(g_internal_verbosity, "Current dir: " LOGURU_FMT(s) "", s_current_dir);
610 		}
611 		VLOG_F(g_internal_verbosity, "stderr verbosity: " LOGURU_FMT(d) "", g_stderr_verbosity);
612 		VLOG_F(g_internal_verbosity, "-----------------------------------");
613 
614 		install_signal_handlers(options.unsafe_signal_handler);
615 
616 		atexit(on_atexit);
617 	}
618 
shutdown()619 	void shutdown()
620 	{
621 		VLOG_F(g_internal_verbosity, "loguru::shutdown()");
622 		remove_all_callbacks();
623 		set_fatal_handler(nullptr);
624 		set_verbosity_to_name_callback(nullptr);
625 		set_name_to_verbosity_callback(nullptr);
626 	}
627 
write_date_time(char * buff,size_t buff_size)628 	void write_date_time(char* buff, size_t buff_size)
629 	{
630 		auto now = system_clock::now();
631 		long long ms_since_epoch = duration_cast<milliseconds>(now.time_since_epoch()).count();
632 		time_t sec_since_epoch = time_t(ms_since_epoch / 1000);
633 		tm time_info;
634 		localtime_r(&sec_since_epoch, &time_info);
635 		snprintf(buff, buff_size, "%04d%02d%02d_%02d%02d%02d.%03lld",
636 			1900 + time_info.tm_year, 1 + time_info.tm_mon, time_info.tm_mday,
637 			time_info.tm_hour, time_info.tm_min, time_info.tm_sec, ms_since_epoch % 1000);
638 	}
639 
argv0_filename()640 	const char* argv0_filename()
641 	{
642 		return s_argv0_filename.c_str();
643 	}
644 
arguments()645 	const char* arguments()
646 	{
647 		return s_arguments.c_str();
648 	}
649 
current_dir()650 	const char* current_dir()
651 	{
652 		return s_current_dir;
653 	}
654 
home_dir()655 	const char* home_dir()
656 	{
657 		#ifdef _WIN32
658 			auto user_profile = getenv("USERPROFILE");
659 			CHECK_F(user_profile != nullptr, "Missing USERPROFILE");
660 			return user_profile;
661 		#else // _WIN32
662 			auto home = getenv("HOME");
663 			CHECK_F(home != nullptr, "Missing HOME");
664 			return home;
665 		#endif // _WIN32
666 	}
667 
suggest_log_path(const char * prefix,char * buff,unsigned buff_size)668 	void suggest_log_path(const char* prefix, char* buff, unsigned buff_size)
669 	{
670 		if (prefix[0] == '~') {
671 			snprintf(buff, buff_size - 1, "%s%s", home_dir(), prefix + 1);
672 		} else {
673 			snprintf(buff, buff_size - 1, "%s", prefix);
674 		}
675 
676 		// Check for terminating /
677 		size_t n = strlen(buff);
678 		if (n != 0) {
679 			if (buff[n - 1] != '/') {
680 				CHECK_F(n + 2 < buff_size, "Filename buffer too small");
681 				buff[n] = '/';
682 				buff[n + 1] = '\0';
683 			}
684 		}
685 
686 		strncat(buff, s_argv0_filename.c_str(), buff_size - strlen(buff) - 1);
687 		strncat(buff, "/",                      buff_size - strlen(buff) - 1);
688 		write_date_time(buff + strlen(buff),    buff_size - strlen(buff));
689 		strncat(buff, ".log",                   buff_size - strlen(buff) - 1);
690 	}
691 
create_directories(const char * file_path_const)692 	bool create_directories(const char* file_path_const)
693 	{
694 		CHECK_F(file_path_const && *file_path_const);
695 		char* file_path = STRDUP(file_path_const);
696 		for (char* p = strchr(file_path + 1, '/'); p; p = strchr(p + 1, '/')) {
697 			*p = '\0';
698 
699 	#ifdef _WIN32
700 			if (_mkdir(file_path) == -1) {
701 	#else
702 			if (mkdir(file_path, 0755) == -1) {
703 	#endif
704 				if (errno != EEXIST) {
705 					LOG_F(ERROR, "Failed to create directory '" LOGURU_FMT(s) "'", file_path);
706 					LOG_IF_F(ERROR, errno == EACCES,       "EACCES");
707 					LOG_IF_F(ERROR, errno == ENAMETOOLONG, "ENAMETOOLONG");
708 					LOG_IF_F(ERROR, errno == ENOENT,       "ENOENT");
709 					LOG_IF_F(ERROR, errno == ENOTDIR,      "ENOTDIR");
710 					LOG_IF_F(ERROR, errno == ELOOP,        "ELOOP");
711 
712 					*p = '/';
713 					free(file_path);
714 					return false;
715 				}
716 			}
717 			*p = '/';
718 		}
719 		free(file_path);
720 		return true;
721 	}
722 	bool add_file(const char* path_in, FileMode mode, Verbosity verbosity)
723 	{
724 		char path[PATH_MAX];
725 		if (path_in[0] == '~') {
726 			snprintf(path, sizeof(path) - 1, "%s%s", home_dir(), path_in + 1);
727 		} else {
728 			snprintf(path, sizeof(path) - 1, "%s", path_in);
729 		}
730 
731 		if (!create_directories(path)) {
732 			LOG_F(ERROR, "Failed to create directories to '" LOGURU_FMT(s) "'", path);
733 		}
734 
735 		const char* mode_str = (mode == FileMode::Truncate ? "w" : "a");
736 		auto file = fopen(path, mode_str);
737 		if (!file) {
738 			LOG_F(ERROR, "Failed to open '" LOGURU_FMT(s) "'", path);
739 			return false;
740 		}
741 #if LOGURU_WITH_FILEABS
742 		FileAbs* file_abs = new FileAbs(); // this is deleted in file_close;
743 		snprintf(file_abs->path, sizeof(file_abs->path) - 1, "%s", path);
744 		snprintf(file_abs->mode_str, sizeof(file_abs->mode_str) - 1, "%s", mode_str);
745 		stat(file_abs->path, &file_abs->st);
746 		file_abs->fp = file;
747 		file_abs->verbosity = verbosity;
748 		add_callback(path_in, file_log, file_abs, verbosity, file_close, file_flush);
749 #else
750 		add_callback(path_in, file_log, file, verbosity, file_close, file_flush);
751 #endif
752 
753 		if (mode == FileMode::Append) {
754 			fprintf(file, "\n\n\n\n\n");
755 		}
756 		if (!s_arguments.empty()) {
757 			fprintf(file, "arguments: %s\n", s_arguments.c_str());
758 		}
759 		if (strlen(s_current_dir) != 0) {
760 			fprintf(file, "Current dir: %s\n", s_current_dir);
761 		}
762 		fprintf(file, "File verbosity level: %d\n", verbosity);
763 		if (g_preamble) {
764 			char preamble_explain[LOGURU_PREAMBLE_WIDTH];
765 			print_preamble_header(preamble_explain, sizeof(preamble_explain));
766 			fprintf(file, "%s\n", preamble_explain);
767 		}
768 		fflush(file);
769 
770 		VLOG_F(g_internal_verbosity, "Logging to '" LOGURU_FMT(s) "', mode: '" LOGURU_FMT(s) "', verbosity: " LOGURU_FMT(d) "", path, mode_str, verbosity);
771 		return true;
772 	}
773 
774 	// Will be called right before abort().
775 	void set_fatal_handler(fatal_handler_t handler)
776 	{
777 		s_fatal_handler = handler;
778 	}
779 
780 	fatal_handler_t get_fatal_handler()
781 	{
782 		return s_fatal_handler;
783 	}
784 
785 	void set_verbosity_to_name_callback(verbosity_to_name_t callback)
786 	{
787 		s_verbosity_to_name_callback = callback;
788 	}
789 
790 	void set_name_to_verbosity_callback(name_to_verbosity_t callback)
791 	{
792 		s_name_to_verbosity_callback = callback;
793 	}
794 
795 	void add_stack_cleanup(const char* find_this, const char* replace_with_this)
796 	{
797 		if (strlen(find_this) <= strlen(replace_with_this)) {
798 			LOG_F(WARNING, "add_stack_cleanup: the replacement should be shorter than the pattern!");
799 			return;
800 		}
801 
802 		s_user_stack_cleanups.push_back(StringPair(find_this, replace_with_this));
803 	}
804 
805 	static void on_callback_change()
806 	{
807 		s_max_out_verbosity = Verbosity_OFF;
808 		for (const auto& callback : s_callbacks) {
809 			s_max_out_verbosity = std::max(s_max_out_verbosity, callback.verbosity);
810 		}
811 	}
812 
813 	void add_callback(
814 		const char*     id,
815 		log_handler_t   callback,
816 		void*           user_data,
817 		Verbosity       verbosity,
818 		close_handler_t on_close,
819 		flush_handler_t on_flush)
820 	{
821 		std::lock_guard<std::recursive_mutex> lock(s_mutex);
822 		s_callbacks.push_back(Callback{id, callback, user_data, verbosity, on_close, on_flush, 0});
823 		on_callback_change();
824 	}
825 
826 	// Returns a custom verbosity name if one is available, or nullptr.
827 	// See also set_verbosity_to_name_callback.
828 	const char* get_verbosity_name(Verbosity verbosity)
829 	{
830 		auto name = s_verbosity_to_name_callback
831 				? (*s_verbosity_to_name_callback)(verbosity)
832 				: nullptr;
833 
834 		// Use standard replacements if callback fails:
835 		if (!name)
836 		{
837 			if (verbosity <= Verbosity_FATAL) {
838 				name = "FATL";
839 			} else if (verbosity == Verbosity_ERROR) {
840 				name = "ERR";
841 			} else if (verbosity == Verbosity_WARNING) {
842 				name = "WARN";
843 			} else if (verbosity == Verbosity_INFO) {
844 				name = "INFO";
845 			}
846 		}
847 
848 		return name;
849 	}
850 
851 	// Returns Verbosity_INVALID if the name is not found.
852 	// See also set_name_to_verbosity_callback.
853 	Verbosity get_verbosity_from_name(const char* name)
854 	{
855 		auto verbosity = s_name_to_verbosity_callback
856 				? (*s_name_to_verbosity_callback)(name)
857 				: Verbosity_INVALID;
858 
859 		// Use standard replacements if callback fails:
860 		if (verbosity == Verbosity_INVALID) {
861 			if (strcmp(name, "OFF") == 0) {
862 				verbosity = Verbosity_OFF;
863 			} else if (strcmp(name, "INFO") == 0) {
864 				verbosity = Verbosity_INFO;
865 			} else if (strcmp(name, "WARNING") == 0) {
866 				verbosity = Verbosity_WARNING;
867 			} else if (strcmp(name, "ERROR") == 0) {
868 				verbosity = Verbosity_ERROR;
869 			} else if (strcmp(name, "FATAL") == 0) {
870 				verbosity = Verbosity_FATAL;
871 			}
872 		}
873 
874 		return verbosity;
875 	}
876 
877 	bool remove_callback(const char* id)
878 	{
879 		std::lock_guard<std::recursive_mutex> lock(s_mutex);
880 		auto it = std::find_if(begin(s_callbacks), end(s_callbacks), [&](const Callback& c) { return c.id == id; });
881 		if (it != s_callbacks.end()) {
882 			if (it->close) { it->close(it->user_data); }
883 			s_callbacks.erase(it);
884 			on_callback_change();
885 			return true;
886 		} else {
887 			LOG_F(ERROR, "Failed to locate callback with id '" LOGURU_FMT(s) "'", id);
888 			return false;
889 		}
890 	}
891 
892 	void remove_all_callbacks()
893 	{
894 		std::lock_guard<std::recursive_mutex> lock(s_mutex);
895 		for (auto& callback : s_callbacks) {
896 			if (callback.close) {
897 				callback.close(callback.user_data);
898 			}
899 		}
900 		s_callbacks.clear();
901 		on_callback_change();
902 	}
903 
904 	// Returns the maximum of g_stderr_verbosity and all file/custom outputs.
905 	Verbosity current_verbosity_cutoff()
906 	{
907 		return g_stderr_verbosity > s_max_out_verbosity ?
908 			   g_stderr_verbosity : s_max_out_verbosity;
909 	}
910 
911 #if LOGURU_WINTHREADS
912 	char* get_thread_name_win32()
913 	{
914 		__declspec( thread ) static char thread_name[LOGURU_THREADNAME_WIDTH + 1] = {0};
915 		return &thread_name[0];
916 	}
917 #endif // LOGURU_WINTHREADS
918 
919 	void set_thread_name(const char* name)
920 	{
921 		#if LOGURU_PTLS_NAMES
922 			(void)pthread_once(&s_pthread_key_once, make_pthread_key_name);
923 			(void)pthread_setspecific(s_pthread_key_name, STRDUP(name));
924 
925 		#elif LOGURU_PTHREADS
926 			#ifdef __APPLE__
927 				pthread_setname_np(name);
928 			#elif defined(__FreeBSD__) || (defined(__OpenBSD__)||defined(__DragonFly__))
929 				pthread_set_name_np(pthread_self(), name);
930 			#elif defined(__linux__)
931 				pthread_setname_np(pthread_self(), name);
932 			#endif
933 		#elif LOGURU_WINTHREADS
934 			strncpy_s(get_thread_name_win32(), LOGURU_THREADNAME_WIDTH + 1, name, _TRUNCATE);
935 		#else // LOGURU_PTHREADS
936 			(void)name;
937 		#endif // LOGURU_PTHREADS
938 	}
939 
940 #if LOGURU_PTLS_NAMES
941 	const char* get_thread_name_ptls()
942 	{
943 		(void)pthread_once(&s_pthread_key_once, make_pthread_key_name);
944 		return static_cast<const char*>(pthread_getspecific(s_pthread_key_name));
945 	}
946 #endif // LOGURU_PTLS_NAMES
947 
948 	void get_thread_name(char* buffer, unsigned long long length, bool right_align_hext_id)
949 	{
950 #ifdef _WIN32
951 		(void)right_align_hext_id;
952 #endif
953 		CHECK_NE_F(length, 0u, "Zero length buffer in get_thread_name");
954 		CHECK_NOTNULL_F(buffer, "nullptr in get_thread_name");
955 #if LOGURU_PTHREADS
956 		auto thread = pthread_self();
957 		#if LOGURU_PTLS_NAMES
958 			if (const char* name = get_thread_name_ptls()) {
959 				snprintf(buffer, length, "%s", name);
960 			} else {
961 				buffer[0] = 0;
962 			}
963 		#elif defined(__APPLE__) || defined(__linux__)
964 			pthread_getname_np(thread, buffer, length);
965 		#else
966 			buffer[0] = 0;
967 		#endif
968 
969 		if (buffer[0] == 0) {
970 			#ifdef __APPLE__
971 				uint64_t thread_id;
972 				pthread_threadid_np(thread, &thread_id);
973 			#elif defined(__FreeBSD__)
974 				long thread_id;
975 				(void)thr_self(&thread_id);
976 			#elif (defined(__OpenBSD__)||defined(__DragonFly__))
977 				unsigned thread_id = -1;
978 			#else
979 				uint64_t thread_id = thread;
980 			#endif
981 			if (right_align_hext_id) {
982 				snprintf(buffer, length, "%*X", static_cast<int>(length - 1), static_cast<unsigned>(thread_id));
983 			} else {
984 				snprintf(buffer, length, "%X", static_cast<unsigned>(thread_id));
985 			}
986 		}
987 #elif LOGURU_WINTHREADS
988 		if (const char* name = get_thread_name_win32()) {
989 			snprintf(buffer, (size_t)length, "%s", name);
990 		} else {
991 			buffer[0] = 0;
992 		}
993 #else // !LOGURU_WINTHREADS && !LOGURU_WINTHREADS
994 		buffer[0] = 0;
995 #endif
996 
997 	}
998 
999 	// ------------------------------------------------------------------------
1000 	// Stack traces
1001 
1002 #if LOGURU_STACKTRACES
1003 	Text demangle(const char* name)
1004 	{
1005 		int status = -1;
1006 		char* demangled = abi::__cxa_demangle(name, 0, 0, &status);
1007 		Text result{status == 0 ? demangled : STRDUP(name)};
1008 		return result;
1009 	}
1010 
1011 	#if LOGURU_RTTI
1012 		template <class T>
1013 		std::string type_name()
1014 		{
1015 			auto demangled = demangle(typeid(T).name());
1016 			return demangled.c_str();
1017 		}
1018 	#endif // LOGURU_RTTI
1019 
1020 	static const StringPairList REPLACE_LIST = {
1021 		#if LOGURU_RTTI
1022 			{ type_name<std::string>(),    "std::string"    },
1023 			{ type_name<std::wstring>(),   "std::wstring"   },
1024 			{ type_name<std::u16string>(), "std::u16string" },
1025 			{ type_name<std::u32string>(), "std::u32string" },
1026 		#endif // LOGURU_RTTI
1027 		{ "std::__1::",                "std::"          },
1028 		{ "__thiscall ",               ""               },
1029 		{ "__cdecl ",                  ""               },
1030 	};
1031 
1032 	void do_replacements(const StringPairList& replacements, std::string& str)
1033 	{
1034 		for (auto&& p : replacements) {
1035 			if (p.first.size() <= p.second.size()) {
1036 				// On gcc, "type_name<std::string>()" is "std::string"
1037 				continue;
1038 			}
1039 
1040 			size_t it;
1041 			while ((it=str.find(p.first)) != std::string::npos) {
1042 				str.replace(it, p.first.size(), p.second);
1043 			}
1044 		}
1045 	}
1046 
1047 	std::string prettify_stacktrace(const std::string& input)
1048 	{
1049 		std::string output = input;
1050 
1051 		do_replacements(s_user_stack_cleanups, output);
1052 		do_replacements(REPLACE_LIST, output);
1053 
1054 		try {
1055 			std::regex std_allocator_re(R"(,\s*std::allocator<[^<>]+>)");
1056 			output = std::regex_replace(output, std_allocator_re, std::string(""));
1057 
1058 			std::regex template_spaces_re(R"(<\s*([^<> ]+)\s*>)");
1059 			output = std::regex_replace(output, template_spaces_re, std::string("<$1>"));
1060 		} catch (std::regex_error&) {
1061 			// Probably old GCC.
1062 		}
1063 
1064 		return output;
1065 	}
1066 
1067 	std::string stacktrace_as_stdstring(int skip)
1068 	{
1069 		// From https://gist.github.com/fmela/591333
1070 		void* callstack[128];
1071 		const auto max_frames = sizeof(callstack) / sizeof(callstack[0]);
1072 		int num_frames = backtrace(callstack, max_frames);
1073 		char** symbols = backtrace_symbols(callstack, num_frames);
1074 
1075 		std::string result;
1076 		// Print stack traces so the most relevant ones are written last
1077 		// Rationale: http://yellerapp.com/posts/2015-01-22-upside-down-stacktraces.html
1078 		for (int i = num_frames - 1; i >= skip; --i) {
1079 			char buf[1024];
1080 			Dl_info info;
1081 			if (dladdr(callstack[i], &info) && info.dli_sname) {
1082 				char* demangled = NULL;
1083 				int status = -1;
1084 				if (info.dli_sname[0] == '_') {
1085 					demangled = abi::__cxa_demangle(info.dli_sname, 0, 0, &status);
1086 				}
1087 				snprintf(buf, sizeof(buf), "%-3d %*p %s + %zd\n",
1088 						 i - skip, int(2 + sizeof(void*) * 2), callstack[i],
1089 						 status == 0 ? demangled :
1090 						 info.dli_sname == 0 ? symbols[i] : info.dli_sname,
1091 						 static_cast<char*>(callstack[i]) - static_cast<char*>(info.dli_saddr));
1092 				free(demangled);
1093 			} else {
1094 				snprintf(buf, sizeof(buf), "%-3d %*p %s\n",
1095 						 i - skip, int(2 + sizeof(void*) * 2), callstack[i], symbols[i]);
1096 			}
1097 			result += buf;
1098 		}
1099 		free(symbols);
1100 
1101 		if (num_frames == max_frames) {
1102 			result = "[truncated]\n" + result;
1103 		}
1104 
1105 		if (!result.empty() && result[result.size() - 1] == '\n') {
1106 			result.resize(result.size() - 1);
1107 		}
1108 
1109 		return prettify_stacktrace(result);
1110 	}
1111 
1112 #else // LOGURU_STACKTRACES
1113 	Text demangle(const char* name)
1114 	{
1115 		return Text(STRDUP(name));
1116 	}
1117 
1118 	std::string stacktrace_as_stdstring(int)
1119 	{
1120 		// No stacktraces available on this platform"
1121 		return "";
1122 	}
1123 
1124 #endif // LOGURU_STACKTRACES
1125 
1126 	Text stacktrace(int skip)
1127 	{
1128 		auto str = stacktrace_as_stdstring(skip + 1);
1129 		return Text(STRDUP(str.c_str()));
1130 	}
1131 
1132 	// ------------------------------------------------------------------------
1133 
1134 	static void print_preamble_header(char* out_buff, size_t out_buff_size)
1135 	{
1136 		if (out_buff_size == 0) { return; }
1137 		out_buff[0] = '\0';
1138 		long pos = 0;
1139 		if (g_preamble_date && pos < out_buff_size) {
1140 			pos += snprintf(out_buff + pos, out_buff_size - pos, "date       ");
1141 		}
1142 		if (g_preamble_time && pos < out_buff_size) {
1143 			pos += snprintf(out_buff + pos, out_buff_size - pos, "time         ");
1144 		}
1145 		if (g_preamble_uptime && pos < out_buff_size) {
1146 			pos += snprintf(out_buff + pos, out_buff_size - pos, "( uptime  ) ");
1147 		}
1148 		if (g_preamble_thread && pos < out_buff_size) {
1149 			pos += snprintf(out_buff + pos, out_buff_size - pos, "[%-*s]", LOGURU_THREADNAME_WIDTH, " thread name/id");
1150 		}
1151 		if (g_preamble_file && pos < out_buff_size) {
1152 			pos += snprintf(out_buff + pos, out_buff_size - pos, "%*s:line  ", LOGURU_FILENAME_WIDTH, "file");
1153 		}
1154 		if (g_preamble_verbose && pos < out_buff_size) {
1155 			pos += snprintf(out_buff + pos, out_buff_size - pos, "   v");
1156 		}
1157 		if (g_preamble_pipe && pos < out_buff_size) {
1158 			pos += snprintf(out_buff + pos, out_buff_size - pos, "| ");
1159 		}
1160 	}
1161 
1162 	static void print_preamble(char* out_buff, size_t out_buff_size, Verbosity verbosity, const char* file, unsigned line)
1163 	{
1164 		if (out_buff_size == 0) { return; }
1165 		out_buff[0] = '\0';
1166 		if (!g_preamble) { return; }
1167 		long long ms_since_epoch = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
1168 		time_t sec_since_epoch = time_t(ms_since_epoch / 1000);
1169 		tm time_info;
1170 		localtime_r(&sec_since_epoch, &time_info);
1171 
1172 		auto uptime_ms = duration_cast<milliseconds>(steady_clock::now() - s_start_time).count();
1173 		auto uptime_sec = uptime_ms / 1000.0;
1174 
1175 		char thread_name[LOGURU_THREADNAME_WIDTH + 1] = {0};
1176 		get_thread_name(thread_name, LOGURU_THREADNAME_WIDTH + 1, true);
1177 
1178 		if (s_strip_file_path) {
1179 			file = filename(file);
1180 		}
1181 
1182 		char level_buff[6];
1183 		const char* custom_level_name = get_verbosity_name(verbosity);
1184 		if (custom_level_name) {
1185 			snprintf(level_buff, sizeof(level_buff) - 1, "%s", custom_level_name);
1186 		} else {
1187 			snprintf(level_buff, sizeof(level_buff) - 1, "% 4d", verbosity);
1188 		}
1189 
1190 		long pos = 0;
1191 
1192 		if (g_preamble_date && pos < out_buff_size) {
1193 			pos += snprintf(out_buff + pos, out_buff_size - pos, "%04d-%02d-%02d ",
1194 				             1900 + time_info.tm_year, 1 + time_info.tm_mon, time_info.tm_mday);
1195 		}
1196 		if (g_preamble_time && pos < out_buff_size) {
1197 			pos += snprintf(out_buff + pos, out_buff_size - pos, "%02d:%02d:%02d.%03lld ",
1198 			               time_info.tm_hour, time_info.tm_min, time_info.tm_sec, ms_since_epoch % 1000);
1199 		}
1200 		if (g_preamble_uptime && pos < out_buff_size) {
1201 			pos += snprintf(out_buff + pos, out_buff_size - pos, "(%8.3fs) ",
1202 			               uptime_sec);
1203 		}
1204 		if (g_preamble_thread && pos < out_buff_size) {
1205 			pos += snprintf(out_buff + pos, out_buff_size - pos, "[%-*s]",
1206 			               LOGURU_THREADNAME_WIDTH, thread_name);
1207 		}
1208 		if (g_preamble_file && pos < out_buff_size) {
1209 			char shortened_filename[LOGURU_FILENAME_WIDTH + 1];
1210 			snprintf(shortened_filename, LOGURU_FILENAME_WIDTH + 1, "%s", file);
1211 			pos += snprintf(out_buff + pos, out_buff_size - pos, "%*s:%-5u ",
1212 			               LOGURU_FILENAME_WIDTH, shortened_filename, line);
1213 		}
1214 		if (g_preamble_verbose && pos < out_buff_size) {
1215 			pos += snprintf(out_buff + pos, out_buff_size - pos, "%4s",
1216 			               level_buff);
1217 		}
1218 		if (g_preamble_pipe && pos < out_buff_size) {
1219 			pos += snprintf(out_buff + pos, out_buff_size - pos, "| ");
1220 		}
1221 	}
1222 
1223 	// stack_trace_skip is just if verbosity == FATAL.
1224 	static void log_message(int stack_trace_skip, Message& message, bool with_indentation, bool abort_if_fatal)
1225 	{
1226 		const auto verbosity = message.verbosity;
1227 		std::lock_guard<std::recursive_mutex> lock(s_mutex);
1228 
1229 		if (message.verbosity == Verbosity_FATAL) {
1230 			auto st = loguru::stacktrace(stack_trace_skip + 2);
1231 			if (!st.empty()) {
1232 				RAW_LOG_F(ERROR, "Stack trace:\n" LOGURU_FMT(s) "", st.c_str());
1233 			}
1234 
1235 			auto ec = loguru::get_error_context();
1236 			if (!ec.empty()) {
1237 				RAW_LOG_F(ERROR, "" LOGURU_FMT(s) "", ec.c_str());
1238 			}
1239 		}
1240 
1241 		if (with_indentation) {
1242 			message.indentation = indentation(s_stderr_indentation);
1243 		}
1244 
1245 		if (verbosity <= g_stderr_verbosity) {
1246 			if (g_colorlogtostderr && s_terminal_has_color) {
1247 				if (verbosity > Verbosity_WARNING) {
1248 					fprintf(stderr, "%s%s%s%s%s%s%s%s\n",
1249 						terminal_reset(),
1250 						terminal_dim(),
1251 						message.preamble,
1252 						message.indentation,
1253 						verbosity == Verbosity_INFO ? terminal_reset() : "", // un-dim for info
1254 						message.prefix,
1255 						message.message,
1256 						terminal_reset());
1257 				} else {
1258 					fprintf(stderr, "%s%s%s%s%s%s%s\n",
1259 						terminal_reset(),
1260 						verbosity == Verbosity_WARNING ? terminal_yellow() : terminal_red(),
1261 						message.preamble,
1262 						message.indentation,
1263 						message.prefix,
1264 						message.message,
1265 						terminal_reset());
1266 				}
1267 			} else {
1268 				fprintf(stderr, "%s%s%s%s\n",
1269 					message.preamble, message.indentation, message.prefix, message.message);
1270 			}
1271 
1272 			if (g_flush_interval_ms == 0) {
1273 				fflush(stderr);
1274 			} else {
1275 				s_needs_flushing = true;
1276 			}
1277 		}
1278 
1279 		for (auto& p : s_callbacks) {
1280 			if (verbosity <= p.verbosity) {
1281 				if (with_indentation) {
1282 					message.indentation = indentation(p.indentation);
1283 				}
1284 				p.callback(p.user_data, message);
1285 				if (g_flush_interval_ms == 0) {
1286 					if (p.flush) { p.flush(p.user_data); }
1287 				} else {
1288 					s_needs_flushing = true;
1289 				}
1290 			}
1291 		}
1292 
1293 		if (g_flush_interval_ms > 0 && !s_flush_thread) {
1294 			s_flush_thread = new std::thread([](){
1295 				for (;;) {
1296 					if (s_needs_flushing) {
1297 						flush();
1298 					}
1299 					std::this_thread::sleep_for(std::chrono::milliseconds(g_flush_interval_ms));
1300 				}
1301 			});
1302 		}
1303 
1304 		if (message.verbosity == Verbosity_FATAL) {
1305 			flush();
1306 
1307 			if (s_fatal_handler) {
1308 				s_fatal_handler(message);
1309 				flush();
1310 			}
1311 
1312 			if (abort_if_fatal) {
1313 #if LOGURU_CATCH_SIGABRT && !defined(_WIN32)
1314 				// Make sure we don't catch our own abort:
1315 				signal(SIGABRT, SIG_DFL);
1316 #endif
1317 				abort();
1318 			}
1319 		}
1320 	}
1321 
1322 	// stack_trace_skip is just if verbosity == FATAL.
1323 	void log_to_everywhere(int stack_trace_skip, Verbosity verbosity,
1324 	                       const char* file, unsigned line,
1325 	                       const char* prefix, const char* buff)
1326 	{
1327 		char preamble_buff[LOGURU_PREAMBLE_WIDTH];
1328 		print_preamble(preamble_buff, sizeof(preamble_buff), verbosity, file, line);
1329 		auto message = Message{verbosity, file, line, preamble_buff, "", prefix, buff};
1330 		log_message(stack_trace_skip + 1, message, true, true);
1331 	}
1332 
1333 #if LOGURU_USE_FMTLIB
1334 	void vlog(Verbosity verbosity, const char* file, unsigned line, const char* format, fmt::format_args args)
1335 	{
1336 		auto formatted = fmt::vformat(format, args);
1337 		log_to_everywhere(1, verbosity, file, line, "", formatted.c_str());
1338 	}
1339 
1340 	void raw_vlog(Verbosity verbosity, const char* file, unsigned line, const char* format, fmt::format_args args)
1341 	{
1342 		auto formatted = fmt::vformat(format, args);
1343 		auto message = Message{verbosity, file, line, "", "", "", formatted.c_str()};
1344 		log_message(1, message, false, true);
1345 	}
1346 #else
1347 	void log(Verbosity verbosity, const char* file, unsigned line, const char* format, ...)
1348 	{
1349 		va_list vlist;
1350 		va_start(vlist, format);
1351 		auto buff = vtextprintf(format, vlist);
1352 		log_to_everywhere(1, verbosity, file, line, "", buff.c_str());
1353 		va_end(vlist);
1354 	}
1355 
1356 	void raw_log(Verbosity verbosity, const char* file, unsigned line, const char* format, ...)
1357 	{
1358 		va_list vlist;
1359 		va_start(vlist, format);
1360 		auto buff = vtextprintf(format, vlist);
1361 		auto message = Message{verbosity, file, line, "", "", "", buff.c_str()};
1362 		log_message(1, message, false, true);
1363 		va_end(vlist);
1364 	}
1365 #endif
1366 
1367 	void flush()
1368 	{
1369 		std::lock_guard<std::recursive_mutex> lock(s_mutex);
1370 		fflush(stderr);
1371 		for (const auto& callback : s_callbacks)
1372 		{
1373 			if (callback.flush) {
1374 				callback.flush(callback.user_data);
1375 			}
1376 		}
1377 		s_needs_flushing = false;
1378 	}
1379 
1380 	LogScopeRAII::LogScopeRAII(Verbosity verbosity, const char* file, unsigned line, const char* format, ...)
1381 		: _verbosity(verbosity), _file(file), _line(line)
1382 	{
1383 		if (verbosity <= current_verbosity_cutoff()) {
1384 			std::lock_guard<std::recursive_mutex> lock(s_mutex);
1385 			_indent_stderr = (verbosity <= g_stderr_verbosity);
1386 			_start_time_ns = now_ns();
1387 			va_list vlist;
1388 			va_start(vlist, format);
1389 			vsnprintf(_name, sizeof(_name), format, vlist);
1390 			log_to_everywhere(1, _verbosity, file, line, "{ ", _name);
1391 			va_end(vlist);
1392 
1393 			if (_indent_stderr) {
1394 				++s_stderr_indentation;
1395 			}
1396 
1397 			for (auto& p : s_callbacks) {
1398 				if (verbosity <= p.verbosity) {
1399 					++p.indentation;
1400 				}
1401 			}
1402 		} else {
1403 			_file = nullptr;
1404 		}
1405 	}
1406 
1407 	LogScopeRAII::~LogScopeRAII()
1408 	{
1409 		if (_file) {
1410 			std::lock_guard<std::recursive_mutex> lock(s_mutex);
1411 			if (_indent_stderr && s_stderr_indentation > 0) {
1412 				--s_stderr_indentation;
1413 			}
1414 			for (auto& p : s_callbacks) {
1415 				// Note: Callback indentation cannot change!
1416 				if (_verbosity <= p.verbosity) {
1417 					// in unlikely case this callback is new
1418 					if (p.indentation > 0) {
1419 						--p.indentation;
1420 					}
1421 				}
1422 			}
1423 #if LOGURU_VERBOSE_SCOPE_ENDINGS
1424 			auto duration_sec = (now_ns() - _start_time_ns) / 1e9;
1425 #if LOGURU_USE_FMTLIB
1426 			auto buff = textprintf("{:.{}f} s: {:s}", duration_sec, LOGURU_SCOPE_TIME_PRECISION, _name);
1427 #else
1428 			auto buff = textprintf("%.*f s: %s", LOGURU_SCOPE_TIME_PRECISION, duration_sec, _name);
1429 #endif
1430 			log_to_everywhere(1, _verbosity, _file, _line, "} ", buff.c_str());
1431 #else
1432 			log_to_everywhere(1, _verbosity, _file, _line, "}", "");
1433 #endif
1434 		}
1435 	}
1436 
1437 #if LOGURU_USE_FMTLIB
1438 	void vlog_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line, const char* format, fmt::format_args args)
1439 	{
1440 		auto formatted = fmt::vformat(format, args);
1441 		log_to_everywhere(stack_trace_skip + 1, Verbosity_FATAL, file, line, expr, formatted.c_str());
1442 		abort(); // log_to_everywhere already does this, but this makes the analyzer happy.
1443 	}
1444 #else
1445 	void log_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line, const char* format, ...)
1446 	{
1447 		va_list vlist;
1448 		va_start(vlist, format);
1449 		auto buff = vtextprintf(format, vlist);
1450 		log_to_everywhere(stack_trace_skip + 1, Verbosity_FATAL, file, line, expr, buff.c_str());
1451 		va_end(vlist);
1452 		abort(); // log_to_everywhere already does this, but this makes the analyzer happy.
1453 	}
1454 #endif
1455 
1456 	void log_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line)
1457 	{
1458 		log_and_abort(stack_trace_skip + 1, expr, file, line, " ");
1459 	}
1460 
1461 	// ----------------------------------------------------------------------------
1462 	// Streams:
1463 
1464 #if LOGURU_USE_FMTLIB
1465 	template<typename... Args>
1466 	std::string vstrprintf(const char* format, const Args&... args)
1467 	{
1468 		auto text = textprintf(format, args...);
1469 		std::string result = text.c_str();
1470 		return result;
1471 	}
1472 
1473 	template<typename... Args>
1474 	std::string strprintf(const char* format, const Args&... args)
1475 	{
1476 		return vstrprintf(format, args...);
1477 	}
1478 #else
1479 	std::string vstrprintf(const char* format, va_list vlist)
1480 	{
1481 		auto text = vtextprintf(format, vlist);
1482 		std::string result = text.c_str();
1483 		return result;
1484 	}
1485 
1486 	std::string strprintf(const char* format, ...)
1487 	{
1488 		va_list vlist;
1489 		va_start(vlist, format);
1490 		auto result = vstrprintf(format, vlist);
1491 		va_end(vlist);
1492 		return result;
1493 	}
1494 #endif
1495 
1496 	#if LOGURU_WITH_STREAMS
1497 
1498 	StreamLogger::~StreamLogger() noexcept(false)
1499 	{
1500 		auto message = _ss.str();
1501 		log(_verbosity, _file, _line, LOGURU_FMT(s), message.c_str());
1502 	}
1503 
1504 	AbortLogger::~AbortLogger() noexcept(false)
1505 	{
1506 		auto message = _ss.str();
1507 		loguru::log_and_abort(1, _expr, _file, _line, LOGURU_FMT(s), message.c_str());
1508 	}
1509 
1510 	#endif // LOGURU_WITH_STREAMS
1511 
1512 	// ----------------------------------------------------------------------------
1513 	// 888888 88""Yb 88""Yb  dP"Yb  88""Yb      dP""b8  dP"Yb  88b 88 888888 888888 Yb  dP 888888
1514 	// 88__   88__dP 88__dP dP   Yb 88__dP     dP   `" dP   Yb 88Yb88   88   88__    YbdP    88
1515 	// 88""   88"Yb  88"Yb  Yb   dP 88"Yb      Yb      Yb   dP 88 Y88   88   88""    dPYb    88
1516 	// 888888 88  Yb 88  Yb  YbodP  88  Yb      YboodP  YbodP  88  Y8   88   888888 dP  Yb   88
1517 	// ----------------------------------------------------------------------------
1518 
1519 	struct StringStream
1520 	{
1521 		std::string str;
1522 	};
1523 
1524 	// Use this in your EcPrinter implementations.
1525 	void stream_print(StringStream& out_string_stream, const char* text)
1526 	{
1527 		out_string_stream.str += text;
1528 	}
1529 
1530 	// ----------------------------------------------------------------------------
1531 
1532 	using ECPtr = EcEntryBase*;
1533 
1534 #if defined(_WIN32) || (defined(__APPLE__) && !TARGET_OS_IPHONE)
1535 	#ifdef __APPLE__
1536 		#define LOGURU_THREAD_LOCAL __thread
1537 	#else
1538 		#define LOGURU_THREAD_LOCAL thread_local
1539 	#endif
1540 	static LOGURU_THREAD_LOCAL ECPtr thread_ec_ptr = nullptr;
1541 
1542 	ECPtr& get_thread_ec_head_ref()
1543 	{
1544 		return thread_ec_ptr;
1545 	}
1546 #else // !thread_local
1547 	static pthread_once_t s_ec_pthread_once = PTHREAD_ONCE_INIT;
1548 	static pthread_key_t  s_ec_pthread_key;
1549 
1550 	void free_ec_head_ref(void* io_error_context)
1551 	{
1552 		delete reinterpret_cast<ECPtr*>(io_error_context);
1553 	}
1554 
1555 	void ec_make_pthread_key()
1556 	{
1557 		(void)pthread_key_create(&s_ec_pthread_key, free_ec_head_ref);
1558 	}
1559 
1560 	ECPtr& get_thread_ec_head_ref()
1561 	{
1562 		(void)pthread_once(&s_ec_pthread_once, ec_make_pthread_key);
1563 		auto ec = reinterpret_cast<ECPtr*>(pthread_getspecific(s_ec_pthread_key));
1564 		if (ec == nullptr) {
1565 			ec = new ECPtr(nullptr);
1566 			(void)pthread_setspecific(s_ec_pthread_key, ec);
1567 		}
1568 		return *ec;
1569 	}
1570 #endif // !thread_local
1571 
1572 	// ----------------------------------------------------------------------------
1573 
1574 	EcHandle get_thread_ec_handle()
1575 	{
1576 		return get_thread_ec_head_ref();
1577 	}
1578 
1579 	Text get_error_context()
1580 	{
1581 		return get_error_context_for(get_thread_ec_head_ref());
1582 	}
1583 
1584 	Text get_error_context_for(const EcEntryBase* ec_head)
1585 	{
1586 		std::vector<const EcEntryBase*> stack;
1587 		while (ec_head) {
1588 			stack.push_back(ec_head);
1589 			ec_head = ec_head->_previous;
1590 		}
1591 		std::reverse(stack.begin(), stack.end());
1592 
1593 		StringStream result;
1594 		if (!stack.empty()) {
1595 			result.str += "------------------------------------------------\n";
1596 			for (auto entry : stack) {
1597 				const auto description = std::string(entry->_descr) + ":";
1598 #if LOGURU_USE_FMTLIB
1599 				auto prefix = textprintf("[ErrorContext] {.{}s}:{:-5u} {:-20s} ",
1600 					filename(entry->_file), LOGURU_FILENAME_WIDTH, entry->_line, description.c_str());
1601 #else
1602 				auto prefix = textprintf("[ErrorContext] %*s:%-5u %-20s ",
1603 					LOGURU_FILENAME_WIDTH, filename(entry->_file), entry->_line, description.c_str());
1604 #endif
1605 				result.str += prefix.c_str();
1606 				entry->print_value(result);
1607 				result.str += "\n";
1608 			}
1609 			result.str += "------------------------------------------------";
1610 		}
1611 		return Text(STRDUP(result.str.c_str()));
1612 	}
1613 
1614 	EcEntryBase::EcEntryBase(const char* file, unsigned line, const char* descr)
1615 		: _file(file), _line(line), _descr(descr)
1616 	{
1617 		EcEntryBase*& ec_head = get_thread_ec_head_ref();
1618 		_previous = ec_head;
1619 		ec_head = this;
1620 	}
1621 
1622 	EcEntryBase::~EcEntryBase()
1623 	{
1624 		get_thread_ec_head_ref() = _previous;
1625 	}
1626 
1627 	// ------------------------------------------------------------------------
1628 
1629 	Text ec_to_text(const char* value)
1630 	{
1631 		// Add quotes around the string to make it obvious where it begin and ends.
1632 		// This is great for detecting erroneous leading or trailing spaces in e.g. an identifier.
1633 		auto str = "\"" + std::string(value) + "\"";
1634 		return Text{STRDUP(str.c_str())};
1635 	}
1636 
1637 	Text ec_to_text(char c)
1638 	{
1639 		// Add quotes around the character to make it obvious where it begin and ends.
1640 		std::string str = "'";
1641 
1642 		auto write_hex_digit = [&](unsigned num)
1643 		{
1644 			if (num < 10u) { str += char('0' + num); }
1645 			else           { str += char('a' + num - 10); }
1646 		};
1647 
1648 		auto write_hex_16 = [&](uint16_t n)
1649 		{
1650 			write_hex_digit((n >> 12u) & 0x0f);
1651 			write_hex_digit((n >>  8u) & 0x0f);
1652 			write_hex_digit((n >>  4u) & 0x0f);
1653 			write_hex_digit((n >>  0u) & 0x0f);
1654 		};
1655 
1656 		if      (c == '\\') { str += "\\\\"; }
1657 		else if (c == '\"') { str += "\\\""; }
1658 		else if (c == '\'') { str += "\\\'"; }
1659 		else if (c == '\0') { str += "\\0";  }
1660 		else if (c == '\b') { str += "\\b";  }
1661 		else if (c == '\f') { str += "\\f";  }
1662 		else if (c == '\n') { str += "\\n";  }
1663 		else if (c == '\r') { str += "\\r";  }
1664 		else if (c == '\t') { str += "\\t";  }
1665 		else if (0 <= c && c < 0x20) {
1666 			str += "\\u";
1667 			write_hex_16(static_cast<uint16_t>(c));
1668 		} else { str += c; }
1669 
1670 		str += "'";
1671 
1672 		return Text{STRDUP(str.c_str())};
1673 	}
1674 
1675 	#define DEFINE_EC(Type)                        \
1676 		Text ec_to_text(Type value)                \
1677 		{                                          \
1678 			auto str = std::to_string(value);      \
1679 			return Text{STRDUP(str.c_str())};      \
1680 		}
1681 
1682 	DEFINE_EC(int)
1683 	DEFINE_EC(unsigned int)
1684 	DEFINE_EC(long)
1685 	DEFINE_EC(unsigned long)
1686 	DEFINE_EC(long long)
1687 	DEFINE_EC(unsigned long long)
1688 	DEFINE_EC(float)
1689 	DEFINE_EC(double)
1690 	DEFINE_EC(long double)
1691 
1692 	#undef DEFINE_EC
1693 
1694 	Text ec_to_text(EcHandle ec_handle)
1695 	{
1696 		Text parent_ec = get_error_context_for(ec_handle);
1697 		char* with_newline = reinterpret_cast<char*>(malloc(strlen(parent_ec.c_str()) + 2));
1698 		with_newline[0] = '\n';
1699 		strcpy(with_newline + 1, parent_ec.c_str());
1700 		return Text(with_newline);
1701 	}
1702 
1703 	// ----------------------------------------------------------------------------
1704 
1705 } // namespace loguru
1706 
1707 // ----------------------------------------------------------------------------
1708 // .dP"Y8 88  dP""b8 88b 88    db    88     .dP"Y8
1709 // `Ybo." 88 dP   `" 88Yb88   dPYb   88     `Ybo."
1710 // o.`Y8b 88 Yb  "88 88 Y88  dP__Yb  88  .o o.`Y8b
1711 // 8bodP' 88  YboodP 88  Y8 dP""""Yb 88ood8 8bodP'
1712 // ----------------------------------------------------------------------------
1713 
1714 #ifdef _WIN32
1715 namespace loguru {
install_signal_handlers(bool unsafe_signal_handler)1716 	void install_signal_handlers(bool unsafe_signal_handler)
1717 	{
1718 		(void)unsafe_signal_handler;
1719 		// TODO: implement signal handlers on windows
1720 	}
1721 } // namespace loguru
1722 
1723 #else // _WIN32
1724 
1725 namespace loguru
1726 {
1727 	struct Signal
1728 	{
1729 		int         number;
1730 		const char* name;
1731 	};
1732 	const Signal ALL_SIGNALS[] = {
1733 #if LOGURU_CATCH_SIGABRT
1734 		{ SIGABRT, "SIGABRT" },
1735 #endif
1736 		{ SIGBUS,  "SIGBUS"  },
1737 		{ SIGFPE,  "SIGFPE"  },
1738 		{ SIGILL,  "SIGILL"  },
1739 		{ SIGINT,  "SIGINT"  },
1740 		{ SIGSEGV, "SIGSEGV" },
1741 		{ SIGTERM, "SIGTERM" },
1742 	};
1743 
write_to_stderr(const char * data,size_t size)1744 	void write_to_stderr(const char* data, size_t size)
1745 	{
1746 		auto result = write(STDERR_FILENO, data, size);
1747 		(void)result; // Ignore errors.
1748 	}
1749 
write_to_stderr(const char * data)1750 	void write_to_stderr(const char* data)
1751 	{
1752 		write_to_stderr(data, strlen(data));
1753 	}
1754 
call_default_signal_handler(int signal_number)1755 	void call_default_signal_handler(int signal_number)
1756 	{
1757 		struct sigaction sig_action;
1758 		memset(&sig_action, 0, sizeof(sig_action));
1759 		sigemptyset(&sig_action.sa_mask);
1760 		sig_action.sa_handler = SIG_DFL;
1761 		sigaction(signal_number, &sig_action, NULL);
1762 		kill(getpid(), signal_number);
1763 	}
1764 
1765 	static bool s_unsafe_signal_handler = false;
1766 
signal_handler(int signal_number,siginfo_t *,void *)1767 	void signal_handler(int signal_number, siginfo_t*, void*)
1768 	{
1769 		const char* signal_name = "UNKNOWN SIGNAL";
1770 
1771 		for (const auto& s : ALL_SIGNALS) {
1772 			if (s.number == signal_number) {
1773 				signal_name = s.name;
1774 				break;
1775 			}
1776 		}
1777 
1778 		// --------------------------------------------------------------------
1779 		/* There are few things that are safe to do in a signal handler,
1780 		   but writing to stderr is one of them.
1781 		   So we first print out what happened to stderr so we're sure that gets out,
1782 		   then we do the unsafe things, like logging the stack trace.
1783 		*/
1784 
1785 		if (g_colorlogtostderr && s_terminal_has_color) {
1786 			write_to_stderr(terminal_reset());
1787 			write_to_stderr(terminal_bold());
1788 			write_to_stderr(terminal_light_red());
1789 		}
1790 		write_to_stderr("\n");
1791 		write_to_stderr("Loguru caught a signal: ");
1792 		write_to_stderr(signal_name);
1793 		write_to_stderr("\n");
1794 		if (g_colorlogtostderr && s_terminal_has_color) {
1795 			write_to_stderr(terminal_reset());
1796 		}
1797 
1798 		// --------------------------------------------------------------------
1799 
1800 		if (s_unsafe_signal_handler) {
1801 			// --------------------------------------------------------------------
1802 			/* Now we do unsafe things. This can for example lead to deadlocks if
1803 			   the signal was triggered from the system's memory management functions
1804 			   and the code below tries to do allocations.
1805 			*/
1806 
1807 			flush();
1808 			char preamble_buff[LOGURU_PREAMBLE_WIDTH];
1809 			print_preamble(preamble_buff, sizeof(preamble_buff), Verbosity_FATAL, "", 0);
1810 			auto message = Message{Verbosity_FATAL, "", 0, preamble_buff, "", "Signal: ", signal_name};
1811 			try {
1812 				log_message(1, message, false, false);
1813 			} catch (...) {
1814 				// This can happed due to s_fatal_handler.
1815 				write_to_stderr("Exception caught and ignored by Loguru signal handler.\n");
1816 			}
1817 			flush();
1818 
1819 			// --------------------------------------------------------------------
1820 		}
1821 
1822 		call_default_signal_handler(signal_number);
1823 	}
1824 
install_signal_handlers(bool unsafe_signal_handler)1825 	void install_signal_handlers(bool unsafe_signal_handler)
1826 	{
1827 		s_unsafe_signal_handler = unsafe_signal_handler;
1828 
1829 		struct sigaction sig_action;
1830 		memset(&sig_action, 0, sizeof(sig_action));
1831 		sigemptyset(&sig_action.sa_mask);
1832 		sig_action.sa_flags |= SA_SIGINFO;
1833 		sig_action.sa_sigaction = &signal_handler;
1834 		for (const auto& s : ALL_SIGNALS) {
1835 			CHECK_F(sigaction(s.number, &sig_action, NULL) != -1,
1836 				"Failed to install handler for " LOGURU_FMT(s) "", s.name);
1837 		}
1838 	}
1839 } // namespace loguru
1840 
1841 #endif // _WIN32
1842 
1843 #ifdef _WIN32
1844 #ifdef _MSC_VER
1845 #pragma warning(pop)
1846 #endif // _MSC_VER
1847 #endif // _WIN32
1848 
1849 #endif // LOGURU_IMPLEMENTATION
1850