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