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     import core.lifetime : emplace;
1441     import std.stdio : stderr;
1442 
1443     __gshared align(__traits(classInstanceAlignment, FileLogger))
1444         void[__traits(classInstanceSize, FileLogger)] _buffer = void;
1445 
1446     import std.concurrency : initOnce;
1447     initOnce!stdSharedDefaultLogger({
1448         auto buffer = cast(ubyte[]) _buffer;
1449         return cast(shared) emplace!(FileLogger)(buffer, stderr, LogLevel.info);
1450     }());
1451 
1452     return atomicLoad(stdSharedDefaultLogger);
1453 }
1454 
1455 /** This property sets and gets the default `Logger`. Unless set to another
1456 logger by the user, the default logger's log level is LogLevel.info.
1457 
1458 Example:
1459 -------------
1460 sharedLog = new FileLogger(yourFile);
1461 -------------
1462 The example sets a new `FileLogger` as new `sharedLog`.
1463 
1464 If at some point you want to use the original default logger again, you can
1465 use $(D sharedLog = null;). This will put back the original.
1466 
1467 Note:
1468 While getting and setting `sharedLog` is thread-safe, it has to be considered
1469 that the returned reference is only a current snapshot and in the following
1470 code, you must make sure no other thread reassigns to it between reading and
1471 writing `sharedLog`.
1472 
1473 `sharedLog` is only thread-safe if the used `Logger` is thread-safe.
1474 The default `Logger` is thread-safe.
1475 -------------
1476 if (sharedLog !is myLogger)
1477     sharedLog = new myLogger;
1478 -------------
1479 */
1480 @property shared(Logger) sharedLog() @safe
1481 {
1482     // If we have set up our own logger use that
1483     if (auto logger = atomicLoad!(MemoryOrder.seq)(stdSharedLogger))
1484     {
1485         return atomicLoad(logger);
1486     }
1487     else
1488     {
1489         // Otherwise resort to the default logger
1490         return defaultSharedLoggerImpl;
1491     }
1492 }
1493 
1494 /// Ditto
1495 @property void sharedLog(shared(Logger) logger) @safe
1496 {
1497     atomicStore!(MemoryOrder.seq)(stdSharedLogger, atomicLoad(logger));
1498 }
1499 
1500 /** This methods get and set the global `LogLevel`.
1501 
1502 Every log message with a `LogLevel` lower as the global `LogLevel`
1503 will be discarded before it reaches `writeLogMessage` method of any
1504 `Logger`.
1505 */
1506 /* Implementation note:
1507 For any public logging call, the global log level shall only be queried once on
1508 entry. Otherwise when another threads changes the level, we would work with
1509 different levels at different spots in the code.
1510 */
1511 @property LogLevel globalLogLevel() @safe @nogc
1512 {
1513     return trustedLoad(stdLoggerGlobalLogLevel);
1514 }
1515 
1516 /// Ditto
1517 @property void globalLogLevel(LogLevel ll) @safe
1518 {
1519     trustedStore(stdLoggerGlobalLogLevel, ll);
1520 }
1521 
1522 // Thread Local
1523 
1524 /** The `StdForwardLogger` will always forward anything to the sharedLog.
1525 
1526 The `StdForwardLogger` will not throw if data is logged with $(D
1527 LogLevel.fatal).
1528 */
1529 class StdForwardLogger : Logger
1530 {
1531     /** The default constructor for the `StdForwardLogger`.
1532 
1533     Params:
1534       lv = The `LogLevel` for the `MultiLogger`. By default the $(D
1535           LogLevel) is `all`.
1536     */
1537     this(const LogLevel lv = LogLevel.all) @safe
1538     {
1539         super(lv);
1540         this.fatalHandler = delegate() {};
1541     }
1542 
1543     override protected void writeLogMsg(ref LogEntry payload) @trusted
1544     {
1545         synchronized (sharedLog.mutex)
1546         {
1547             (cast() sharedLog).forwardMsg(payload);
1548         }
1549     }
1550 }
1551 
1552 ///
1553 @safe unittest
1554 {
1555     auto nl1 = new StdForwardLogger(LogLevel.all);
1556 }
1557 
1558 @safe unittest
1559 {
1560     import core.thread : Thread, msecs;
1561 
1562     static class RaceLogger : Logger
1563     {
1564         int value;
1565         this() @safe shared
1566         {
1567             super(LogLevel.init);
1568         }
1569         override void writeLogMsg(ref LogEntry payload) @safe
1570         {
1571             import core.thread : Thread, msecs;
1572             if (payload.msg == "foo")
1573             {
1574                 value = 42;
1575                 () @trusted { Thread.sleep(100.msecs); }();
1576                 assert(value == 42, "Another thread changed the value");
1577             }
1578             else
1579             {
1580                 () @trusted { Thread.sleep(50.msecs); } ();
1581                 value = 13;
1582             }
1583         }
1584     }
1585 
1586     auto oldSharedLog = sharedLog;
1587 
1588     sharedLog = new shared RaceLogger;
1589     scope(exit)
1590     {
1591         sharedLog = oldSharedLog;
1592     }
1593     Thread toWaitFor;
1594     () @trusted { toWaitFor = new Thread(() { log("foo"); }).start(); }();
1595     log("bar");
1596 
1597     () @trusted
1598     {
1599         toWaitFor.join();
1600     }();
1601 }
1602 
1603 /** This `LogLevel` is unqiue to every thread.
1604 
1605 The thread local `Logger` will use this `LogLevel` to filter log calls
1606 every same way as presented earlier.
1607 */
1608 //public LogLevel threadLogLevel = LogLevel.all;
1609 private Logger stdLoggerThreadLogger;
1610 private Logger stdLoggerDefaultThreadLogger;
1611 
1612 /* This method returns the thread local default Logger.
1613 */
1614 private @property Logger stdThreadLocalLogImpl() @trusted
1615 {
1616     import core.lifetime : emplace;
1617 
1618     static align(__traits(classInstanceAlignment, StdForwardLogger))
1619         void[__traits(classInstanceSize, StdForwardLogger)] buffer;
1620 
1621     if (stdLoggerDefaultThreadLogger is null)
1622     {
1623         stdLoggerDefaultThreadLogger = emplace!StdForwardLogger(buffer, LogLevel.all);
1624     }
1625     return stdLoggerDefaultThreadLogger;
1626 }
1627 
1628 /** This function returns a thread unique `Logger`, that by default
1629 propagates all data logged to it to the `sharedLog`.
1630 
1631 These properties can be used to set and get this `Logger`. Every
1632 modification to this `Logger` will only be visible in the thread the
1633 modification has been done from.
1634 
1635 This `Logger` is called by the free standing log functions. This allows to
1636 create thread local redirections and still use the free standing log
1637 functions.
1638 */
1639 @property Logger stdThreadLocalLog() @safe
1640 {
1641     // If we have set up our own logger use that
1642     if (auto logger = stdLoggerThreadLogger)
1643         return logger;
1644     else
1645         // Otherwise resort to the default logger
1646         return stdThreadLocalLogImpl;
1647 }
1648 
1649 /// Ditto
1650 @property void stdThreadLocalLog(Logger logger) @safe
1651 {
1652     stdLoggerThreadLogger = logger;
1653 }
1654 
1655 /// Ditto
1656 @system unittest
1657 {
1658     import std.logger.filelogger : FileLogger;
1659     import std.file : deleteme, remove;
1660     Logger l = stdThreadLocalLog;
1661     stdThreadLocalLog = new FileLogger(deleteme ~ "-someFile.log");
1662     scope(exit) remove(deleteme ~ "-someFile.log");
1663 
1664     auto tempLog = stdThreadLocalLog;
1665     stdThreadLocalLog = l;
1666     destroy(tempLog);
1667 }
1668 
1669 @safe unittest
1670 {
1671     LogLevel ll = globalLogLevel;
1672     globalLogLevel = LogLevel.fatal;
1673     assert(globalLogLevel == LogLevel.fatal);
1674     globalLogLevel = ll;
1675 }
1676 
1677 package class TestLogger : Logger
1678 {
1679     int line = -1;
1680     string file = null;
1681     string func = null;
1682     string prettyFunc = null;
1683     string msg = null;
1684     LogLevel lvl;
1685 
1686     this(const LogLevel lv = LogLevel.all) @safe
1687     {
1688         super(lv);
1689     }
1690 
1691     override protected void writeLogMsg(ref LogEntry payload) @safe
1692     {
1693         this.line = payload.line;
1694         this.file = payload.file;
1695         this.func = payload.funcName;
1696         this.prettyFunc = payload.prettyFuncName;
1697         this.lvl = payload.logLevel;
1698         this.msg = payload.msg;
1699     }
1700 }
1701 
1702 version (StdUnittest) private void testFuncNames(Logger logger) @safe
1703 {
1704     string s = "I'm here";
1705     logger.log(s);
1706 }
1707 
1708 @safe unittest
1709 {
1710     auto tl1 = new TestLogger();
1711     testFuncNames(tl1);
1712     assert(tl1.func == "std.logger.core.testFuncNames", tl1.func);
1713     assert(tl1.prettyFunc ==
1714         "void std.logger.core.testFuncNames(Logger logger) @safe",
1715         tl1.prettyFunc);
1716     assert(tl1.msg == "I'm here", tl1.msg);
1717 }
1718 
1719 @safe unittest
1720 {
1721     auto tl1 = new TestLogger(LogLevel.all);
1722     tl1.log();
1723     assert(tl1.line == __LINE__ - 1);
1724     tl1.log(true);
1725     assert(tl1.line == __LINE__ - 1);
1726     tl1.log(false);
1727     assert(tl1.line == __LINE__ - 3);
1728     tl1.log(LogLevel.info);
1729     assert(tl1.line == __LINE__ - 1);
1730     tl1.log(LogLevel.off);
1731     assert(tl1.line == __LINE__ - 3);
1732     tl1.log(LogLevel.info, true);
1733     assert(tl1.line == __LINE__ - 1);
1734     tl1.log(LogLevel.info, false);
1735     assert(tl1.line == __LINE__ - 3);
1736 
1737     auto oldunspecificLogger = sharedLog;
1738     scope(exit) {
1739         sharedLog = atomicLoad(oldunspecificLogger);
1740     }
1741 
1742     () @trusted {
1743         sharedLog = cast(shared) tl1;
1744     }();
1745 
1746     log();
1747     assert(tl1.line == __LINE__ - 1);
1748 
1749     log(LogLevel.info);
1750     assert(tl1.line == __LINE__ - 1);
1751 
1752     log(true);
1753     assert(tl1.line == __LINE__ - 1);
1754 
1755     log(LogLevel.warning, true);
1756     assert(tl1.line == __LINE__ - 1);
1757 
1758     trace();
1759     assert(tl1.line == __LINE__ - 1);
1760 }
1761 
1762 @safe unittest
1763 {
1764     import std.logger.multilogger : MultiLogger;
1765 
1766     auto tl1 = new TestLogger;
1767     auto tl2 = new TestLogger;
1768 
1769     auto ml = new MultiLogger();
1770     ml.insertLogger("one", tl1);
1771     ml.insertLogger("two", tl2);
1772 
1773     string msg = "Hello Logger World";
1774     ml.log(msg);
1775     int lineNumber = __LINE__ - 1;
1776     assert(tl1.msg == msg);
1777     assert(tl1.line == lineNumber);
1778     assert(tl2.msg == msg);
1779     assert(tl2.line == lineNumber);
1780 
1781     ml.removeLogger("one");
1782     ml.removeLogger("two");
1783     auto n = ml.removeLogger("one");
1784     assert(n is null);
1785 }
1786 
1787 @safe unittest
1788 {
1789     bool errorThrown = false;
1790     auto tl = new TestLogger;
1791     auto dele = delegate() {
1792         errorThrown = true;
1793     };
1794     tl.fatalHandler = dele;
1795     tl.fatal();
1796     assert(errorThrown);
1797 }
1798 
1799 @safe unittest
1800 {
1801     import std.conv : to;
1802     import std.exception : assertThrown, assertNotThrown;
1803     import std.format : format;
1804 
1805     auto l = new TestLogger(LogLevel.all);
1806     string msg = "Hello Logger World";
1807     l.log(msg);
1808     int lineNumber = __LINE__ - 1;
1809     assert(l.msg == msg);
1810     assert(l.line == lineNumber);
1811     assert(l.logLevel == LogLevel.all);
1812 
1813     l.log(true, msg);
1814     lineNumber = __LINE__ - 1;
1815     assert(l.msg == msg, l.msg);
1816     assert(l.line == lineNumber);
1817     assert(l.logLevel == LogLevel.all);
1818 
1819     l.log(false, msg);
1820     assert(l.msg == msg);
1821     assert(l.line == lineNumber, to!string(l.line));
1822     assert(l.logLevel == LogLevel.all);
1823 
1824     msg = "%s Another message";
1825     l.logf(msg, "Yet");
1826     lineNumber = __LINE__ - 1;
1827     assert(l.msg == msg.format("Yet"));
1828     assert(l.line == lineNumber);
1829     assert(l.logLevel == LogLevel.all);
1830 
1831     l.logf(true, msg, "Yet");
1832     lineNumber = __LINE__ - 1;
1833     assert(l.msg == msg.format("Yet"));
1834     assert(l.line == lineNumber);
1835     assert(l.logLevel == LogLevel.all);
1836 
1837     l.logf(false, msg, "Yet");
1838     assert(l.msg == msg.format("Yet"));
1839     assert(l.line == lineNumber);
1840     assert(l.logLevel == LogLevel.all);
1841 
1842     () @trusted {
1843         assertThrown!Throwable(l.logf(LogLevel.fatal, msg, "Yet"));
1844     } ();
1845     lineNumber = __LINE__ - 2;
1846     assert(l.msg == msg.format("Yet"));
1847     assert(l.line == lineNumber);
1848     assert(l.logLevel == LogLevel.all);
1849 
1850     () @trusted {
1851         assertThrown!Throwable(l.logf(LogLevel.fatal, true, msg, "Yet"));
1852     } ();
1853     lineNumber = __LINE__ - 2;
1854     assert(l.msg == msg.format("Yet"));
1855     assert(l.line == lineNumber);
1856     assert(l.logLevel == LogLevel.all);
1857 
1858     assertNotThrown(l.logf(LogLevel.fatal, false, msg, "Yet"));
1859     assert(l.msg == msg.format("Yet"));
1860     assert(l.line == lineNumber);
1861     assert(l.logLevel == LogLevel.all);
1862 
1863     Logger oldunspecificLogger;
1864     () @trusted {
1865         oldunspecificLogger = cast() sharedLog;
1866     }();
1867 
1868     assert(oldunspecificLogger.logLevel == LogLevel.info,
1869          to!string(oldunspecificLogger.logLevel));
1870 
1871     assert(l.logLevel == LogLevel.all);
1872 
1873     () @trusted {
1874         sharedLog = cast(shared) l;
1875     }();
1876 
1877     assert(globalLogLevel == LogLevel.all,
1878             to!string(globalLogLevel));
1879 
1880     scope(exit)
1881     {
1882         () @trusted {
1883             sharedLog = atomicLoad(cast(shared) oldunspecificLogger);
1884         }();
1885     }
1886 
1887     () @trusted {
1888         assert((cast() sharedLog).logLevel == LogLevel.all);
1889     }();
1890 
1891     assert(stdThreadLocalLog.logLevel == LogLevel.all);
1892     assert(globalLogLevel == LogLevel.all);
1893 
1894     msg = "Another message";
1895     log(msg);
1896     lineNumber = __LINE__ - 1;
1897     assert(l.logLevel == LogLevel.all);
1898     assert(l.line == lineNumber, to!string(l.line));
1899     assert(l.msg == msg, l.msg);
1900 
1901     log(true, msg);
1902     lineNumber = __LINE__ - 1;
1903     assert(l.msg == msg);
1904     assert(l.line == lineNumber);
1905     assert(l.logLevel == LogLevel.all);
1906 
1907     log(false, msg);
1908     assert(l.msg == msg);
1909     assert(l.line == lineNumber);
1910     assert(l.logLevel == LogLevel.all);
1911 
1912     msg = "%s Another message";
1913     logf(msg, "Yet");
1914     lineNumber = __LINE__ - 1;
1915     assert(l.msg == msg.format("Yet"));
1916     assert(l.line == lineNumber);
1917     assert(l.logLevel == LogLevel.all);
1918 
1919     logf(true, msg, "Yet");
1920     lineNumber = __LINE__ - 1;
1921     assert(l.msg == msg.format("Yet"));
1922     assert(l.line == lineNumber);
1923     assert(l.logLevel == LogLevel.all);
1924 
1925     logf(false, msg, "Yet");
1926     assert(l.msg == msg.format("Yet"));
1927     assert(l.line == lineNumber);
1928     assert(l.logLevel == LogLevel.all);
1929 
1930     msg = "%s Another message";
1931     () @trusted {
1932         assertThrown!Throwable(logf(LogLevel.fatal, msg, "Yet"));
1933     } ();
1934     lineNumber = __LINE__ - 2;
1935     assert(l.msg == msg.format("Yet"), l.msg);
1936     assert(l.line == lineNumber);
1937     assert(l.logLevel == LogLevel.all);
1938 
1939     () @trusted {
1940         assertThrown!Throwable(logf(LogLevel.fatal, true, msg, "Yet"));
1941     } ();
1942     lineNumber = __LINE__ - 2;
1943     assert(l.msg == msg.format("Yet"));
1944     assert(l.line == lineNumber);
1945     assert(l.logLevel == LogLevel.all);
1946 
1947     assertNotThrown(logf(LogLevel.fatal, false, msg, "Yet"));
1948     assert(l.msg == msg.format("Yet"));
1949     assert(l.line == lineNumber);
1950     assert(l.logLevel == LogLevel.all);
1951 }
1952 
1953 @system unittest // default logger
1954 {
1955     import std.file : deleteme, exists, remove;
1956     import std.stdio : File;
1957     import std.string : indexOf;
1958 
1959     string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
1960     FileLogger l = new FileLogger(filename);
1961     auto oldunspecificLogger = sharedLog;
1962 
1963     sharedLog = cast(shared) l;
1964 
1965     scope(exit)
1966     {
1967         remove(filename);
1968         assert(!exists(filename));
1969         sharedLog = atomicLoad(oldunspecificLogger);
1970         globalLogLevel = LogLevel.all;
1971     }
1972 
1973     string notWritten = "this should not be written to file";
1974     string written = "this should be written to file";
1975 
1976     globalLogLevel = LogLevel.critical;
1977     assert(globalLogLevel == LogLevel.critical);
1978 
1979     log(LogLevel.warning, notWritten);
1980     log(LogLevel.critical, written);
1981 
1982     l.file.flush();
1983     l.file.close();
1984 
1985     auto file = File(filename, "r");
1986     assert(!file.eof);
1987 
1988     string readLine = file.readln();
1989     assert(readLine.indexOf(written) != -1, readLine);
1990     assert(readLine.indexOf(notWritten) == -1, readLine);
1991     file.close();
1992 }
1993 
1994 @system unittest
1995 {
1996     import std.file : deleteme, remove;
1997     import std.stdio : File;
1998     import std.string : indexOf;
1999 
2000     string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
2001     auto oldunspecificLogger = sharedLog;
2002 
2003     scope(exit)
2004     {
2005         remove(filename);
2006         sharedLog = atomicLoad(oldunspecificLogger);
2007         globalLogLevel = LogLevel.all;
2008     }
2009 
2010     string notWritten = "this should not be written to file";
2011     string written = "this should be written to file";
2012 
2013     auto l = new FileLogger(filename);
2014     sharedLog = cast(shared) l;
2015 
2016     () @trusted {
2017         (cast() sharedLog).logLevel = LogLevel.critical;
2018     }();
2019 
2020     log(LogLevel.error, false, notWritten);
2021     log(LogLevel.critical, true, written);
2022     destroy(l);
2023 
2024     auto file = File(filename, "r");
2025     auto readLine = file.readln();
2026     assert(!readLine.empty, readLine);
2027     assert(readLine.indexOf(written) != -1);
2028     assert(readLine.indexOf(notWritten) == -1);
2029     file.close();
2030 }
2031 
2032 @safe unittest
2033 {
2034     import std.conv : to;
2035 
2036     auto tl = new TestLogger(LogLevel.all);
2037     int l = __LINE__;
2038     tl.info("a");
2039     assert(tl.line == l+1);
2040     assert(tl.msg == "a");
2041     assert(tl.logLevel == LogLevel.all);
2042     assert(globalLogLevel == LogLevel.all);
2043     l = __LINE__;
2044     tl.trace("b");
2045     assert(tl.msg == "b", tl.msg);
2046     assert(tl.line == l+1, to!string(tl.line));
2047 }
2048 
2049 // testing possible log conditions
2050 @safe unittest
2051 {
2052     import std.conv : to;
2053     import std.format : format;
2054     import std.string : indexOf;
2055 
2056     auto oldunspecificLogger = sharedLog;
2057 
2058     auto mem = new TestLogger;
2059     mem.fatalHandler = delegate() {};
2060 
2061     () @trusted {
2062         sharedLog = cast(shared) mem;
2063     }();
2064 
2065     scope(exit)
2066     {
2067         sharedLog = atomicLoad(oldunspecificLogger);
2068         globalLogLevel = LogLevel.all;
2069     }
2070 
2071     int value = 0;
2072     foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
2073             LogLevel.info, LogLevel.warning, LogLevel.error,
2074             LogLevel.critical, LogLevel.fatal, LogLevel.off])
2075     {
2076 
2077         globalLogLevel = gll;
2078 
2079         foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
2080                 LogLevel.info, LogLevel.warning, LogLevel.error,
2081                 LogLevel.critical, LogLevel.fatal, LogLevel.off])
2082         {
2083 
2084             mem.logLevel = ll;
2085 
2086             foreach (cond; [true, false])
2087             {
2088                 foreach (condValue; [true, false])
2089                 {
2090                     foreach (memOrG; [true, false])
2091                     {
2092                         foreach (prntf; [true, false])
2093                         {
2094                             foreach (ll2; [cast(LogLevel) LogLevel.all, LogLevel.trace,
2095                                     LogLevel.info, LogLevel.warning,
2096                                     LogLevel.error, LogLevel.critical,
2097                                     LogLevel.fatal, LogLevel.off])
2098                             {
2099                                 foreach (singleMulti; 0 .. 2)
2100                                 {
2101                                     int lineCall;
2102                                     mem.msg = "-1";
2103                                     if (memOrG)
2104                                     {
2105                                         if (prntf)
2106                                         {
2107                                             if (cond)
2108                                             {
2109                                                 if (singleMulti == 0)
2110                                                 {
2111                                                     mem.logf(ll2, condValue, "%s",
2112                                                         value);
2113                                                     lineCall = __LINE__;
2114                                                 }
2115                                                 else
2116                                                 {
2117                                                     mem.logf(ll2, condValue,
2118                                                         "%d %d", value, value);
2119                                                     lineCall = __LINE__;
2120                                                 }
2121                                             }
2122                                             else
2123                                             {
2124                                                 if (singleMulti == 0)
2125                                                 {
2126                                                     mem.logf(ll2, "%s", value);
2127                                                     lineCall = __LINE__;
2128                                                 }
2129                                                 else
2130                                                 {
2131                                                     mem.logf(ll2, "%d %d",
2132                                                         value, value);
2133                                                     lineCall = __LINE__;
2134                                                 }
2135                                             }
2136                                         }
2137                                         else
2138                                         {
2139                                             if (cond)
2140                                             {
2141                                                 if (singleMulti == 0)
2142                                                 {
2143                                                     mem.log(ll2, condValue,
2144                                                         to!string(value));
2145                                                     lineCall = __LINE__;
2146                                                 }
2147                                                 else
2148                                                 {
2149                                                     mem.log(ll2, condValue,
2150                                                         to!string(value), value);
2151                                                     lineCall = __LINE__;
2152                                                 }
2153                                             }
2154                                             else
2155                                             {
2156                                                 if (singleMulti == 0)
2157                                                 {
2158                                                     mem.log(ll2, to!string(value));
2159                                                     lineCall = __LINE__;
2160                                                 }
2161                                                 else
2162                                                 {
2163                                                     mem.log(ll2,
2164                                                         to!string(value),
2165                                                         value);
2166                                                     lineCall = __LINE__;
2167                                                 }
2168                                             }
2169                                         }
2170                                     }
2171                                     else
2172                                     {
2173                                         if (prntf)
2174                                         {
2175                                             if (cond)
2176                                             {
2177                                                 if (singleMulti == 0)
2178                                                 {
2179                                                     logf(ll2, condValue, "%s",
2180                                                         value);
2181                                                     lineCall = __LINE__;
2182                                                 }
2183                                                 else
2184                                                 {
2185                                                     logf(ll2, condValue,
2186                                                         "%s %d", value, value);
2187                                                     lineCall = __LINE__;
2188                                                 }
2189                                             }
2190                                             else
2191                                             {
2192                                                 if (singleMulti == 0)
2193                                                 {
2194                                                     logf(ll2, "%s", value);
2195                                                     lineCall = __LINE__;
2196                                                 }
2197                                                 else
2198                                                 {
2199                                                     logf(ll2, "%s %s", value,
2200                                                         value);
2201                                                     lineCall = __LINE__;
2202                                                 }
2203                                             }
2204                                         }
2205                                         else
2206                                         {
2207                                             if (cond)
2208                                             {
2209                                                 if (singleMulti == 0)
2210                                                 {
2211                                                     log(ll2, condValue,
2212                                                         to!string(value));
2213                                                     lineCall = __LINE__;
2214                                                 }
2215                                                 else
2216                                                 {
2217                                                     log(ll2, condValue, value,
2218                                                         to!string(value));
2219                                                     lineCall = __LINE__;
2220                                                 }
2221                                             }
2222                                             else
2223                                             {
2224                                                 if (singleMulti == 0)
2225                                                 {
2226                                                     log(ll2, to!string(value));
2227                                                     lineCall = __LINE__;
2228                                                 }
2229                                                 else
2230                                                 {
2231                                                     log(ll2, value,
2232                                                         to!string(value));
2233                                                     lineCall = __LINE__;
2234                                                 }
2235                                             }
2236                                         }
2237                                     }
2238 
2239                                     string valueStr = to!string(value);
2240                                     ++value;
2241 
2242                                     bool ll2Off = (ll2 != LogLevel.off);
2243                                     bool gllOff = (gll != LogLevel.off);
2244                                     bool llOff = (ll != LogLevel.off);
2245                                     bool condFalse = (cond ? condValue : true);
2246                                     bool ll2VSgll = (ll2 >= gll);
2247                                     bool ll2VSll = (ll2 >= ll);
2248 
2249                                     bool shouldLog = ll2Off && gllOff && llOff
2250                                         && condFalse && ll2VSgll && ll2VSll;
2251 
2252                                     /*
2253                                     writefln(
2254                                         "go(%b) ll2o(%b) c(%b) lg(%b) ll(%b) s(%b)"
2255                                         , gll != LogLevel.off, ll2 != LogLevel.off,
2256                                         cond ? condValue : true,
2257                                         ll2 >= gll, ll2 >= ll, shouldLog);
2258                                     */
2259 
2260 
2261                                     if (shouldLog)
2262                                     {
2263                                         assert(mem.msg.indexOf(valueStr) != -1,
2264                                             format(
2265                                             "lineCall(%d) ll2Off(%u) gll(%u) ll(%u) ll2(%u) " ~
2266                                             "cond(%b) condValue(%b)" ~
2267                                             " memOrG(%b) shouldLog(%b) %s == %s" ~
2268                                             " %b %b %b %b %b",
2269                                             lineCall, ll2Off, gll, ll, ll2, cond,
2270                                             condValue, memOrG, shouldLog, mem.msg,
2271                                             valueStr, gllOff, llOff, condFalse,
2272                                             ll2VSgll, ll2VSll
2273                                         ));
2274                                     }
2275                                     else
2276                                     {
2277                                         assert(mem.msg.indexOf(valueStr),
2278                                             format(
2279                                             "lineCall(%d) ll2Off(%u) gll(%u) ll(%u) ll2(%u) " ~
2280                                             "cond(%b) condValue(%b)" ~
2281                                             " memOrG(%b) shouldLog(%b) %s == %s" ~
2282                                             " %b %b %b %b %b",
2283                                             lineCall, ll2Off, gll, ll, ll2, cond,
2284                                             condValue, memOrG, shouldLog, mem.msg,
2285                                             valueStr, gllOff, llOff, condFalse,
2286                                             ll2VSgll, ll2VSll
2287                                         ));
2288                                     }
2289                                 }
2290                             }
2291                         }
2292                     }
2293                 }
2294             }
2295         }
2296     }
2297 }
2298 
2299 // more testing
2300 @safe unittest
2301 {
2302     import std.conv : to;
2303     import std.format : format;
2304     import std.string : indexOf;
2305 
2306     auto oldunspecificLogger = sharedLog;
2307 
2308     auto mem = new TestLogger;
2309     mem.fatalHandler = delegate() {};
2310 
2311     () @trusted {
2312         sharedLog = cast(shared) mem;
2313     }();
2314 
2315     scope(exit)
2316     {
2317         sharedLog = atomicLoad(oldunspecificLogger);
2318         globalLogLevel = LogLevel.all;
2319     }
2320 
2321     int value = 0;
2322     foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
2323             LogLevel.info, LogLevel.warning, LogLevel.error,
2324             LogLevel.critical, LogLevel.fatal, LogLevel.off])
2325     {
2326 
2327         globalLogLevel = gll;
2328 
2329         foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
2330                 LogLevel.info, LogLevel.warning, LogLevel.error,
2331                 LogLevel.critical, LogLevel.fatal, LogLevel.off])
2332         {
2333             mem.logLevel = ll;
2334 
2335             foreach (tll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
2336                     LogLevel.info, LogLevel.warning, LogLevel.error,
2337                     LogLevel.critical, LogLevel.fatal, LogLevel.off])
2338             {
2339                 stdThreadLocalLog.logLevel = tll;
2340 
2341                 foreach (cond; [true, false])
2342                 {
2343                     foreach (condValue; [true, false])
2344                     {
2345                         foreach (memOrG; [true, false])
2346                         {
2347                             foreach (prntf; [true, false])
2348                             {
2349                                 foreach (singleMulti; 0 .. 2)
2350                                 {
2351                                     int lineCall;
2352                                     mem.msg = "-1";
2353                                     if (memOrG)
2354                                     {
2355                                         if (prntf)
2356                                         {
2357                                             if (cond)
2358                                             {
2359                                                 if (singleMulti == 0)
2360                                                 {
2361                                                     mem.logf(condValue, "%s",
2362                                                         value);
2363                                                     lineCall = __LINE__;
2364                                                 }
2365                                                 else
2366                                                 {
2367                                                     mem.logf(condValue,
2368                                                         "%d %d", value, value);
2369                                                     lineCall = __LINE__;
2370                                                 }
2371                                             }
2372                                             else
2373                                             {
2374                                                 if (singleMulti == 0)
2375                                                 {
2376                                                     mem.logf("%s", value);
2377                                                     lineCall = __LINE__;
2378                                                 }
2379                                                 else
2380                                                 {
2381                                                     mem.logf("%d %d",
2382                                                         value, value);
2383                                                     lineCall = __LINE__;
2384                                                 }
2385                                             }
2386                                         }
2387                                         else
2388                                         {
2389                                             if (cond)
2390                                             {
2391                                                 if (singleMulti == 0)
2392                                                 {
2393                                                     mem.log(condValue,
2394                                                         to!string(value));
2395                                                     lineCall = __LINE__;
2396                                                 }
2397                                                 else
2398                                                 {
2399                                                     mem.log(condValue,
2400                                                         to!string(value), value);
2401                                                     lineCall = __LINE__;
2402                                                 }
2403                                             }
2404                                             else
2405                                             {
2406                                                 if (singleMulti == 0)
2407                                                 {
2408                                                     mem.log(to!string(value));
2409                                                     lineCall = __LINE__;
2410                                                 }
2411                                                 else
2412                                                 {
2413                                                     mem.log(to!string(value),
2414                                                         value);
2415                                                     lineCall = __LINE__;
2416                                                 }
2417                                             }
2418                                         }
2419                                     }
2420                                     else
2421                                     {
2422                                         if (prntf)
2423                                         {
2424                                             if (cond)
2425                                             {
2426                                                 if (singleMulti == 0)
2427                                                 {
2428                                                     logf(condValue, "%s", value);
2429                                                     lineCall = __LINE__;
2430                                                 }
2431                                                 else
2432                                                 {
2433                                                     logf(condValue, "%s %d", value,
2434                                                         value);
2435                                                     lineCall = __LINE__;
2436                                                 }
2437                                             }
2438                                             else
2439                                             {
2440                                                 if (singleMulti == 0)
2441                                                 {
2442                                                     logf("%s", value);
2443                                                     lineCall = __LINE__;
2444                                                 }
2445                                                 else
2446                                                 {
2447                                                     logf("%s %s", value, value);
2448                                                     lineCall = __LINE__;
2449                                                 }
2450                                             }
2451                                         }
2452                                         else
2453                                         {
2454                                             if (cond)
2455                                             {
2456                                                 if (singleMulti == 0)
2457                                                 {
2458                                                     log(condValue,
2459                                                         to!string(value));
2460                                                     lineCall = __LINE__;
2461                                                 }
2462                                                 else
2463                                                 {
2464                                                     log(condValue, value,
2465                                                         to!string(value));
2466                                                     lineCall = __LINE__;
2467                                                 }
2468                                             }
2469                                             else
2470                                             {
2471                                                 if (singleMulti == 0)
2472                                                 {
2473                                                     log(to!string(value));
2474                                                     lineCall = __LINE__;
2475                                                 }
2476                                                 else
2477                                                 {
2478                                                     log(value, to!string(value));
2479                                                     lineCall = __LINE__;
2480                                                 }
2481                                             }
2482                                         }
2483                                     }
2484 
2485                                     string valueStr = to!string(value);
2486                                     ++value;
2487 
2488                                     bool gllOff = (gll != LogLevel.off);
2489                                     bool llOff = (ll != LogLevel.off);
2490                                     bool tllOff = (tll != LogLevel.off);
2491                                     bool llVSgll = (ll >= gll);
2492                                     bool tllVSll =
2493                                         (stdThreadLocalLog.logLevel >= ll);
2494                                     bool condFalse = (cond ? condValue : true);
2495 
2496                                     bool shouldLog = gllOff && llOff
2497                                         && (memOrG ? true : tllOff)
2498                                         && (memOrG ?
2499                                             (ll >= gll) :
2500                                             (tll >= gll && tll >= ll))
2501                                         && condFalse;
2502 
2503                                     if (shouldLog)
2504                                     {
2505                                         assert(mem.msg.indexOf(valueStr) != -1,
2506                                             format("\ngll(%s) ll(%s) tll(%s) " ~
2507                                                 "cond(%s) condValue(%s) " ~
2508                                                 "memOrG(%s) prntf(%s) " ~
2509                                                 "singleMulti(%s)",
2510                                                 gll, ll, tll, cond, condValue,
2511                                                 memOrG, prntf, singleMulti)
2512                                             ~ format(" gllOff(%s) llOff(%s) " ~
2513                                                 "llVSgll(%s) tllVSll(%s) " ~
2514                                                 "tllOff(%s) condFalse(%s) "
2515                                                 ~ "shoudlLog(%s)",
2516                                                 gll != LogLevel.off,
2517                                                 ll != LogLevel.off, llVSgll,
2518                                                 tllVSll, tllOff, condFalse,
2519                                                 shouldLog)
2520                                             ~ format("msg(%s) line(%s) " ~
2521                                                 "lineCall(%s) valueStr(%s)",
2522                                                 mem.msg, mem.line, lineCall,
2523                                                 valueStr)
2524                                         );
2525                                     }
2526                                     else
2527                                     {
2528                                         assert(mem.msg.indexOf(valueStr) == -1,
2529                                             format("\ngll(%s) ll(%s) tll(%s) " ~
2530                                                 "cond(%s) condValue(%s) " ~
2531                                                 "memOrG(%s) prntf(%s) " ~
2532                                                 "singleMulti(%s)",
2533                                                 gll, ll, tll, cond, condValue,
2534                                                 memOrG, prntf, singleMulti)
2535                                             ~ format(" gllOff(%s) llOff(%s) " ~
2536                                                 "llVSgll(%s) tllVSll(%s) " ~
2537                                                 "tllOff(%s) condFalse(%s) "
2538                                                 ~ "shoudlLog(%s)",
2539                                                 gll != LogLevel.off,
2540                                                 ll != LogLevel.off, llVSgll,
2541                                                 tllVSll, tllOff, condFalse,
2542                                                 shouldLog)
2543                                             ~ format("msg(%s) line(%s) " ~
2544                                                 "lineCall(%s) valueStr(%s)",
2545                                                 mem.msg, mem.line, lineCall,
2546                                                 valueStr)
2547                                         );
2548                                     }
2549                                 }
2550                             }
2551                         }
2552                     }
2553                 }
2554             }
2555         }
2556     }
2557 }
2558 
2559 // testing more possible log conditions
2560 @safe unittest
2561 {
2562     bool fatalLog;
2563     auto mem = new TestLogger;
2564     mem.fatalHandler = delegate() { fatalLog = true; };
2565     auto oldunspecificLogger = sharedLog;
2566 
2567     stdThreadLocalLog.logLevel = LogLevel.all;
2568 
2569     () @trusted {
2570         sharedLog = cast(shared) mem;
2571     }();
2572 
2573     scope(exit)
2574     {
2575         sharedLog = atomicLoad(oldunspecificLogger);
2576         globalLogLevel = LogLevel.all;
2577     }
2578 
2579     foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
2580             LogLevel.info, LogLevel.warning, LogLevel.error,
2581             LogLevel.critical, LogLevel.fatal, LogLevel.off])
2582     {
2583 
2584         globalLogLevel = gll;
2585 
2586         foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
2587                 LogLevel.info, LogLevel.warning, LogLevel.error,
2588                 LogLevel.critical, LogLevel.fatal, LogLevel.off])
2589         {
2590             mem.logLevel = ll;
2591 
2592             foreach (tll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
2593                     LogLevel.info, LogLevel.warning, LogLevel.error,
2594                     LogLevel.critical, LogLevel.fatal, LogLevel.off])
2595             {
2596                 stdThreadLocalLog.logLevel = tll;
2597 
2598                 foreach (cond; [true, false])
2599                 {
2600                     assert(globalLogLevel == gll);
2601                     assert(mem.logLevel == ll);
2602 
2603                     bool gllVSll = LogLevel.trace >= globalLogLevel;
2604                     bool llVSgll = ll >= globalLogLevel;
2605                     bool lVSll = LogLevel.trace >= ll;
2606                     bool gllOff = globalLogLevel != LogLevel.off;
2607                     bool llOff = mem.logLevel != LogLevel.off;
2608                     bool tllOff = stdThreadLocalLog.logLevel != LogLevel.off;
2609                     bool tllVSll = tll >= ll;
2610                     bool tllVSgll = tll >= gll;
2611                     bool lVSgll = LogLevel.trace >= tll;
2612 
2613                     bool test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
2614                     bool testG = gllOff && llOff && tllOff && lVSgll && tllVSll && tllVSgll && cond;
2615 
2616                     mem.line = -1;
2617                     /*
2618                     writefln("gll(%3u) ll(%3u) cond(%b) test(%b)",
2619                         gll, ll, cond, test);
2620                     writefln("%b %b %b %b %b %b test2(%b)", llVSgll, gllVSll, lVSll,
2621                         gllOff, llOff, cond, test2);
2622                     */
2623 
2624                     mem.trace(__LINE__); int line = __LINE__;
2625                     assert(test ? mem.line == line : true); line = -1;
2626 
2627                     trace(__LINE__); line = __LINE__;
2628                     assert(testG ? mem.line == line : true); line = -1;
2629 
2630                     mem.trace(cond, __LINE__); line = __LINE__;
2631                     assert(test ? mem.line == line : true); line = -1;
2632 
2633                     trace(cond, __LINE__); line = __LINE__;
2634                     assert(testG ? mem.line == line : true); line = -1;
2635 
2636                     mem.tracef("%d", __LINE__); line = __LINE__;
2637                     assert(test ? mem.line == line : true); line = -1;
2638 
2639                     tracef("%d", __LINE__); line = __LINE__;
2640                     assert(testG ? mem.line == line : true); line = -1;
2641 
2642                     mem.tracef(cond, "%d", __LINE__); line = __LINE__;
2643                     assert(test ? mem.line == line : true); line = -1;
2644 
2645                     tracef(cond, "%d", __LINE__); line = __LINE__;
2646                     assert(testG ? mem.line == line : true); line = -1;
2647 
2648                     llVSgll = ll >= globalLogLevel;
2649                     lVSll = LogLevel.info >= ll;
2650                     lVSgll = LogLevel.info >= tll;
2651                     test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
2652                     testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
2653                         lVSgll && cond;
2654 
2655                     mem.info(__LINE__); line = __LINE__;
2656                     assert(test ? mem.line == line : true); line = -1;
2657 
2658                     info(__LINE__); line = __LINE__;
2659                     assert(testG ? mem.line == line : true); line = -1;
2660 
2661                     mem.info(cond, __LINE__); line = __LINE__;
2662                     assert(test ? mem.line == line : true); line = -1;
2663 
2664                     info(cond, __LINE__); line = __LINE__;
2665                     assert(testG ? mem.line == line : true); line = -1;
2666 
2667                     mem.infof("%d", __LINE__); line = __LINE__;
2668                     assert(test ? mem.line == line : true); line = -1;
2669 
2670                     infof("%d", __LINE__); line = __LINE__;
2671                     assert(testG ? mem.line == line : true); line = -1;
2672 
2673                     mem.infof(cond, "%d", __LINE__); line = __LINE__;
2674                     assert(test ? mem.line == line : true); line = -1;
2675 
2676                     infof(cond, "%d", __LINE__); line = __LINE__;
2677                     assert(testG ? mem.line == line : true); line = -1;
2678 
2679                     llVSgll = ll >= globalLogLevel;
2680                     lVSll = LogLevel.warning >= ll;
2681                     lVSgll = LogLevel.warning >= tll;
2682                     test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
2683                     testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
2684                         lVSgll && cond;
2685 
2686                     mem.warning(__LINE__); line = __LINE__;
2687                     assert(test ? mem.line == line : true); line = -1;
2688 
2689                     warning(__LINE__); line = __LINE__;
2690                     assert(testG ? mem.line == line : true); line = -1;
2691 
2692                     mem.warning(cond, __LINE__); line = __LINE__;
2693                     assert(test ? mem.line == line : true); line = -1;
2694 
2695                     warning(cond, __LINE__); line = __LINE__;
2696                     assert(testG ? mem.line == line : true); line = -1;
2697 
2698                     mem.warningf("%d", __LINE__); line = __LINE__;
2699                     assert(test ? mem.line == line : true); line = -1;
2700 
2701                     warningf("%d", __LINE__); line = __LINE__;
2702                     assert(testG ? mem.line == line : true); line = -1;
2703 
2704                     mem.warningf(cond, "%d", __LINE__); line = __LINE__;
2705                     assert(test ? mem.line == line : true); line = -1;
2706 
2707                     warningf(cond, "%d", __LINE__); line = __LINE__;
2708                     assert(testG ? mem.line == line : true); line = -1;
2709 
2710                     llVSgll = ll >= globalLogLevel;
2711                     lVSll = LogLevel.critical >= ll;
2712                     lVSgll = LogLevel.critical >= tll;
2713                     test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
2714                     testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
2715                         lVSgll && cond;
2716 
2717                     mem.critical(__LINE__); line = __LINE__;
2718                     assert(test ? mem.line == line : true); line = -1;
2719 
2720                     critical(__LINE__); line = __LINE__;
2721                     assert(testG ? mem.line == line : true); line = -1;
2722 
2723                     mem.critical(cond, __LINE__); line = __LINE__;
2724                     assert(test ? mem.line == line : true); line = -1;
2725 
2726                     critical(cond, __LINE__); line = __LINE__;
2727                     assert(testG ? mem.line == line : true); line = -1;
2728 
2729                     mem.criticalf("%d", __LINE__); line = __LINE__;
2730                     assert(test ? mem.line == line : true); line = -1;
2731 
2732                     criticalf("%d", __LINE__); line = __LINE__;
2733                     assert(testG ? mem.line == line : true); line = -1;
2734 
2735                     mem.criticalf(cond, "%d", __LINE__); line = __LINE__;
2736                     assert(test ? mem.line == line : true); line = -1;
2737 
2738                     criticalf(cond, "%d", __LINE__); line = __LINE__;
2739                     assert(testG ? mem.line == line : true); line = -1;
2740 
2741                     llVSgll = ll >= globalLogLevel;
2742                     lVSll = LogLevel.fatal >= ll;
2743                     lVSgll = LogLevel.fatal >= tll;
2744                     test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
2745                     testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
2746                         lVSgll && cond;
2747 
2748                     mem.fatal(__LINE__); line = __LINE__;
2749                     assert(test ? mem.line == line : true); line = -1;
2750                     assert(test ? fatalLog : true);
2751                     fatalLog = false;
2752 
2753                     fatal(__LINE__); line = __LINE__;
2754                     assert(testG ? mem.line == line : true); line = -1;
2755                     assert(testG ? fatalLog : true);
2756                     fatalLog = false;
2757 
2758                     mem.fatal(cond, __LINE__); line = __LINE__;
2759                     assert(test ? mem.line == line : true); line = -1;
2760                     assert(test ? fatalLog : true);
2761                     fatalLog = false;
2762 
2763                     fatal(cond, __LINE__); line = __LINE__;
2764                     assert(testG ? mem.line == line : true); line = -1;
2765                     assert(testG ? fatalLog : true);
2766                     fatalLog = false;
2767 
2768                     mem.fatalf("%d", __LINE__); line = __LINE__;
2769                     assert(test ? mem.line == line : true); line = -1;
2770                     assert(test ? fatalLog : true);
2771                     fatalLog = false;
2772 
2773                     fatalf("%d", __LINE__); line = __LINE__;
2774                     assert(testG ? mem.line == line : true); line = -1;
2775                     assert(testG ? fatalLog : true);
2776                     fatalLog = false;
2777 
2778                     mem.fatalf(cond, "%d", __LINE__); line = __LINE__;
2779                     assert(test ? mem.line == line : true); line = -1;
2780                     assert(test ? fatalLog : true);
2781                     fatalLog = false;
2782 
2783                     fatalf(cond, "%d", __LINE__); line = __LINE__;
2784                     assert(testG ? mem.line == line : true); line = -1;
2785                     assert(testG ? fatalLog : true);
2786                     fatalLog = false;
2787                 }
2788             }
2789         }
2790     }
2791 }
2792 
2793 // Issue #5
2794 @safe unittest
2795 {
2796     import std.string : indexOf;
2797 
2798     auto oldunspecificLogger = sharedLog;
2799 
2800     scope(exit)
2801     {
2802         sharedLog = atomicLoad(oldunspecificLogger);
2803         globalLogLevel = LogLevel.all;
2804     }
2805 
2806     auto tl = new TestLogger(LogLevel.info);
2807 
2808     () @trusted {
2809         sharedLog = cast(shared) tl;
2810     }();
2811 
2812     trace("trace");
2813     assert(tl.msg.indexOf("trace") == -1);
2814 }
2815 
2816 // Issue #5
2817 @safe unittest
2818 {
2819     import std.logger.multilogger : MultiLogger;
2820     import std.string : indexOf;
2821 
2822     stdThreadLocalLog.logLevel = LogLevel.all;
2823 
2824     auto oldunspecificLogger = sharedLog;
2825 
2826     scope(exit)
2827     {
2828         sharedLog = atomicLoad(oldunspecificLogger);
2829         globalLogLevel = LogLevel.all;
2830     }
2831 
2832     auto logger = new MultiLogger(LogLevel.error);
2833 
2834     auto tl = new TestLogger(LogLevel.info);
2835     logger.insertLogger("required", tl);
2836 
2837     () @trusted {
2838         sharedLog = cast(shared) logger;
2839     }();
2840 
2841     trace("trace");
2842     assert(tl.msg.indexOf("trace") == -1);
2843     info("info");
2844     assert(tl.msg.indexOf("info") == -1);
2845     error("error");
2846     assert(tl.msg.indexOf("error") == 0);
2847 }
2848 
2849 @system unittest
2850 {
2851     import std.exception : assertThrown;
2852     auto tl = new TestLogger();
2853     assertThrown!Throwable(tl.fatal("fatal"));
2854 }
2855 
2856 // log objects with non-safe toString
2857 @system unittest
2858 {
2859     struct Test
2860     {
2861         string toString() const @system
2862         {
2863             return "test";
2864         }
2865     }
2866 
2867     auto tl = new TestLogger();
2868     tl.info(Test.init);
2869     assert(tl.msg == "test");
2870 }
2871 
2872 // Workaround for atomics not allowed in @safe code
2873 private auto trustedLoad(T)(ref shared T value) @trusted
2874 {
2875     return atomicLoad!(MemoryOrder.acq)(value);
2876 }
2877 
2878 // ditto
2879 private void trustedStore(T)(ref shared T dst, ref T src) @trusted
2880 {
2881     atomicStore!(MemoryOrder.rel)(dst, src);
2882 }
2883 
2884 // check that thread-local logging does not propagate
2885 // to shared logger
2886 @system unittest
2887 {
2888     import core.thread, std.concurrency;
2889 
2890     static shared logged_count = 0;
2891 
2892     class TestLog : Logger
2893     {
2894         Tid tid;
2895 
2896         this()
2897         {
2898             super (LogLevel.trace);
2899             this.tid = thisTid;
2900         }
2901 
2902         override void writeLogMsg(ref LogEntry payload) @trusted
2903         {
2904             assert(thisTid == this.tid);
2905             atomicOp!"+="(logged_count, 1);
2906         }
2907     }
2908 
2909     class IgnoredLog : Logger
2910     {
2911         this()
2912         {
2913             super (LogLevel.trace);
2914         }
2915 
2916         override void writeLogMsg(ref LogEntry payload) @trusted
2917         {
2918             assert(false);
2919         }
2920     }
2921 
2922     auto oldSharedLog = sharedLog;
2923     scope(exit)
2924     {
2925         sharedLog = atomicLoad(oldSharedLog);
2926     }
2927 
2928     () @trusted {
2929         sharedLog = cast(shared) new IgnoredLog;
2930     }();
2931 
2932     Thread[] spawned;
2933 
2934     foreach (i; 0 .. 4)
2935     {
2936         spawned ~= new Thread({
2937             stdThreadLocalLog = new TestLog;
2938             trace("zzzzzzzzzz");
2939         });
2940         spawned[$-1].start();
2941     }
2942 
2943     foreach (t; spawned)
2944         t.join();
2945 
2946     assert(atomicOp!"=="(logged_count, 4));
2947 }
2948 
2949 @safe unittest
2950 {
2951     auto dl = () @trusted {
2952         return cast(FileLogger) cast() sharedLog;
2953     }();
2954     assert(dl !is null);
2955     assert(dl.logLevel == LogLevel.info);
2956     assert(globalLogLevel == LogLevel.all);
2957 
2958     auto tl = cast(StdForwardLogger) stdThreadLocalLog;
2959     assert(tl !is null);
2960     stdThreadLocalLog.logLevel = LogLevel.all;
2961 }
2962 
2963 // https://issues.dlang.org/show_bug.cgi?id=14940
2964 @safe unittest
2965 {
2966     import std.typecons : Nullable;
2967 
2968     Nullable!int a = 1;
2969     auto l = new TestLogger();
2970     l.infof("log: %s", a);
2971     assert(l.msg == "log: 1");
2972 }
2973 
2974 // Ensure @system toString methods work
2975 @system unittest
2976 {
2977     enum SystemToStringMsg = "SystemToString";
2978     static struct SystemToString
2979     {
2980         string toString() @system
2981         {
2982             return SystemToStringMsg;
2983         }
2984     }
2985 
2986     auto tl = new TestLogger();
2987 
2988     SystemToString sts;
2989     tl.logf("%s", sts);
2990     assert(tl.msg == SystemToStringMsg);
2991 }
2992 
2993 // https://issues.dlang.org/show_bug.cgi?id=17328
2994 @safe unittest
2995 {
2996     import std.format : format;
2997 
2998     ubyte[] data = [0];
2999     string s = format("%(%02x%)", data); // format 00
3000     assert(s == "00");
3001 
3002     auto tl = new TestLogger();
3003 
3004     tl.infof("%(%02x%)", data);    // infof    000
3005 
3006     size_t i;
3007     string fs = tl.msg;
3008     for (; i < s.length; ++i)
3009     {
3010         assert(s[s.length - 1 - i] == fs[fs.length - 1 - i], fs);
3011     }
3012     assert(fs.length == 2);
3013 }
3014 
3015 // https://issues.dlang.org/show_bug.cgi?id=15954
3016 @safe unittest
3017 {
3018     import std.conv : to;
3019     auto tl = new TestLogger();
3020     tl.log("123456789".to!wstring);
3021     assert(tl.msg == "123456789");
3022 }
3023 
3024 // https://issues.dlang.org/show_bug.cgi?id=16256
3025 @safe unittest
3026 {
3027     import std.conv : to;
3028     auto tl = new TestLogger();
3029     tl.log("123456789"d);
3030     assert(tl.msg == "123456789");
3031 }
3032 
3033 // https://issues.dlang.org/show_bug.cgi?id=15517
3034 @system unittest
3035 {
3036     import std.file : exists, remove, tempDir;
3037     import std.path : buildPath;
3038     import std.stdio : File;
3039     import std.string : indexOf;
3040 
3041     string fn = tempDir.buildPath("logfile.log");
3042     if (exists(fn))
3043     {
3044         remove(fn);
3045     }
3046 
3047     auto oldShared = sharedLog;
3048     scope(exit)
3049     {
3050         sharedLog = atomicLoad(oldShared);
3051         if (exists(fn))
3052         {
3053             remove(fn);
3054         }
3055     }
3056 
3057     auto ts = [ "Test log 1", "Test log 2", "Test log 3"];
3058 
3059     auto fl = new FileLogger(fn);
3060 
3061     () @trusted {
3062         sharedLog = cast(shared) fl;
3063     }();
3064 
3065     assert(exists(fn));
3066 
3067     foreach (t; ts)
3068     {
3069         log(t);
3070     }
3071 
3072     auto f = File(fn);
3073     auto l = f.byLine();
3074     assert(!l.empty);
3075     size_t idx;
3076     foreach (it; l)
3077     {
3078         assert(it.indexOf(ts[idx]) != -1, it);
3079         ++idx;
3080     }
3081 
3082     assert(exists(fn));
3083     fl.file.close();
3084 }