The OpenD Programming Language

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 }