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