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