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