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