1/// 2module std.experimental.logger.core; 3 4import core.sync.mutex : Mutex; 5import std.datetime.date : DateTime; 6import std.datetime.systime : Clock, SysTime; 7import std.range.primitives; 8import std.traits; 9 10import std.experimental.logger.filelogger; 11 12/** This template evaluates if the passed $(D LogLevel) is active. 13The previously described version statements are used to decide if the 14$(D LogLevel) is active. The version statements only influence the compile 15unit they are used with, therefore this function can only disable logging this 16specific compile unit. 17*/ 18template isLoggingActiveAt(LogLevel ll) 19{ 20 version (StdLoggerDisableLogging) 21 { 22 enum isLoggingActiveAt = false; 23 } 24 else 25 { 26 static if (ll == LogLevel.trace) 27 { 28 version (StdLoggerDisableTrace) enum isLoggingActiveAt = false; 29 } 30 else static if (ll == LogLevel.info) 31 { 32 version (StdLoggerDisableInfo) enum isLoggingActiveAt = false; 33 } 34 else static if (ll == LogLevel.warning) 35 { 36 version (StdLoggerDisableWarning) enum isLoggingActiveAt = false; 37 } 38 else static if (ll == LogLevel.error) 39 { 40 version (StdLoggerDisableError) enum isLoggingActiveAt = false; 41 } 42 else static if (ll == LogLevel.critical) 43 { 44 version (StdLoggerDisableCritical) enum isLoggingActiveAt = false; 45 } 46 else static if (ll == LogLevel.fatal) 47 { 48 version (StdLoggerDisableFatal) enum isLoggingActiveAt = false; 49 } 50 // If `isLoggingActiveAt` didn't get defined above to false, 51 // we default it to true. 52 static if (!is(typeof(isLoggingActiveAt) == bool)) 53 { 54 enum isLoggingActiveAt = true; 55 } 56 } 57} 58 59/// This compile-time flag is $(D true) if logging is not statically disabled. 60enum isLoggingActive = isLoggingActiveAt!(LogLevel.all); 61 62/** This functions is used at runtime to determine if a $(D LogLevel) is 63active. The same previously defined version statements are used to disable 64certain levels. Again the version statements are associated with a compile 65unit and can therefore not disable logging in other compile units. 66pure bool isLoggingEnabled()(LogLevel ll) @safe nothrow @nogc 67*/ 68bool isLoggingEnabled()(LogLevel ll, LogLevel loggerLL, 69 LogLevel globalLL, lazy bool condition = true) @safe 70{ 71 switch (ll) 72 { 73 case LogLevel.trace: 74 version (StdLoggerDisableTrace) return false; 75 else break; 76 case LogLevel.info: 77 version (StdLoggerDisableInfo) return false; 78 else break; 79 case LogLevel.warning: 80 version (StdLoggerDisableWarning) return false; 81 else break; 82 case LogLevel.critical: 83 version (StdLoggerDisableCritical) return false; 84 else break; 85 case LogLevel.fatal: 86 version (StdLoggerDisableFatal) return false; 87 else break; 88 default: break; 89 } 90 91 return ll >= globalLL 92 && ll >= loggerLL 93 && ll != LogLevel.off 94 && globalLL != LogLevel.off 95 && loggerLL != LogLevel.off 96 && condition; 97} 98 99/** This template returns the $(D LogLevel) named "logLevel" of type $(D 100LogLevel) defined in a user defined module where the filename has the 101suffix "_loggerconfig.d". This $(D LogLevel) sets the minimal $(D LogLevel) 102of the module. 103 104A minimal $(D LogLevel) can be defined on a per module basis. 105In order to define a module $(D LogLevel) a file with a modulename 106"MODULENAME_loggerconfig" must be found. If no such module exists and the 107module is a nested module, it is checked if there exists a 108"PARENT_MODULE_loggerconfig" module with such a symbol. 109If this module exists and it contains a $(D LogLevel) called logLevel this $(D 110LogLevel) will be used. This parent lookup is continued until there is no 111parent module. Then the moduleLogLevel is $(D LogLevel.all). 112*/ 113template moduleLogLevel(string moduleName) 114if (!moduleName.length) 115{ 116 // default 117 enum moduleLogLevel = LogLevel.all; 118} 119 120/// 121@system unittest 122{ 123 static assert(moduleLogLevel!"" == LogLevel.all); 124} 125 126/// ditto 127template moduleLogLevel(string moduleName) 128if (moduleName.length) 129{ 130 import std.string : format; 131 mixin(q{ 132 static if (__traits(compiles, {import %1$s : logLevel;})) 133 { 134 import %1$s : logLevel; 135 static assert(is(typeof(logLevel) : LogLevel), 136 "Expect 'logLevel' to be of Type 'LogLevel'."); 137 // don't enforce enum here 138 alias moduleLogLevel = logLevel; 139 } 140 else 141 // use logLevel of package or default 142 alias moduleLogLevel = moduleLogLevel!(parentOf(moduleName)); 143 }.format(moduleName ~ "_loggerconfig")); 144} 145 146/// 147@system unittest 148{ 149 static assert(moduleLogLevel!"not.amodule.path" == LogLevel.all); 150} 151 152private string parentOf(string mod) 153{ 154 foreach_reverse (i, c; mod) 155 if (c == '.') return mod[0 .. i]; 156 return null; 157} 158 159/* This function formates a $(D SysTime) into an $(D OutputRange). 160 161The $(D SysTime) is formatted similar to 162$(LREF std.datatime.DateTime.toISOExtString) except the fractional second part. 163The fractional second part is in milliseconds and is always 3 digits. 164*/ 165void systimeToISOString(OutputRange)(OutputRange o, const ref SysTime time) 166if (isOutputRange!(OutputRange,string)) 167{ 168 import std.format : formattedWrite; 169 170 const auto dt = cast(DateTime) time; 171 const auto fsec = time.fracSecs.total!"msecs"; 172 173 formattedWrite(o, "%04d-%02d-%02dT%02d:%02d:%02d.%03d", 174 dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, 175 fsec); 176} 177 178/** This function logs data. 179 180In order for the data to be processed, the $(D LogLevel) of the log call must 181be greater or equal to the $(D LogLevel) of the $(D sharedLog) and the 182$(D defaultLogLevel); additionally the condition passed must be $(D true). 183 184Params: 185 ll = The $(D LogLevel) used by this log call. 186 condition = The condition must be $(D true) for the data to be logged. 187 args = The data that should be logged. 188 189Example: 190-------------------- 191log(LogLevel.warning, true, "Hello World", 3.1415); 192-------------------- 193*/ 194void log(int line = __LINE__, string file = __FILE__, 195 string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, 196 string moduleName = __MODULE__, A...)(const LogLevel ll, 197 lazy bool condition, lazy A args) 198if (args.length != 1) 199{ 200 static if (isLoggingActive) 201 { 202 if (ll >= moduleLogLevel!moduleName) 203 { 204 stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName) 205 (ll, condition, args); 206 } 207 } 208} 209 210/// Ditto 211void log(T, string moduleName = __MODULE__)(const LogLevel ll, 212 lazy bool condition, lazy T arg, int line = __LINE__, string file = __FILE__, 213 string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__) 214{ 215 static if (isLoggingActive) 216 { 217 if (ll >= moduleLogLevel!moduleName) 218 { 219 stdThreadLocalLog.log!(T,moduleName)(ll, condition, arg, line, file, funcName, 220 prettyFuncName); 221 } 222 } 223} 224 225/** This function logs data. 226 227In order for the data to be processed the $(D LogLevel) of the log call must 228be greater or equal to the $(D LogLevel) of the $(D sharedLog). 229 230Params: 231 ll = The $(D LogLevel) used by this log call. 232 args = The data that should be logged. 233 234Example: 235-------------------- 236log(LogLevel.warning, "Hello World", 3.1415); 237-------------------- 238*/ 239void log(int line = __LINE__, string file = __FILE__, 240 string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, 241 string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args) 242if (args.length > 1 && !is(Unqual!(A[0]) : bool)) 243{ 244 static if (isLoggingActive) 245 { 246 if (ll >= moduleLogLevel!moduleName) 247 { 248 stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName) 249 (ll, args); 250 } 251 } 252} 253 254/// Ditto 255void log(T, string moduleName = __MODULE__)(const LogLevel ll, lazy T arg, 256 int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, 257 string prettyFuncName = __PRETTY_FUNCTION__) 258{ 259 static if (isLoggingActive) 260 { 261 if (ll >= moduleLogLevel!moduleName) 262 { 263 stdThreadLocalLog.log!T(ll, arg, line, file, funcName, prettyFuncName, 264 moduleName); 265 } 266 } 267} 268 269/** This function logs data. 270 271In order for the data to be processed the $(D LogLevel) of the 272$(D sharedLog) must be greater or equal to the $(D defaultLogLevel) 273add the condition passed must be $(D true). 274 275Params: 276 condition = The condition must be $(D true) for the data to be logged. 277 args = The data that should be logged. 278 279Example: 280-------------------- 281log(true, "Hello World", 3.1415); 282-------------------- 283*/ 284void log(int line = __LINE__, string file = __FILE__, 285 string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, 286 string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args) 287if (args.length != 1) 288{ 289 static if (isLoggingActive) 290 { 291 stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName) 292 (stdThreadLocalLog.logLevel, condition, args); 293 } 294} 295 296/// Ditto 297void log(T, string moduleName = __MODULE__)(lazy bool condition, lazy T arg, 298 int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__, 299 string prettyFuncName = __PRETTY_FUNCTION__) 300{ 301 static if (isLoggingActive) 302 { 303 stdThreadLocalLog.log!(T,moduleName)(stdThreadLocalLog.logLevel, 304 condition, arg, line, file, funcName, prettyFuncName); 305 } 306} 307 308/** This function logs data. 309 310In order for the data to be processed the $(D LogLevel) of the 311$(D sharedLog) must be greater or equal to the $(D defaultLogLevel). 312 313Params: 314 args = The data that should be logged. 315 316Example: 317-------------------- 318log("Hello World", 3.1415); 319-------------------- 320*/ 321void log(int line = __LINE__, string file = __FILE__, 322 string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, 323 string moduleName = __MODULE__, A...)(lazy A args) 324if ((args.length > 1 && !is(Unqual!(A[0]) : bool) 325 && !is(Unqual!(A[0]) == LogLevel)) 326 || args.length == 0) 327{ 328 static if (isLoggingActive) 329 { 330 stdThreadLocalLog.log!(line, file, funcName, 331 prettyFuncName, moduleName)(stdThreadLocalLog.logLevel, args); 332 } 333} 334 335void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__, 336 string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, 337 string moduleName = __MODULE__) 338{ 339 static if (isLoggingActive) 340 { 341 stdThreadLocalLog.log!T(stdThreadLocalLog.logLevel, arg, line, file, 342 funcName, prettyFuncName, moduleName); 343 } 344} 345 346/** This function logs data in a $(D printf)-style manner. 347 348In order for the data to be processed the $(D LogLevel) of the log call must 349be greater or equal to the $(D LogLevel) of the $(D sharedLog) and the 350$(D defaultLogLevel) additionally the condition passed must be $(D true). 351 352Params: 353 ll = The $(D LogLevel) used by this log call. 354 condition = The condition must be $(D true) for the data to be logged. 355 msg = The $(D printf)-style string. 356 args = The data that should be logged. 357 358Example: 359-------------------- 360logf(LogLevel.warning, true, "Hello World %f", 3.1415); 361-------------------- 362*/ 363void logf(int line = __LINE__, string file = __FILE__, 364 string funcName = __FUNCTION__, 365 string prettyFuncName = __PRETTY_FUNCTION__, 366 string moduleName = __MODULE__, A...)(const LogLevel ll, 367 lazy bool condition, lazy string msg, lazy A args) 368{ 369 static if (isLoggingActive) 370 { 371 if (ll >= moduleLogLevel!moduleName) 372 { 373 stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName) 374 (ll, condition, msg, args); 375 } 376 } 377} 378 379/** This function logs data in a $(D printf)-style manner. 380 381In order for the data to be processed the $(D LogLevel) of the log call must 382be greater or equal to the $(D LogLevel) of the $(D sharedLog) and the 383$(D defaultLogLevel). 384 385Params: 386 ll = The $(D LogLevel) used by this log call. 387 msg = The $(D printf)-style string. 388 args = The data that should be logged. 389 390Example: 391-------------------- 392logf(LogLevel.warning, "Hello World %f", 3.1415); 393-------------------- 394*/ 395void logf(int line = __LINE__, string file = __FILE__, 396 string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, 397 string moduleName = __MODULE__, A...)(const LogLevel ll, lazy string msg, 398 lazy A args) 399{ 400 static if (isLoggingActive) 401 { 402 if (ll >= moduleLogLevel!moduleName) 403 { 404 stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName) 405 (ll, msg, args); 406 } 407 } 408} 409 410/** This function logs data in a $(D printf)-style manner. 411 412In order for the data to be processed the $(D LogLevel) of the log call must 413be greater or equal to the $(D defaultLogLevel) additionally the condition 414passed must be $(D true). 415 416Params: 417 condition = The condition must be $(D true) for the data to be logged. 418 msg = The $(D printf)-style string. 419 args = The data that should be logged. 420 421Example: 422-------------------- 423logf(true, "Hello World %f", 3.1415); 424-------------------- 425*/ 426void logf(int line = __LINE__, string file = __FILE__, 427 string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__, 428 string moduleName = __MODULE__, A...)(lazy bool condition, 429 lazy string msg, lazy A args) 430{ 431 static if (isLoggingActive) 432 { 433 stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName) 434 (stdThreadLocalLog.logLevel, condition, msg, args); 435 } 436} 437 438/** This function logs data in a $(D printf)-style manner. 439 440In order for the data to be processed the $(D LogLevel) of the log call must 441be greater or equal to the $(D defaultLogLevel). 442 443Params: 444 msg = The $(D printf)-style string. 445 args = The data that should be logged. 446 447Example: 448-------------------- 449logf("Hello World %f", 3.1415); 450-------------------- 451*/ 452void logf(int line = __LINE__, string file = __FILE__, 453 string funcName = __FUNCTION__, 454 string prettyFuncName = __PRETTY_FUNCTION__, 455 string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) 456{ 457 static if (isLoggingActive) 458 { 459 stdThreadLocalLog.logf!(line, file, funcName,prettyFuncName, moduleName) 460 (stdThreadLocalLog.logLevel, msg, args); 461 } 462} 463 464/** This template provides the global log functions with the $(D LogLevel) 465is encoded in the function name. 466 467The aliases following this template create the public names of these log 468functions. 469*/ 470template defaultLogFunction(LogLevel ll) 471{ 472 void defaultLogFunction(int line = __LINE__, string file = __FILE__, 473 string funcName = __FUNCTION__, 474 string prettyFuncName = __PRETTY_FUNCTION__, 475 string moduleName = __MODULE__, A...)(lazy A args) 476 if ((args.length > 0 && !is(Unqual!(A[0]) : bool)) || args.length == 0) 477 { 478 static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName) 479 { 480 stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName, 481 prettyFuncName, moduleName)(args); 482 } 483 } 484 485 void defaultLogFunction(int line = __LINE__, string file = __FILE__, 486 string funcName = __FUNCTION__, 487 string prettyFuncName = __PRETTY_FUNCTION__, 488 string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args) 489 { 490 static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName) 491 { 492 stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName, 493 prettyFuncName, moduleName)(condition, args); 494 } 495 } 496} 497 498/** This function logs data to the $(D stdThreadLocalLog), optionally depending 499on a condition. 500 501In order for the resulting log message to be logged the $(D LogLevel) must 502be greater or equal than the $(D LogLevel) of the $(D stdThreadLocalLog) and 503must be greater or equal than the global $(D LogLevel). 504Additionally the $(D LogLevel) must be greater or equal than the $(D LogLevel) 505of the $(D stdSharedLogger). 506If a condition is given, it must evaluate to $(D true). 507 508Params: 509 condition = The condition must be $(D true) for the data to be logged. 510 args = The data that should be logged. 511 512Example: 513-------------------- 514trace(1337, "is number"); 515info(1337, "is number"); 516error(1337, "is number"); 517critical(1337, "is number"); 518fatal(1337, "is number"); 519trace(true, 1337, "is number"); 520info(false, 1337, "is number"); 521error(true, 1337, "is number"); 522critical(false, 1337, "is number"); 523fatal(true, 1337, "is number"); 524-------------------- 525*/ 526alias trace = defaultLogFunction!(LogLevel.trace); 527/// Ditto 528alias info = defaultLogFunction!(LogLevel.info); 529/// Ditto 530alias warning = defaultLogFunction!(LogLevel.warning); 531/// Ditto 532alias error = defaultLogFunction!(LogLevel.error); 533/// Ditto 534alias critical = defaultLogFunction!(LogLevel.critical); 535/// Ditto 536alias fatal = defaultLogFunction!(LogLevel.fatal); 537 538/** This template provides the global $(D printf)-style log functions with 539the $(D LogLevel) is encoded in the function name. 540 541The aliases following this template create the public names of the log 542functions. 543*/ 544template defaultLogFunctionf(LogLevel ll) 545{ 546 void defaultLogFunctionf(int line = __LINE__, string file = __FILE__, 547 string funcName = __FUNCTION__, 548 string prettyFuncName = __PRETTY_FUNCTION__, 549 string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) 550 { 551 static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName) 552 { 553 stdThreadLocalLog.memLogFunctions!(ll).logImplf!(line, file, funcName, 554 prettyFuncName, moduleName)(msg, args); 555 } 556 } 557 558 void defaultLogFunctionf(int line = __LINE__, string file = __FILE__, 559 string funcName = __FUNCTION__, 560 string prettyFuncName = __PRETTY_FUNCTION__, 561 string moduleName = __MODULE__, A...)(lazy bool condition, 562 lazy string msg, lazy A args) 563 { 564 static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName) 565 { 566 stdThreadLocalLog.memLogFunctions!(ll).logImplf!(line, file, funcName, 567 prettyFuncName, moduleName)(condition, msg, args); 568 } 569 } 570} 571 572/** This function logs data to the $(D sharedLog) in a $(D printf)-style 573manner. 574 575In order for the resulting log message to be logged the $(D LogLevel) must 576be greater or equal than the $(D LogLevel) of the $(D sharedLog) and 577must be greater or equal than the global $(D LogLevel). 578Additionally the $(D LogLevel) must be greater or equal than the $(D LogLevel) 579of the $(D stdSharedLogger). 580 581Params: 582 msg = The $(D printf)-style string. 583 args = The data that should be logged. 584 585Example: 586-------------------- 587tracef("is number %d", 1); 588infof("is number %d", 2); 589errorf("is number %d", 3); 590criticalf("is number %d", 4); 591fatalf("is number %d", 5); 592-------------------- 593 594The second version of the function logs data to the $(D sharedLog) in a $(D 595printf)-style manner. 596 597In order for the resulting log message to be logged the $(D LogLevel) must 598be greater or equal than the $(D LogLevel) of the $(D sharedLog) and 599must be greater or equal than the global $(D LogLevel). 600Additionally the $(D LogLevel) must be greater or equal than the $(D LogLevel) 601of the $(D stdSharedLogger). 602 603Params: 604 condition = The condition must be $(D true) for the data to be logged. 605 msg = The $(D printf)-style string. 606 args = The data that should be logged. 607 608Example: 609-------------------- 610tracef(false, "is number %d", 1); 611infof(false, "is number %d", 2); 612errorf(true, "is number %d", 3); 613criticalf(true, "is number %d", 4); 614fatalf(someFunct(), "is number %d", 5); 615-------------------- 616*/ 617alias tracef = defaultLogFunctionf!(LogLevel.trace); 618/// Ditto 619alias infof = defaultLogFunctionf!(LogLevel.info); 620/// Ditto 621alias warningf = defaultLogFunctionf!(LogLevel.warning); 622/// Ditto 623alias errorf = defaultLogFunctionf!(LogLevel.error); 624/// Ditto 625alias criticalf = defaultLogFunctionf!(LogLevel.critical); 626/// Ditto 627alias fatalf = defaultLogFunctionf!(LogLevel.fatal); 628 629private struct MsgRange 630{ 631 import std.traits : isSomeString, isSomeChar; 632 633 private Logger log; 634 635 this(Logger log) @safe 636 { 637 this.log = log; 638 } 639 640 void put(T)(T msg) @safe 641 if (isSomeString!T) 642 { 643 log.logMsgPart(msg); 644 } 645 646 void put(dchar elem) @safe 647 { 648 import std.utf : encode; 649 char[4] buffer; 650 size_t len = encode(buffer, elem); 651 log.logMsgPart(buffer[0 .. len]); 652 } 653} 654 655private void formatString(A...)(MsgRange oRange, A args) 656{ 657 import std.format : formattedWrite; 658 659 foreach (arg; args) 660 { 661 formattedWrite(oRange, "%s", arg); 662 } 663} 664 665@system unittest 666{ 667 void dummy() @safe 668 { 669 auto tl = new TestLogger(); 670 auto dst = MsgRange(tl); 671 formatString(dst, "aaa", "bbb"); 672 } 673 674 dummy(); 675} 676 677/** 678There are eight usable logging level. These level are $(I all), $(I trace), 679$(I info), $(I warning), $(I error), $(I critical), $(I fatal), and $(I off). 680If a log function with $(D LogLevel.fatal) is called the shutdown handler of 681that logger is called. 682*/ 683enum LogLevel : ubyte 684{ 685 all = 1, /** Lowest possible assignable $(D LogLevel). */ 686 trace = 32, /** $(D LogLevel) for tracing the execution of the program. */ 687 info = 64, /** This level is used to display information about the 688 program. */ 689 warning = 96, /** warnings about the program should be displayed with this 690 level. */ 691 error = 128, /** Information about errors should be logged with this 692 level.*/ 693 critical = 160, /** Messages that inform about critical errors should be 694 logged with this level. */ 695 fatal = 192, /** Log messages that describe fatal errors should use this 696 level. */ 697 off = ubyte.max /** Highest possible $(D LogLevel). */ 698} 699 700/** This class is the base of every logger. In order to create a new kind of 701logger a deriving class needs to implement the $(D writeLogMsg) method. By 702default this is not thread-safe. 703 704It is also possible to $(D override) the three methods $(D beginLogMsg), 705$(D logMsgPart) and $(D finishLogMsg) together, this option gives more 706flexibility. 707*/ 708abstract class Logger 709{ 710 import std.array : appender, Appender; 711 import std.concurrency : thisTid, Tid; 712 713 /** LogEntry is a aggregation combining all information associated 714 with a log message. This aggregation will be passed to the method 715 writeLogMsg. 716 */ 717 protected struct LogEntry 718 { 719 /// the filename the log function was called from 720 string file; 721 /// the line number the log function was called from 722 int line; 723 /// the name of the function the log function was called from 724 string funcName; 725 /// the pretty formatted name of the function the log function was 726 /// called from 727 string prettyFuncName; 728 /// the name of the module the log message is coming from 729 string moduleName; 730 /// the $(D LogLevel) associated with the log message 731 LogLevel logLevel; 732 /// thread id of the log message 733 Tid threadId; 734 /// the time the message was logged 735 SysTime timestamp; 736 /// the message of the log message 737 string msg; 738 /// A refernce to the $(D Logger) used to create this $(D LogEntry) 739 Logger logger; 740 } 741 742 /** 743 Every subclass of `Logger` has to call this constructor from their 744 constructor. It sets the `LogLevel`, and creates a fatal handler. The fatal 745 handler will throw an `Error` if a log call is made with level 746 `LogLevel.fatal`. 747 748 Params: 749 lv = `LogLevel` to use for this `Logger` instance. 750 */ 751 this(LogLevel lv) @safe 752 { 753 this.logLevel_ = lv; 754 this.fatalHandler_ = delegate() { 755 throw new Error("A fatal log message was logged"); 756 }; 757 758 this.mutex = new Mutex(); 759 } 760 761 /** A custom logger must implement this method in order to work in a 762 $(D MultiLogger) and $(D ArrayLogger). 763 764 Params: 765 payload = All information associated with call to log function. 766 767 See_Also: beginLogMsg, logMsgPart, finishLogMsg 768 */ 769 abstract protected void writeLogMsg(ref LogEntry payload) @safe; 770 771 /* The default implementation will use an $(D std.array.appender) 772 internally to construct the message string. This means dynamic, 773 GC memory allocation. A logger can avoid this allocation by 774 reimplementing $(D beginLogMsg), $(D logMsgPart) and $(D finishLogMsg). 775 $(D beginLogMsg) is always called first, followed by any number of calls 776 to $(D logMsgPart) and one call to $(D finishLogMsg). 777 778 As an example for such a custom $(D Logger) compare this: 779 ---------------- 780 class CLogger : Logger 781 { 782 override void beginLogMsg(string file, int line, string funcName, 783 string prettyFuncName, string moduleName, LogLevel logLevel, 784 Tid threadId, SysTime timestamp) 785 { 786 ... logic here 787 } 788 789 override void logMsgPart(const(char)[] msg) 790 { 791 ... logic here 792 } 793 794 override void finishLogMsg() 795 { 796 ... logic here 797 } 798 799 void writeLogMsg(ref LogEntry payload) 800 { 801 this.beginLogMsg(payload.file, payload.line, payload.funcName, 802 payload.prettyFuncName, payload.moduleName, payload.logLevel, 803 payload.threadId, payload.timestamp, payload.logger); 804 805 this.logMsgPart(payload.msg); 806 this.finishLogMsg(); 807 } 808 } 809 ---------------- 810 */ 811 protected void beginLogMsg(string file, int line, string funcName, 812 string prettyFuncName, string moduleName, LogLevel logLevel, 813 Tid threadId, SysTime timestamp, Logger logger) 814 @safe 815 { 816 static if (isLoggingActive) 817 { 818 msgAppender = appender!string(); 819 header = LogEntry(file, line, funcName, prettyFuncName, 820 moduleName, logLevel, threadId, timestamp, null, logger); 821 } 822 } 823 824 /** Logs a part of the log message. */ 825 protected void logMsgPart(const(char)[] msg) @safe 826 { 827 static if (isLoggingActive) 828 { 829 msgAppender.put(msg); 830 } 831 } 832 833 /** Signals that the message has been written and no more calls to 834 $(D logMsgPart) follow. */ 835 protected void finishLogMsg() @safe 836 { 837 static if (isLoggingActive) 838 { 839 header.msg = msgAppender.data; 840 this.writeLogMsg(header); 841 } 842 } 843 844 /** The $(D LogLevel) determines if the log call are processed or dropped 845 by the $(D Logger). In order for the log call to be processed the 846 $(D LogLevel) of the log call must be greater or equal to the $(D LogLevel) 847 of the $(D logger). 848 849 These two methods set and get the $(D LogLevel) of the used $(D Logger). 850 851 Example: 852 ----------- 853 auto f = new FileLogger(stdout); 854 f.logLevel = LogLevel.info; 855 assert(f.logLevel == LogLevel.info); 856 ----------- 857 */ 858 @property final LogLevel logLevel() const pure @safe @nogc 859 { 860 return trustedLoad(this.logLevel_); 861 } 862 863 /// Ditto 864 @property final void logLevel(const LogLevel lv) @safe @nogc 865 { 866 synchronized (mutex) this.logLevel_ = lv; 867 } 868 869 /** This $(D delegate) is called in case a log message with 870 $(D LogLevel.fatal) gets logged. 871 872 By default an $(D Error) will be thrown. 873 */ 874 @property final void delegate() fatalHandler() @safe @nogc 875 { 876 synchronized (mutex) return this.fatalHandler_; 877 } 878 879 /// Ditto 880 @property final void fatalHandler(void delegate() @safe fh) @safe @nogc 881 { 882 synchronized (mutex) this.fatalHandler_ = fh; 883 } 884 885 /** This method allows forwarding log entries from one logger to another. 886 887 $(D forwardMsg) will ensure proper synchronization and then call 888 $(D writeLogMsg). This is an API for implementing your own loggers and 889 should not be called by normal user code. A notable difference from other 890 logging functions is that the $(D globalLogLevel) wont be evaluated again 891 since it is assumed that the caller already checked that. 892 */ 893 void forwardMsg(ref LogEntry payload) @trusted 894 { 895 static if (isLoggingActive) synchronized (mutex) 896 { 897 if (isLoggingEnabled(payload.logLevel, this.logLevel_, 898 globalLogLevel)) 899 { 900 this.writeLogMsg(payload); 901 902 if (payload.logLevel == LogLevel.fatal) 903 this.fatalHandler_(); 904 } 905 } 906 } 907 908 /** This template provides the log functions for the $(D Logger) $(D class) 909 with the $(D LogLevel) encoded in the function name. 910 911 For further information see the the two functions defined inside of this 912 template. 913 914 The aliases following this template create the public names of these log 915 functions. 916 */ 917 template memLogFunctions(LogLevel ll) 918 { 919 /** This function logs data to the used $(D Logger). 920 921 In order for the resulting log message to be logged the $(D LogLevel) 922 must be greater or equal than the $(D LogLevel) of the used $(D Logger) 923 and must be greater or equal than the global $(D LogLevel). 924 925 Params: 926 args = The data that should be logged. 927 928 Example: 929 -------------------- 930 auto s = new FileLogger(stdout); 931 s.trace(1337, "is number"); 932 s.info(1337, "is number"); 933 s.error(1337, "is number"); 934 s.critical(1337, "is number"); 935 s.fatal(1337, "is number"); 936 -------------------- 937 */ 938 void logImpl(int line = __LINE__, string file = __FILE__, 939 string funcName = __FUNCTION__, 940 string prettyFuncName = __PRETTY_FUNCTION__, 941 string moduleName = __MODULE__, A...)(lazy A args) 942 if (args.length == 0 || (args.length > 0 && !is(A[0] : bool))) 943 { 944 static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName) 945 synchronized (mutex) 946 { 947 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) 948 { 949 this.beginLogMsg(file, line, funcName, prettyFuncName, 950 moduleName, ll, thisTid, Clock.currTime, this); 951 952 auto writer = MsgRange(this); 953 formatString(writer, args); 954 955 this.finishLogMsg(); 956 957 static if (ll == LogLevel.fatal) 958 this.fatalHandler_(); 959 } 960 } 961 } 962 963 /** This function logs data to the used $(D Logger) depending on a 964 condition. 965 966 In order for the resulting log message to be logged the $(D LogLevel) must 967 be greater or equal than the $(D LogLevel) of the used $(D Logger) and 968 must be greater or equal than the global $(D LogLevel) additionally the 969 condition passed must be $(D true). 970 971 Params: 972 condition = The condition must be $(D true) for the data to be logged. 973 args = The data that should be logged. 974 975 Example: 976 -------------------- 977 auto s = new FileLogger(stdout); 978 s.trace(true, 1337, "is number"); 979 s.info(false, 1337, "is number"); 980 s.error(true, 1337, "is number"); 981 s.critical(false, 1337, "is number"); 982 s.fatal(true, 1337, "is number"); 983 -------------------- 984 */ 985 void logImpl(int line = __LINE__, string file = __FILE__, 986 string funcName = __FUNCTION__, 987 string prettyFuncName = __PRETTY_FUNCTION__, 988 string moduleName = __MODULE__, A...)(lazy bool condition, 989 lazy A args) 990 { 991 static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName) 992 synchronized (mutex) 993 { 994 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, 995 condition)) 996 { 997 this.beginLogMsg(file, line, funcName, prettyFuncName, 998 moduleName, ll, thisTid, Clock.currTime, this); 999 1000 auto writer = MsgRange(this); 1001 formatString(writer, args); 1002 1003 this.finishLogMsg(); 1004 1005 static if (ll == LogLevel.fatal) 1006 this.fatalHandler_(); 1007 } 1008 } 1009 } 1010 1011 /** This function logs data to the used $(D Logger) in a 1012 $(D printf)-style manner. 1013 1014 In order for the resulting log message to be logged the $(D LogLevel) 1015 must be greater or equal than the $(D LogLevel) of the used $(D Logger) 1016 and must be greater or equal than the global $(D LogLevel) additionally 1017 the passed condition must be $(D true). 1018 1019 Params: 1020 condition = The condition must be $(D true) for the data to be logged. 1021 msg = The $(D printf)-style string. 1022 args = The data that should be logged. 1023 1024 Example: 1025 -------------------- 1026 auto s = new FileLogger(stderr); 1027 s.tracef(true, "is number %d", 1); 1028 s.infof(true, "is number %d", 2); 1029 s.errorf(false, "is number %d", 3); 1030 s.criticalf(someFunc(), "is number %d", 4); 1031 s.fatalf(true, "is number %d", 5); 1032 -------------------- 1033 */ 1034 void logImplf(int line = __LINE__, string file = __FILE__, 1035 string funcName = __FUNCTION__, 1036 string prettyFuncName = __PRETTY_FUNCTION__, 1037 string moduleName = __MODULE__, A...)(lazy bool condition, 1038 lazy string msg, lazy A args) 1039 { 1040 static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName) 1041 synchronized (mutex) 1042 { 1043 import std.format : formattedWrite; 1044 1045 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, 1046 condition)) 1047 { 1048 this.beginLogMsg(file, line, funcName, prettyFuncName, 1049 moduleName, ll, thisTid, Clock.currTime, this); 1050 1051 auto writer = MsgRange(this); 1052 formattedWrite(writer, msg, args); 1053 1054 this.finishLogMsg(); 1055 1056 static if (ll == LogLevel.fatal) 1057 this.fatalHandler_(); 1058 } 1059 } 1060 } 1061 1062 /** This function logs data to the used $(D Logger) in a 1063 $(D printf)-style manner. 1064 1065 In order for the resulting log message to be logged the $(D LogLevel) must 1066 be greater or equal than the $(D LogLevel) of the used $(D Logger) and 1067 must be greater or equal than the global $(D LogLevel). 1068 1069 Params: 1070 msg = The $(D printf)-style string. 1071 args = The data that should be logged. 1072 1073 Example: 1074 -------------------- 1075 auto s = new FileLogger(stderr); 1076 s.tracef("is number %d", 1); 1077 s.infof("is number %d", 2); 1078 s.errorf("is number %d", 3); 1079 s.criticalf("is number %d", 4); 1080 s.fatalf("is number %d", 5); 1081 -------------------- 1082 */ 1083 void logImplf(int line = __LINE__, string file = __FILE__, 1084 string funcName = __FUNCTION__, 1085 string prettyFuncName = __PRETTY_FUNCTION__, 1086 string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) 1087 { 1088 static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName) 1089 synchronized (mutex) 1090 { 1091 import std.format : formattedWrite; 1092 1093 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) 1094 { 1095 this.beginLogMsg(file, line, funcName, prettyFuncName, 1096 moduleName, ll, thisTid, Clock.currTime, this); 1097 1098 auto writer = MsgRange(this); 1099 formattedWrite(writer, msg, args); 1100 1101 this.finishLogMsg(); 1102 1103 static if (ll == LogLevel.fatal) 1104 this.fatalHandler_(); 1105 } 1106 } 1107 } 1108 } 1109 1110 /// Ditto 1111 alias trace = memLogFunctions!(LogLevel.trace).logImpl; 1112 /// Ditto 1113 alias tracef = memLogFunctions!(LogLevel.trace).logImplf; 1114 /// Ditto 1115 alias info = memLogFunctions!(LogLevel.info).logImpl; 1116 /// Ditto 1117 alias infof = memLogFunctions!(LogLevel.info).logImplf; 1118 /// Ditto 1119 alias warning = memLogFunctions!(LogLevel.warning).logImpl; 1120 /// Ditto 1121 alias warningf = memLogFunctions!(LogLevel.warning).logImplf; 1122 /// Ditto 1123 alias error = memLogFunctions!(LogLevel.error).logImpl; 1124 /// Ditto 1125 alias errorf = memLogFunctions!(LogLevel.error).logImplf; 1126 /// Ditto 1127 alias critical = memLogFunctions!(LogLevel.critical).logImpl; 1128 /// Ditto 1129 alias criticalf = memLogFunctions!(LogLevel.critical).logImplf; 1130 /// Ditto 1131 alias fatal = memLogFunctions!(LogLevel.fatal).logImpl; 1132 /// Ditto 1133 alias fatalf = memLogFunctions!(LogLevel.fatal).logImplf; 1134 1135 /** This method logs data with the $(D LogLevel) of the used $(D Logger). 1136 1137 This method takes a $(D bool) as first argument. In order for the 1138 data to be processed the $(D bool) must be $(D true) and the $(D LogLevel) 1139 of the Logger must be greater or equal to the global $(D LogLevel). 1140 1141 Params: 1142 args = The data that should be logged. 1143 condition = The condition must be $(D true) for the data to be logged. 1144 args = The data that is to be logged. 1145 1146 Returns: The logger used by the logging function as reference. 1147 1148 Example: 1149 -------------------- 1150 auto l = new StdioLogger(); 1151 l.log(1337); 1152 -------------------- 1153 */ 1154 void log(int line = __LINE__, string file = __FILE__, 1155 string funcName = __FUNCTION__, 1156 string prettyFuncName = __PRETTY_FUNCTION__, 1157 string moduleName = __MODULE__, A...)(const LogLevel ll, 1158 lazy bool condition, lazy A args) 1159 if (args.length != 1) 1160 { 1161 static if (isLoggingActive) synchronized (mutex) 1162 { 1163 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition)) 1164 { 1165 this.beginLogMsg(file, line, funcName, prettyFuncName, 1166 moduleName, ll, thisTid, Clock.currTime, this); 1167 1168 auto writer = MsgRange(this); 1169 formatString(writer, args); 1170 1171 this.finishLogMsg(); 1172 1173 if (ll == LogLevel.fatal) 1174 this.fatalHandler_(); 1175 } 1176 } 1177 } 1178 1179 /// Ditto 1180 void log(T, string moduleName = __MODULE__)(const LogLevel ll, 1181 lazy bool condition, lazy T args, int line = __LINE__, 1182 string file = __FILE__, string funcName = __FUNCTION__, 1183 string prettyFuncName = __PRETTY_FUNCTION__) 1184 { 1185 static if (isLoggingActive) synchronized (mutex) 1186 { 1187 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, 1188 condition) && ll >= moduleLogLevel!moduleName) 1189 { 1190 this.beginLogMsg(file, line, funcName, prettyFuncName, 1191 moduleName, ll, thisTid, Clock.currTime, this); 1192 auto writer = MsgRange(this); 1193 formatString(writer, args); 1194 1195 this.finishLogMsg(); 1196 1197 if (ll == LogLevel.fatal) 1198 this.fatalHandler_(); 1199 } 1200 } 1201 } 1202 1203 /** This function logs data to the used $(D Logger) with a specific 1204 $(D LogLevel). 1205 1206 In order for the resulting log message to be logged the $(D LogLevel) 1207 must be greater or equal than the $(D LogLevel) of the used $(D Logger) 1208 and must be greater or equal than the global $(D LogLevel). 1209 1210 Params: 1211 ll = The specific $(D LogLevel) used for logging the log message. 1212 args = The data that should be logged. 1213 1214 Example: 1215 -------------------- 1216 auto s = new FileLogger(stdout); 1217 s.log(LogLevel.trace, 1337, "is number"); 1218 s.log(LogLevel.info, 1337, "is number"); 1219 s.log(LogLevel.warning, 1337, "is number"); 1220 s.log(LogLevel.error, 1337, "is number"); 1221 s.log(LogLevel.fatal, 1337, "is number"); 1222 -------------------- 1223 */ 1224 void log(int line = __LINE__, string file = __FILE__, 1225 string funcName = __FUNCTION__, 1226 string prettyFuncName = __PRETTY_FUNCTION__, 1227 string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args) 1228 if ((args.length > 1 && !is(Unqual!(A[0]) : bool)) || args.length == 0) 1229 { 1230 static if (isLoggingActive) synchronized (mutex) 1231 { 1232 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) 1233 { 1234 this.beginLogMsg(file, line, funcName, prettyFuncName, 1235 moduleName, ll, thisTid, Clock.currTime, this); 1236 1237 auto writer = MsgRange(this); 1238 formatString(writer, args); 1239 1240 this.finishLogMsg(); 1241 1242 if (ll == LogLevel.fatal) 1243 this.fatalHandler_(); 1244 } 1245 } 1246 } 1247 1248 /// Ditto 1249 void log(T)(const LogLevel ll, lazy T args, int line = __LINE__, 1250 string file = __FILE__, string funcName = __FUNCTION__, 1251 string prettyFuncName = __PRETTY_FUNCTION__, 1252 string moduleName = __MODULE__) 1253 { 1254 static if (isLoggingActive) synchronized (mutex) 1255 { 1256 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) 1257 { 1258 this.beginLogMsg(file, line, funcName, prettyFuncName, 1259 moduleName, ll, thisTid, Clock.currTime, this); 1260 auto writer = MsgRange(this); 1261 formatString(writer, args); 1262 1263 this.finishLogMsg(); 1264 1265 if (ll == LogLevel.fatal) 1266 this.fatalHandler_(); 1267 } 1268 } 1269 } 1270 1271 /** This function logs data to the used $(D Logger) depending on a 1272 explicitly passed condition with the $(D LogLevel) of the used 1273 $(D Logger). 1274 1275 In order for the resulting log message to be logged the $(D LogLevel) 1276 of the used $(D Logger) must be greater or equal than the global 1277 $(D LogLevel) and the condition must be $(D true). 1278 1279 Params: 1280 condition = The condition must be $(D true) for the data to be logged. 1281 args = The data that should be logged. 1282 1283 Example: 1284 -------------------- 1285 auto s = new FileLogger(stdout); 1286 s.log(true, 1337, "is number"); 1287 s.log(true, 1337, "is number"); 1288 s.log(true, 1337, "is number"); 1289 s.log(false, 1337, "is number"); 1290 s.log(false, 1337, "is number"); 1291 -------------------- 1292 */ 1293 void log(int line = __LINE__, string file = __FILE__, 1294 string funcName = __FUNCTION__, 1295 string prettyFuncName = __PRETTY_FUNCTION__, 1296 string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args) 1297 if (args.length != 1) 1298 { 1299 static if (isLoggingActive) synchronized (mutex) 1300 { 1301 if (isLoggingEnabled(this.logLevel_, this.logLevel_, 1302 globalLogLevel, condition)) 1303 { 1304 this.beginLogMsg(file, line, funcName, prettyFuncName, 1305 moduleName, this.logLevel_, thisTid, Clock.currTime, this); 1306 1307 auto writer = MsgRange(this); 1308 formatString(writer, args); 1309 1310 this.finishLogMsg(); 1311 1312 if (this.logLevel_ == LogLevel.fatal) 1313 this.fatalHandler_(); 1314 } 1315 } 1316 } 1317 1318 /// Ditto 1319 void log(T)(lazy bool condition, lazy T args, int line = __LINE__, 1320 string file = __FILE__, string funcName = __FUNCTION__, 1321 string prettyFuncName = __PRETTY_FUNCTION__, 1322 string moduleName = __MODULE__) 1323 { 1324 static if (isLoggingActive) synchronized (mutex) 1325 { 1326 if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel, 1327 condition)) 1328 { 1329 this.beginLogMsg(file, line, funcName, prettyFuncName, 1330 moduleName, this.logLevel_, thisTid, Clock.currTime, this); 1331 auto writer = MsgRange(this); 1332 formatString(writer, args); 1333 1334 this.finishLogMsg(); 1335 1336 if (this.logLevel_ == LogLevel.fatal) 1337 this.fatalHandler_(); 1338 } 1339 } 1340 } 1341 1342 /** This function logs data to the used $(D Logger) with the $(D LogLevel) 1343 of the used $(D Logger). 1344 1345 In order for the resulting log message to be logged the $(D LogLevel) 1346 of the used $(D Logger) must be greater or equal than the global 1347 $(D LogLevel). 1348 1349 Params: 1350 args = The data that should be logged. 1351 1352 Example: 1353 -------------------- 1354 auto s = new FileLogger(stdout); 1355 s.log(1337, "is number"); 1356 s.log(info, 1337, "is number"); 1357 s.log(1337, "is number"); 1358 s.log(1337, "is number"); 1359 s.log(1337, "is number"); 1360 -------------------- 1361 */ 1362 void log(int line = __LINE__, string file = __FILE__, 1363 string funcName = __FUNCTION__, 1364 string prettyFuncName = __PRETTY_FUNCTION__, 1365 string moduleName = __MODULE__, A...)(lazy A args) 1366 if ((args.length > 1 1367 && !is(Unqual!(A[0]) : bool) 1368 && !is(Unqual!(A[0]) == LogLevel)) 1369 || args.length == 0) 1370 { 1371 static if (isLoggingActive) synchronized (mutex) 1372 { 1373 if (isLoggingEnabled(this.logLevel_, this.logLevel_, 1374 globalLogLevel)) 1375 { 1376 this.beginLogMsg(file, line, funcName, prettyFuncName, 1377 moduleName, this.logLevel_, thisTid, Clock.currTime, this); 1378 auto writer = MsgRange(this); 1379 formatString(writer, args); 1380 1381 this.finishLogMsg(); 1382 1383 if (this.logLevel_ == LogLevel.fatal) 1384 this.fatalHandler_(); 1385 } 1386 } 1387 } 1388 1389 /// Ditto 1390 void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__, 1391 string funcName = __FUNCTION__, 1392 string prettyFuncName = __PRETTY_FUNCTION__, 1393 string moduleName = __MODULE__) 1394 { 1395 static if (isLoggingActive) synchronized (mutex) 1396 { 1397 if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel)) 1398 { 1399 this.beginLogMsg(file, line, funcName, prettyFuncName, 1400 moduleName, this.logLevel_, thisTid, Clock.currTime, this); 1401 auto writer = MsgRange(this); 1402 formatString(writer, arg); 1403 1404 this.finishLogMsg(); 1405 1406 if (this.logLevel_ == LogLevel.fatal) 1407 this.fatalHandler_(); 1408 } 1409 } 1410 } 1411 1412 /** This function logs data to the used $(D Logger) with a specific 1413 $(D LogLevel) and depending on a condition in a $(D printf)-style manner. 1414 1415 In order for the resulting log message to be logged the $(D LogLevel) 1416 must be greater or equal than the $(D LogLevel) of the used $(D Logger) 1417 and must be greater or equal than the global $(D LogLevel) and the 1418 condition must be $(D true). 1419 1420 Params: 1421 ll = The specific $(D LogLevel) used for logging the log message. 1422 condition = The condition must be $(D true) for the data to be logged. 1423 msg = The format string used for this log call. 1424 args = The data that should be logged. 1425 1426 Example: 1427 -------------------- 1428 auto s = new FileLogger(stdout); 1429 s.logf(LogLevel.trace, true ,"%d %s", 1337, "is number"); 1430 s.logf(LogLevel.info, true ,"%d %s", 1337, "is number"); 1431 s.logf(LogLevel.warning, true ,"%d %s", 1337, "is number"); 1432 s.logf(LogLevel.error, false ,"%d %s", 1337, "is number"); 1433 s.logf(LogLevel.fatal, true ,"%d %s", 1337, "is number"); 1434 -------------------- 1435 */ 1436 void logf(int line = __LINE__, string file = __FILE__, 1437 string funcName = __FUNCTION__, 1438 string prettyFuncName = __PRETTY_FUNCTION__, 1439 string moduleName = __MODULE__, A...)(const LogLevel ll, 1440 lazy bool condition, lazy string msg, lazy A args) 1441 { 1442 static if (isLoggingActive) synchronized (mutex) 1443 { 1444 import std.format : formattedWrite; 1445 1446 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition)) 1447 { 1448 this.beginLogMsg(file, line, funcName, prettyFuncName, 1449 moduleName, ll, thisTid, Clock.currTime, this); 1450 1451 auto writer = MsgRange(this); 1452 formattedWrite(writer, msg, args); 1453 1454 this.finishLogMsg(); 1455 1456 if (ll == LogLevel.fatal) 1457 this.fatalHandler_(); 1458 } 1459 } 1460 } 1461 1462 /** This function logs data to the used $(D Logger) with a specific 1463 $(D LogLevel) in a $(D printf)-style manner. 1464 1465 In order for the resulting log message to be logged the $(D LogLevel) 1466 must be greater or equal than the $(D LogLevel) of the used $(D Logger) 1467 and must be greater or equal than the global $(D LogLevel). 1468 1469 Params: 1470 ll = The specific $(D LogLevel) used for logging the log message. 1471 msg = The format string used for this log call. 1472 args = The data that should be logged. 1473 1474 Example: 1475 -------------------- 1476 auto s = new FileLogger(stdout); 1477 s.logf(LogLevel.trace, "%d %s", 1337, "is number"); 1478 s.logf(LogLevel.info, "%d %s", 1337, "is number"); 1479 s.logf(LogLevel.warning, "%d %s", 1337, "is number"); 1480 s.logf(LogLevel.error, "%d %s", 1337, "is number"); 1481 s.logf(LogLevel.fatal, "%d %s", 1337, "is number"); 1482 -------------------- 1483 */ 1484 void logf(int line = __LINE__, string file = __FILE__, 1485 string funcName = __FUNCTION__, 1486 string prettyFuncName = __PRETTY_FUNCTION__, 1487 string moduleName = __MODULE__, A...)(const LogLevel ll, 1488 lazy string msg, lazy A args) 1489 { 1490 static if (isLoggingActive) synchronized (mutex) 1491 { 1492 import std.format : formattedWrite; 1493 1494 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel)) 1495 { 1496 this.beginLogMsg(file, line, funcName, prettyFuncName, 1497 moduleName, ll, thisTid, Clock.currTime, this); 1498 1499 auto writer = MsgRange(this); 1500 formattedWrite(writer, msg, args); 1501 1502 this.finishLogMsg(); 1503 1504 if (ll == LogLevel.fatal) 1505 this.fatalHandler_(); 1506 } 1507 } 1508 } 1509 1510 /** This function logs data to the used $(D Logger) depending on a 1511 condition with the $(D LogLevel) of the used $(D Logger) in a 1512 $(D printf)-style manner. 1513 1514 In order for the resulting log message to be logged the $(D LogLevel) 1515 of the used $(D Logger) must be greater or equal than the global 1516 $(D LogLevel) and the condition must be $(D true). 1517 1518 Params: 1519 condition = The condition must be $(D true) for the data to be logged. 1520 msg = The format string used for this log call. 1521 args = The data that should be logged. 1522 1523 Example: 1524 -------------------- 1525 auto s = new FileLogger(stdout); 1526 s.logf(true ,"%d %s", 1337, "is number"); 1527 s.logf(true ,"%d %s", 1337, "is number"); 1528 s.logf(true ,"%d %s", 1337, "is number"); 1529 s.logf(false ,"%d %s", 1337, "is number"); 1530 s.logf(true ,"%d %s", 1337, "is number"); 1531 -------------------- 1532 */ 1533 void logf(int line = __LINE__, string file = __FILE__, 1534 string funcName = __FUNCTION__, 1535 string prettyFuncName = __PRETTY_FUNCTION__, 1536 string moduleName = __MODULE__, A...)(lazy bool condition, 1537 lazy string msg, lazy A args) 1538 { 1539 static if (isLoggingActive) synchronized (mutex) 1540 { 1541 import std.format : formattedWrite; 1542 1543 if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel, 1544 condition)) 1545 { 1546 this.beginLogMsg(file, line, funcName, prettyFuncName, 1547 moduleName, this.logLevel_, thisTid, Clock.currTime, this); 1548 1549 auto writer = MsgRange(this); 1550 formattedWrite(writer, msg, args); 1551 1552 this.finishLogMsg(); 1553 1554 if (this.logLevel_ == LogLevel.fatal) 1555 this.fatalHandler_(); 1556 } 1557 } 1558 } 1559 1560 /** This method logs data to the used $(D Logger) with the $(D LogLevel) 1561 of the this $(D Logger) in a $(D printf)-style manner. 1562 1563 In order for the data to be processed the $(D LogLevel) of the $(D Logger) 1564 must be greater or equal to the global $(D LogLevel). 1565 1566 Params: 1567 msg = The format string used for this log call. 1568 args = The data that should be logged. 1569 1570 Example: 1571 -------------------- 1572 auto s = new FileLogger(stdout); 1573 s.logf("%d %s", 1337, "is number"); 1574 s.logf("%d %s", 1337, "is number"); 1575 s.logf("%d %s", 1337, "is number"); 1576 s.logf("%d %s", 1337, "is number"); 1577 s.logf("%d %s", 1337, "is number"); 1578 -------------------- 1579 */ 1580 void logf(int line = __LINE__, string file = __FILE__, 1581 string funcName = __FUNCTION__, 1582 string prettyFuncName = __PRETTY_FUNCTION__, 1583 string moduleName = __MODULE__, A...)(lazy string msg, lazy A args) 1584 { 1585 static if (isLoggingActive) synchronized (mutex) 1586 { 1587 import std.format : formattedWrite; 1588 1589 if (isLoggingEnabled(this.logLevel_, this.logLevel_, 1590 globalLogLevel)) 1591 { 1592 this.beginLogMsg(file, line, funcName, prettyFuncName, 1593 moduleName, this.logLevel_, thisTid, Clock.currTime, this); 1594 1595 auto writer = MsgRange(this); 1596 formattedWrite(writer, msg, args); 1597 1598 this.finishLogMsg(); 1599 1600 if (this.logLevel_ == LogLevel.fatal) 1601 this.fatalHandler_(); 1602 } 1603 } 1604 } 1605 1606 private void delegate() @safe fatalHandler_; 1607 private shared LogLevel logLevel_ = LogLevel.info; 1608 private Mutex mutex; 1609 1610 protected Appender!string msgAppender; 1611 protected LogEntry header; 1612} 1613 1614// Thread Global 1615 1616private __gshared Logger stdSharedDefaultLogger; 1617private shared Logger stdSharedLogger; 1618private shared LogLevel stdLoggerGlobalLogLevel = LogLevel.all; 1619 1620/* This method returns the global default Logger. 1621 * Marked @trusted because of excessive reliance on __gshared data 1622 */ 1623private @property Logger defaultSharedLoggerImpl() @trusted 1624{ 1625 import std.conv : emplace; 1626 import std.stdio : stderr; 1627 1628 static __gshared align(FileLogger.alignof) void[__traits(classInstanceSize, FileLogger)] _buffer; 1629 1630 import std.concurrency : initOnce; 1631 initOnce!stdSharedDefaultLogger({ 1632 auto buffer = cast(ubyte[]) _buffer; 1633 return emplace!FileLogger(buffer, stderr, LogLevel.all); 1634 }()); 1635 1636 return stdSharedDefaultLogger; 1637} 1638 1639/** This property sets and gets the default $(D Logger). 1640 1641Example: 1642------------- 1643sharedLog = new FileLogger(yourFile); 1644------------- 1645The example sets a new $(D FileLogger) as new $(D sharedLog). 1646 1647If at some point you want to use the original default logger again, you can 1648use $(D sharedLog = null;). This will put back the original. 1649 1650Note: 1651While getting and setting $(D sharedLog) is thread-safe, it has to be considered 1652that the returned reference is only a current snapshot and in the following 1653code, you must make sure no other thread reassigns to it between reading and 1654writing $(D sharedLog). 1655 1656$(D sharedLog) is only thread-safe if the the used $(D Logger) is thread-safe. 1657The default $(D Logger) is thread-safe. 1658------------- 1659if (sharedLog !is myLogger) 1660 sharedLog = new myLogger; 1661------------- 1662*/ 1663@property Logger sharedLog() @safe 1664{ 1665 static auto trustedLoad(ref shared Logger logger) @trusted 1666 { 1667 import core.atomic : atomicLoad, MemoryOrder; 1668 return cast() atomicLoad!(MemoryOrder.acq)(logger); 1669 //FIXME: Casting shared away here. Not good. See issue 16232. 1670 } 1671 1672 // If we have set up our own logger use that 1673 if (auto logger = trustedLoad(stdSharedLogger)) 1674 { 1675 return logger; 1676 } 1677 else 1678 { 1679 // Otherwise resort to the default logger 1680 return defaultSharedLoggerImpl; 1681 } 1682} 1683 1684/// Ditto 1685@property void sharedLog(Logger logger) @trusted 1686{ 1687 import core.atomic : atomicStore, MemoryOrder; 1688 atomicStore!(MemoryOrder.rel)(stdSharedLogger, cast(shared) logger); 1689} 1690 1691/** This methods get and set the global $(D LogLevel). 1692 1693Every log message with a $(D LogLevel) lower as the global $(D LogLevel) 1694will be discarded before it reaches $(D writeLogMessage) method of any 1695$(D Logger). 1696*/ 1697/* Implementation note: 1698For any public logging call, the global log level shall only be queried once on 1699entry. Otherwise when another threads changes the level, we would work with 1700different levels at different spots in the code. 1701*/ 1702@property LogLevel globalLogLevel() @safe @nogc 1703{ 1704 return trustedLoad(stdLoggerGlobalLogLevel); 1705} 1706 1707/// Ditto 1708@property void globalLogLevel(LogLevel ll) @safe 1709{ 1710 trustedStore(stdLoggerGlobalLogLevel, ll); 1711} 1712 1713// Thread Local 1714 1715/** The $(D StdForwardLogger) will always forward anything to the sharedLog. 1716 1717The $(D StdForwardLogger) will not throw if data is logged with $(D 1718LogLevel.fatal). 1719*/ 1720class StdForwardLogger : Logger 1721{ 1722 /** The default constructor for the $(D StdForwardLogger). 1723 1724 Params: 1725 lv = The $(D LogLevel) for the $(D MultiLogger). By default the $(D 1726 LogLevel) is $(D all). 1727 */ 1728 this(const LogLevel lv = LogLevel.all) @safe 1729 { 1730 super(lv); 1731 this.fatalHandler = delegate() {}; 1732 } 1733 1734 override protected void writeLogMsg(ref LogEntry payload) 1735 { 1736 sharedLog.forwardMsg(payload); 1737 } 1738} 1739 1740/// 1741@safe unittest 1742{ 1743 auto nl1 = new StdForwardLogger(LogLevel.all); 1744} 1745 1746/** This $(D LogLevel) is unqiue to every thread. 1747 1748The thread local $(D Logger) will use this $(D LogLevel) to filter log calls 1749every same way as presented earlier. 1750*/ 1751//public LogLevel threadLogLevel = LogLevel.all; 1752private Logger stdLoggerThreadLogger; 1753private Logger stdLoggerDefaultThreadLogger; 1754 1755/* This method returns the thread local default Logger. 1756*/ 1757private @property Logger stdThreadLocalLogImpl() @trusted 1758{ 1759 import std.conv : emplace; 1760 1761 static void*[(__traits(classInstanceSize, StdForwardLogger) - 1) / (void*).sizeof + 1] _buffer; 1762 1763 auto buffer = cast(ubyte[]) _buffer; 1764 1765 if (stdLoggerDefaultThreadLogger is null) 1766 { 1767 stdLoggerDefaultThreadLogger = emplace!StdForwardLogger(buffer, LogLevel.all); 1768 } 1769 return stdLoggerDefaultThreadLogger; 1770} 1771 1772/** This function returns a thread unique $(D Logger), that by default 1773propergates all data logged to it to the $(D sharedLog). 1774 1775These properties can be used to set and get this $(D Logger). Every 1776modification to this $(D Logger) will only be visible in the thread the 1777modification has been done from. 1778 1779This $(D Logger) is called by the free standing log functions. This allows to 1780create thread local redirections and still use the free standing log 1781functions. 1782*/ 1783@property Logger stdThreadLocalLog() @safe 1784{ 1785 // If we have set up our own logger use that 1786 if (auto logger = stdLoggerThreadLogger) 1787 return logger; 1788 else 1789 // Otherwise resort to the default logger 1790 return stdThreadLocalLogImpl; 1791} 1792 1793/// Ditto 1794@property void stdThreadLocalLog(Logger logger) @safe 1795{ 1796 stdLoggerThreadLogger = logger; 1797} 1798 1799/// Ditto 1800@system unittest 1801{ 1802 import std.experimental.logger.filelogger : FileLogger; 1803 import std.file : deleteme, remove; 1804 Logger l = stdThreadLocalLog; 1805 stdThreadLocalLog = new FileLogger(deleteme ~ "-someFile.log"); 1806 scope(exit) remove(deleteme ~ "-someFile.log"); 1807 1808 auto tempLog = stdThreadLocalLog; 1809 stdThreadLocalLog = l; 1810 destroy(tempLog); 1811} 1812 1813@safe unittest 1814{ 1815 LogLevel ll = globalLogLevel; 1816 globalLogLevel = LogLevel.fatal; 1817 assert(globalLogLevel == LogLevel.fatal); 1818 globalLogLevel = ll; 1819} 1820 1821package class TestLogger : Logger 1822{ 1823 int line = -1; 1824 string file = null; 1825 string func = null; 1826 string prettyFunc = null; 1827 string msg = null; 1828 LogLevel lvl; 1829 1830 this(const LogLevel lv = LogLevel.all) @safe 1831 { 1832 super(lv); 1833 } 1834 1835 override protected void writeLogMsg(ref LogEntry payload) @safe 1836 { 1837 this.line = payload.line; 1838 this.file = payload.file; 1839 this.func = payload.funcName; 1840 this.prettyFunc = payload.prettyFuncName; 1841 this.lvl = payload.logLevel; 1842 this.msg = payload.msg; 1843 } 1844} 1845 1846version (unittest) private void testFuncNames(Logger logger) @safe 1847{ 1848 string s = "I'm here"; 1849 logger.log(s); 1850} 1851 1852@safe unittest 1853{ 1854 auto tl1 = new TestLogger(); 1855 testFuncNames(tl1); 1856 assert(tl1.func == "std.experimental.logger.core.testFuncNames", tl1.func); 1857 assert(tl1.prettyFunc == 1858 "void std.experimental.logger.core.testFuncNames(Logger logger) @safe", 1859 tl1.prettyFunc); 1860 assert(tl1.msg == "I'm here", tl1.msg); 1861} 1862 1863@safe unittest 1864{ 1865 auto tl1 = new TestLogger(LogLevel.all); 1866 tl1.log(); 1867 assert(tl1.line == __LINE__ - 1); 1868 tl1.log(true); 1869 assert(tl1.line == __LINE__ - 1); 1870 tl1.log(false); 1871 assert(tl1.line == __LINE__ - 3); 1872 tl1.log(LogLevel.info); 1873 assert(tl1.line == __LINE__ - 1); 1874 tl1.log(LogLevel.off); 1875 assert(tl1.line == __LINE__ - 3); 1876 tl1.log(LogLevel.info, true); 1877 assert(tl1.line == __LINE__ - 1); 1878 tl1.log(LogLevel.info, false); 1879 assert(tl1.line == __LINE__ - 3); 1880 1881 auto oldunspecificLogger = sharedLog; 1882 scope(exit) { 1883 sharedLog = oldunspecificLogger; 1884 } 1885 1886 sharedLog = tl1; 1887 1888 log(); 1889 assert(tl1.line == __LINE__ - 1); 1890 1891 log(LogLevel.info); 1892 assert(tl1.line == __LINE__ - 1); 1893 1894 log(true); 1895 assert(tl1.line == __LINE__ - 1); 1896 1897 log(LogLevel.warning, true); 1898 assert(tl1.line == __LINE__ - 1); 1899 1900 trace(); 1901 assert(tl1.line == __LINE__ - 1); 1902} 1903 1904@safe unittest 1905{ 1906 import std.experimental.logger.multilogger : MultiLogger; 1907 1908 auto tl1 = new TestLogger; 1909 auto tl2 = new TestLogger; 1910 1911 auto ml = new MultiLogger(); 1912 ml.insertLogger("one", tl1); 1913 ml.insertLogger("two", tl2); 1914 1915 string msg = "Hello Logger World"; 1916 ml.log(msg); 1917 int lineNumber = __LINE__ - 1; 1918 assert(tl1.msg == msg); 1919 assert(tl1.line == lineNumber); 1920 assert(tl2.msg == msg); 1921 assert(tl2.line == lineNumber); 1922 1923 ml.removeLogger("one"); 1924 ml.removeLogger("two"); 1925 auto n = ml.removeLogger("one"); 1926 assert(n is null); 1927} 1928 1929@safe unittest 1930{ 1931 bool errorThrown = false; 1932 auto tl = new TestLogger; 1933 auto dele = delegate() { 1934 errorThrown = true; 1935 }; 1936 tl.fatalHandler = dele; 1937 tl.fatal(); 1938 assert(errorThrown); 1939} 1940 1941@safe unittest 1942{ 1943 import std.conv : to; 1944 import std.exception : assertThrown, assertNotThrown; 1945 import std.format : format; 1946 1947 auto l = new TestLogger(LogLevel.all); 1948 string msg = "Hello Logger World"; 1949 l.log(msg); 1950 int lineNumber = __LINE__ - 1; 1951 assert(l.msg == msg); 1952 assert(l.line == lineNumber); 1953 assert(l.logLevel == LogLevel.all); 1954 1955 l.log(true, msg); 1956 lineNumber = __LINE__ - 1; 1957 assert(l.msg == msg, l.msg); 1958 assert(l.line == lineNumber); 1959 assert(l.logLevel == LogLevel.all); 1960 1961 l.log(false, msg); 1962 assert(l.msg == msg); 1963 assert(l.line == lineNumber, to!string(l.line)); 1964 assert(l.logLevel == LogLevel.all); 1965 1966 msg = "%s Another message"; 1967 l.logf(msg, "Yet"); 1968 lineNumber = __LINE__ - 1; 1969 assert(l.msg == msg.format("Yet")); 1970 assert(l.line == lineNumber); 1971 assert(l.logLevel == LogLevel.all); 1972 1973 l.logf(true, msg, "Yet"); 1974 lineNumber = __LINE__ - 1; 1975 assert(l.msg == msg.format("Yet")); 1976 assert(l.line == lineNumber); 1977 assert(l.logLevel == LogLevel.all); 1978 1979 l.logf(false, msg, "Yet"); 1980 assert(l.msg == msg.format("Yet")); 1981 assert(l.line == lineNumber); 1982 assert(l.logLevel == LogLevel.all); 1983 1984 () @trusted { 1985 assertThrown!Throwable(l.logf(LogLevel.fatal, msg, "Yet")); 1986 } (); 1987 lineNumber = __LINE__ - 2; 1988 assert(l.msg == msg.format("Yet")); 1989 assert(l.line == lineNumber); 1990 assert(l.logLevel == LogLevel.all); 1991 1992 () @trusted { 1993 assertThrown!Throwable(l.logf(LogLevel.fatal, true, msg, "Yet")); 1994 } (); 1995 lineNumber = __LINE__ - 2; 1996 assert(l.msg == msg.format("Yet")); 1997 assert(l.line == lineNumber); 1998 assert(l.logLevel == LogLevel.all); 1999 2000 assertNotThrown(l.logf(LogLevel.fatal, false, msg, "Yet")); 2001 assert(l.msg == msg.format("Yet")); 2002 assert(l.line == lineNumber); 2003 assert(l.logLevel == LogLevel.all); 2004 2005 auto oldunspecificLogger = sharedLog; 2006 2007 assert(oldunspecificLogger.logLevel == LogLevel.all, 2008 to!string(oldunspecificLogger.logLevel)); 2009 2010 assert(l.logLevel == LogLevel.all); 2011 sharedLog = l; 2012 assert(globalLogLevel == LogLevel.all, 2013 to!string(globalLogLevel)); 2014 2015 scope(exit) 2016 { 2017 sharedLog = oldunspecificLogger; 2018 } 2019 2020 assert(sharedLog.logLevel == LogLevel.all); 2021 assert(stdThreadLocalLog.logLevel == LogLevel.all); 2022 assert(globalLogLevel == LogLevel.all); 2023 2024 msg = "Another message"; 2025 log(msg); 2026 lineNumber = __LINE__ - 1; 2027 assert(l.logLevel == LogLevel.all); 2028 assert(l.line == lineNumber, to!string(l.line)); 2029 assert(l.msg == msg, l.msg); 2030 2031 log(true, msg); 2032 lineNumber = __LINE__ - 1; 2033 assert(l.msg == msg); 2034 assert(l.line == lineNumber); 2035 assert(l.logLevel == LogLevel.all); 2036 2037 log(false, msg); 2038 assert(l.msg == msg); 2039 assert(l.line == lineNumber); 2040 assert(l.logLevel == LogLevel.all); 2041 2042 msg = "%s Another message"; 2043 logf(msg, "Yet"); 2044 lineNumber = __LINE__ - 1; 2045 assert(l.msg == msg.format("Yet")); 2046 assert(l.line == lineNumber); 2047 assert(l.logLevel == LogLevel.all); 2048 2049 logf(true, msg, "Yet"); 2050 lineNumber = __LINE__ - 1; 2051 assert(l.msg == msg.format("Yet")); 2052 assert(l.line == lineNumber); 2053 assert(l.logLevel == LogLevel.all); 2054 2055 logf(false, msg, "Yet"); 2056 assert(l.msg == msg.format("Yet")); 2057 assert(l.line == lineNumber); 2058 assert(l.logLevel == LogLevel.all); 2059 2060 msg = "%s Another message"; 2061 () @trusted { 2062 assertThrown!Throwable(logf(LogLevel.fatal, msg, "Yet")); 2063 } (); 2064 lineNumber = __LINE__ - 2; 2065 assert(l.msg == msg.format("Yet")); 2066 assert(l.line == lineNumber); 2067 assert(l.logLevel == LogLevel.all); 2068 2069 () @trusted { 2070 assertThrown!Throwable(logf(LogLevel.fatal, true, msg, "Yet")); 2071 } (); 2072 lineNumber = __LINE__ - 2; 2073 assert(l.msg == msg.format("Yet")); 2074 assert(l.line == lineNumber); 2075 assert(l.logLevel == LogLevel.all); 2076 2077 assertNotThrown(logf(LogLevel.fatal, false, msg, "Yet")); 2078 assert(l.msg == msg.format("Yet")); 2079 assert(l.line == lineNumber); 2080 assert(l.logLevel == LogLevel.all); 2081} 2082 2083@system unittest // default logger 2084{ 2085 import std.file : deleteme, exists, remove; 2086 import std.stdio : File; 2087 import std.string : indexOf; 2088 2089 string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; 2090 FileLogger l = new FileLogger(filename); 2091 auto oldunspecificLogger = sharedLog; 2092 sharedLog = l; 2093 2094 scope(exit) 2095 { 2096 remove(filename); 2097 assert(!exists(filename)); 2098 sharedLog = oldunspecificLogger; 2099 globalLogLevel = LogLevel.all; 2100 } 2101 2102 string notWritten = "this should not be written to file"; 2103 string written = "this should be written to file"; 2104 2105 globalLogLevel = LogLevel.critical; 2106 assert(globalLogLevel == LogLevel.critical); 2107 2108 log(LogLevel.warning, notWritten); 2109 log(LogLevel.critical, written); 2110 2111 l.file.flush(); 2112 l.file.close(); 2113 2114 auto file = File(filename, "r"); 2115 assert(!file.eof); 2116 2117 string readLine = file.readln(); 2118 assert(readLine.indexOf(written) != -1, readLine); 2119 assert(readLine.indexOf(notWritten) == -1, readLine); 2120 file.close(); 2121} 2122 2123@system unittest 2124{ 2125 import std.file : deleteme, remove; 2126 import std.stdio : File; 2127 import std.string : indexOf; 2128 2129 string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; 2130 auto oldunspecificLogger = sharedLog; 2131 2132 scope(exit) 2133 { 2134 remove(filename); 2135 sharedLog = oldunspecificLogger; 2136 globalLogLevel = LogLevel.all; 2137 } 2138 2139 string notWritten = "this should not be written to file"; 2140 string written = "this should be written to file"; 2141 2142 auto l = new FileLogger(filename); 2143 sharedLog = l; 2144 sharedLog.logLevel = LogLevel.critical; 2145 2146 log(LogLevel.error, false, notWritten); 2147 log(LogLevel.critical, true, written); 2148 destroy(l); 2149 2150 auto file = File(filename, "r"); 2151 auto readLine = file.readln(); 2152 assert(!readLine.empty, readLine); 2153 assert(readLine.indexOf(written) != -1); 2154 assert(readLine.indexOf(notWritten) == -1); 2155 file.close(); 2156} 2157 2158@safe unittest 2159{ 2160 import std.conv : to; 2161 2162 auto tl = new TestLogger(LogLevel.all); 2163 int l = __LINE__; 2164 tl.info("a"); 2165 assert(tl.line == l+1); 2166 assert(tl.msg == "a"); 2167 assert(tl.logLevel == LogLevel.all); 2168 assert(globalLogLevel == LogLevel.all); 2169 l = __LINE__; 2170 tl.trace("b"); 2171 assert(tl.msg == "b", tl.msg); 2172 assert(tl.line == l+1, to!string(tl.line)); 2173} 2174 2175// testing possible log conditions 2176@safe unittest 2177{ 2178 import std.conv : to; 2179 import std.format : format; 2180 import std.string : indexOf; 2181 2182 auto oldunspecificLogger = sharedLog; 2183 2184 auto mem = new TestLogger; 2185 mem.fatalHandler = delegate() {}; 2186 sharedLog = mem; 2187 2188 scope(exit) 2189 { 2190 sharedLog = oldunspecificLogger; 2191 globalLogLevel = LogLevel.all; 2192 } 2193 2194 int value = 0; 2195 foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace, 2196 LogLevel.info, LogLevel.warning, LogLevel.error, 2197 LogLevel.critical, LogLevel.fatal, LogLevel.off]) 2198 { 2199 2200 globalLogLevel = gll; 2201 2202 foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace, 2203 LogLevel.info, LogLevel.warning, LogLevel.error, 2204 LogLevel.critical, LogLevel.fatal, LogLevel.off]) 2205 { 2206 2207 mem.logLevel = ll; 2208 2209 foreach (cond; [true, false]) 2210 { 2211 foreach (condValue; [true, false]) 2212 { 2213 foreach (memOrG; [true, false]) 2214 { 2215 foreach (prntf; [true, false]) 2216 { 2217 foreach (ll2; [cast(LogLevel) LogLevel.all, LogLevel.trace, 2218 LogLevel.info, LogLevel.warning, 2219 LogLevel.error, LogLevel.critical, 2220 LogLevel.fatal, LogLevel.off]) 2221 { 2222 foreach (singleMulti; 0 .. 2) 2223 { 2224 int lineCall; 2225 mem.msg = "-1"; 2226 if (memOrG) 2227 { 2228 if (prntf) 2229 { 2230 if (cond) 2231 { 2232 if (singleMulti == 0) 2233 { 2234 mem.logf(ll2, condValue, "%s", 2235 value); 2236 lineCall = __LINE__; 2237 } 2238 else 2239 { 2240 mem.logf(ll2, condValue, 2241 "%d %d", value, value); 2242 lineCall = __LINE__; 2243 } 2244 } 2245 else 2246 { 2247 if (singleMulti == 0) 2248 { 2249 mem.logf(ll2, "%s", value); 2250 lineCall = __LINE__; 2251 } 2252 else 2253 { 2254 mem.logf(ll2, "%d %d", 2255 value, value); 2256 lineCall = __LINE__; 2257 } 2258 } 2259 } 2260 else 2261 { 2262 if (cond) 2263 { 2264 if (singleMulti == 0) 2265 { 2266 mem.log(ll2, condValue, 2267 to!string(value)); 2268 lineCall = __LINE__; 2269 } 2270 else 2271 { 2272 mem.log(ll2, condValue, 2273 to!string(value), value); 2274 lineCall = __LINE__; 2275 } 2276 } 2277 else 2278 { 2279 if (singleMulti == 0) 2280 { 2281 mem.log(ll2, to!string(value)); 2282 lineCall = __LINE__; 2283 } 2284 else 2285 { 2286 mem.log(ll2, 2287 to!string(value), 2288 value); 2289 lineCall = __LINE__; 2290 } 2291 } 2292 } 2293 } 2294 else 2295 { 2296 if (prntf) 2297 { 2298 if (cond) 2299 { 2300 if (singleMulti == 0) 2301 { 2302 logf(ll2, condValue, "%s", 2303 value); 2304 lineCall = __LINE__; 2305 } 2306 else 2307 { 2308 logf(ll2, condValue, 2309 "%s %d", value, value); 2310 lineCall = __LINE__; 2311 } 2312 } 2313 else 2314 { 2315 if (singleMulti == 0) 2316 { 2317 logf(ll2, "%s", value); 2318 lineCall = __LINE__; 2319 } 2320 else 2321 { 2322 logf(ll2, "%s %s", value, 2323 value); 2324 lineCall = __LINE__; 2325 } 2326 } 2327 } 2328 else 2329 { 2330 if (cond) 2331 { 2332 if (singleMulti == 0) 2333 { 2334 log(ll2, condValue, 2335 to!string(value)); 2336 lineCall = __LINE__; 2337 } 2338 else 2339 { 2340 log(ll2, condValue, value, 2341 to!string(value)); 2342 lineCall = __LINE__; 2343 } 2344 } 2345 else 2346 { 2347 if (singleMulti == 0) 2348 { 2349 log(ll2, to!string(value)); 2350 lineCall = __LINE__; 2351 } 2352 else 2353 { 2354 log(ll2, value, 2355 to!string(value)); 2356 lineCall = __LINE__; 2357 } 2358 } 2359 } 2360 } 2361 2362 string valueStr = to!string(value); 2363 ++value; 2364 2365 bool ll2Off = (ll2 != LogLevel.off); 2366 bool gllOff = (gll != LogLevel.off); 2367 bool llOff = (ll != LogLevel.off); 2368 bool condFalse = (cond ? condValue : true); 2369 bool ll2VSgll = (ll2 >= gll); 2370 bool ll2VSll = (ll2 >= ll); 2371 2372 bool shouldLog = ll2Off && gllOff && llOff 2373 && condFalse && ll2VSgll && ll2VSll; 2374 2375 /* 2376 writefln( 2377 "go(%b) ll2o(%b) c(%b) lg(%b) ll(%b) s(%b)" 2378 , gll != LogLevel.off, ll2 != LogLevel.off, 2379 cond ? condValue : true, 2380 ll2 >= gll, ll2 >= ll, shouldLog); 2381 */ 2382 2383 2384 if (shouldLog) 2385 { 2386 assert(mem.msg.indexOf(valueStr) != -1, 2387 format( 2388 "lineCall(%d) ll2Off(%u) gll(%u) ll(%u) ll2(%u) " ~ 2389 "cond(%b) condValue(%b)" ~ 2390 " memOrG(%b) shouldLog(%b) %s == %s" ~ 2391 " %b %b %b %b %b", 2392 lineCall, ll2Off, gll, ll, ll2, cond, 2393 condValue, memOrG, shouldLog, mem.msg, 2394 valueStr, gllOff, llOff, condFalse, 2395 ll2VSgll, ll2VSll 2396 )); 2397 } 2398 else 2399 { 2400 assert(mem.msg.indexOf(valueStr), 2401 format( 2402 "lineCall(%d) ll2Off(%u) gll(%u) ll(%u) ll2(%u) " ~ 2403 "cond(%b) condValue(%b)" ~ 2404 " memOrG(%b) shouldLog(%b) %s == %s" ~ 2405 " %b %b %b %b %b", 2406 lineCall, ll2Off, gll, ll, ll2, cond, 2407 condValue, memOrG, shouldLog, mem.msg, 2408 valueStr, gllOff, llOff, condFalse, 2409 ll2VSgll, ll2VSll 2410 )); 2411 } 2412 } 2413 } 2414 } 2415 } 2416 } 2417 } 2418 } 2419 } 2420} 2421 2422// more testing 2423@safe unittest 2424{ 2425 import std.conv : to; 2426 import std.format : format; 2427 import std.string : indexOf; 2428 2429 auto oldunspecificLogger = sharedLog; 2430 2431 auto mem = new TestLogger; 2432 mem.fatalHandler = delegate() {}; 2433 sharedLog = mem; 2434 2435 scope(exit) 2436 { 2437 sharedLog = oldunspecificLogger; 2438 globalLogLevel = LogLevel.all; 2439 } 2440 2441 int value = 0; 2442 foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace, 2443 LogLevel.info, LogLevel.warning, LogLevel.error, 2444 LogLevel.critical, LogLevel.fatal, LogLevel.off]) 2445 { 2446 2447 globalLogLevel = gll; 2448 2449 foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace, 2450 LogLevel.info, LogLevel.warning, LogLevel.error, 2451 LogLevel.critical, LogLevel.fatal, LogLevel.off]) 2452 { 2453 mem.logLevel = ll; 2454 2455 foreach (tll; [cast(LogLevel) LogLevel.all, LogLevel.trace, 2456 LogLevel.info, LogLevel.warning, LogLevel.error, 2457 LogLevel.critical, LogLevel.fatal, LogLevel.off]) 2458 { 2459 stdThreadLocalLog.logLevel = tll; 2460 2461 foreach (cond; [true, false]) 2462 { 2463 foreach (condValue; [true, false]) 2464 { 2465 foreach (memOrG; [true, false]) 2466 { 2467 foreach (prntf; [true, false]) 2468 { 2469 foreach (singleMulti; 0 .. 2) 2470 { 2471 int lineCall; 2472 mem.msg = "-1"; 2473 if (memOrG) 2474 { 2475 if (prntf) 2476 { 2477 if (cond) 2478 { 2479 if (singleMulti == 0) 2480 { 2481 mem.logf(condValue, "%s", 2482 value); 2483 lineCall = __LINE__; 2484 } 2485 else 2486 { 2487 mem.logf(condValue, 2488 "%d %d", value, value); 2489 lineCall = __LINE__; 2490 } 2491 } 2492 else 2493 { 2494 if (singleMulti == 0) 2495 { 2496 mem.logf("%s", value); 2497 lineCall = __LINE__; 2498 } 2499 else 2500 { 2501 mem.logf("%d %d", 2502 value, value); 2503 lineCall = __LINE__; 2504 } 2505 } 2506 } 2507 else 2508 { 2509 if (cond) 2510 { 2511 if (singleMulti == 0) 2512 { 2513 mem.log(condValue, 2514 to!string(value)); 2515 lineCall = __LINE__; 2516 } 2517 else 2518 { 2519 mem.log(condValue, 2520 to!string(value), value); 2521 lineCall = __LINE__; 2522 } 2523 } 2524 else 2525 { 2526 if (singleMulti == 0) 2527 { 2528 mem.log(to!string(value)); 2529 lineCall = __LINE__; 2530 } 2531 else 2532 { 2533 mem.log(to!string(value), 2534 value); 2535 lineCall = __LINE__; 2536 } 2537 } 2538 } 2539 } 2540 else 2541 { 2542 if (prntf) 2543 { 2544 if (cond) 2545 { 2546 if (singleMulti == 0) 2547 { 2548 logf(condValue, "%s", value); 2549 lineCall = __LINE__; 2550 } 2551 else 2552 { 2553 logf(condValue, "%s %d", value, 2554 value); 2555 lineCall = __LINE__; 2556 } 2557 } 2558 else 2559 { 2560 if (singleMulti == 0) 2561 { 2562 logf("%s", value); 2563 lineCall = __LINE__; 2564 } 2565 else 2566 { 2567 logf("%s %s", value, value); 2568 lineCall = __LINE__; 2569 } 2570 } 2571 } 2572 else 2573 { 2574 if (cond) 2575 { 2576 if (singleMulti == 0) 2577 { 2578 log(condValue, 2579 to!string(value)); 2580 lineCall = __LINE__; 2581 } 2582 else 2583 { 2584 log(condValue, value, 2585 to!string(value)); 2586 lineCall = __LINE__; 2587 } 2588 } 2589 else 2590 { 2591 if (singleMulti == 0) 2592 { 2593 log(to!string(value)); 2594 lineCall = __LINE__; 2595 } 2596 else 2597 { 2598 log(value, to!string(value)); 2599 lineCall = __LINE__; 2600 } 2601 } 2602 } 2603 } 2604 2605 string valueStr = to!string(value); 2606 ++value; 2607 2608 bool gllOff = (gll != LogLevel.off); 2609 bool llOff = (ll != LogLevel.off); 2610 bool tllOff = (tll != LogLevel.off); 2611 bool llVSgll = (ll >= gll); 2612 bool tllVSll = 2613 (stdThreadLocalLog.logLevel >= ll); 2614 bool condFalse = (cond ? condValue : true); 2615 2616 bool shouldLog = gllOff && llOff 2617 && (memOrG ? true : tllOff) 2618 && (memOrG ? 2619 (ll >= gll) : 2620 (tll >= gll && tll >= ll)) 2621 && condFalse; 2622 2623 if (shouldLog) 2624 { 2625 assert(mem.msg.indexOf(valueStr) != -1, 2626 format("\ngll(%s) ll(%s) tll(%s) " ~ 2627 "cond(%s) condValue(%s) " ~ 2628 "memOrG(%s) prntf(%s) " ~ 2629 "singleMulti(%s)", 2630 gll, ll, tll, cond, condValue, 2631 memOrG, prntf, singleMulti) 2632 ~ format(" gllOff(%s) llOff(%s) " ~ 2633 "llVSgll(%s) tllVSll(%s) " ~ 2634 "tllOff(%s) condFalse(%s) " 2635 ~ "shoudlLog(%s)", 2636 gll != LogLevel.off, 2637 ll != LogLevel.off, llVSgll, 2638 tllVSll, tllOff, condFalse, 2639 shouldLog) 2640 ~ format("msg(%s) line(%s) " ~ 2641 "lineCall(%s) valueStr(%s)", 2642 mem.msg, mem.line, lineCall, 2643 valueStr) 2644 ); 2645 } 2646 else 2647 { 2648 assert(mem.msg.indexOf(valueStr) == -1, 2649 format("\ngll(%s) ll(%s) tll(%s) " ~ 2650 "cond(%s) condValue(%s) " ~ 2651 "memOrG(%s) prntf(%s) " ~ 2652 "singleMulti(%s)", 2653 gll, ll, tll, cond, condValue, 2654 memOrG, prntf, singleMulti) 2655 ~ format(" gllOff(%s) llOff(%s) " ~ 2656 "llVSgll(%s) tllVSll(%s) " ~ 2657 "tllOff(%s) condFalse(%s) " 2658 ~ "shoudlLog(%s)", 2659 gll != LogLevel.off, 2660 ll != LogLevel.off, llVSgll, 2661 tllVSll, tllOff, condFalse, 2662 shouldLog) 2663 ~ format("msg(%s) line(%s) " ~ 2664 "lineCall(%s) valueStr(%s)", 2665 mem.msg, mem.line, lineCall, 2666 valueStr) 2667 ); 2668 } 2669 } 2670 } 2671 } 2672 } 2673 } 2674 } 2675 } 2676 } 2677} 2678 2679// testing more possible log conditions 2680@safe unittest 2681{ 2682 bool fatalLog; 2683 auto mem = new TestLogger; 2684 mem.fatalHandler = delegate() { fatalLog = true; }; 2685 auto oldunspecificLogger = sharedLog; 2686 2687 stdThreadLocalLog.logLevel = LogLevel.all; 2688 2689 sharedLog = mem; 2690 scope(exit) 2691 { 2692 sharedLog = oldunspecificLogger; 2693 globalLogLevel = LogLevel.all; 2694 } 2695 2696 foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace, 2697 LogLevel.info, LogLevel.warning, LogLevel.error, 2698 LogLevel.critical, LogLevel.fatal, LogLevel.off]) 2699 { 2700 2701 globalLogLevel = gll; 2702 2703 foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace, 2704 LogLevel.info, LogLevel.warning, LogLevel.error, 2705 LogLevel.critical, LogLevel.fatal, LogLevel.off]) 2706 { 2707 mem.logLevel = ll; 2708 2709 foreach (tll; [cast(LogLevel) LogLevel.all, LogLevel.trace, 2710 LogLevel.info, LogLevel.warning, LogLevel.error, 2711 LogLevel.critical, LogLevel.fatal, LogLevel.off]) 2712 { 2713 stdThreadLocalLog.logLevel = tll; 2714 2715 foreach (cond; [true, false]) 2716 { 2717 assert(globalLogLevel == gll); 2718 assert(mem.logLevel == ll); 2719 2720 bool gllVSll = LogLevel.trace >= globalLogLevel; 2721 bool llVSgll = ll >= globalLogLevel; 2722 bool lVSll = LogLevel.trace >= ll; 2723 bool gllOff = globalLogLevel != LogLevel.off; 2724 bool llOff = mem.logLevel != LogLevel.off; 2725 bool tllOff = stdThreadLocalLog.logLevel != LogLevel.off; 2726 bool tllVSll = tll >= ll; 2727 bool tllVSgll = tll >= gll; 2728 bool lVSgll = LogLevel.trace >= tll; 2729 2730 bool test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; 2731 bool testG = gllOff && llOff && tllOff && lVSgll && tllVSll && tllVSgll && cond; 2732 2733 mem.line = -1; 2734 /* 2735 writefln("gll(%3u) ll(%3u) cond(%b) test(%b)", 2736 gll, ll, cond, test); 2737 writefln("%b %b %b %b %b %b test2(%b)", llVSgll, gllVSll, lVSll, 2738 gllOff, llOff, cond, test2); 2739 */ 2740 2741 mem.trace(__LINE__); int line = __LINE__; 2742 assert(test ? mem.line == line : true); line = -1; 2743 2744 trace(__LINE__); line = __LINE__; 2745 assert(testG ? mem.line == line : true); line = -1; 2746 2747 mem.trace(cond, __LINE__); line = __LINE__; 2748 assert(test ? mem.line == line : true); line = -1; 2749 2750 trace(cond, __LINE__); line = __LINE__; 2751 assert(testG ? mem.line == line : true); line = -1; 2752 2753 mem.tracef("%d", __LINE__); line = __LINE__; 2754 assert(test ? mem.line == line : true); line = -1; 2755 2756 tracef("%d", __LINE__); line = __LINE__; 2757 assert(testG ? mem.line == line : true); line = -1; 2758 2759 mem.tracef(cond, "%d", __LINE__); line = __LINE__; 2760 assert(test ? mem.line == line : true); line = -1; 2761 2762 tracef(cond, "%d", __LINE__); line = __LINE__; 2763 assert(testG ? mem.line == line : true); line = -1; 2764 2765 llVSgll = ll >= globalLogLevel; 2766 lVSll = LogLevel.info >= ll; 2767 lVSgll = LogLevel.info >= tll; 2768 test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; 2769 testG = gllOff && llOff && tllOff && tllVSll && tllVSgll && 2770 lVSgll && cond; 2771 2772 mem.info(__LINE__); line = __LINE__; 2773 assert(test ? mem.line == line : true); line = -1; 2774 2775 info(__LINE__); line = __LINE__; 2776 assert(testG ? mem.line == line : true); line = -1; 2777 2778 mem.info(cond, __LINE__); line = __LINE__; 2779 assert(test ? mem.line == line : true); line = -1; 2780 2781 info(cond, __LINE__); line = __LINE__; 2782 assert(testG ? mem.line == line : true); line = -1; 2783 2784 mem.infof("%d", __LINE__); line = __LINE__; 2785 assert(test ? mem.line == line : true); line = -1; 2786 2787 infof("%d", __LINE__); line = __LINE__; 2788 assert(testG ? mem.line == line : true); line = -1; 2789 2790 mem.infof(cond, "%d", __LINE__); line = __LINE__; 2791 assert(test ? mem.line == line : true); line = -1; 2792 2793 infof(cond, "%d", __LINE__); line = __LINE__; 2794 assert(testG ? mem.line == line : true); line = -1; 2795 2796 llVSgll = ll >= globalLogLevel; 2797 lVSll = LogLevel.warning >= ll; 2798 lVSgll = LogLevel.warning >= tll; 2799 test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; 2800 testG = gllOff && llOff && tllOff && tllVSll && tllVSgll && 2801 lVSgll && cond; 2802 2803 mem.warning(__LINE__); line = __LINE__; 2804 assert(test ? mem.line == line : true); line = -1; 2805 2806 warning(__LINE__); line = __LINE__; 2807 assert(testG ? mem.line == line : true); line = -1; 2808 2809 mem.warning(cond, __LINE__); line = __LINE__; 2810 assert(test ? mem.line == line : true); line = -1; 2811 2812 warning(cond, __LINE__); line = __LINE__; 2813 assert(testG ? mem.line == line : true); line = -1; 2814 2815 mem.warningf("%d", __LINE__); line = __LINE__; 2816 assert(test ? mem.line == line : true); line = -1; 2817 2818 warningf("%d", __LINE__); line = __LINE__; 2819 assert(testG ? mem.line == line : true); line = -1; 2820 2821 mem.warningf(cond, "%d", __LINE__); line = __LINE__; 2822 assert(test ? mem.line == line : true); line = -1; 2823 2824 warningf(cond, "%d", __LINE__); line = __LINE__; 2825 assert(testG ? mem.line == line : true); line = -1; 2826 2827 llVSgll = ll >= globalLogLevel; 2828 lVSll = LogLevel.critical >= ll; 2829 lVSgll = LogLevel.critical >= tll; 2830 test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; 2831 testG = gllOff && llOff && tllOff && tllVSll && tllVSgll && 2832 lVSgll && cond; 2833 2834 mem.critical(__LINE__); line = __LINE__; 2835 assert(test ? mem.line == line : true); line = -1; 2836 2837 critical(__LINE__); line = __LINE__; 2838 assert(testG ? mem.line == line : true); line = -1; 2839 2840 mem.critical(cond, __LINE__); line = __LINE__; 2841 assert(test ? mem.line == line : true); line = -1; 2842 2843 critical(cond, __LINE__); line = __LINE__; 2844 assert(testG ? mem.line == line : true); line = -1; 2845 2846 mem.criticalf("%d", __LINE__); line = __LINE__; 2847 assert(test ? mem.line == line : true); line = -1; 2848 2849 criticalf("%d", __LINE__); line = __LINE__; 2850 assert(testG ? mem.line == line : true); line = -1; 2851 2852 mem.criticalf(cond, "%d", __LINE__); line = __LINE__; 2853 assert(test ? mem.line == line : true); line = -1; 2854 2855 criticalf(cond, "%d", __LINE__); line = __LINE__; 2856 assert(testG ? mem.line == line : true); line = -1; 2857 2858 llVSgll = ll >= globalLogLevel; 2859 lVSll = LogLevel.fatal >= ll; 2860 lVSgll = LogLevel.fatal >= tll; 2861 test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond; 2862 testG = gllOff && llOff && tllOff && tllVSll && tllVSgll && 2863 lVSgll && cond; 2864 2865 mem.fatal(__LINE__); line = __LINE__; 2866 assert(test ? mem.line == line : true); line = -1; 2867 assert(test ? fatalLog : true); 2868 fatalLog = false; 2869 2870 fatal(__LINE__); line = __LINE__; 2871 assert(testG ? mem.line == line : true); line = -1; 2872 assert(testG ? fatalLog : true); 2873 fatalLog = false; 2874 2875 mem.fatal(cond, __LINE__); line = __LINE__; 2876 assert(test ? mem.line == line : true); line = -1; 2877 assert(test ? fatalLog : true); 2878 fatalLog = false; 2879 2880 fatal(cond, __LINE__); line = __LINE__; 2881 assert(testG ? mem.line == line : true); line = -1; 2882 assert(testG ? fatalLog : true); 2883 fatalLog = false; 2884 2885 mem.fatalf("%d", __LINE__); line = __LINE__; 2886 assert(test ? mem.line == line : true); line = -1; 2887 assert(test ? fatalLog : true); 2888 fatalLog = false; 2889 2890 fatalf("%d", __LINE__); line = __LINE__; 2891 assert(testG ? mem.line == line : true); line = -1; 2892 assert(testG ? fatalLog : true); 2893 fatalLog = false; 2894 2895 mem.fatalf(cond, "%d", __LINE__); line = __LINE__; 2896 assert(test ? mem.line == line : true); line = -1; 2897 assert(test ? fatalLog : true); 2898 fatalLog = false; 2899 2900 fatalf(cond, "%d", __LINE__); line = __LINE__; 2901 assert(testG ? mem.line == line : true); line = -1; 2902 assert(testG ? fatalLog : true); 2903 fatalLog = false; 2904 } 2905 } 2906 } 2907 } 2908} 2909 2910// Issue #5 2911@safe unittest 2912{ 2913 import std.string : indexOf; 2914 2915 auto oldunspecificLogger = sharedLog; 2916 2917 scope(exit) 2918 { 2919 sharedLog = oldunspecificLogger; 2920 globalLogLevel = LogLevel.all; 2921 } 2922 2923 auto tl = new TestLogger(LogLevel.info); 2924 sharedLog = tl; 2925 2926 trace("trace"); 2927 assert(tl.msg.indexOf("trace") == -1); 2928} 2929 2930// Issue #5 2931@safe unittest 2932{ 2933 import std.experimental.logger.multilogger : MultiLogger; 2934 import std.string : indexOf; 2935 2936 stdThreadLocalLog.logLevel = LogLevel.all; 2937 2938 auto oldunspecificLogger = sharedLog; 2939 2940 scope(exit) 2941 { 2942 sharedLog = oldunspecificLogger; 2943 globalLogLevel = LogLevel.all; 2944 } 2945 2946 auto logger = new MultiLogger(LogLevel.error); 2947 2948 auto tl = new TestLogger(LogLevel.info); 2949 logger.insertLogger("required", tl); 2950 sharedLog = logger; 2951 2952 trace("trace"); 2953 assert(tl.msg.indexOf("trace") == -1); 2954 info("info"); 2955 assert(tl.msg.indexOf("info") == -1); 2956 error("error"); 2957 assert(tl.msg.indexOf("error") == 0); 2958} 2959 2960@system unittest 2961{ 2962 import std.exception : assertThrown; 2963 auto tl = new TestLogger(); 2964 assertThrown!Throwable(tl.fatal("fatal")); 2965} 2966 2967// log objects with non-safe toString 2968@system unittest 2969{ 2970 struct Test 2971 { 2972 string toString() const @system 2973 { 2974 return "test"; 2975 } 2976 } 2977 2978 auto tl = new TestLogger(); 2979 tl.info(Test.init); 2980 assert(tl.msg == "test"); 2981} 2982 2983// Workaround for atomics not allowed in @safe code 2984private auto trustedLoad(T)(ref shared T value) @trusted 2985{ 2986 import core.atomic : atomicLoad, MemoryOrder; 2987 return atomicLoad!(MemoryOrder.acq)(value); 2988} 2989 2990// ditto 2991private void trustedStore(T)(ref shared T dst, ref T src) @trusted 2992{ 2993 import core.atomic : atomicStore, MemoryOrder; 2994 atomicStore!(MemoryOrder.rel)(dst, src); 2995} 2996 2997// check that thread-local logging does not propagate 2998// to shared logger 2999@system unittest 3000{ 3001 import core.atomic, core.thread, std.concurrency; 3002 3003 static shared logged_count = 0; 3004 3005 class TestLog : Logger 3006 { 3007 Tid tid; 3008 3009 this() 3010 { 3011 super (LogLevel.trace); 3012 this.tid = thisTid; 3013 } 3014 3015 override void writeLogMsg(ref LogEntry payload) @trusted 3016 { 3017 assert(thisTid == this.tid); 3018 atomicOp!"+="(logged_count, 1); 3019 } 3020 } 3021 3022 class IgnoredLog : Logger 3023 { 3024 this() 3025 { 3026 super (LogLevel.trace); 3027 } 3028 3029 override void writeLogMsg(ref LogEntry payload) @trusted 3030 { 3031 assert(false); 3032 } 3033 } 3034 3035 auto oldSharedLog = sharedLog; 3036 scope(exit) 3037 { 3038 sharedLog = oldSharedLog; 3039 } 3040 3041 sharedLog = new IgnoredLog; 3042 Thread[] spawned; 3043 3044 foreach (i; 0 .. 4) 3045 { 3046 spawned ~= new Thread({ 3047 stdThreadLocalLog = new TestLog; 3048 trace("zzzzzzzzzz"); 3049 }); 3050 spawned[$-1].start(); 3051 } 3052 3053 foreach (t; spawned) 3054 t.join(); 3055 3056 assert(atomicOp!"=="(logged_count, 4)); 3057} 3058 3059@safe unittest 3060{ 3061 auto dl = cast(FileLogger) sharedLog; 3062 assert(dl !is null); 3063 assert(dl.logLevel == LogLevel.all); 3064 assert(globalLogLevel == LogLevel.all); 3065 3066 auto tl = cast(StdForwardLogger) stdThreadLocalLog; 3067 assert(tl !is null); 3068 stdThreadLocalLog.logLevel = LogLevel.all; 3069} 3070 3071// Issue 14940 3072@safe unittest 3073{ 3074 import std.typecons : Nullable; 3075 3076 Nullable!int a = 1; 3077 auto l = new TestLogger(); 3078 l.infof("log: %s", a); 3079 assert(l.msg == "log: 1"); 3080} 3081 3082// Ensure @system toString methods work 3083@system unittest 3084{ 3085 enum SystemToStringMsg = "SystemToString"; 3086 static struct SystemToString 3087 { 3088 string toString() @system 3089 { 3090 return SystemToStringMsg; 3091 } 3092 } 3093 3094 auto tl = new TestLogger(); 3095 3096 SystemToString sts; 3097 tl.logf("%s", sts); 3098 assert(tl.msg == SystemToStringMsg); 3099} 3100 3101// Issue 17328 3102@safe unittest 3103{ 3104 import std.format : format; 3105 3106 ubyte[] data = [0]; 3107 string s = format("%(%02x%)", data); // format 00 3108 assert(s == "00"); 3109 3110 auto tl = new TestLogger(); 3111 3112 tl.infof("%(%02x%)", data); // infof 000 3113 3114 size_t i; 3115 string fs = tl.msg; 3116 for (; i < s.length; ++i) 3117 { 3118 assert(s[s.length - 1 - i] == fs[fs.length - 1 - i], fs); 3119 } 3120 assert(fs.length == 2); 3121} 3122 3123// Issue 15954 3124@safe unittest 3125{ 3126 import std.conv : to; 3127 auto tl = new TestLogger(); 3128 tl.log("123456789".to!wstring); 3129 assert(tl.msg == "123456789"); 3130} 3131 3132// Issue 16256 3133@safe unittest 3134{ 3135 import std.conv : to; 3136 auto tl = new TestLogger(); 3137 tl.log("123456789"d); 3138 assert(tl.msg == "123456789"); 3139} 3140 3141// Issue 15517 3142@system unittest 3143{ 3144 import std.file : exists, remove; 3145 import std.stdio : File; 3146 import std.string : indexOf; 3147 3148 string fn = "logfile.log"; 3149 if (exists(fn)) 3150 { 3151 remove(fn); 3152 } 3153 3154 auto oldShared = sharedLog; 3155 scope(exit) 3156 { 3157 sharedLog = oldShared; 3158 if (exists(fn)) 3159 { 3160 remove(fn); 3161 } 3162 } 3163 3164 auto ts = [ "Test log 1", "Test log 2", "Test log 3"]; 3165 3166 auto fl = new FileLogger(fn); 3167 sharedLog = fl; 3168 assert(exists(fn)); 3169 3170 foreach (t; ts) 3171 { 3172 log(t); 3173 } 3174 3175 auto f = File(fn); 3176 auto l = f.byLine(); 3177 assert(!l.empty); 3178 size_t idx; 3179 foreach (it; l) 3180 { 3181 assert(it.indexOf(ts[idx]) != -1, it); 3182 ++idx; 3183 } 3184 3185 assert(exists(fn)); 3186 fl.file.close(); 3187} 3188