1 /* 2 Loguru logging library for C++, by Emil Ernerfeldt. 3 www.github.com/emilk/loguru 4 If you find Loguru useful, please let me know on twitter or in a mail! 5 Twitter: @ernerfeldt 6 Mail: emil.ernerfeldt@gmail.com 7 Website: www.ilikebigbits.com 8 9 # License 10 This software is in the public domain. Where that dedication is not 11 recognized, you are granted a perpetual, irrevocable license to copy 12 and modify this file as you see fit. 13 14 # Inspiration 15 Much of Loguru was inspired by GLOG, https://code.google.com/p/google-glog/. 16 The whole "single header" and public domain is fully due Sean T. Barrett 17 and his wonderful stb libraries at https://github.com/nothings/stb. 18 19 # Version history 20 * Version 0.1.0 - 2015-03-22 - Works great on Mac. 21 * Version 0.2.0 - 2015-09-17 - Removed the only dependency. 22 * Version 0.3.0 - 2015-10-02 - Drop-in replacement for most of GLOG 23 * Version 0.4.0 - 2015-10-07 - Single-file! 24 * Version 0.5.0 - 2015-10-17 - Improved file logging 25 * Version 0.6.0 - 2015-10-24 - Add stack traces 26 * Version 0.7.0 - 2015-10-27 - Signals 27 * Version 0.8.0 - 2015-10-30 - Color logging. 28 * Version 0.9.0 - 2015-11-26 - ABORT_S and proper handling of FATAL 29 * Version 1.0.0 - 2016-02-14 - ERROR_CONTEXT 30 * Version 1.1.0 - 2016-02-19 - -v OFF, -v INFO etc 31 * Version 1.1.1 - 2016-02-20 - textprintf vs strprintf 32 * Version 1.1.2 - 2016-02-22 - Remove g_alsologtostderr 33 * Version 1.1.3 - 2016-02-29 - ERROR_CONTEXT as linked list 34 * Version 1.2.0 - 2016-03-19 - Add get_thread_name() 35 * Version 1.2.1 - 2016-03-20 - Minor fixes 36 * Version 1.2.2 - 2016-03-29 - Fix issues with set_fatal_handler throwing an exception 37 * Version 1.2.3 - 2016-05-16 - Log current working directory in loguru::init(). 38 * Version 1.2.4 - 2016-05-18 - Custom replacement for -v in loguru::init() by bjoernpollex 39 * Version 1.2.5 - 2016-05-18 - Add ability to print ERROR_CONTEXT of parent thread. 40 * Version 1.2.6 - 2016-05-19 - Bug fix regarding VLOG verbosity argument lacking (). 41 * Version 1.2.7 - 2016-05-23 - Fix PATH_MAX problem. 42 * Version 1.2.8 - 2016-05-26 - Add shutdown() and remove_all_callbacks() 43 * Version 1.2.9 - 2016-06-09 - Use a monotonic clock for uptime. 44 * Version 1.3.0 - 2016-07-20 - Fix issues with callback flush/close not being called. 45 * Version 1.3.1 - 2016-07-20 - Add LOGURU_UNSAFE_SIGNAL_HANDLER to toggle stacktrace on signals. 46 * Version 1.3.2 - 2016-07-20 - Add loguru::arguments() 47 * Version 1.4.0 - 2016-09-15 - Semantic versioning + add loguru::create_directories 48 * Version 1.4.1 - 2016-09-29 - Customize formating with LOGURU_FILENAME_WIDTH 49 * Version 1.5.0 - 2016-12-22 - LOGURU_USE_FMTLIB by kolis and LOGURU_WITH_FILEABS by scinart 50 * Version 1.5.1 - 2017-08-08 - Terminal colors on Windows 10 thanks to looki 51 * Version 1.6.0 - 2018-01-03 - Add LOGURU_RTTI and LOGURU_STACKTRACES settings 52 * Version 1.7.0 - 2018-01-03 - Add ability to turn off the preamble with loguru::g_preamble 53 * Version 1.7.1 - 2018-04-05 - Add function get_fatal_handler 54 55 # Compiling 56 Just include <loguru.hpp> where you want to use Loguru. 57 Then, in one .cpp file: 58 #define LOGURU_IMPLEMENTATION 1 59 #include <loguru.hpp> 60 Make sure you compile with -std=c++11 -lstdc++ -lpthread -ldl 61 62 # Usage 63 #include <loguru.hpp> 64 65 // Optional, but useful to time-stamp the start of the log. 66 // Will also detect verbosity level on command line as -v. 67 loguru::init(argc, argv); 68 69 // Put every log message in "everything.log": 70 loguru::add_file("everything.log", loguru::Append, loguru::Verbosity_MAX); 71 72 // Only log INFO, WARNING, ERROR and FATAL to "latest_readable.log": 73 loguru::add_file("latest_readable.log", loguru::Truncate, loguru::Verbosity_INFO); 74 75 // Only show most relevant things on stderr: 76 loguru::g_stderr_verbosity = 1; 77 78 // Or just go with what Loguru suggests: 79 char log_path[PATH_MAX]; 80 loguru::suggest_log_path("~/loguru/", log_path, sizeof(log_path)); 81 loguru::add_file(log_path, loguru::FileMode::Truncate, loguru::Verbosity_MAX); 82 83 LOG_SCOPE_F(INFO, "Will indent all log messages within this scope."); 84 LOG_F(INFO, "I'm hungry for some %.3f!", 3.14159); 85 LOG_F(2, "Will only show if verbosity is 2 or higher"); 86 VLOG_F(get_log_level(), "Use vlog for dynamic log level (integer in the range 0-9, inclusive)"); 87 LOG_IF_F(ERROR, badness, "Will only show if badness happens"); 88 auto fp = fopen(filename, "r"); 89 CHECK_F(fp != nullptr, "Failed to open file '%s'", filename); 90 CHECK_GT_F(length, 0); // Will print the value of `length` on failure. 91 CHECK_EQ_F(a, b, "You can also supply a custom message, like to print something: %d", a + b); 92 93 // Each function also comes with a version prefixed with D for Debug: 94 DCHECK_F(expensive_check(x)); // Only checked #if LOGURU_DEBUG_CHECKS 95 DLOG_F("Only written in debug-builds"); 96 97 // Turn off writing to stderr: 98 loguru::g_stderr_verbosity = loguru::Verbosity_OFF; 99 100 // Turn off writing err/warn in red: 101 loguru::g_colorlogtostderr = false; 102 103 // Throw exceptions instead of aborting on CHECK fails: 104 loguru::set_fatal_handler([](const loguru::Message& message){ 105 throw std::runtime_error(std::string(message.prefix) + message.message); 106 }); 107 108 If you prefer logging with streams: 109 110 #define LOGURU_WITH_STREAMS 1 111 #include <loguru.hpp> 112 ... 113 LOG_S(INFO) << "Look at my custom object: " << a.cross(b); 114 CHECK_EQ_S(pi, 3.14) << "Maybe it is closer to " << M_PI; 115 116 Before including <loguru.hpp> you may optionally want to define the following to 1: 117 118 LOGURU_DEBUG_LOGGING (default 1 #if !NDEBUG, else 0): 119 Enables debug versions of logging statements. 120 121 LOGURU_DEBUG_CHECKS (default 1 #if !NDEBUG, else 0): 122 Enables debug versions of checks. 123 124 LOGURU_REDEFINE_ASSERT (default 0): 125 Redefine "assert" call Loguru version (!NDEBUG only). 126 127 LOGURU_WITH_STREAMS (default 0): 128 Add support for _S versions for all LOG and CHECK functions: 129 LOG_S(INFO) << "My vec3: " << x.cross(y); 130 CHECK_EQ_S(a, b) << "I expected a and b to be the same!"; 131 This is off by default to keep down compilation times. 132 133 LOGURU_REPLACE_GLOG (default 0): 134 Make Loguru mimic GLOG as close as possible, 135 including #defining LOG, CHECK, VLOG_IS_ON etc. 136 LOGURU_REPLACE_GLOG implies LOGURU_WITH_STREAMS. 137 138 LOGURU_UNSAFE_SIGNAL_HANDLER (default 1): 139 Make Loguru try to do unsafe but useful things, 140 like printing a stack trace, when catching signals. 141 This may lead to bad things like deadlocks in certain situations. 142 143 LOGURU_USE_FMTLIB (default 0): 144 Use fmtlib formatting. See https://github.com/fmtlib/fmt 145 This will make loguru.hpp depend on <fmt/format.h> 146 You will need to link against `fmtlib` or use the `FMT_HEADER_ONLY` preprocessor definition. 147 Feature by kolis (https://github.com/emilk/loguru/pull/22) 148 149 LOGURU_WITH_FILEABS (default 0): 150 When LOGURU_WITH_FILEABS is defined, a check of file change will be performed on every call to file_log. 151 If the file is moved, or inode changes, file is reopened using the same FileMode as is done by add_file. 152 Such a scheme is useful if you have a daemon program that moves the log file every 24 hours and expects new file to be created. 153 Feature by scinart (https://github.com/emilk/loguru/pull/23). 154 155 LOGURU_STACKTRACES (default 1 on supported platforms): 156 Print stack traces on abort. 157 158 LOGURU_RTTI (try to detect automatically by default): 159 Set to 0 if your platform does not support runtime type information (-fno-rtti). 160 161 You can also configure: 162 loguru::g_flush_interval_ms: 163 If set to zero Loguru will flush on every line (unbuffered mode). 164 Else Loguru will flush outputs every g_flush_interval_ms milliseconds (buffered mode). 165 The default is g_flush_interval_ms=0, i.e. unbuffered mode. 166 167 # Notes: 168 * Any arguments to CHECK:s are only evaluated once. 169 * Any arguments to LOG functions or LOG_SCOPE are only evaluated iff the verbosity test passes. 170 * Any arguments to LOG_IF functions are only evaluated if the test passes. 171 */ 172 173 // Disable all warnings from gcc/clang: 174 #if defined(__clang__) 175 #pragma clang system_header 176 #elif defined(__GNUC__) 177 #pragma GCC system_header 178 #endif 179 180 #ifndef LOGURU_HAS_DECLARED_FORMAT_HEADER 181 #define LOGURU_HAS_DECLARED_FORMAT_HEADER 182 183 #if defined(_MSC_VER) 184 #include <sal.h> // Needed for _In_z_ etc annotations 185 #endif 186 187 // ---------------------------------------------------------------------------- 188 189 #ifndef LOGURU_SCOPE_TEXT_SIZE 190 // Maximum length of text that can be printed by a LOG_SCOPE. 191 // This should be long enough to get most things, but short enough not to clutter the stack. 192 #define LOGURU_SCOPE_TEXT_SIZE 196 193 #endif 194 195 #ifndef LOGURU_FILENAME_WIDTH 196 // Width of the column containing the file name 197 #define LOGURU_FILENAME_WIDTH 23 198 #endif 199 200 #ifndef LOGURU_THREADNAME_WIDTH 201 // Width of the column containing the thread name 202 #define LOGURU_THREADNAME_WIDTH 16 203 #endif 204 205 #ifndef LOGURU_CATCH_SIGABRT 206 // Should Loguru catch SIGABRT to print stack trace etc? 207 #define LOGURU_CATCH_SIGABRT 1 208 #endif 209 210 #ifndef LOGURU_REDEFINE_ASSERT 211 #define LOGURU_REDEFINE_ASSERT 0 212 #endif 213 214 #ifndef LOGURU_WITH_STREAMS 215 #define LOGURU_WITH_STREAMS 0 216 #endif 217 218 #ifndef LOGURU_REPLACE_GLOG 219 #define LOGURU_REPLACE_GLOG 0 220 #endif 221 222 #if LOGURU_REPLACE_GLOG 223 #undef LOGURU_WITH_STREAMS 224 #define LOGURU_WITH_STREAMS 1 225 #endif 226 227 #ifndef LOGURU_UNSAFE_SIGNAL_HANDLER 228 #define LOGURU_UNSAFE_SIGNAL_HANDLER 1 229 #endif 230 231 #if LOGURU_IMPLEMENTATION 232 #undef LOGURU_WITH_STREAMS 233 #define LOGURU_WITH_STREAMS 1 234 #endif 235 236 #ifndef LOGURU_USE_FMTLIB 237 #define LOGURU_USE_FMTLIB 0 238 #endif 239 240 #ifndef LOGURU_WITH_FILEABS 241 #define LOGURU_WITH_FILEABS 0 242 #endif 243 244 #ifndef LOGURU_RTTI 245 #if defined(__clang__) 246 #if __has_feature(cxx_rtti) 247 #define LOGURU_RTTI 1 248 #endif 249 #elif defined(__GNUG__) 250 #if defined(__GXX_RTTI) 251 #define LOGURU_RTTI 1 252 #endif 253 #elif defined(_MSC_VER) 254 #if defined(_CPPRTTI) 255 #define LOGURU_RTTI 1 256 #endif 257 #endif 258 #endif 259 260 // -------------------------------------------------------------------- 261 // Utility macros 262 263 #define LOGURU_CONCATENATE_IMPL(s1, s2) s1 ## s2 264 #define LOGURU_CONCATENATE(s1, s2) LOGURU_CONCATENATE_IMPL(s1, s2) 265 266 #ifdef __COUNTER__ 267 # define LOGURU_ANONYMOUS_VARIABLE(str) LOGURU_CONCATENATE(str, __COUNTER__) 268 #else 269 # define LOGURU_ANONYMOUS_VARIABLE(str) LOGURU_CONCATENATE(str, __LINE__) 270 #endif 271 272 #if defined(__clang__) || defined(__GNUC__) 273 // Helper macro for declaring functions as having similar signature to printf. 274 // This allows the compiler to catch format errors at compile-time. 275 #define LOGURU_PRINTF_LIKE(fmtarg, firstvararg) __attribute__((__format__ (__printf__, fmtarg, firstvararg))) 276 #define LOGURU_FORMAT_STRING_TYPE const char* 277 #elif defined(_MSC_VER) 278 #define LOGURU_PRINTF_LIKE(fmtarg, firstvararg) 279 #define LOGURU_FORMAT_STRING_TYPE _In_z_ _Printf_format_string_ const char* 280 #else 281 #define LOGURU_PRINTF_LIKE(fmtarg, firstvararg) 282 #define LOGURU_FORMAT_STRING_TYPE const char* 283 #endif 284 285 // Used to mark log_and_abort for the benefit of the static analyzer and optimizer. 286 #if defined(_MSC_VER) 287 #define LOGURU_NORETURN __declspec(noreturn) 288 #else 289 #define LOGURU_NORETURN __attribute__((noreturn)) 290 #endif 291 292 #if defined(_MSC_VER) 293 #define LOGURU_PREDICT_FALSE(x) (x) 294 #define LOGURU_PREDICT_TRUE(x) (x) 295 #else 296 #define LOGURU_PREDICT_FALSE(x) (__builtin_expect(x, 0)) 297 #define LOGURU_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) 298 #endif 299 300 #if LOGURU_USE_FMTLIB 301 #include <fmt/format.h> 302 #endif 303 304 // -------------------------------------------------------------------- 305 306 namespace loguru 307 { 308 // Simple RAII ownership of a char*. 309 class Text 310 { 311 public: Text(char * owned_str)312 explicit Text(char* owned_str) : _str(owned_str) {} 313 ~Text(); Text(Text && t)314 Text(Text&& t) 315 { 316 _str = t._str; 317 t._str = nullptr; 318 } 319 Text(Text& t) = delete; 320 Text& operator=(Text& t) = delete; 321 void operator=(Text&& t) = delete; 322 c_str() const323 const char* c_str() const { return _str; } empty() const324 bool empty() const { return _str == nullptr || *_str == '\0'; } 325 release()326 char* release() 327 { 328 auto result = _str; 329 _str = nullptr; 330 return result; 331 } 332 333 private: 334 char* _str; 335 }; 336 337 // Like printf, but returns the formated text. 338 Text textprintf(LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(1, 2); 339 340 // Overloaded for variadic template matching. 341 Text textprintf(); 342 343 using Verbosity = int; 344 345 #undef FATAL 346 #undef ERROR 347 #undef WARNING 348 #undef INFO 349 #undef MAX 350 351 enum NamedVerbosity : Verbosity 352 { 353 // You may use Verbosity_OFF on g_stderr_verbosity, but for nothing else! 354 Verbosity_OFF = -9, // Never do LOG_F(OFF) 355 356 // Prefer to use ABORT_F or ABORT_S over LOG_F(FATAL) or LOG_S(FATAL). 357 Verbosity_FATAL = -3, 358 Verbosity_ERROR = -2, 359 Verbosity_WARNING = -1, 360 361 // Normal messages. By default written to stderr. 362 Verbosity_INFO = 0, 363 364 // Same as Verbosity_INFO in every way. 365 Verbosity_0 = 0, 366 367 // Verbosity levels 1-9 are generally not written to stderr, but are written to file. 368 Verbosity_1 = +1, 369 Verbosity_2 = +2, 370 Verbosity_3 = +3, 371 Verbosity_4 = +4, 372 Verbosity_5 = +5, 373 Verbosity_6 = +6, 374 Verbosity_7 = +7, 375 Verbosity_8 = +8, 376 Verbosity_9 = +9, 377 378 // Don not use higher verbosity levels, as that will make grepping log files harder. 379 Verbosity_MAX = +9, 380 }; 381 382 struct Message 383 { 384 // You would generally print a Message by just concating the buffers without spacing. 385 // Optionally, ignore preamble and indentation. 386 Verbosity verbosity; // Already part of preamble 387 const char* filename; // Already part of preamble 388 unsigned line; // Already part of preamble 389 const char* preamble; // Date, time, uptime, thread, file:line, verbosity. 390 const char* indentation; // Just a bunch of spacing. 391 const char* prefix; // Assertion failure info goes here (or ""). 392 const char* message; // User message goes here. 393 }; 394 395 /* Everything with a verbosity equal or greater than g_stderr_verbosity will be 396 written to stderr. You can set this in code or via the -v argument. 397 Set to logurur::Verbosity_OFF to write nothing to stderr. 398 Default is 0, i.e. only log ERROR, WARNING and INFO are written to stderr. 399 */ 400 extern Verbosity g_stderr_verbosity; 401 extern bool g_colorlogtostderr; // True by default. 402 extern unsigned g_flush_interval_ms; // 0 (unbuffered) by default. 403 extern bool g_preamble; // Prefix each log line with date, time etc? True by default. 404 405 // Turn off individual parts of the preamble 406 extern bool g_preamble_date; // The date field 407 extern bool g_preamble_time; // The time of the current day 408 extern bool g_preamble_uptime; // The time since init call 409 extern bool g_preamble_thread; // The logging thread 410 extern bool g_preamble_file; // The file from which the log originates from 411 extern bool g_preamble_verbose; // The verbosity field 412 extern bool g_preamble_pipe; // The pipe symbol right before the message 413 414 // May not throw! 415 typedef void (*log_handler_t)(void* user_data, const Message& message); 416 typedef void (*close_handler_t)(void* user_data); 417 typedef void (*flush_handler_t)(void* user_data); 418 419 // May throw if that's how you'd like to handle your errors. 420 typedef void (*fatal_handler_t)(const Message& message); 421 422 /* Should be called from the main thread. 423 You don't *need* to call this, but if you do you get: 424 * Signal handlers installed 425 * Program arguments logged 426 * Working dir logged 427 * Optional -v verbosity flag parsed 428 * Main thread name set to "main thread" 429 * Explanation of the preamble (date, threanmae etc) logged 430 431 loguru::init() will look for arguments meant for loguru and remove them. 432 Arguments meant for loguru are: 433 -v n Set loguru::g_stderr_verbosity level. Examples: 434 -v 3 Show verbosity level 3 and lower. 435 -v 0 Only show INFO, WARNING, ERROR, FATAL (default). 436 -v INFO Only show INFO, WARNING, ERROR, FATAL (default). 437 -v WARNING Only show WARNING, ERROR, FATAL. 438 -v ERROR Only show ERROR, FATAL. 439 -v FATAL Only show FATAL. 440 -v OFF Turn off logging to stderr. 441 442 Tip: You can set g_stderr_verbosity before calling loguru::init. 443 That way you can set the default but have the user override it with the -v flag. 444 Note that -v does not affect file logging (see loguru::add_file). 445 446 You can use something else instead of "-v" via verbosity_flag. 447 You can also set verbosity_flag to nullptr. 448 */ 449 void init(int& argc, char* argv[], const char* verbosity_flag = "-v"); 450 451 // Will call remove_all_callbacks(). After calling this, logging will still go to stderr. 452 // You generally don't need to call this. 453 void shutdown(); 454 455 // What ~ will be replaced with, e.g. "/home/your_user_name/" 456 const char* home_dir(); 457 458 /* Returns the name of the app as given in argv[0] but without leading path. 459 That is, if argv[0] is "../foo/app" this will return "app". 460 */ 461 const char* argv0_filename(); 462 463 // Returns all arguments given to loguru::init(), but escaped with a single space as separator. 464 const char* arguments(); 465 466 // Returns the path to the current working dir when loguru::init() was called. 467 const char* current_dir(); 468 469 // Returns the part of the path after the last / or \ (if any). 470 const char* filename(const char* path); 471 472 // e.g. "foo/bar/baz.ext" will create the directories "foo/" and "foo/bar/" 473 bool create_directories(const char* file_path_const); 474 475 // Writes date and time with millisecond precision, e.g. "20151017_161503.123" 476 void write_date_time(char* buff, unsigned buff_size); 477 478 // Helper: thread-safe version strerror 479 Text errno_as_text(); 480 481 /* Given a prefix of e.g. "~/loguru/" this might return 482 "/home/your_username/loguru/app_name/20151017_161503.123.log" 483 484 where "app_name" is a sanitized version of argv[0]. 485 */ 486 void suggest_log_path(const char* prefix, char* buff, unsigned buff_size); 487 488 enum FileMode { Truncate, Append }; 489 490 /* Will log to a file at the given path. 491 Any logging message with a verbosity lower or equal to 492 the given verbosity will be included. 493 The function will create all directories in 'path' if needed. 494 If path starts with a ~, it will be replaced with loguru::home_dir() 495 To stop the file logging, just call loguru::remove_callback(path) with the same path. 496 */ 497 bool add_file(const char* path, FileMode mode, Verbosity verbosity); 498 499 /* Will be called right before abort(). 500 You can for instance use this to print custom error messages, or throw an exception. 501 Feel free to call LOG:ing function from this, but not FATAL ones! */ 502 void set_fatal_handler(fatal_handler_t handler); 503 504 // Get the current fatal handler, if any. Default value is nullptr. 505 fatal_handler_t get_fatal_handler(); 506 507 /* Will be called on each log messages with a verbosity less or equal to the given one. 508 Useful for displaying messages on-screen in a game, for example. 509 The given on_close is also expected to flush (if desired). 510 */ 511 void add_callback( 512 const char* id, 513 log_handler_t callback, 514 void* user_data, 515 Verbosity verbosity, 516 close_handler_t on_close = nullptr, 517 flush_handler_t on_flush = nullptr); 518 519 // Returns true iff the callback was found (and removed). 520 bool remove_callback(const char* id); 521 522 // Shut down all file logging and any other callback hooks installed. 523 void remove_all_callbacks(); 524 525 // Returns the maximum of g_stderr_verbosity and all file/custom outputs. 526 Verbosity current_verbosity_cutoff(); 527 528 #if LOGURU_USE_FMTLIB 529 // Actual logging function. Use the LOG macro instead of calling this directly. 530 void log(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, fmt::ArgList args); 531 FMT_VARIADIC(void, log, Verbosity, const char*, unsigned, LOGURU_FORMAT_STRING_TYPE) 532 533 // Log without any preamble or indentation. 534 void raw_log(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, fmt::ArgList args); 535 FMT_VARIADIC(void, raw_log, Verbosity, const char*, unsigned, LOGURU_FORMAT_STRING_TYPE) 536 #else // LOGURU_USE_FMTLIB? 537 // Actual logging function. Use the LOG macro instead of calling this directly. 538 void log(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(4, 5); 539 540 // Log without any preamble or indentation. 541 void raw_log(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(4, 5); 542 #endif // !LOGURU_USE_FMTLIB 543 544 // Helper class for LOG_SCOPE_F 545 class LogScopeRAII 546 { 547 public: LogScopeRAII()548 LogScopeRAII() : _file(nullptr) {} // No logging 549 LogScopeRAII(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(5, 6); 550 ~LogScopeRAII(); 551 552 LogScopeRAII(LogScopeRAII&& other) = default; 553 554 private: 555 LogScopeRAII(const LogScopeRAII&) = delete; 556 LogScopeRAII& operator=(const LogScopeRAII&) = delete; 557 void operator=(LogScopeRAII&&) = delete; 558 559 Verbosity _verbosity; 560 const char* _file; // Set to null if we are disabled due to verbosity 561 unsigned _line; 562 bool _indent_stderr; // Did we? 563 long long _start_time_ns; 564 char _name[LOGURU_SCOPE_TEXT_SIZE]; 565 }; 566 567 // Marked as 'noreturn' for the benefit of the static analyzer and optimizer. 568 // stack_trace_skip is the number of extrace stack frames to skip above log_and_abort. 569 LOGURU_NORETURN void log_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(5, 6); 570 LOGURU_NORETURN void log_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line); 571 572 // Flush output to stderr and files. 573 // If g_flush_interval_ms is set to non-zero, this will be called automatically this often. 574 // If not set, you do not need to call this at all. 575 void flush(); 576 format_value(const T &)577 template<class T> inline Text format_value(const T&) { return textprintf("N/A"); } format_value(const char & v)578 template<> inline Text format_value(const char& v) { return textprintf("%c", v); } format_value(const int & v)579 template<> inline Text format_value(const int& v) { return textprintf("%d", v); } format_value(const unsigned int & v)580 template<> inline Text format_value(const unsigned int& v) { return textprintf("%u", v); } format_value(const long & v)581 template<> inline Text format_value(const long& v) { return textprintf("%lu", v); } format_value(const unsigned long & v)582 template<> inline Text format_value(const unsigned long& v) { return textprintf("%ld", v); } format_value(const long long & v)583 template<> inline Text format_value(const long long& v) { return textprintf("%llu", v); } format_value(const unsigned long long & v)584 template<> inline Text format_value(const unsigned long long& v) { return textprintf("%lld", v); } format_value(const float & v)585 template<> inline Text format_value(const float& v) { return textprintf("%f", v); } format_value(const double & v)586 template<> inline Text format_value(const double& v) { return textprintf("%f", v); } 587 588 /* Thread names can be set for the benefit of readable logs. 589 If you do not set the thread name, a hex id will be shown instead. 590 These thread names may or may not be the same as the system thread names, 591 depending on the system. 592 Try to limit the thread name to 15 characters or less. */ 593 void set_thread_name(const char* name); 594 595 /* Returns the thread name for this thread. 596 On OSX this will return the system thread name (settable from both within and without Loguru). 597 On other systems it will return whatever you set in set_thread_name(); 598 If no thread name is set, this will return a hexadecimal thread id. 599 length should be the number of bytes available in the buffer. 600 17 is a good number for length. 601 right_align_hext_id means any hexadecimal thread id will be written to the end of buffer. 602 */ 603 void get_thread_name(char* buffer, unsigned long long length, bool right_align_hext_id); 604 605 /* Generates a readable stacktrace as a string. 606 'skip' specifies how many stack frames to skip. 607 For instance, the default skip (1) means: 608 don't include the call to loguru::stacktrace in the stack trace. */ 609 Text stacktrace(int skip = 1); 610 611 /* Add a string to be replaced with something else in the stack output. 612 613 For instance, instead of having a stack trace look like this: 614 0x41f541 some_function(std::basic_ofstream<char, std::char_traits<char> >&) 615 You can clean it up with: 616 auto verbose_type_name = loguru::demangle(typeid(std::ofstream).name()); 617 loguru::add_stack_cleanup(verbose_type_name.c_str(); "std::ofstream"); 618 So the next time you will instead see: 619 0x41f541 some_function(std::ofstream&) 620 621 `replace_with_this` must be shorter than `find_this`. 622 */ 623 void add_stack_cleanup(const char* find_this, const char* replace_with_this); 624 625 // Example: demangle(typeid(std::ofstream).name()) -> "std::basic_ofstream<char, std::char_traits<char> >" 626 Text demangle(const char* name); 627 628 // ------------------------------------------------------------------------ 629 /* 630 Not all terminals support colors, but if they do, and g_colorlogtostderr 631 is set, Loguru will write them to stderr to make errors in red, etc. 632 633 You also have the option to manually use them, via the function below. 634 635 Note, however, that if you do, the color codes could end up in your logfile! 636 637 This means if you intend to use them functions you should either: 638 * Use them on the stderr/stdout directly (bypass Loguru). 639 * Don't add file outputs to Loguru. 640 * Expect some \e[1m things in your logfile. 641 642 Usage: 643 printf("%sRed%sGreen%sBold green%sClear again\n", 644 loguru::terminal_red(), loguru::terminal_green(), 645 loguru::terminal_bold(), loguru::terminal_reset()); 646 647 If the terminal at hand does not support colors the above output 648 will just not have funky \e[1m things showing. 649 */ 650 651 // Do the output terminal support colors? 652 bool terminal_has_color(); 653 654 // Colors 655 const char* terminal_black(); 656 const char* terminal_red(); 657 const char* terminal_green(); 658 const char* terminal_yellow(); 659 const char* terminal_blue(); 660 const char* terminal_purple(); 661 const char* terminal_cyan(); 662 const char* terminal_light_gray(); 663 const char* terminal_light_red(); 664 const char* terminal_white(); 665 666 // Formating 667 const char* terminal_bold(); 668 const char* terminal_underline(); 669 670 // You should end each line with this! 671 const char* terminal_reset(); 672 673 // -------------------------------------------------------------------- 674 // Error context related: 675 676 struct StringStream; 677 678 // Use this in your EcEntryBase::print_value overload. 679 void stream_print(StringStream& out_string_stream, const char* text); 680 681 class EcEntryBase 682 { 683 public: 684 EcEntryBase(const char* file, unsigned line, const char* descr); 685 ~EcEntryBase(); 686 EcEntryBase(const EcEntryBase&) = delete; 687 EcEntryBase(EcEntryBase&&) = delete; 688 EcEntryBase& operator=(const EcEntryBase&) = delete; 689 EcEntryBase& operator=(EcEntryBase&&) = delete; 690 691 virtual void print_value(StringStream& out_string_stream) const = 0; 692 previous() const693 EcEntryBase* previous() const { return _previous; } 694 695 // private: 696 const char* _file; 697 unsigned _line; 698 const char* _descr; 699 EcEntryBase* _previous; 700 }; 701 702 template<typename T> 703 class EcEntryData : public EcEntryBase 704 { 705 public: 706 using Printer = Text(*)(T data); 707 EcEntryData(const char * file,unsigned line,const char * descr,T data,Printer && printer)708 EcEntryData(const char* file, unsigned line, const char* descr, T data, Printer&& printer) 709 : EcEntryBase(file, line, descr), _data(data), _printer(printer) {} 710 print_value(StringStream & out_string_stream) const711 virtual void print_value(StringStream& out_string_stream) const override 712 { 713 const auto str = _printer(_data); 714 stream_print(out_string_stream, str.c_str()); 715 } 716 717 private: 718 T _data; 719 Printer _printer; 720 }; 721 722 // template<typename Printer> 723 // class EcEntryLambda : public EcEntryBase 724 // { 725 // public: 726 // EcEntryLambda(const char* file, unsigned line, const char* descr, Printer&& printer) 727 // : EcEntryBase(file, line, descr), _printer(std::move(printer)) {} 728 729 // virtual void print_value(StringStream& out_string_stream) const override 730 // { 731 // const auto str = _printer(); 732 // stream_print(out_string_stream, str.c_str()); 733 // } 734 735 // private: 736 // Printer _printer; 737 // }; 738 739 // template<typename Printer> 740 // EcEntryLambda<Printer> make_ec_entry_lambda(const char* file, unsigned line, const char* descr, Printer&& printer) 741 // { 742 // return {file, line, descr, std::move(printer)}; 743 // } 744 745 template <class T> 746 struct decay_char_array { using type = T; }; 747 748 template <unsigned long long N> 749 struct decay_char_array<const char(&)[N]> { using type = const char*; }; 750 751 template <class T> 752 struct make_const_ptr { using type = T; }; 753 754 template <class T> 755 struct make_const_ptr<T*> { using type = const T*; }; 756 757 template <class T> 758 struct make_ec_type { using type = typename make_const_ptr<typename decay_char_array<T>::type>::type; }; 759 760 /* A stack trace gives you the names of the function at the point of a crash. 761 With ERROR_CONTEXT, you can also get the values of select local variables. 762 Usage: 763 764 void process_customers(const std::string& filename) 765 { 766 ERROR_CONTEXT("Processing file", filename.c_str()); 767 for (int customer_index : ...) 768 { 769 ERROR_CONTEXT("Customer index", customer_index); 770 ... 771 } 772 } 773 774 The context is in effect during the scope of the ERROR_CONTEXT. 775 Use loguru::get_error_context() to get the contents of the active error contexts. 776 777 Example result: 778 779 ------------------------------------------------ 780 [ErrorContext] main.cpp:416 Processing file: "customers.json" 781 [ErrorContext] main.cpp:417 Customer index: 42 782 ------------------------------------------------ 783 784 Error contexts are printed automatically on crashes, and only on crashes. 785 This makes them much faster than logging the value of a variable. 786 */ 787 #define ERROR_CONTEXT(descr, data) \ 788 const loguru::EcEntryData<loguru::make_ec_type<decltype(data)>::type> \ 789 LOGURU_ANONYMOUS_VARIABLE(error_context_scope_)( \ 790 __FILE__, __LINE__, descr, data, \ 791 static_cast<loguru::EcEntryData<loguru::make_ec_type<decltype(data)>::type>::Printer>(loguru::ec_to_text) ) // For better error messages 792 793 /* 794 #define ERROR_CONTEXT(descr, data) \ 795 const auto LOGURU_ANONYMOUS_VARIABLE(error_context_scope_)( \ 796 loguru::make_ec_entry_lambda(__FILE__, __LINE__, descr, \ 797 [=](){ return loguru::ec_to_text(data); })) 798 */ 799 800 using EcHandle = const EcEntryBase*; 801 802 /* 803 Get a light-weight handle to the error context stack on this thread. 804 The handle is valid as long as the current thread has no changes to its error context stack. 805 You can pass the handle to loguru::get_error_context on another thread. 806 This can be very useful for when you have a parent thread spawning several working threads, 807 and you want the error context of the parent thread to get printed (too) when there is an 808 error on the child thread. You can accomplish this thusly: 809 810 void foo(const char* parameter) 811 { 812 ERROR_CONTEXT("parameter", parameter) 813 const auto parent_ec_handle = loguru::get_thread_ec_handle(); 814 815 std::thread([=]{ 816 loguru::set_thread_name("child thread"); 817 ERROR_CONTEXT("parent context", parent_ec_handle); 818 dangerous_code(); 819 }.join(); 820 } 821 822 */ 823 EcHandle get_thread_ec_handle(); 824 825 // Get a string describing the current stack of error context. Empty string if there is none. 826 Text get_error_context(); 827 828 // Get a string describing the error context of the given thread handle. 829 Text get_error_context_for(EcHandle ec_handle); 830 831 // ------------------------------------------------------------------------ 832 833 Text ec_to_text(const char* data); 834 Text ec_to_text(char data); 835 Text ec_to_text(int data); 836 Text ec_to_text(unsigned int data); 837 Text ec_to_text(long data); 838 Text ec_to_text(unsigned long data); 839 Text ec_to_text(long long data); 840 Text ec_to_text(unsigned long long data); 841 Text ec_to_text(float data); 842 Text ec_to_text(double data); 843 Text ec_to_text(long double data); 844 Text ec_to_text(EcHandle); 845 846 /* 847 You can add ERROR_CONTEXT support for your own types by overloading ec_to_text. Here's how: 848 849 some.hpp: 850 namespace loguru { 851 Text ec_to_text(MySmallType data) 852 Text ec_to_text(const MyBigType* data) 853 } // namespace loguru 854 855 some.cpp: 856 namespace loguru { 857 Text ec_to_text(MySmallType small_value) 858 { 859 // Called only when needed, i.e. on a crash. 860 std::string str = small_value.as_string(); // Format 'small_value' here somehow. 861 return Text{strdup(str.c_str())}; 862 } 863 864 Text ec_to_text(const MyBigType* big_value) 865 { 866 // Called only when needed, i.e. on a crash. 867 std::string str = big_value->as_string(); // Format 'big_value' here somehow. 868 return Text{strdup(str.c_str())}; 869 } 870 } // namespace loguru 871 872 Any file that include some.hpp: 873 void foo(MySmallType small, const MyBigType& big) 874 { 875 ERROR_CONTEXT("Small", small); // Copy ´small` by value. 876 ERROR_CONTEXT("Big", &big); // `big` should not change during this scope! 877 .... 878 } 879 */ 880 } // namespace loguru 881 882 // -------------------------------------------------------------------- 883 // Logging macros 884 885 // LOG_F(2, "Only logged if verbosity is 2 or higher: %d", some_number); 886 #define VLOG_F(verbosity, ...) \ 887 ((verbosity) > loguru::current_verbosity_cutoff()) ? (void)0 \ 888 : loguru::log(verbosity, __FILE__, __LINE__, __VA_ARGS__) 889 890 // LOG_F(INFO, "Foo: %d", some_number); 891 #define LOG_F(verbosity_name, ...) VLOG_F(loguru::Verbosity_ ## verbosity_name, __VA_ARGS__) 892 893 #define VLOG_IF_F(verbosity, cond, ...) \ 894 ((verbosity) > loguru::current_verbosity_cutoff() || (cond) == false) \ 895 ? (void)0 \ 896 : loguru::log(verbosity, __FILE__, __LINE__, __VA_ARGS__) 897 898 #define LOG_IF_F(verbosity_name, cond, ...) \ 899 VLOG_IF_F(loguru::Verbosity_ ## verbosity_name, cond, __VA_ARGS__) 900 901 #define VLOG_SCOPE_F(verbosity, ...) \ 902 loguru::LogScopeRAII LOGURU_ANONYMOUS_VARIABLE(error_context_RAII_) = \ 903 ((verbosity) > loguru::current_verbosity_cutoff()) ? loguru::LogScopeRAII() : \ 904 loguru::LogScopeRAII(verbosity, __FILE__, __LINE__, __VA_ARGS__) 905 906 // Raw logging - no preamble, no indentation. Slightly faster than full logging. 907 #define RAW_VLOG_F(verbosity, ...) \ 908 ((verbosity) > loguru::current_verbosity_cutoff()) ? (void)0 \ 909 : loguru::raw_log(verbosity, __FILE__, __LINE__, __VA_ARGS__) 910 911 #define RAW_LOG_F(verbosity_name, ...) RAW_VLOG_F(loguru::Verbosity_ ## verbosity_name, __VA_ARGS__) 912 913 // Use to book-end a scope. Affects logging on all threads. 914 #define LOG_SCOPE_F(verbosity_name, ...) \ 915 VLOG_SCOPE_F(loguru::Verbosity_ ## verbosity_name, __VA_ARGS__) 916 917 #define LOG_SCOPE_FUNCTION(verbosity_name) LOG_SCOPE_F(verbosity_name, __func__) 918 919 // ----------------------------------------------- 920 // ABORT_F macro. Usage: ABORT_F("Cause of error: %s", error_str); 921 922 // Message is optional 923 #define ABORT_F(...) loguru::log_and_abort(0, "ABORT: ", __FILE__, __LINE__, __VA_ARGS__) 924 925 // -------------------------------------------------------------------- 926 // CHECK_F macros: 927 928 #define CHECK_WITH_INFO_F(test, info, ...) \ 929 LOGURU_PREDICT_TRUE((test) == true) ? (void)0 : loguru::log_and_abort(0, "CHECK FAILED: " info " ", __FILE__, \ 930 __LINE__, ##__VA_ARGS__) 931 932 /* Checked at runtime too. Will print error, then call fatal_handler (if any), then 'abort'. 933 Note that the test must be boolean. 934 CHECK_F(ptr); will not compile, but CHECK_F(ptr != nullptr); will. */ 935 #define CHECK_F(test, ...) CHECK_WITH_INFO_F(test, #test, ##__VA_ARGS__) 936 937 #define CHECK_NOTNULL_F(x, ...) CHECK_WITH_INFO_F((x) != nullptr, #x " != nullptr", ##__VA_ARGS__) 938 939 #define CHECK_OP_F(expr_left, expr_right, op, ...) \ 940 do \ 941 { \ 942 auto val_left = expr_left; \ 943 auto val_right = expr_right; \ 944 if (! LOGURU_PREDICT_TRUE(val_left op val_right)) \ 945 { \ 946 auto str_left = loguru::format_value(val_left); \ 947 auto str_right = loguru::format_value(val_right); \ 948 auto fail_info = loguru::textprintf("CHECK FAILED: %s %s %s (%s %s %s) ", \ 949 #expr_left, #op, #expr_right, str_left.c_str(), #op, str_right.c_str()); \ 950 auto user_msg = loguru::textprintf(__VA_ARGS__); \ 951 loguru::log_and_abort(0, fail_info.c_str(), __FILE__, __LINE__, \ 952 "%s", user_msg.c_str()); \ 953 } \ 954 } while (false) 955 956 #ifndef LOGURU_DEBUG_LOGGING 957 #ifndef NDEBUG 958 #define LOGURU_DEBUG_LOGGING 1 959 #else 960 #define LOGURU_DEBUG_LOGGING 0 961 #endif 962 #endif 963 964 #if LOGURU_DEBUG_LOGGING 965 // Debug logging enabled: 966 #define DLOG_F(verbosity_name, ...) LOG_F(verbosity_name, __VA_ARGS__) 967 #define DVLOG_F(verbosity, ...) VLOG_F(verbosity, __VA_ARGS__) 968 #define DLOG_IF_F(verbosity_name, ...) LOG_IF_F(verbosity_name, __VA_ARGS__) 969 #define DVLOG_IF_F(verbosity, ...) VLOG_IF_F(verbosity, __VA_ARGS__) 970 #define DRAW_LOG_F(verbosity_name, ...) RAW_LOG_F(verbosity_name, __VA_ARGS__) 971 #define DRAW_VLOG_F(verbosity, ...) RAW_VLOG_F(verbosity, __VA_ARGS__) 972 #else 973 // Debug logging disabled: 974 #define DLOG_F(verbosity_name, ...) 975 #define DVLOG_F(verbosity, ...) 976 #define DLOG_IF_F(verbosity_name, ...) 977 #define DVLOG_IF_F(verbosity, ...) 978 #define DRAW_LOG_F(verbosity_name, ...) 979 #define DRAW_VLOG_F(verbosity, ...) 980 #endif 981 982 #define CHECK_EQ_F(a, b, ...) CHECK_OP_F(a, b, ==, ##__VA_ARGS__) 983 #define CHECK_NE_F(a, b, ...) CHECK_OP_F(a, b, !=, ##__VA_ARGS__) 984 #define CHECK_LT_F(a, b, ...) CHECK_OP_F(a, b, < , ##__VA_ARGS__) 985 #define CHECK_GT_F(a, b, ...) CHECK_OP_F(a, b, > , ##__VA_ARGS__) 986 #define CHECK_LE_F(a, b, ...) CHECK_OP_F(a, b, <=, ##__VA_ARGS__) 987 #define CHECK_GE_F(a, b, ...) CHECK_OP_F(a, b, >=, ##__VA_ARGS__) 988 989 #ifndef LOGURU_DEBUG_CHECKS 990 #ifndef NDEBUG 991 #define LOGURU_DEBUG_CHECKS 1 992 #else 993 #define LOGURU_DEBUG_CHECKS 0 994 #endif 995 #endif 996 997 #if LOGURU_DEBUG_CHECKS 998 // Debug checks enabled: 999 #define DCHECK_F(test, ...) CHECK_F(test, ##__VA_ARGS__) 1000 #define DCHECK_NOTNULL_F(x, ...) CHECK_NOTNULL_F(x, ##__VA_ARGS__) 1001 #define DCHECK_EQ_F(a, b, ...) CHECK_EQ_F(a, b, ##__VA_ARGS__) 1002 #define DCHECK_NE_F(a, b, ...) CHECK_NE_F(a, b, ##__VA_ARGS__) 1003 #define DCHECK_LT_F(a, b, ...) CHECK_LT_F(a, b, ##__VA_ARGS__) 1004 #define DCHECK_LE_F(a, b, ...) CHECK_LE_F(a, b, ##__VA_ARGS__) 1005 #define DCHECK_GT_F(a, b, ...) CHECK_GT_F(a, b, ##__VA_ARGS__) 1006 #define DCHECK_GE_F(a, b, ...) CHECK_GE_F(a, b, ##__VA_ARGS__) 1007 #else 1008 // Debug checks disabled: 1009 #define DCHECK_F(test, ...) 1010 #define DCHECK_NOTNULL_F(x, ...) 1011 #define DCHECK_EQ_F(a, b, ...) 1012 #define DCHECK_NE_F(a, b, ...) 1013 #define DCHECK_LT_F(a, b, ...) 1014 #define DCHECK_LE_F(a, b, ...) 1015 #define DCHECK_GT_F(a, b, ...) 1016 #define DCHECK_GE_F(a, b, ...) 1017 #endif // NDEBUG 1018 1019 1020 #ifdef LOGURU_REDEFINE_ASSERT 1021 #undef assert 1022 #ifndef NDEBUG 1023 // Debug: 1024 #define assert(test) CHECK_WITH_INFO_F(!!(test), #test) // HACK 1025 #else 1026 #define assert(test) 1027 #endif 1028 #endif // LOGURU_REDEFINE_ASSERT 1029 1030 #endif // LOGURU_HAS_DECLARED_FORMAT_HEADER 1031 1032 // ---------------------------------------------------------------------------- 1033 // .dP"Y8 888888 88""Yb 888888 db 8b d8 .dP"Y8 1034 // `Ybo." 88 88__dP 88__ dPYb 88b d88 `Ybo." 1035 // o.`Y8b 88 88"Yb 88"" dP__Yb 88YbdP88 o.`Y8b 1036 // 8bodP' 88 88 Yb 888888 dP""""Yb 88 YY 88 8bodP' 1037 1038 #if LOGURU_WITH_STREAMS 1039 #ifndef LOGURU_HAS_DECLARED_STREAMS_HEADER 1040 #define LOGURU_HAS_DECLARED_STREAMS_HEADER 1041 1042 /* This file extends loguru to enable std::stream-style logging, a la Glog. 1043 It's an optional feature behind the LOGURU_WITH_STREAMS settings 1044 because including it everywhere will slow down compilation times. 1045 */ 1046 1047 #include <cstdarg> 1048 #include <sstream> // Adds about 38 kLoC on clang. 1049 #include <string> 1050 1051 namespace loguru 1052 { 1053 // Like sprintf, but returns the formated text. 1054 std::string strprintf(LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(1, 2); 1055 1056 // Like vsprintf, but returns the formated text. 1057 std::string vstrprintf(LOGURU_FORMAT_STRING_TYPE format, va_list) LOGURU_PRINTF_LIKE(1, 0); 1058 1059 class StreamLogger 1060 { 1061 public: StreamLogger(Verbosity verbosity,const char * file,unsigned line)1062 StreamLogger(Verbosity verbosity, const char* file, unsigned line) : _verbosity(verbosity), _file(file), _line(line) {} 1063 ~StreamLogger() noexcept(false); 1064 1065 template<typename T> operator <<(const T & t)1066 StreamLogger& operator<<(const T& t) 1067 { 1068 _ss << t; 1069 return *this; 1070 } 1071 1072 // std::endl and other iomanip:s. operator <<(std::ostream & (* f)(std::ostream &))1073 StreamLogger& operator<<(std::ostream&(*f)(std::ostream&)) 1074 { 1075 f(_ss); 1076 return *this; 1077 } 1078 1079 private: 1080 Verbosity _verbosity; 1081 const char* _file; 1082 unsigned _line; 1083 std::ostringstream _ss; 1084 }; 1085 1086 class AbortLogger 1087 { 1088 public: AbortLogger(const char * expr,const char * file,unsigned line)1089 AbortLogger(const char* expr, const char* file, unsigned line) : _expr(expr), _file(file), _line(line) { } 1090 LOGURU_NORETURN ~AbortLogger() noexcept(false); 1091 1092 template<typename T> operator <<(const T & t)1093 AbortLogger& operator<<(const T& t) 1094 { 1095 _ss << t; 1096 return *this; 1097 } 1098 1099 // std::endl and other iomanip:s. operator <<(std::ostream & (* f)(std::ostream &))1100 AbortLogger& operator<<(std::ostream&(*f)(std::ostream&)) 1101 { 1102 f(_ss); 1103 return *this; 1104 } 1105 1106 private: 1107 const char* _expr; 1108 const char* _file; 1109 unsigned _line; 1110 std::ostringstream _ss; 1111 }; 1112 1113 class Voidify 1114 { 1115 public: Voidify()1116 Voidify() {} 1117 // This has to be an operator with a precedence lower than << but higher than ?: operator &(const StreamLogger &)1118 void operator&(const StreamLogger&) { } operator &(const AbortLogger &)1119 void operator&(const AbortLogger&) { } 1120 }; 1121 1122 /* Helper functions for CHECK_OP_S macro. 1123 GLOG trick: The (int, int) specialization works around the issue that the compiler 1124 will not instantiate the template version of the function on values of unnamed enum type. */ 1125 #define DEFINE_CHECK_OP_IMPL(name, op) \ 1126 template <typename T1, typename T2> \ 1127 inline std::string* name(const char* expr, const T1& v1, const char* op_str, const T2& v2) \ 1128 { \ 1129 if (LOGURU_PREDICT_TRUE(v1 op v2)) { return NULL; } \ 1130 std::ostringstream ss; \ 1131 ss << "CHECK FAILED: " << expr << " (" << v1 << " " << op_str << " " << v2 << ") "; \ 1132 return new std::string(ss.str()); \ 1133 } \ 1134 inline std::string* name(const char* expr, int v1, const char* op_str, int v2) \ 1135 { \ 1136 return name<int, int>(expr, v1, op_str, v2); \ 1137 } 1138 1139 DEFINE_CHECK_OP_IMPL(check_EQ_impl, ==) 1140 DEFINE_CHECK_OP_IMPL(check_NE_impl, !=) 1141 DEFINE_CHECK_OP_IMPL(check_LE_impl, <=) 1142 DEFINE_CHECK_OP_IMPL(check_LT_impl, < ) 1143 DEFINE_CHECK_OP_IMPL(check_GE_impl, >=) 1144 DEFINE_CHECK_OP_IMPL(check_GT_impl, > ) 1145 #undef DEFINE_CHECK_OP_IMPL 1146 1147 /* GLOG trick: Function is overloaded for integral types to allow static const integrals 1148 declared in classes and not defined to be used as arguments to CHECK* macros. */ 1149 template <class T> referenceable_value(const T & t)1150 inline const T& referenceable_value(const T& t) { return t; } referenceable_value(char t)1151 inline char referenceable_value(char t) { return t; } referenceable_value(unsigned char t)1152 inline unsigned char referenceable_value(unsigned char t) { return t; } referenceable_value(signed char t)1153 inline signed char referenceable_value(signed char t) { return t; } referenceable_value(short t)1154 inline short referenceable_value(short t) { return t; } referenceable_value(unsigned short t)1155 inline unsigned short referenceable_value(unsigned short t) { return t; } referenceable_value(int t)1156 inline int referenceable_value(int t) { return t; } referenceable_value(unsigned int t)1157 inline unsigned int referenceable_value(unsigned int t) { return t; } referenceable_value(long t)1158 inline long referenceable_value(long t) { return t; } referenceable_value(unsigned long t)1159 inline unsigned long referenceable_value(unsigned long t) { return t; } referenceable_value(long long t)1160 inline long long referenceable_value(long long t) { return t; } referenceable_value(unsigned long long t)1161 inline unsigned long long referenceable_value(unsigned long long t) { return t; } 1162 } // namespace loguru 1163 1164 // ----------------------------------------------- 1165 // Logging macros: 1166 1167 // usage: LOG_STREAM(INFO) << "Foo " << std::setprecision(10) << some_value; 1168 #define VLOG_IF_S(verbosity, cond) \ 1169 ((verbosity) > loguru::current_verbosity_cutoff() || (cond) == false) \ 1170 ? (void)0 \ 1171 : loguru::Voidify() & loguru::StreamLogger(verbosity, __FILE__, __LINE__) 1172 #define LOG_IF_S(verbosity_name, cond) VLOG_IF_S(loguru::Verbosity_ ## verbosity_name, cond) 1173 #define VLOG_S(verbosity) VLOG_IF_S(verbosity, true) 1174 #define LOG_S(verbosity_name) VLOG_S(loguru::Verbosity_ ## verbosity_name) 1175 1176 // ----------------------------------------------- 1177 // ABORT_S macro. Usage: ABORT_S() << "Causo of error: " << details; 1178 1179 #define ABORT_S() loguru::Voidify() & loguru::AbortLogger("ABORT: ", __FILE__, __LINE__) 1180 1181 // ----------------------------------------------- 1182 // CHECK_S macros: 1183 1184 #define CHECK_WITH_INFO_S(cond, info) \ 1185 LOGURU_PREDICT_TRUE((cond) == true) \ 1186 ? (void)0 \ 1187 : loguru::Voidify() & loguru::AbortLogger("CHECK FAILED: " info " ", __FILE__, __LINE__) 1188 1189 #define CHECK_S(cond) CHECK_WITH_INFO_S(cond, #cond) 1190 #define CHECK_NOTNULL_S(x) CHECK_WITH_INFO_S((x) != nullptr, #x " != nullptr") 1191 1192 #define CHECK_OP_S(function_name, expr1, op, expr2) \ 1193 while (auto error_string = loguru::function_name(#expr1 " " #op " " #expr2, \ 1194 loguru::referenceable_value(expr1), #op, \ 1195 loguru::referenceable_value(expr2))) \ 1196 loguru::AbortLogger(error_string->c_str(), __FILE__, __LINE__) 1197 1198 #define CHECK_EQ_S(expr1, expr2) CHECK_OP_S(check_EQ_impl, expr1, ==, expr2) 1199 #define CHECK_NE_S(expr1, expr2) CHECK_OP_S(check_NE_impl, expr1, !=, expr2) 1200 #define CHECK_LE_S(expr1, expr2) CHECK_OP_S(check_LE_impl, expr1, <=, expr2) 1201 #define CHECK_LT_S(expr1, expr2) CHECK_OP_S(check_LT_impl, expr1, < , expr2) 1202 #define CHECK_GE_S(expr1, expr2) CHECK_OP_S(check_GE_impl, expr1, >=, expr2) 1203 #define CHECK_GT_S(expr1, expr2) CHECK_OP_S(check_GT_impl, expr1, > , expr2) 1204 1205 #if LOGURU_DEBUG_LOGGING 1206 // Debug logging enabled: 1207 #define DVLOG_IF_S(verbosity, cond) VLOG_IF_S(verbosity, cond) 1208 #define DLOG_IF_S(verbosity_name, cond) LOG_IF_S(verbosity_name, cond) 1209 #define DVLOG_S(verbosity) VLOG_S(verbosity) 1210 #define DLOG_S(verbosity_name) LOG_S(verbosity_name) 1211 #else 1212 // Debug logging disabled: 1213 #define DVLOG_IF_S(verbosity, cond) \ 1214 (true || (verbosity) > loguru::current_verbosity_cutoff() || (cond) == false) \ 1215 ? (void)0 \ 1216 : loguru::Voidify() & loguru::StreamLogger(verbosity, __FILE__, __LINE__) 1217 1218 #define DLOG_IF_S(verbosity_name, cond) DVLOG_IF_S(loguru::Verbosity_ ## verbosity_name, cond) 1219 #define DVLOG_S(verbosity) DVLOG_IF_S(verbosity, true) 1220 #define DLOG_S(verbosity_name) DVLOG_S(loguru::Verbosity_ ## verbosity_name) 1221 #endif 1222 1223 #if LOGURU_DEBUG_CHECKS 1224 // Debug checks enabled: 1225 #define DCHECK_S(cond) CHECK_S(cond) 1226 #define DCHECK_NOTNULL_S(x) CHECK_NOTNULL_S(x) 1227 #define DCHECK_EQ_S(a, b) CHECK_EQ_S(a, b) 1228 #define DCHECK_NE_S(a, b) CHECK_NE_S(a, b) 1229 #define DCHECK_LT_S(a, b) CHECK_LT_S(a, b) 1230 #define DCHECK_LE_S(a, b) CHECK_LE_S(a, b) 1231 #define DCHECK_GT_S(a, b) CHECK_GT_S(a, b) 1232 #define DCHECK_GE_S(a, b) CHECK_GE_S(a, b) 1233 #else 1234 // Debug checks disabled: 1235 #define DCHECK_S(cond) CHECK_S(true || (cond)) 1236 #define DCHECK_NOTNULL_S(x) CHECK_S(true || (x) != nullptr) 1237 #define DCHECK_EQ_S(a, b) CHECK_S(true || (a) == (b)) 1238 #define DCHECK_NE_S(a, b) CHECK_S(true || (a) != (b)) 1239 #define DCHECK_LT_S(a, b) CHECK_S(true || (a) < (b)) 1240 #define DCHECK_LE_S(a, b) CHECK_S(true || (a) <= (b)) 1241 #define DCHECK_GT_S(a, b) CHECK_S(true || (a) > (b)) 1242 #define DCHECK_GE_S(a, b) CHECK_S(true || (a) >= (b)) 1243 #endif 1244 1245 #if LOGURU_REPLACE_GLOG 1246 #undef LOG 1247 #undef VLOG 1248 #undef LOG_IF 1249 #undef VLOG_IF 1250 #undef CHECK 1251 #undef CHECK_NOTNULL 1252 #undef CHECK_EQ 1253 #undef CHECK_NE 1254 #undef CHECK_LT 1255 #undef CHECK_LE 1256 #undef CHECK_GT 1257 #undef CHECK_GE 1258 #undef DLOG 1259 #undef DVLOG 1260 #undef DLOG_IF 1261 #undef DVLOG_IF 1262 #undef DCHECK 1263 #undef DCHECK_NOTNULL 1264 #undef DCHECK_EQ 1265 #undef DCHECK_NE 1266 #undef DCHECK_LT 1267 #undef DCHECK_LE 1268 #undef DCHECK_GT 1269 #undef DCHECK_GE 1270 #undef VLOG_IS_ON 1271 1272 #define LOG LOG_S 1273 #define VLOG VLOG_S 1274 #define LOG_IF LOG_IF_S 1275 #define VLOG_IF VLOG_IF_S 1276 #define CHECK(cond) CHECK_S(!!(cond)) 1277 #define CHECK_NOTNULL CHECK_NOTNULL_S 1278 #define CHECK_EQ CHECK_EQ_S 1279 #define CHECK_NE CHECK_NE_S 1280 #define CHECK_LT CHECK_LT_S 1281 #define CHECK_LE CHECK_LE_S 1282 #define CHECK_GT CHECK_GT_S 1283 #define CHECK_GE CHECK_GE_S 1284 #define DLOG DLOG_S 1285 #define DVLOG DVLOG_S 1286 #define DLOG_IF DLOG_IF_S 1287 #define DVLOG_IF DVLOG_IF_S 1288 #define DCHECK DCHECK_S 1289 #define DCHECK_NOTNULL DCHECK_NOTNULL_S 1290 #define DCHECK_EQ DCHECK_EQ_S 1291 #define DCHECK_NE DCHECK_NE_S 1292 #define DCHECK_LT DCHECK_LT_S 1293 #define DCHECK_LE DCHECK_LE_S 1294 #define DCHECK_GT DCHECK_GT_S 1295 #define DCHECK_GE DCHECK_GE_S 1296 #define VLOG_IS_ON(verbosity) ((verbosity) <= loguru::current_verbosity_cutoff()) 1297 1298 #endif // LOGURU_REPLACE_GLOG 1299 1300 #endif // LOGURU_WITH_STREAMS 1301 1302 #endif // LOGURU_HAS_DECLARED_STREAMS_HEADER 1303 1304 // ---------------------------------------------------------------------------- 1305 // 88 8b d8 88""Yb 88 888888 8b d8 888888 88b 88 888888 db 888888 88 dP"Yb 88b 88 1306 // 88 88b d88 88__dP 88 88__ 88b d88 88__ 88Yb88 88 dPYb 88 88 dP Yb 88Yb88 1307 // 88 88YbdP88 88""" 88 .o 88"" 88YbdP88 88"" 88 Y88 88 dP__Yb 88 88 Yb dP 88 Y88 1308 // 88 88 YY 88 88 88ood8 888888 88 YY 88 888888 88 Y8 88 dP""""Yb 88 88 YbodP 88 Y8 1309 1310 /* In one of your .cpp files you need to do the following: 1311 #define LOGURU_IMPLEMENTATION 1 1312 #include <loguru.hpp> 1313 1314 This will define all the Loguru functions so that the linker may find them. 1315 */ 1316 1317 #if defined(LOGURU_IMPLEMENTATION) && !defined(LOGURU_HAS_BEEN_IMPLEMENTED) 1318 #define LOGURU_HAS_BEEN_IMPLEMENTED 1319 1320 #define LOGURU_PREAMBLE_WIDTH (53 + LOGURU_THREADNAME_WIDTH + LOGURU_FILENAME_WIDTH) 1321 1322 #include <algorithm> 1323 #include <atomic> 1324 #include <chrono> 1325 #include <cstdarg> 1326 #include <cstdio> 1327 #include <cstdlib> 1328 #include <cstring> 1329 #include <mutex> 1330 #include <regex> 1331 #include <string> 1332 #include <thread> 1333 #include <vector> 1334 1335 #ifdef _WIN32 1336 #include <direct.h> 1337 1338 #define localtime_r(a, b) localtime_s(b, a) // No localtime_r with MSVC, but arguments are swapped for localtime_s 1339 #else 1340 #include <signal.h> 1341 #include <sys/stat.h> // mkdir 1342 #include <unistd.h> // STDERR_FILENO 1343 #endif 1344 1345 #ifdef __linux__ 1346 #include <linux/limits.h> // PATH_MAX 1347 #elif !defined(_WIN32) 1348 #include <limits.h> // PATH_MAX 1349 #endif 1350 1351 #ifndef PATH_MAX 1352 #define PATH_MAX 1024 1353 #endif 1354 1355 #ifdef __APPLE__ 1356 #include "TargetConditionals.h" 1357 #endif 1358 1359 // TODO: use defined(_POSIX_VERSION) for some of these things? 1360 1361 #if defined(_WIN32) || defined(__CYGWIN__) 1362 #define LOGURU_PTHREADS 0 1363 #define LOGURU_WINTHREADS 1 1364 #ifndef LOGURU_STACKTRACES 1365 #define LOGURU_STACKTRACES 0 1366 #endif 1367 #elif defined(__rtems__) 1368 #define LOGURU_PTHREADS 1 1369 #define LOGURU_WINTHREADS 0 1370 #ifndef LOGURU_STACKTRACES 1371 #define LOGURU_STACKTRACES 0 1372 #endif 1373 #else 1374 #define LOGURU_PTHREADS 1 1375 #define LOGURU_WINTHREADS 0 1376 #ifndef LOGURU_STACKTRACES 1377 #define LOGURU_STACKTRACES 1 1378 #endif 1379 #endif 1380 1381 #if LOGURU_STACKTRACES 1382 #include <cxxabi.h> // for __cxa_demangle 1383 #include <dlfcn.h> // for dladdr 1384 #include <execinfo.h> // for backtrace 1385 #endif // LOGURU_STACKTRACES 1386 1387 #if LOGURU_PTHREADS 1388 #include <pthread.h> 1389 #if defined(__FreeBSD__) 1390 #include <pthread_np.h> 1391 #ifndef __DragonFly__ 1392 #include <sys/thr.h> 1393 #endif 1394 #elif defined(__OpenBSD__) 1395 #include <pthread_np.h> 1396 #endif 1397 1398 #ifdef __linux__ 1399 /* On Linux, the default thread name is the same as the name of the binary. 1400 Additionally, all new threads inherit the name of the thread it got forked from. 1401 For this reason, Loguru use the pthread Thread Local Storage 1402 for storing thread names on Linux. */ 1403 #define LOGURU_PTLS_NAMES 1 1404 #endif 1405 #endif 1406 1407 #if LOGURU_WINTHREADS 1408 #ifndef _WIN32_WINNT 1409 #define _WIN32_WINNT 0x0502 1410 #endif 1411 #define WIN32_LEAN_AND_MEAN 1412 #define NOMINMAX 1413 #include <windows.h> 1414 #endif 1415 1416 #ifndef LOGURU_PTLS_NAMES 1417 #define LOGURU_PTLS_NAMES 0 1418 #endif 1419 1420 namespace loguru 1421 { 1422 using namespace std::chrono; 1423 1424 #if LOGURU_WITH_FILEABS 1425 struct FileAbs 1426 { 1427 char path[PATH_MAX]; 1428 char mode_str[4]; 1429 Verbosity verbosity; 1430 struct stat st; 1431 FILE* fp; 1432 bool is_reopening = false; // to prevent recursive call in file_reopen. 1433 decltype(steady_clock::now()) last_check_time = steady_clock::now(); 1434 }; 1435 #else 1436 typedef FILE* FileAbs; 1437 #endif 1438 1439 struct Callback 1440 { 1441 std::string id; 1442 log_handler_t callback; 1443 void* user_data; 1444 Verbosity verbosity; // Does not change! 1445 close_handler_t close; 1446 flush_handler_t flush; 1447 unsigned indentation; 1448 }; 1449 1450 using CallbackVec = std::vector<Callback>; 1451 1452 using StringPair = std::pair<std::string, std::string>; 1453 using StringPairList = std::vector<StringPair>; 1454 1455 const auto SCOPE_TIME_PRECISION = 3; // 3=ms, 6≈us, 9=ns 1456 1457 const auto s_start_time = steady_clock::now(); 1458 1459 Verbosity g_stderr_verbosity = Verbosity_0; 1460 bool g_colorlogtostderr = true; 1461 unsigned g_flush_interval_ms = 0; 1462 bool g_preamble = true; 1463 1464 // Preamble details 1465 bool g_preamble_date = true; 1466 bool g_preamble_time = true; 1467 bool g_preamble_uptime = true; 1468 bool g_preamble_thread = true; 1469 bool g_preamble_file = true; 1470 bool g_preamble_verbose = true; 1471 bool g_preamble_pipe = true; 1472 1473 static std::recursive_mutex s_mutex; 1474 static Verbosity s_max_out_verbosity = Verbosity_OFF; 1475 static std::string s_argv0_filename; 1476 static std::string s_arguments; 1477 static char s_current_dir[PATH_MAX]; 1478 static CallbackVec s_callbacks; 1479 static fatal_handler_t s_fatal_handler = nullptr; 1480 static StringPairList s_user_stack_cleanups; 1481 static bool s_strip_file_path = true; 1482 static std::atomic<unsigned> s_stderr_indentation { 0 }; 1483 1484 // For periodic flushing: 1485 static std::thread* s_flush_thread = nullptr; 1486 static bool s_needs_flushing = false; 1487 __anon1ad9de9b0102()1488 static const bool s_terminal_has_color = [](){ 1489 #ifdef _WIN32 1490 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING 1491 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 1492 #endif 1493 1494 HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); 1495 if (hOut != INVALID_HANDLE_VALUE) { 1496 DWORD dwMode = 0; 1497 GetConsoleMode(hOut, &dwMode); 1498 dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; 1499 return SetConsoleMode(hOut, dwMode) != 0; 1500 } 1501 return false; 1502 #else 1503 if (const char* term = getenv("TERM")) { 1504 return 0 == strcmp(term, "cygwin") 1505 || 0 == strcmp(term, "linux") 1506 || 0 == strcmp(term, "rxvt-unicode-256color") 1507 || 0 == strcmp(term, "screen") 1508 || 0 == strcmp(term, "screen-256color") 1509 || 0 == strcmp(term, "tmux-256color") 1510 || 0 == strcmp(term, "xterm") 1511 || 0 == strcmp(term, "xterm-256color") 1512 || 0 == strcmp(term, "xterm-termite") 1513 || 0 == strcmp(term, "xterm-color"); 1514 } else { 1515 return false; 1516 } 1517 #endif 1518 }(); 1519 1520 static void print_preamble_header(char* out_buff, size_t out_buff_size); 1521 1522 #if LOGURU_PTLS_NAMES 1523 static pthread_once_t s_pthread_key_once = PTHREAD_ONCE_INIT; 1524 static pthread_key_t s_pthread_key_name; 1525 make_pthread_key_name()1526 void make_pthread_key_name() 1527 { 1528 (void)pthread_key_create(&s_pthread_key_name, free); 1529 } 1530 #endif 1531 1532 // ------------------------------------------------------------------------------ 1533 // Colors 1534 terminal_has_color()1535 bool terminal_has_color() { return s_terminal_has_color; } 1536 1537 // Colors 1538 1539 #ifdef _WIN32 1540 #define VTSEQ(ID) ("\x1b[1;" #ID "m") 1541 #else 1542 #define VTSEQ(ID) ("\e[" #ID "m") 1543 #endif 1544 terminal_black()1545 const char* terminal_black() { return s_terminal_has_color ? VTSEQ(30) : ""; } terminal_red()1546 const char* terminal_red() { return s_terminal_has_color ? VTSEQ(31) : ""; } terminal_green()1547 const char* terminal_green() { return s_terminal_has_color ? VTSEQ(32) : ""; } terminal_yellow()1548 const char* terminal_yellow() { return s_terminal_has_color ? VTSEQ(33) : ""; } terminal_blue()1549 const char* terminal_blue() { return s_terminal_has_color ? VTSEQ(34) : ""; } terminal_purple()1550 const char* terminal_purple() { return s_terminal_has_color ? VTSEQ(35) : ""; } terminal_cyan()1551 const char* terminal_cyan() { return s_terminal_has_color ? VTSEQ(36) : ""; } terminal_light_gray()1552 const char* terminal_light_gray() { return s_terminal_has_color ? VTSEQ(37) : ""; } terminal_white()1553 const char* terminal_white() { return s_terminal_has_color ? VTSEQ(37) : ""; } terminal_light_red()1554 const char* terminal_light_red() { return s_terminal_has_color ? VTSEQ(91) : ""; } terminal_dim()1555 const char* terminal_dim() { return s_terminal_has_color ? VTSEQ(2) : ""; } 1556 1557 // Formating terminal_bold()1558 const char* terminal_bold() { return s_terminal_has_color ? VTSEQ(1) : ""; } terminal_underline()1559 const char* terminal_underline() { return s_terminal_has_color ? VTSEQ(4) : ""; } 1560 1561 // You should end each line with this! terminal_reset()1562 const char* terminal_reset() { return s_terminal_has_color ? VTSEQ(0) : ""; } 1563 1564 // ------------------------------------------------------------------------------ 1565 #if LOGURU_WITH_FILEABS 1566 void file_reopen(void* user_data); to_file(void * user_data)1567 inline FILE* to_file(void* user_data) { return reinterpret_cast<FileAbs*>(user_data)->fp; } 1568 #else to_file(void * user_data)1569 inline FILE* to_file(void* user_data) { return reinterpret_cast<FILE*>(user_data); } 1570 #endif 1571 file_log(void * user_data,const Message & message)1572 void file_log(void* user_data, const Message& message) 1573 { 1574 #if LOGURU_WITH_FILEABS 1575 FileAbs* file_abs = reinterpret_cast<FileAbs*>(user_data); 1576 if (file_abs->is_reopening) { 1577 return; 1578 } 1579 // It is better checking file change every minute/hour/day, 1580 // instead of doing this every time we log. 1581 // Here check_interval is set to zero to enable checking every time; 1582 const auto check_interval = seconds(0); 1583 if (duration_cast<seconds>(steady_clock::now() - file_abs->last_check_time) > check_interval) { 1584 file_abs->last_check_time = steady_clock::now(); 1585 file_reopen(user_data); 1586 } 1587 FILE* file = to_file(user_data); 1588 if (!file) { 1589 return; 1590 } 1591 #else 1592 FILE* file = to_file(user_data); 1593 #endif 1594 fprintf(file, "%s%s%s%s\n", 1595 message.preamble, message.indentation, message.prefix, message.message); 1596 if (g_flush_interval_ms == 0) { 1597 fflush(file); 1598 } 1599 } 1600 file_close(void * user_data)1601 void file_close(void* user_data) 1602 { 1603 FILE* file = to_file(user_data); 1604 if (file) { 1605 fclose(file); 1606 } 1607 #if LOGURU_WITH_FILEABS 1608 delete reinterpret_cast<FileAbs*>(user_data); 1609 #endif 1610 } 1611 file_flush(void * user_data)1612 void file_flush(void* user_data) 1613 { 1614 FILE* file = to_file(user_data); 1615 fflush(file); 1616 } 1617 1618 #if LOGURU_WITH_FILEABS file_reopen(void * user_data)1619 void file_reopen(void* user_data) 1620 { 1621 FileAbs * file_abs = reinterpret_cast<FileAbs*>(user_data); 1622 struct stat st; 1623 int ret; 1624 if (!file_abs->fp || (ret = stat(file_abs->path, &st)) == -1 || (st.st_ino != file_abs->st.st_ino)) { 1625 file_abs->is_reopening = true; 1626 if (file_abs->fp) { 1627 fclose(file_abs->fp); 1628 } 1629 if (!file_abs->fp) { 1630 LOG_F(INFO, "Reopening file '%s' due to previous error", file_abs->path); 1631 } 1632 else if (ret < 0) { 1633 const auto why = errno_as_text(); 1634 LOG_F(INFO, "Reopening file '%s' due to '%s'", file_abs->path, why.c_str()); 1635 } else { 1636 LOG_F(INFO, "Reopening file '%s' due to file changed", file_abs->path); 1637 } 1638 // try reopen current file. 1639 if (!create_directories(file_abs->path)) { 1640 LOG_F(ERROR, "Failed to create directories to '%s'", file_abs->path); 1641 } 1642 file_abs->fp = fopen(file_abs->path, file_abs->mode_str); 1643 if (!file_abs->fp) { 1644 LOG_F(ERROR, "Failed to open '%s'", file_abs->path); 1645 } else { 1646 stat(file_abs->path, &file_abs->st); 1647 } 1648 file_abs->is_reopening = false; 1649 } 1650 } 1651 #endif 1652 // ------------------------------------------------------------------------------ 1653 1654 // Helpers: 1655 ~Text()1656 Text::~Text() { free(_str); } 1657 1658 LOGURU_PRINTF_LIKE(1, 0) vtextprintf(const char * format,va_list vlist)1659 static Text vtextprintf(const char* format, va_list vlist) 1660 { 1661 #ifdef _WIN32 1662 int bytes_needed = _vscprintf(format, vlist); 1663 CHECK_F(bytes_needed >= 0, "Bad string format: '%s'", format); 1664 char* buff = (char*)malloc(bytes_needed+1); 1665 vsnprintf(buff, bytes_needed+1, format, vlist); 1666 return Text(buff); 1667 #else 1668 char* buff = nullptr; 1669 int result = vasprintf(&buff, format, vlist); 1670 CHECK_F(result >= 0, "Bad string format: '%s'", format); 1671 return Text(buff); 1672 #endif 1673 } 1674 textprintf(const char * format,...)1675 Text textprintf(const char* format, ...) 1676 { 1677 va_list vlist; 1678 va_start(vlist, format); 1679 auto result = vtextprintf(format, vlist); 1680 va_end(vlist); 1681 return result; 1682 } 1683 1684 // Overloaded for variadic template matching. textprintf()1685 Text textprintf() 1686 { 1687 return Text(static_cast<char*>(calloc(1, 1))); 1688 } 1689 indentation(unsigned depth)1690 static const char* indentation(unsigned depth) 1691 { 1692 static const char buff[] = 1693 ". . . . . . . . . . " ". . . . . . . . . . " 1694 ". . . . . . . . . . " ". . . . . . . . . . " 1695 ". . . . . . . . . . " ". . . . . . . . . . " 1696 ". . . . . . . . . . " ". . . . . . . . . . " 1697 ". . . . . . . . . . " ". . . . . . . . . . "; 1698 static const size_t INDENTATION_WIDTH = 4; 1699 static const size_t NUM_INDENTATIONS = (sizeof(buff) - 1) / INDENTATION_WIDTH; 1700 depth = std::min<unsigned>(depth, NUM_INDENTATIONS); 1701 return buff + INDENTATION_WIDTH * (NUM_INDENTATIONS - depth); 1702 } 1703 parse_args(int & argc,char * argv[],const char * verbosity_flag)1704 static void parse_args(int& argc, char* argv[], const char* verbosity_flag) 1705 { 1706 int arg_dest = 1; 1707 int out_argc = argc; 1708 1709 for (int arg_it = 1; arg_it < argc; ++arg_it) { 1710 auto cmd = argv[arg_it]; 1711 auto arg_len = strlen(verbosity_flag); 1712 if (strncmp(cmd, verbosity_flag, arg_len) == 0 && !std::isalpha(cmd[arg_len], std::locale(""))) { 1713 out_argc -= 1; 1714 auto value_str = cmd + arg_len; 1715 if (value_str[0] == '\0') { 1716 // Value in separate argument 1717 arg_it += 1; 1718 CHECK_LT_F(arg_it, argc, "Missing verbosiy level after %s", verbosity_flag); 1719 value_str = argv[arg_it]; 1720 out_argc -= 1; 1721 } 1722 if (*value_str == '=') { value_str += 1; } 1723 1724 if (strcmp(value_str, "OFF") == 0) { 1725 g_stderr_verbosity = Verbosity_OFF; 1726 } else if (strcmp(value_str, "INFO") == 0) { 1727 g_stderr_verbosity = Verbosity_INFO; 1728 } else if (strcmp(value_str, "WARNING") == 0) { 1729 g_stderr_verbosity = Verbosity_WARNING; 1730 } else if (strcmp(value_str, "ERROR") == 0) { 1731 g_stderr_verbosity = Verbosity_ERROR; 1732 } else if (strcmp(value_str, "FATAL") == 0) { 1733 g_stderr_verbosity = Verbosity_FATAL; 1734 } else { 1735 char* end = 0; 1736 g_stderr_verbosity = static_cast<int>(strtol(value_str, &end, 10)); 1737 CHECK_F(end && *end == '\0', 1738 "Invalid verbosity. Expected integer, INFO, WARNING, ERROR or OFF, got '%s'", value_str); 1739 } 1740 } else { 1741 argv[arg_dest++] = argv[arg_it]; 1742 } 1743 } 1744 1745 argc = out_argc; 1746 argv[argc] = nullptr; 1747 } 1748 now_ns()1749 static long long now_ns() 1750 { 1751 return duration_cast<nanoseconds>(high_resolution_clock::now().time_since_epoch()).count(); 1752 } 1753 1754 // Returns the part of the path after the last / or \ (if any). filename(const char * path)1755 const char* filename(const char* path) 1756 { 1757 for (auto ptr = path; *ptr; ++ptr) { 1758 if (*ptr == '/' || *ptr == '\\') { 1759 path = ptr + 1; 1760 } 1761 } 1762 return path; 1763 } 1764 1765 // ------------------------------------------------------------------------------ 1766 on_atexit()1767 static void on_atexit() 1768 { 1769 LOG_F(INFO, "atexit"); 1770 flush(); 1771 } 1772 1773 static void install_signal_handlers(); 1774 write_hex_digit(std::string & out,unsigned num)1775 static void write_hex_digit(std::string& out, unsigned num) 1776 { 1777 DCHECK_LT_F(num, 16u); 1778 if (num < 10u) { out.push_back(char('0' + num)); } 1779 else { out.push_back(char('A' + num - 10)); } 1780 } 1781 write_hex_byte(std::string & out,uint8_t n)1782 static void write_hex_byte(std::string& out, uint8_t n) 1783 { 1784 write_hex_digit(out, n >> 4u); 1785 write_hex_digit(out, n & 0x0f); 1786 } 1787 escape(std::string & out,const std::string & str)1788 static void escape(std::string& out, const std::string& str) 1789 { 1790 for (char c : str) { 1791 /**/ if (c == '\a') { out += "\\a"; } 1792 else if (c == '\b') { out += "\\b"; } 1793 else if (c == '\f') { out += "\\f"; } 1794 else if (c == '\n') { out += "\\n"; } 1795 else if (c == '\r') { out += "\\r"; } 1796 else if (c == '\t') { out += "\\t"; } 1797 else if (c == '\v') { out += "\\v"; } 1798 else if (c == '\\') { out += "\\\\"; } 1799 else if (c == '\'') { out += "\\\'"; } 1800 else if (c == '\"') { out += "\\\""; } 1801 else if (c == ' ') { out += "\\ "; } 1802 else if (0 <= c && c < 0x20) { // ASCI control character: 1803 // else if (c < 0x20 || c != (c & 127)) { // ASCII control character or UTF-8: 1804 out += "\\x"; 1805 write_hex_byte(out, static_cast<uint8_t>(c)); 1806 } else { out += c; } 1807 } 1808 } 1809 errno_as_text()1810 Text errno_as_text() 1811 { 1812 char buff[256]; 1813 #if defined(__GLIBC__) && defined(_GNU_SOURCE) 1814 // GNU Version 1815 return Text(strdup(strerror_r(errno, buff, sizeof(buff)))); 1816 #elif defined(__APPLE__) || _POSIX_C_SOURCE >= 200112L 1817 // XSI Version 1818 strerror_r(errno, buff, sizeof(buff)); 1819 return Text(strdup(buff)); 1820 #elif defined(_WIN32) 1821 strerror_s(buff, sizeof(buff), errno); 1822 return Text(strdup(buff)); 1823 #else 1824 // Not thread-safe. 1825 return Text(strdup(strerror(errno))); 1826 #endif 1827 } 1828 init(int & argc,char * argv[],const char * verbosity_flag)1829 void init(int& argc, char* argv[], const char* verbosity_flag) 1830 { 1831 CHECK_GT_F(argc, 0, "Expected proper argc/argv"); 1832 CHECK_EQ_F(argv[argc], nullptr, "Expected proper argc/argv"); 1833 1834 s_argv0_filename = filename(argv[0]); 1835 1836 #ifdef _WIN32 1837 #define getcwd _getcwd 1838 #endif 1839 1840 if (!getcwd(s_current_dir, sizeof(s_current_dir))) 1841 { 1842 const auto error_text = errno_as_text(); 1843 LOG_F(WARNING, "Failed to get current working directory: %s", error_text.c_str()); 1844 } 1845 1846 s_arguments = ""; 1847 for (int i = 0; i < argc; ++i) { 1848 escape(s_arguments, argv[i]); 1849 if (i + 1 < argc) { 1850 s_arguments += " "; 1851 } 1852 } 1853 1854 if (verbosity_flag) { 1855 parse_args(argc, argv, verbosity_flag); 1856 } 1857 1858 #if LOGURU_PTLS_NAMES || LOGURU_WINTHREADS 1859 set_thread_name("main thread"); 1860 #elif LOGURU_PTHREADS 1861 char old_thread_name[16] = {0}; 1862 auto this_thread = pthread_self(); 1863 #if defined(__APPLE__) || defined(__linux__) 1864 pthread_getname_np(this_thread, old_thread_name, sizeof(old_thread_name)); 1865 #endif 1866 if (old_thread_name[0] == 0) { 1867 #ifdef __APPLE__ 1868 pthread_setname_np("main thread"); 1869 #elif defined(__FreeBSD__) || defined(__OpenBSD__) 1870 pthread_set_name_np(this_thread, "main thread"); 1871 #elif defined(__linux__) 1872 pthread_setname_np(this_thread, "main thread"); 1873 #endif 1874 } 1875 #endif // LOGURU_PTHREADS 1876 1877 if (g_stderr_verbosity >= Verbosity_INFO) { 1878 if (g_preamble) { 1879 char preamble_explain[LOGURU_PREAMBLE_WIDTH]; 1880 print_preamble_header(preamble_explain, sizeof(preamble_explain)); 1881 if (g_colorlogtostderr && s_terminal_has_color) { 1882 fprintf(stderr, "%s%s%s\n", terminal_reset(), terminal_dim(), preamble_explain); 1883 } else { 1884 fprintf(stderr, "%s\n", preamble_explain); 1885 } 1886 } 1887 fflush(stderr); 1888 } 1889 LOG_F(INFO, "arguments: %s", s_arguments.c_str()); 1890 if (strlen(s_current_dir) != 0) 1891 { 1892 LOG_F(INFO, "Current dir: %s", s_current_dir); 1893 } 1894 LOG_F(INFO, "stderr verbosity: %d", g_stderr_verbosity); 1895 LOG_F(INFO, "-----------------------------------"); 1896 1897 install_signal_handlers(); 1898 1899 atexit(on_atexit); 1900 } 1901 shutdown()1902 void shutdown() 1903 { 1904 LOG_F(INFO, "loguru::shutdown()"); 1905 remove_all_callbacks(); 1906 set_fatal_handler(nullptr); 1907 } 1908 write_date_time(char * buff,size_t buff_size)1909 void write_date_time(char* buff, size_t buff_size) 1910 { 1911 auto now = system_clock::now(); 1912 long long ms_since_epoch = duration_cast<milliseconds>(now.time_since_epoch()).count(); 1913 time_t sec_since_epoch = time_t(ms_since_epoch / 1000); 1914 tm time_info; 1915 localtime_r(&sec_since_epoch, &time_info); 1916 snprintf(buff, buff_size, "%04d%02d%02d_%02d%02d%02d.%03lld", 1917 1900 + time_info.tm_year, 1 + time_info.tm_mon, time_info.tm_mday, 1918 time_info.tm_hour, time_info.tm_min, time_info.tm_sec, ms_since_epoch % 1000); 1919 } 1920 argv0_filename()1921 const char* argv0_filename() 1922 { 1923 return s_argv0_filename.c_str(); 1924 } 1925 arguments()1926 const char* arguments() 1927 { 1928 return s_arguments.c_str(); 1929 } 1930 current_dir()1931 const char* current_dir() 1932 { 1933 return s_current_dir; 1934 } 1935 home_dir()1936 const char* home_dir() 1937 { 1938 #ifdef _WIN32 1939 auto user_profile = getenv("USERPROFILE"); 1940 CHECK_F(user_profile != nullptr, "Missing USERPROFILE"); 1941 return user_profile; 1942 #else // _WIN32 1943 auto home = getenv("HOME"); 1944 CHECK_F(home != nullptr, "Missing HOME"); 1945 return home; 1946 #endif // _WIN32 1947 } 1948 suggest_log_path(const char * prefix,char * buff,unsigned buff_size)1949 void suggest_log_path(const char* prefix, char* buff, unsigned buff_size) 1950 { 1951 if (prefix[0] == '~') { 1952 snprintf(buff, buff_size - 1, "%s%s", home_dir(), prefix + 1); 1953 } else { 1954 snprintf(buff, buff_size - 1, "%s", prefix); 1955 } 1956 1957 // Check for terminating / 1958 size_t n = strlen(buff); 1959 if (n != 0) { 1960 if (buff[n - 1] != '/') { 1961 CHECK_F(n + 2 < buff_size, "Filename buffer too small"); 1962 buff[n] = '/'; 1963 buff[n + 1] = '\0'; 1964 } 1965 } 1966 1967 strncat(buff, s_argv0_filename.c_str(), buff_size - strlen(buff) - 1); 1968 strncat(buff, "/", buff_size - strlen(buff) - 1); 1969 write_date_time(buff + strlen(buff), buff_size - strlen(buff)); 1970 strncat(buff, ".log", buff_size - strlen(buff) - 1); 1971 } 1972 create_directories(const char * file_path_const)1973 bool create_directories(const char* file_path_const) 1974 { 1975 CHECK_F(file_path_const && *file_path_const); 1976 char* file_path = strdup(file_path_const); 1977 for (char* p = strchr(file_path + 1, '/'); p; p = strchr(p + 1, '/')) { 1978 *p = '\0'; 1979 1980 #ifdef _WIN32 1981 if (_mkdir(file_path) == -1) { 1982 #else 1983 if (mkdir(file_path, 0755) == -1) { 1984 #endif 1985 if (errno != EEXIST) { 1986 LOG_F(ERROR, "Failed to create directory '%s'", file_path); 1987 LOG_IF_F(ERROR, errno == EACCES, "EACCES"); 1988 LOG_IF_F(ERROR, errno == ENAMETOOLONG, "ENAMETOOLONG"); 1989 LOG_IF_F(ERROR, errno == ENOENT, "ENOENT"); 1990 LOG_IF_F(ERROR, errno == ENOTDIR, "ENOTDIR"); 1991 LOG_IF_F(ERROR, errno == ELOOP, "ELOOP"); 1992 1993 *p = '/'; 1994 free(file_path); 1995 return false; 1996 } 1997 } 1998 *p = '/'; 1999 } 2000 free(file_path); 2001 return true; 2002 } 2003 bool add_file(const char* path_in, FileMode mode, Verbosity verbosity) 2004 { 2005 char path[PATH_MAX]; 2006 if (path_in[0] == '~') { 2007 snprintf(path, sizeof(path) - 1, "%s%s", home_dir(), path_in + 1); 2008 } else { 2009 snprintf(path, sizeof(path) - 1, "%s", path_in); 2010 } 2011 2012 if (!create_directories(path)) { 2013 LOG_F(ERROR, "Failed to create directories to '%s'", path); 2014 } 2015 2016 const char* mode_str = (mode == FileMode::Truncate ? "w" : "a"); 2017 auto file = fopen(path, mode_str); 2018 if (!file) { 2019 LOG_F(ERROR, "Failed to open '%s'", path); 2020 return false; 2021 } 2022 #if LOGURU_WITH_FILEABS 2023 FileAbs* file_abs = new FileAbs(); // this is deleted in file_close; 2024 snprintf(file_abs->path, sizeof(file_abs->path) - 1, "%s", path); 2025 snprintf(file_abs->mode_str, sizeof(file_abs->mode_str) - 1, "%s", mode_str); 2026 stat(file_abs->path, &file_abs->st); 2027 file_abs->fp = file; 2028 file_abs->verbosity = verbosity; 2029 add_callback(path_in, file_log, file_abs, verbosity, file_close, file_flush); 2030 #else 2031 add_callback(path_in, file_log, file, verbosity, file_close, file_flush); 2032 #endif 2033 2034 if (mode == FileMode::Append) { 2035 fprintf(file, "\n\n\n\n\n"); 2036 } 2037 if (!s_arguments.empty()) { 2038 fprintf(file, "arguments: %s\n", s_arguments.c_str()); 2039 } 2040 if (strlen(s_current_dir) != 0) { 2041 fprintf(file, "Current dir: %s\n", s_current_dir); 2042 } 2043 fprintf(file, "File verbosity level: %d\n", verbosity); 2044 if (g_preamble) { 2045 char preamble_explain[LOGURU_PREAMBLE_WIDTH]; 2046 print_preamble_header(preamble_explain, sizeof(preamble_explain)); 2047 fprintf(file, "%s\n", preamble_explain); 2048 } 2049 fflush(file); 2050 2051 LOG_F(INFO, "Logging to '%s', mode: '%s', verbosity: %d", path, mode_str, verbosity); 2052 return true; 2053 } 2054 2055 // Will be called right before abort(). 2056 void set_fatal_handler(fatal_handler_t handler) 2057 { 2058 s_fatal_handler = handler; 2059 } 2060 2061 fatal_handler_t get_fatal_handler() 2062 { 2063 return s_fatal_handler; 2064 } 2065 2066 void add_stack_cleanup(const char* find_this, const char* replace_with_this) 2067 { 2068 if (strlen(find_this) <= strlen(replace_with_this)) { 2069 LOG_F(WARNING, "add_stack_cleanup: the replacement should be shorter than the pattern!"); 2070 return; 2071 } 2072 2073 s_user_stack_cleanups.push_back(StringPair(find_this, replace_with_this)); 2074 } 2075 2076 static void on_callback_change() 2077 { 2078 s_max_out_verbosity = Verbosity_OFF; 2079 for (const auto& callback : s_callbacks) { 2080 s_max_out_verbosity = std::max(s_max_out_verbosity, callback.verbosity); 2081 } 2082 } 2083 2084 void add_callback( 2085 const char* id, 2086 log_handler_t callback, 2087 void* user_data, 2088 Verbosity verbosity, 2089 close_handler_t on_close, 2090 flush_handler_t on_flush) 2091 { 2092 std::lock_guard<std::recursive_mutex> lock(s_mutex); 2093 s_callbacks.push_back(Callback{id, callback, user_data, verbosity, on_close, on_flush, 0}); 2094 on_callback_change(); 2095 } 2096 2097 bool remove_callback(const char* id) 2098 { 2099 std::lock_guard<std::recursive_mutex> lock(s_mutex); 2100 auto it = std::find_if(begin(s_callbacks), end(s_callbacks), [&](const Callback& c) { return c.id == id; }); 2101 if (it != s_callbacks.end()) { 2102 if (it->close) { it->close(it->user_data); } 2103 s_callbacks.erase(it); 2104 on_callback_change(); 2105 return true; 2106 } else { 2107 LOG_F(ERROR, "Failed to locate callback with id '%s'", id); 2108 return false; 2109 } 2110 } 2111 2112 void remove_all_callbacks() 2113 { 2114 std::lock_guard<std::recursive_mutex> lock(s_mutex); 2115 for (auto& callback : s_callbacks) { 2116 if (callback.close) { 2117 callback.close(callback.user_data); 2118 } 2119 } 2120 s_callbacks.clear(); 2121 on_callback_change(); 2122 } 2123 2124 // Returns the maximum of g_stderr_verbosity and all file/custom outputs. 2125 Verbosity current_verbosity_cutoff() 2126 { 2127 return g_stderr_verbosity > s_max_out_verbosity ? 2128 g_stderr_verbosity : s_max_out_verbosity; 2129 } 2130 2131 #if LOGURU_WINTHREADS 2132 char* get_thread_name_win32() 2133 { 2134 __declspec( thread ) static char thread_name[LOGURU_THREADNAME_WIDTH + 1] = {0}; 2135 return &thread_name[0]; 2136 } 2137 #endif // LOGURU_WINTHREADS 2138 2139 void set_thread_name(const char* name) 2140 { 2141 #if LOGURU_PTLS_NAMES 2142 (void)pthread_once(&s_pthread_key_once, make_pthread_key_name); 2143 (void)pthread_setspecific(s_pthread_key_name, strdup(name)); 2144 2145 #elif LOGURU_PTHREADS 2146 #ifdef __APPLE__ 2147 pthread_setname_np(name); 2148 #elif defined(__FreeBSD__) || defined(__OpenBSD__) 2149 pthread_set_name_np(pthread_self(), name); 2150 #elif defined(__linux__) 2151 pthread_setname_np(pthread_self(), name); 2152 #endif 2153 #elif LOGURU_WINTHREADS 2154 strncpy_s(get_thread_name_win32(), LOGURU_THREADNAME_WIDTH + 1, name, _TRUNCATE); 2155 #else // LOGURU_PTHREADS 2156 (void)name; 2157 #endif // LOGURU_PTHREADS 2158 } 2159 2160 #if LOGURU_PTLS_NAMES 2161 const char* get_thread_name_ptls() 2162 { 2163 (void)pthread_once(&s_pthread_key_once, make_pthread_key_name); 2164 return static_cast<const char*>(pthread_getspecific(s_pthread_key_name)); 2165 } 2166 #endif // LOGURU_PTLS_NAMES 2167 2168 void get_thread_name(char* buffer, unsigned long long length, bool right_align_hext_id) 2169 { 2170 CHECK_NE_F(length, 0u, "Zero length buffer in get_thread_name"); 2171 CHECK_NOTNULL_F(buffer, "nullptr in get_thread_name"); 2172 #if LOGURU_PTHREADS 2173 auto thread = pthread_self(); 2174 #if LOGURU_PTLS_NAMES 2175 if (const char* name = get_thread_name_ptls()) { 2176 snprintf(buffer, length, "%s", name); 2177 } else { 2178 buffer[0] = 0; 2179 } 2180 #elif defined(__APPLE__) || defined(__linux__) 2181 pthread_getname_np(thread, buffer, length); 2182 #else 2183 buffer[0] = 0; 2184 #endif 2185 2186 if (buffer[0] == 0) { 2187 #ifdef __APPLE__ 2188 uint64_t thread_id; 2189 pthread_threadid_np(thread, &thread_id); 2190 #elif defined(__DragonFly__) 2191 uint64_t thread_id = thread; 2192 #elif defined(__FreeBSD__) 2193 long thread_id; 2194 (void)thr_self(&thread_id); 2195 #elif defined(__OpenBSD__) 2196 unsigned thread_id = -1; 2197 #else 2198 uint64_t thread_id = thread; 2199 #endif 2200 if (right_align_hext_id) { 2201 snprintf(buffer, length, "%*X", length - 1, static_cast<unsigned>(thread_id)); 2202 } else { 2203 snprintf(buffer, length, "%X", static_cast<unsigned>(thread_id)); 2204 } 2205 } 2206 #elif LOGURU_WINTHREADS 2207 if (const char* name = get_thread_name_win32()) { 2208 snprintf(buffer, (size_t)length, "%s", name); 2209 } else { 2210 buffer[0] = 0; 2211 } 2212 #else // !LOGURU_WINTHREADS && !LOGURU_WINTHREADS 2213 buffer[0] = 0; 2214 #endif 2215 2216 } 2217 2218 // ------------------------------------------------------------------------ 2219 // Stack traces 2220 2221 #if LOGURU_STACKTRACES 2222 Text demangle(const char* name) 2223 { 2224 int status = -1; 2225 char* demangled = abi::__cxa_demangle(name, 0, 0, &status); 2226 Text result{status == 0 ? demangled : strdup(name)}; 2227 return result; 2228 } 2229 2230 #if LOGURU_RTTI 2231 template <class T> 2232 std::string type_name() 2233 { 2234 auto demangled = demangle(typeid(T).name()); 2235 return demangled.c_str(); 2236 } 2237 #endif // LOGURU_RTTI 2238 2239 static const StringPairList REPLACE_LIST = { 2240 #if LOGURU_RTTI 2241 { type_name<std::string>(), "std::string" }, 2242 { type_name<std::wstring>(), "std::wstring" }, 2243 { type_name<std::u16string>(), "std::u16string" }, 2244 { type_name<std::u32string>(), "std::u32string" }, 2245 #endif // LOGURU_RTTI 2246 { "std::__1::", "std::" }, 2247 { "__thiscall ", "" }, 2248 { "__cdecl ", "" }, 2249 }; 2250 2251 void do_replacements(const StringPairList& replacements, std::string& str) 2252 { 2253 for (auto&& p : replacements) { 2254 if (p.first.size() <= p.second.size()) { 2255 // On gcc, "type_name<std::string>()" is "std::string" 2256 continue; 2257 } 2258 2259 size_t it; 2260 while ((it=str.find(p.first)) != std::string::npos) { 2261 str.replace(it, p.first.size(), p.second); 2262 } 2263 } 2264 } 2265 2266 std::string prettify_stacktrace(const std::string& input) 2267 { 2268 std::string output = input; 2269 2270 do_replacements(s_user_stack_cleanups, output); 2271 do_replacements(REPLACE_LIST, output); 2272 2273 try { 2274 std::regex std_allocator_re(R"(,\s*std::allocator<[^<>]+>)"); 2275 output = std::regex_replace(output, std_allocator_re, std::string("")); 2276 2277 std::regex template_spaces_re(R"(<\s*([^<> ]+)\s*>)"); 2278 output = std::regex_replace(output, template_spaces_re, std::string("<$1>")); 2279 } catch (std::regex_error&) { 2280 // Probably old GCC. 2281 } 2282 2283 return output; 2284 } 2285 2286 std::string stacktrace_as_stdstring(int skip) 2287 { 2288 // From https://gist.github.com/fmela/591333 2289 void* callstack[128]; 2290 const auto max_frames = sizeof(callstack) / sizeof(callstack[0]); 2291 int num_frames = backtrace(callstack, max_frames); 2292 char** symbols = backtrace_symbols(callstack, num_frames); 2293 2294 std::string result; 2295 // Print stack traces so the most relevant ones are written last 2296 // Rationale: http://yellerapp.com/posts/2015-01-22-upside-down-stacktraces.html 2297 for (int i = num_frames - 1; i >= skip; --i) { 2298 char buf[1024]; 2299 Dl_info info; 2300 if (dladdr(callstack[i], &info) && info.dli_sname) { 2301 char* demangled = NULL; 2302 int status = -1; 2303 if (info.dli_sname[0] == '_') { 2304 demangled = abi::__cxa_demangle(info.dli_sname, 0, 0, &status); 2305 } 2306 snprintf(buf, sizeof(buf), "%-3d %*p %s + %zd\n", 2307 i - skip, int(2 + sizeof(void*) * 2), callstack[i], 2308 status == 0 ? demangled : 2309 info.dli_sname == 0 ? symbols[i] : info.dli_sname, 2310 static_cast<char*>(callstack[i]) - static_cast<char*>(info.dli_saddr)); 2311 free(demangled); 2312 } else { 2313 snprintf(buf, sizeof(buf), "%-3d %*p %s\n", 2314 i - skip, int(2 + sizeof(void*) * 2), callstack[i], symbols[i]); 2315 } 2316 result += buf; 2317 } 2318 free(symbols); 2319 2320 if (num_frames == max_frames) { 2321 result = "[truncated]\n" + result; 2322 } 2323 2324 if (!result.empty() && result[result.size() - 1] == '\n') { 2325 result.resize(result.size() - 1); 2326 } 2327 2328 return prettify_stacktrace(result); 2329 } 2330 2331 #else // LOGURU_STACKTRACES 2332 Text demangle(const char* name) 2333 { 2334 return Text(strdup(name)); 2335 } 2336 2337 std::string stacktrace_as_stdstring(int) 2338 { 2339 // No stacktraces available on this platform" 2340 return ""; 2341 } 2342 2343 #endif // LOGURU_STACKTRACES 2344 2345 Text stacktrace(int skip) 2346 { 2347 auto str = stacktrace_as_stdstring(skip + 1); 2348 return Text(strdup(str.c_str())); 2349 } 2350 2351 // ------------------------------------------------------------------------ 2352 2353 static void print_preamble_header(char* out_buff, size_t out_buff_size) 2354 { 2355 long pos = 0; 2356 snprintf(out_buff, out_buff_size, ""); // Make sure there is a '\0' and handle out_buff_size==0 2357 if (g_preamble_date) { 2358 pos += snprintf(out_buff + pos, out_buff_size - pos, "date "); 2359 } 2360 if (g_preamble_time) { 2361 pos += snprintf(out_buff + pos, out_buff_size - pos, "time "); 2362 } 2363 if (g_preamble_uptime) { 2364 pos += snprintf(out_buff + pos, out_buff_size - pos, "( uptime ) "); 2365 } 2366 if (g_preamble_thread) { 2367 pos += snprintf(out_buff + pos, out_buff_size - pos, "[%-*s]", LOGURU_THREADNAME_WIDTH, " thread name/id"); 2368 } 2369 if (g_preamble_file) { 2370 pos += snprintf(out_buff + pos, out_buff_size - pos, "%*s:line ", LOGURU_FILENAME_WIDTH, "file"); 2371 } 2372 if (g_preamble_verbose) { 2373 pos += snprintf(out_buff + pos, out_buff_size - pos, " v"); 2374 } 2375 if (g_preamble_pipe) { 2376 pos += snprintf(out_buff + pos, out_buff_size - pos, "| "); 2377 } 2378 } 2379 2380 static void print_preamble(char* out_buff, size_t out_buff_size, Verbosity verbosity, const char* file, unsigned line) 2381 { 2382 if (!g_preamble) { 2383 out_buff[0] = '\0'; 2384 return; 2385 } 2386 long long ms_since_epoch = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); 2387 time_t sec_since_epoch = time_t(ms_since_epoch / 1000); 2388 tm time_info; 2389 localtime_r(&sec_since_epoch, &time_info); 2390 2391 auto uptime_ms = duration_cast<milliseconds>(steady_clock::now() - s_start_time).count(); 2392 auto uptime_sec = uptime_ms / 1000.0; 2393 2394 char thread_name[LOGURU_THREADNAME_WIDTH + 1] = {0}; 2395 get_thread_name(thread_name, LOGURU_THREADNAME_WIDTH + 1, true); 2396 2397 if (s_strip_file_path) { 2398 file = filename(file); 2399 } 2400 2401 char level_buff[6]; 2402 if (verbosity <= Verbosity_FATAL) { 2403 snprintf(level_buff, sizeof(level_buff) - 1, "FATL"); 2404 } else if (verbosity == Verbosity_ERROR) { 2405 snprintf(level_buff, sizeof(level_buff) - 1, "ERR"); 2406 } else if (verbosity == Verbosity_WARNING) { 2407 snprintf(level_buff, sizeof(level_buff) - 1, "WARN"); 2408 } else { 2409 snprintf(level_buff, sizeof(level_buff) - 1, "% 4d", verbosity); 2410 } 2411 2412 long pos = 0; 2413 2414 snprintf(out_buff, out_buff_size, ""); // Make sure there is a '\0' and handle out_buff_size==0 2415 if (g_preamble_date) { 2416 pos += snprintf(out_buff + pos, out_buff_size - pos, "%04d-%02d-%02d ", 2417 1900 + time_info.tm_year, 1 + time_info.tm_mon, time_info.tm_mday); 2418 } 2419 if (g_preamble_time) { 2420 pos += snprintf(out_buff + pos, out_buff_size - pos, "%02d:%02d:%02d.%03lld ", 2421 time_info.tm_hour, time_info.tm_min, time_info.tm_sec, ms_since_epoch % 1000); 2422 } 2423 if (g_preamble_uptime) { 2424 pos += snprintf(out_buff + pos, out_buff_size - pos, "(%8.3fs) ", 2425 uptime_sec); 2426 } 2427 if (g_preamble_thread) { 2428 pos += snprintf(out_buff + pos, out_buff_size - pos, "[%-*s]", 2429 LOGURU_THREADNAME_WIDTH, thread_name); 2430 } 2431 if (g_preamble_file) { 2432 pos += snprintf(out_buff + pos, out_buff_size - pos, "%*s:%-5u ", 2433 LOGURU_FILENAME_WIDTH, file, line); 2434 } 2435 if (g_preamble_verbose) { 2436 pos += snprintf(out_buff + pos, out_buff_size - pos, "%4s", 2437 level_buff); 2438 } 2439 if (g_preamble_pipe) { 2440 pos += snprintf(out_buff + pos, out_buff_size - pos, "| "); 2441 } 2442 } 2443 2444 // stack_trace_skip is just if verbosity == FATAL. 2445 static void log_message(int stack_trace_skip, Message& message, bool with_indentation, bool abort_if_fatal) 2446 { 2447 const auto verbosity = message.verbosity; 2448 std::lock_guard<std::recursive_mutex> lock(s_mutex); 2449 2450 if (message.verbosity == Verbosity_FATAL) { 2451 auto st = loguru::stacktrace(stack_trace_skip + 2); 2452 if (!st.empty()) { 2453 RAW_LOG_F(ERROR, "Stack trace:\n%s", st.c_str()); 2454 } 2455 2456 auto ec = loguru::get_error_context(); 2457 if (!ec.empty()) { 2458 RAW_LOG_F(ERROR, "%s", ec.c_str()); 2459 } 2460 } 2461 2462 if (with_indentation) { 2463 message.indentation = indentation(s_stderr_indentation); 2464 } 2465 2466 if (verbosity <= g_stderr_verbosity) { 2467 if (g_colorlogtostderr && s_terminal_has_color) { 2468 if (verbosity > Verbosity_WARNING) { 2469 fprintf(stderr, "%s%s%s%s%s%s%s%s%s\n", 2470 terminal_reset(), 2471 terminal_dim(), 2472 message.preamble, 2473 message.indentation, 2474 terminal_reset(), 2475 verbosity == Verbosity_INFO ? terminal_bold() : terminal_light_gray(), 2476 message.prefix, 2477 message.message, 2478 terminal_reset()); 2479 } else { 2480 fprintf(stderr, "%s%s%s%s%s%s%s%s\n", 2481 terminal_reset(), 2482 terminal_bold(), 2483 verbosity == Verbosity_WARNING ? terminal_red() : terminal_light_red(), 2484 message.preamble, 2485 message.indentation, 2486 message.prefix, 2487 message.message, 2488 terminal_reset()); 2489 } 2490 } else { 2491 fprintf(stderr, "%s%s%s%s\n", 2492 message.preamble, message.indentation, message.prefix, message.message); 2493 } 2494 2495 if (g_flush_interval_ms == 0) { 2496 fflush(stderr); 2497 } else { 2498 s_needs_flushing = true; 2499 } 2500 } 2501 2502 for (auto& p : s_callbacks) { 2503 if (verbosity <= p.verbosity) { 2504 if (with_indentation) { 2505 message.indentation = indentation(p.indentation); 2506 } 2507 p.callback(p.user_data, message); 2508 if (g_flush_interval_ms == 0) { 2509 if (p.flush) { p.flush(p.user_data); } 2510 } else { 2511 s_needs_flushing = true; 2512 } 2513 } 2514 } 2515 2516 if (g_flush_interval_ms > 0 && !s_flush_thread) { 2517 s_flush_thread = new std::thread([](){ 2518 for (;;) { 2519 if (s_needs_flushing) { 2520 flush(); 2521 } 2522 std::this_thread::sleep_for(std::chrono::milliseconds(g_flush_interval_ms)); 2523 } 2524 }); 2525 } 2526 2527 if (message.verbosity == Verbosity_FATAL) { 2528 flush(); 2529 2530 if (s_fatal_handler) { 2531 s_fatal_handler(message); 2532 flush(); 2533 } 2534 2535 if (abort_if_fatal) { 2536 #if LOGURU_CATCH_SIGABRT && !defined(_WIN32) 2537 // Make sure we don't catch our own abort: 2538 signal(SIGABRT, SIG_DFL); 2539 #endif 2540 abort(); 2541 } 2542 } 2543 } 2544 2545 // stack_trace_skip is just if verbosity == FATAL. 2546 void log_to_everywhere(int stack_trace_skip, Verbosity verbosity, 2547 const char* file, unsigned line, 2548 const char* prefix, const char* buff) 2549 { 2550 char preamble_buff[LOGURU_PREAMBLE_WIDTH]; 2551 print_preamble(preamble_buff, sizeof(preamble_buff), verbosity, file, line); 2552 auto message = Message{verbosity, file, line, preamble_buff, "", prefix, buff}; 2553 log_message(stack_trace_skip + 1, message, true, true); 2554 } 2555 2556 #if LOGURU_USE_FMTLIB 2557 void log(Verbosity verbosity, const char* file, unsigned line, const char* format, fmt::ArgList args) 2558 { 2559 auto formatted = fmt::format(format, args); 2560 log_to_everywhere(1, verbosity, file, line, "", formatted.c_str()); 2561 } 2562 2563 void raw_log(Verbosity verbosity, const char* file, unsigned line, const char* format, fmt::ArgList args) 2564 { 2565 auto formatted = fmt::format(format, args); 2566 auto message = Message{verbosity, file, line, "", "", "", formatted.c_str()}; 2567 log_message(1, message, false, true); 2568 } 2569 2570 #else 2571 void log(Verbosity verbosity, const char* file, unsigned line, const char* format, ...) 2572 { 2573 va_list vlist; 2574 va_start(vlist, format); 2575 auto buff = vtextprintf(format, vlist); 2576 log_to_everywhere(1, verbosity, file, line, "", buff.c_str()); 2577 va_end(vlist); 2578 } 2579 2580 void raw_log(Verbosity verbosity, const char* file, unsigned line, const char* format, ...) 2581 { 2582 va_list vlist; 2583 va_start(vlist, format); 2584 auto buff = vtextprintf(format, vlist); 2585 auto message = Message{verbosity, file, line, "", "", "", buff.c_str()}; 2586 log_message(1, message, false, true); 2587 va_end(vlist); 2588 } 2589 #endif 2590 2591 void flush() 2592 { 2593 std::lock_guard<std::recursive_mutex> lock(s_mutex); 2594 fflush(stderr); 2595 for (const auto& callback : s_callbacks) 2596 { 2597 if (callback.flush) { 2598 callback.flush(callback.user_data); 2599 } 2600 } 2601 s_needs_flushing = false; 2602 } 2603 2604 LogScopeRAII::LogScopeRAII(Verbosity verbosity, const char* file, unsigned line, const char* format, ...) 2605 : _verbosity(verbosity), _file(file), _line(line) 2606 { 2607 if (verbosity <= current_verbosity_cutoff()) { 2608 std::lock_guard<std::recursive_mutex> lock(s_mutex); 2609 _indent_stderr = (verbosity <= g_stderr_verbosity); 2610 _start_time_ns = now_ns(); 2611 va_list vlist; 2612 va_start(vlist, format); 2613 vsnprintf(_name, sizeof(_name), format, vlist); 2614 log_to_everywhere(1, _verbosity, file, line, "{ ", _name); 2615 va_end(vlist); 2616 2617 if (_indent_stderr) { 2618 ++s_stderr_indentation; 2619 } 2620 2621 for (auto& p : s_callbacks) { 2622 if (verbosity <= p.verbosity) { 2623 ++p.indentation; 2624 } 2625 } 2626 } else { 2627 _file = nullptr; 2628 } 2629 } 2630 2631 LogScopeRAII::~LogScopeRAII() 2632 { 2633 if (_file) { 2634 std::lock_guard<std::recursive_mutex> lock(s_mutex); 2635 if (_indent_stderr && s_stderr_indentation > 0) { 2636 --s_stderr_indentation; 2637 } 2638 for (auto& p : s_callbacks) { 2639 // Note: Callback indentation cannot change! 2640 if (_verbosity <= p.verbosity) { 2641 // in unlikely case this callback is new 2642 if (p.indentation > 0) { 2643 --p.indentation; 2644 } 2645 } 2646 } 2647 auto duration_sec = (now_ns() - _start_time_ns) / 1e9; 2648 auto buff = textprintf("%.*f s: %s", SCOPE_TIME_PRECISION, duration_sec, _name); 2649 log_to_everywhere(1, _verbosity, _file, _line, "} ", buff.c_str()); 2650 } 2651 } 2652 2653 void log_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line, const char* format, ...) 2654 { 2655 va_list vlist; 2656 va_start(vlist, format); 2657 auto buff = vtextprintf(format, vlist); 2658 log_to_everywhere(stack_trace_skip + 1, Verbosity_FATAL, file, line, expr, buff.c_str()); 2659 va_end(vlist); 2660 abort(); // log_to_everywhere already does this, but this makes the analyzer happy. 2661 } 2662 2663 void log_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line) 2664 { 2665 log_and_abort(stack_trace_skip + 1, expr, file, line, " "); 2666 } 2667 2668 // ---------------------------------------------------------------------------- 2669 // Streams: 2670 2671 std::string vstrprintf(const char* format, va_list vlist) 2672 { 2673 auto text = vtextprintf(format, vlist); 2674 std::string result = text.c_str(); 2675 return result; 2676 } 2677 2678 std::string strprintf(const char* format, ...) 2679 { 2680 va_list vlist; 2681 va_start(vlist, format); 2682 auto result = vstrprintf(format, vlist); 2683 va_end(vlist); 2684 return result; 2685 } 2686 2687 #if LOGURU_WITH_STREAMS 2688 2689 StreamLogger::~StreamLogger() noexcept(false) 2690 { 2691 auto message = _ss.str(); 2692 log(_verbosity, _file, _line, "%s", message.c_str()); 2693 } 2694 2695 AbortLogger::~AbortLogger() noexcept(false) 2696 { 2697 auto message = _ss.str(); 2698 loguru::log_and_abort(1, _expr, _file, _line, "%s", message.c_str()); 2699 } 2700 2701 #endif // LOGURU_WITH_STREAMS 2702 2703 // ---------------------------------------------------------------------------- 2704 // 888888 88""Yb 88""Yb dP"Yb 88""Yb dP""b8 dP"Yb 88b 88 888888 888888 Yb dP 888888 2705 // 88__ 88__dP 88__dP dP Yb 88__dP dP `" dP Yb 88Yb88 88 88__ YbdP 88 2706 // 88"" 88"Yb 88"Yb Yb dP 88"Yb Yb Yb dP 88 Y88 88 88"" dPYb 88 2707 // 888888 88 Yb 88 Yb YbodP 88 Yb YboodP YbodP 88 Y8 88 888888 dP Yb 88 2708 // ---------------------------------------------------------------------------- 2709 2710 struct StringStream 2711 { 2712 std::string str; 2713 }; 2714 2715 // Use this in your EcPrinter implementations. 2716 void stream_print(StringStream& out_string_stream, const char* text) 2717 { 2718 out_string_stream.str += text; 2719 } 2720 2721 // ---------------------------------------------------------------------------- 2722 2723 using ECPtr = EcEntryBase*; 2724 2725 #if defined(_WIN32) || (defined(__APPLE__) && !TARGET_OS_IPHONE) 2726 #ifdef __APPLE__ 2727 #define LOGURU_THREAD_LOCAL __thread 2728 #else 2729 #define LOGURU_THREAD_LOCAL thread_local 2730 #endif 2731 static LOGURU_THREAD_LOCAL ECPtr thread_ec_ptr = nullptr; 2732 2733 ECPtr& get_thread_ec_head_ref() 2734 { 2735 return thread_ec_ptr; 2736 } 2737 #else // !thread_local 2738 static pthread_once_t s_ec_pthread_once = PTHREAD_ONCE_INIT; 2739 static pthread_key_t s_ec_pthread_key; 2740 2741 void free_ec_head_ref(void* io_error_context) 2742 { 2743 delete reinterpret_cast<ECPtr*>(io_error_context); 2744 } 2745 2746 void ec_make_pthread_key() 2747 { 2748 (void)pthread_key_create(&s_ec_pthread_key, free_ec_head_ref); 2749 } 2750 2751 ECPtr& get_thread_ec_head_ref() 2752 { 2753 (void)pthread_once(&s_ec_pthread_once, ec_make_pthread_key); 2754 auto ec = reinterpret_cast<ECPtr*>(pthread_getspecific(s_ec_pthread_key)); 2755 if (ec == nullptr) { 2756 ec = new ECPtr(nullptr); 2757 (void)pthread_setspecific(s_ec_pthread_key, ec); 2758 } 2759 return *ec; 2760 } 2761 #endif // !thread_local 2762 2763 // ---------------------------------------------------------------------------- 2764 2765 EcHandle get_thread_ec_handle() 2766 { 2767 return get_thread_ec_head_ref(); 2768 } 2769 2770 Text get_error_context() 2771 { 2772 return get_error_context_for(get_thread_ec_head_ref()); 2773 } 2774 2775 Text get_error_context_for(const EcEntryBase* ec_head) 2776 { 2777 std::vector<const EcEntryBase*> stack; 2778 while (ec_head) { 2779 stack.push_back(ec_head); 2780 ec_head = ec_head->_previous; 2781 } 2782 std::reverse(stack.begin(), stack.end()); 2783 2784 StringStream result; 2785 if (!stack.empty()) { 2786 result.str += "------------------------------------------------\n"; 2787 for (auto entry : stack) { 2788 const auto description = std::string(entry->_descr) + ":"; 2789 auto prefix = textprintf("[ErrorContext] %*s:%-5u %-20s ", 2790 LOGURU_FILENAME_WIDTH, filename(entry->_file), entry->_line, description.c_str()); 2791 result.str += prefix.c_str(); 2792 entry->print_value(result); 2793 result.str += "\n"; 2794 } 2795 result.str += "------------------------------------------------"; 2796 } 2797 return Text(strdup(result.str.c_str())); 2798 } 2799 2800 EcEntryBase::EcEntryBase(const char* file, unsigned line, const char* descr) 2801 : _file(file), _line(line), _descr(descr) 2802 { 2803 EcEntryBase*& ec_head = get_thread_ec_head_ref(); 2804 _previous = ec_head; 2805 ec_head = this; 2806 } 2807 2808 EcEntryBase::~EcEntryBase() 2809 { 2810 get_thread_ec_head_ref() = _previous; 2811 } 2812 2813 // ------------------------------------------------------------------------ 2814 2815 Text ec_to_text(const char* value) 2816 { 2817 // Add quotes around the string to make it obvious where it begin and ends. 2818 // This is great for detecting erroneous leading or trailing spaces in e.g. an identifier. 2819 auto str = "\"" + std::string(value) + "\""; 2820 return Text{strdup(str.c_str())}; 2821 } 2822 2823 Text ec_to_text(char c) 2824 { 2825 // Add quotes around the character to make it obvious where it begin and ends. 2826 std::string str = "'"; 2827 2828 auto write_hex_digit = [&](unsigned num) 2829 { 2830 if (num < 10u) { str += char('0' + num); } 2831 else { str += char('a' + num - 10); } 2832 }; 2833 2834 auto write_hex_16 = [&](uint16_t n) 2835 { 2836 write_hex_digit((n >> 12u) & 0x0f); 2837 write_hex_digit((n >> 8u) & 0x0f); 2838 write_hex_digit((n >> 4u) & 0x0f); 2839 write_hex_digit((n >> 0u) & 0x0f); 2840 }; 2841 2842 if (c == '\\') { str += "\\\\"; } 2843 else if (c == '\"') { str += "\\\""; } 2844 else if (c == '\'') { str += "\\\'"; } 2845 else if (c == '\0') { str += "\\0"; } 2846 else if (c == '\b') { str += "\\b"; } 2847 else if (c == '\f') { str += "\\f"; } 2848 else if (c == '\n') { str += "\\n"; } 2849 else if (c == '\r') { str += "\\r"; } 2850 else if (c == '\t') { str += "\\t"; } 2851 else if (0 <= c && c < 0x20) { 2852 str += "\\u"; 2853 write_hex_16(static_cast<uint16_t>(c)); 2854 } else { str += c; } 2855 2856 str += "'"; 2857 2858 return Text{strdup(str.c_str())}; 2859 } 2860 2861 #define DEFINE_EC(Type) \ 2862 Text ec_to_text(Type value) \ 2863 { \ 2864 auto str = std::to_string(value); \ 2865 return Text{strdup(str.c_str())}; \ 2866 } 2867 2868 DEFINE_EC(int) 2869 DEFINE_EC(unsigned int) 2870 DEFINE_EC(long) 2871 DEFINE_EC(unsigned long) 2872 DEFINE_EC(long long) 2873 DEFINE_EC(unsigned long long) 2874 DEFINE_EC(float) 2875 DEFINE_EC(double) 2876 DEFINE_EC(long double) 2877 2878 #undef DEFINE_EC 2879 2880 Text ec_to_text(EcHandle ec_handle) 2881 { 2882 Text parent_ec = get_error_context_for(ec_handle); 2883 char* with_newline = (char*)malloc(strlen(parent_ec.c_str()) + 2); 2884 with_newline[0] = '\n'; 2885 strcpy(with_newline + 1, parent_ec.c_str()); 2886 return Text(with_newline); 2887 } 2888 2889 // ---------------------------------------------------------------------------- 2890 2891 } // namespace loguru 2892 2893 // ---------------------------------------------------------------------------- 2894 // .dP"Y8 88 dP""b8 88b 88 db 88 .dP"Y8 2895 // `Ybo." 88 dP `" 88Yb88 dPYb 88 `Ybo." 2896 // o.`Y8b 88 Yb "88 88 Y88 dP__Yb 88 .o o.`Y8b 2897 // 8bodP' 88 YboodP 88 Y8 dP""""Yb 88ood8 8bodP' 2898 // ---------------------------------------------------------------------------- 2899 2900 #ifdef _WIN32 2901 namespace loguru { install_signal_handlers()2902 void install_signal_handlers() 2903 { 2904 #if defined(_MSC_VER) 2905 #pragma message ( "No signal handlers on Win32" ) 2906 #else 2907 #warning "No signal handlers on Win32" 2908 #endif 2909 } 2910 } // namespace loguru 2911 2912 #else // _WIN32 2913 2914 namespace loguru 2915 { 2916 struct Signal 2917 { 2918 int number; 2919 const char* name; 2920 }; 2921 const Signal ALL_SIGNALS[] = { 2922 #if LOGURU_CATCH_SIGABRT 2923 { SIGABRT, "SIGABRT" }, 2924 #endif 2925 { SIGBUS, "SIGBUS" }, 2926 { SIGFPE, "SIGFPE" }, 2927 { SIGILL, "SIGILL" }, 2928 { SIGINT, "SIGINT" }, 2929 { SIGSEGV, "SIGSEGV" }, 2930 { SIGTERM, "SIGTERM" }, 2931 }; 2932 write_to_stderr(const char * data,size_t size)2933 void write_to_stderr(const char* data, size_t size) 2934 { 2935 auto result = write(STDERR_FILENO, data, size); 2936 (void)result; // Ignore errors. 2937 } 2938 write_to_stderr(const char * data)2939 void write_to_stderr(const char* data) 2940 { 2941 write_to_stderr(data, strlen(data)); 2942 } 2943 call_default_signal_handler(int signal_number)2944 void call_default_signal_handler(int signal_number) 2945 { 2946 struct sigaction sig_action; 2947 memset(&sig_action, 0, sizeof(sig_action)); 2948 sigemptyset(&sig_action.sa_mask); 2949 sig_action.sa_handler = SIG_DFL; 2950 sigaction(signal_number, &sig_action, NULL); 2951 kill(getpid(), signal_number); 2952 } 2953 signal_handler(int signal_number,siginfo_t *,void *)2954 void signal_handler(int signal_number, siginfo_t*, void*) 2955 { 2956 const char* signal_name = "UNKNOWN SIGNAL"; 2957 2958 for (const auto& s : ALL_SIGNALS) { 2959 if (s.number == signal_number) { 2960 signal_name = s.name; 2961 break; 2962 } 2963 } 2964 2965 // -------------------------------------------------------------------- 2966 /* There are few things that are safe to do in a signal handler, 2967 but writing to stderr is one of them. 2968 So we first print out what happened to stderr so we're sure that gets out, 2969 then we do the unsafe things, like logging the stack trace. 2970 */ 2971 2972 if (g_colorlogtostderr && s_terminal_has_color) { 2973 write_to_stderr(terminal_reset()); 2974 write_to_stderr(terminal_bold()); 2975 write_to_stderr(terminal_light_red()); 2976 } 2977 write_to_stderr("\n"); 2978 write_to_stderr("Loguru caught a signal: "); 2979 write_to_stderr(signal_name); 2980 write_to_stderr("\n"); 2981 if (g_colorlogtostderr && s_terminal_has_color) { 2982 write_to_stderr(terminal_reset()); 2983 } 2984 2985 // -------------------------------------------------------------------- 2986 2987 #if LOGURU_UNSAFE_SIGNAL_HANDLER 2988 // -------------------------------------------------------------------- 2989 /* Now we do unsafe things. This can for example lead to deadlocks if 2990 the signal was triggered from the system's memory management functions 2991 and the code below tries to do allocations. 2992 */ 2993 2994 flush(); 2995 char preamble_buff[LOGURU_PREAMBLE_WIDTH]; 2996 print_preamble(preamble_buff, sizeof(preamble_buff), Verbosity_FATAL, "", 0); 2997 auto message = Message{Verbosity_FATAL, "", 0, preamble_buff, "", "Signal: ", signal_name}; 2998 try { 2999 log_message(1, message, false, false); 3000 } catch (...) { 3001 // This can happed due to s_fatal_handler. 3002 write_to_stderr("Exception caught and ignored by Loguru signal handler.\n"); 3003 } 3004 flush(); 3005 3006 // -------------------------------------------------------------------- 3007 #endif // LOGURU_UNSAFE_SIGNAL_HANDLER 3008 3009 call_default_signal_handler(signal_number); 3010 } 3011 install_signal_handlers()3012 void install_signal_handlers() 3013 { 3014 struct sigaction sig_action; 3015 memset(&sig_action, 0, sizeof(sig_action)); 3016 sigemptyset(&sig_action.sa_mask); 3017 sig_action.sa_flags |= SA_SIGINFO; 3018 sig_action.sa_sigaction = &signal_handler; 3019 for (const auto& s : ALL_SIGNALS) { 3020 CHECK_F(sigaction(s.number, &sig_action, NULL) != -1, 3021 "Failed to install handler for %s", s.name); 3022 } 3023 } 3024 } // namespace loguru 3025 3026 #endif // _WIN32 3027 3028 #endif // LOGURU_IMPLEMENTATION 3029