2 module std.experimental.logger.core;
4 import core.sync.mutex : Mutex;
5 import std.datetime.date : DateTime;
6 import std.datetime.systime : Clock, SysTime;
7 import std.range.primitives;
10 import std.experimental.logger.filelogger;
12 /** This template evaluates if the passed $(D LogLevel) is active.
13 The previously described version statements are used to decide if the
14 $(D LogLevel) is active. The version statements only influence the compile
15 unit they are used with, therefore this function can only disable logging this
16 specific compile unit.
18 template isLoggingActiveAt(LogLevel ll)
20 version (StdLoggerDisableLogging)
22 enum isLoggingActiveAt = false;
26 static if (ll == LogLevel.trace)
28 version (StdLoggerDisableTrace) enum isLoggingActiveAt = false;
30 else static if (ll == LogLevel.info)
32 version (StdLoggerDisableInfo) enum isLoggingActiveAt = false;
34 else static if (ll == LogLevel.warning)
36 version (StdLoggerDisableWarning) enum isLoggingActiveAt = false;
38 else static if (ll == LogLevel.error)
40 version (StdLoggerDisableError) enum isLoggingActiveAt = false;
42 else static if (ll == LogLevel.critical)
44 version (StdLoggerDisableCritical) enum isLoggingActiveAt = false;
46 else static if (ll == LogLevel.fatal)
48 version (StdLoggerDisableFatal) enum isLoggingActiveAt = false;
50 // If `isLoggingActiveAt` didn't get defined above to false,
51 // we default it to true.
52 static if (!is(typeof(isLoggingActiveAt) == bool))
54 enum isLoggingActiveAt = true;
59 /// This compile-time flag is $(D true) if logging is not statically disabled.
60 enum isLoggingActive = isLoggingActiveAt!(LogLevel.all);
62 /** This functions is used at runtime to determine if a $(D LogLevel) is
63 active. The same previously defined version statements are used to disable
64 certain levels. Again the version statements are associated with a compile
65 unit and can therefore not disable logging in other compile units.
66 pure bool isLoggingEnabled()(LogLevel ll) @safe nothrow @nogc
68 bool isLoggingEnabled()(LogLevel ll, LogLevel loggerLL,
69 LogLevel globalLL, lazy bool condition = true) @safe
74 version (StdLoggerDisableTrace) return false;
77 version (StdLoggerDisableInfo) return false;
79 case LogLevel.warning:
80 version (StdLoggerDisableWarning) return false;
82 case LogLevel.critical:
83 version (StdLoggerDisableCritical) return false;
86 version (StdLoggerDisableFatal) return false;
94 && globalLL != LogLevel.off
95 && loggerLL != LogLevel.off
99 /** This template returns the $(D LogLevel) named "logLevel" of type $(D
100 LogLevel) defined in a user defined module where the filename has the
101 suffix "_loggerconfig.d". This $(D LogLevel) sets the minimal $(D LogLevel)
104 A minimal $(D LogLevel) can be defined on a per module basis.
105 In order to define a module $(D LogLevel) a file with a modulename
106 "MODULENAME_loggerconfig" must be found. If no such module exists and the
107 module is a nested module, it is checked if there exists a
108 "PARENT_MODULE_loggerconfig" module with such a symbol.
109 If this module exists and it contains a $(D LogLevel) called logLevel this $(D
110 LogLevel) will be used. This parent lookup is continued until there is no
111 parent module. Then the moduleLogLevel is $(D LogLevel.all).
113 template moduleLogLevel(string moduleName)
114 if (!moduleName.length)
117 enum moduleLogLevel = LogLevel.all;
123 static assert(moduleLogLevel!"" == LogLevel.all);
127 template moduleLogLevel(string moduleName)
128 if (moduleName.length)
130 import std.string : format;
132 static if (__traits(compiles, {import %1$s : logLevel;}))
134 import %1$s : logLevel;
135 static assert(is(typeof(logLevel) : LogLevel),
136 "Expect 'logLevel' to be of Type 'LogLevel'.");
137 // don't enforce enum here
138 alias moduleLogLevel = logLevel;
141 // use logLevel of package or default
142 alias moduleLogLevel = moduleLogLevel!(parentOf(moduleName));
143 }.format(moduleName ~ "_loggerconfig"));
149 static assert(moduleLogLevel!"not.amodule.path" == LogLevel.all);
152 private string parentOf(string mod)
154 foreach_reverse (i, c; mod)
155 if (c == '.') return mod[0 .. i];
159 /* This function formates a $(D SysTime) into an $(D OutputRange).
161 The $(D SysTime) is formatted similar to
162 $(LREF std.datatime.DateTime.toISOExtString) except the fractional second part.
163 The fractional second part is in milliseconds and is always 3 digits.
165 void systimeToISOString(OutputRange)(OutputRange o, const ref SysTime time)
166 if (isOutputRange!(OutputRange,string))
168 import std.format : formattedWrite;
170 const auto dt = cast(DateTime) time;
171 const auto fsec = time.fracSecs.total!"msecs";
173 formattedWrite(o, "%04d-%02d-%02dT%02d:%02d:%02d.%03d",
174 dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
178 /** This function logs data.
180 In order for the data to be processed, the $(D LogLevel) of the log call must
181 be greater or equal to the $(D LogLevel) of the $(D sharedLog) and the
182 $(D defaultLogLevel); additionally the condition passed must be $(D true).
185 ll = The $(D LogLevel) used by this log call.
186 condition = The condition must be $(D true) for the data to be logged.
187 args = The data that should be logged.
191 log(LogLevel.warning, true, "Hello World", 3.1415);
194 void log(int line = __LINE__, string file = __FILE__,
195 string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
196 string moduleName = __MODULE__, A...)(const LogLevel ll,
197 lazy bool condition, lazy A args)
198 if (args.length != 1)
200 static if (isLoggingActive)
202 if (ll >= moduleLogLevel!moduleName)
204 stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName)
205 (ll, condition, args);
211 void log(T, string moduleName = __MODULE__)(const LogLevel ll,
212 lazy bool condition, lazy T arg, int line = __LINE__, string file = __FILE__,
213 string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__)
215 static if (isLoggingActive)
217 if (ll >= moduleLogLevel!moduleName)
219 stdThreadLocalLog.log!(T,moduleName)(ll, condition, arg, line, file, funcName,
225 /** This function logs data.
227 In order for the data to be processed the $(D LogLevel) of the log call must
228 be greater or equal to the $(D LogLevel) of the $(D sharedLog).
231 ll = The $(D LogLevel) used by this log call.
232 args = The data that should be logged.
236 log(LogLevel.warning, "Hello World", 3.1415);
239 void log(int line = __LINE__, string file = __FILE__,
240 string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
241 string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args)
242 if (args.length > 1 && !is(Unqual!(A[0]) : bool))
244 static if (isLoggingActive)
246 if (ll >= moduleLogLevel!moduleName)
248 stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName)
255 void log(T, string moduleName = __MODULE__)(const LogLevel ll, lazy T arg,
256 int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__,
257 string prettyFuncName = __PRETTY_FUNCTION__)
259 static if (isLoggingActive)
261 if (ll >= moduleLogLevel!moduleName)
263 stdThreadLocalLog.log!T(ll, arg, line, file, funcName, prettyFuncName,
269 /** This function logs data.
271 In order for the data to be processed the $(D LogLevel) of the
272 $(D sharedLog) must be greater or equal to the $(D defaultLogLevel)
273 add the condition passed must be $(D true).
276 condition = The condition must be $(D true) for the data to be logged.
277 args = The data that should be logged.
281 log(true, "Hello World", 3.1415);
284 void log(int line = __LINE__, string file = __FILE__,
285 string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
286 string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args)
287 if (args.length != 1)
289 static if (isLoggingActive)
291 stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName)
292 (stdThreadLocalLog.logLevel, condition, args);
297 void log(T, string moduleName = __MODULE__)(lazy bool condition, lazy T arg,
298 int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__,
299 string prettyFuncName = __PRETTY_FUNCTION__)
301 static if (isLoggingActive)
303 stdThreadLocalLog.log!(T,moduleName)(stdThreadLocalLog.logLevel,
304 condition, arg, line, file, funcName, prettyFuncName);
308 /** This function logs data.
310 In order for the data to be processed the $(D LogLevel) of the
311 $(D sharedLog) must be greater or equal to the $(D defaultLogLevel).
314 args = The data that should be logged.
318 log("Hello World", 3.1415);
321 void log(int line = __LINE__, string file = __FILE__,
322 string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
323 string moduleName = __MODULE__, A...)(lazy A args)
324 if ((args.length > 1 && !is(Unqual!(A[0]) : bool)
325 && !is(Unqual!(A[0]) == LogLevel))
328 static if (isLoggingActive)
330 stdThreadLocalLog.log!(line, file, funcName,
331 prettyFuncName, moduleName)(stdThreadLocalLog.logLevel, args);
335 void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__,
336 string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
337 string moduleName = __MODULE__)
339 static if (isLoggingActive)
341 stdThreadLocalLog.log!T(stdThreadLocalLog.logLevel, arg, line, file,
342 funcName, prettyFuncName, moduleName);
346 /** This function logs data in a $(D printf)-style manner.
348 In order for the data to be processed the $(D LogLevel) of the log call must
349 be greater or equal to the $(D LogLevel) of the $(D sharedLog) and the
350 $(D defaultLogLevel) additionally the condition passed must be $(D true).
353 ll = The $(D LogLevel) used by this log call.
354 condition = The condition must be $(D true) for the data to be logged.
355 msg = The $(D printf)-style string.
356 args = The data that should be logged.
360 logf(LogLevel.warning, true, "Hello World %f", 3.1415);
363 void logf(int line = __LINE__, string file = __FILE__,
364 string funcName = __FUNCTION__,
365 string prettyFuncName = __PRETTY_FUNCTION__,
366 string moduleName = __MODULE__, A...)(const LogLevel ll,
367 lazy bool condition, lazy string msg, lazy A args)
369 static if (isLoggingActive)
371 if (ll >= moduleLogLevel!moduleName)
373 stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName)
374 (ll, condition, msg, args);
379 /** This function logs data in a $(D printf)-style manner.
381 In order for the data to be processed the $(D LogLevel) of the log call must
382 be greater or equal to the $(D LogLevel) of the $(D sharedLog) and the
383 $(D defaultLogLevel).
386 ll = The $(D LogLevel) used by this log call.
387 msg = The $(D printf)-style string.
388 args = The data that should be logged.
392 logf(LogLevel.warning, "Hello World %f", 3.1415);
395 void logf(int line = __LINE__, string file = __FILE__,
396 string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
397 string moduleName = __MODULE__, A...)(const LogLevel ll, lazy string msg,
400 static if (isLoggingActive)
402 if (ll >= moduleLogLevel!moduleName)
404 stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName)
410 /** This function logs data in a $(D printf)-style manner.
412 In order for the data to be processed the $(D LogLevel) of the log call must
413 be greater or equal to the $(D defaultLogLevel) additionally the condition
414 passed must be $(D true).
417 condition = The condition must be $(D true) for the data to be logged.
418 msg = The $(D printf)-style string.
419 args = The data that should be logged.
423 logf(true, "Hello World %f", 3.1415);
426 void logf(int line = __LINE__, string file = __FILE__,
427 string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
428 string moduleName = __MODULE__, A...)(lazy bool condition,
429 lazy string msg, lazy A args)
431 static if (isLoggingActive)
433 stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName)
434 (stdThreadLocalLog.logLevel, condition, msg, args);
438 /** This function logs data in a $(D printf)-style manner.
440 In order for the data to be processed the $(D LogLevel) of the log call must
441 be greater or equal to the $(D defaultLogLevel).
444 msg = The $(D printf)-style string.
445 args = The data that should be logged.
449 logf("Hello World %f", 3.1415);
452 void logf(int line = __LINE__, string file = __FILE__,
453 string funcName = __FUNCTION__,
454 string prettyFuncName = __PRETTY_FUNCTION__,
455 string moduleName = __MODULE__, A...)(lazy string msg, lazy A args)
457 static if (isLoggingActive)
459 stdThreadLocalLog.logf!(line, file, funcName,prettyFuncName, moduleName)
460 (stdThreadLocalLog.logLevel, msg, args);
464 /** This template provides the global log functions with the $(D LogLevel)
465 is encoded in the function name.
467 The aliases following this template create the public names of these log
470 template defaultLogFunction(LogLevel ll)
472 void defaultLogFunction(int line = __LINE__, string file = __FILE__,
473 string funcName = __FUNCTION__,
474 string prettyFuncName = __PRETTY_FUNCTION__,
475 string moduleName = __MODULE__, A...)(lazy A args)
476 if ((args.length > 0 && !is(Unqual!(A[0]) : bool)) || args.length == 0)
478 static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName)
480 stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName,
481 prettyFuncName, moduleName)(args);
485 void defaultLogFunction(int line = __LINE__, string file = __FILE__,
486 string funcName = __FUNCTION__,
487 string prettyFuncName = __PRETTY_FUNCTION__,
488 string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args)
490 static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName)
492 stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName,
493 prettyFuncName, moduleName)(condition, args);
498 /** This function logs data to the $(D stdThreadLocalLog), optionally depending
501 In order for the resulting log message to be logged the $(D LogLevel) must
502 be greater or equal than the $(D LogLevel) of the $(D stdThreadLocalLog) and
503 must be greater or equal than the global $(D LogLevel).
504 Additionally the $(D LogLevel) must be greater or equal than the $(D LogLevel)
505 of the $(D stdSharedLogger).
506 If a condition is given, it must evaluate to $(D true).
509 condition = The condition must be $(D true) for the data to be logged.
510 args = The data that should be logged.
514 trace(1337, "is number");
515 info(1337, "is number");
516 error(1337, "is number");
517 critical(1337, "is number");
518 fatal(1337, "is number");
519 trace(true, 1337, "is number");
520 info(false, 1337, "is number");
521 error(true, 1337, "is number");
522 critical(false, 1337, "is number");
523 fatal(true, 1337, "is number");
526 alias trace = defaultLogFunction!(LogLevel.trace);
528 alias info = defaultLogFunction!(LogLevel.info);
530 alias warning = defaultLogFunction!(LogLevel.warning);
532 alias error = defaultLogFunction!(LogLevel.error);
534 alias critical = defaultLogFunction!(LogLevel.critical);
536 alias fatal = defaultLogFunction!(LogLevel.fatal);
538 /** This template provides the global $(D printf)-style log functions with
539 the $(D LogLevel) is encoded in the function name.
541 The aliases following this template create the public names of the log
544 template defaultLogFunctionf(LogLevel ll)
546 void defaultLogFunctionf(int line = __LINE__, string file = __FILE__,
547 string funcName = __FUNCTION__,
548 string prettyFuncName = __PRETTY_FUNCTION__,
549 string moduleName = __MODULE__, A...)(lazy string msg, lazy A args)
551 static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName)
553 stdThreadLocalLog.memLogFunctions!(ll).logImplf!(line, file, funcName,
554 prettyFuncName, moduleName)(msg, args);
558 void defaultLogFunctionf(int line = __LINE__, string file = __FILE__,
559 string funcName = __FUNCTION__,
560 string prettyFuncName = __PRETTY_FUNCTION__,
561 string moduleName = __MODULE__, A...)(lazy bool condition,
562 lazy string msg, lazy A args)
564 static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName)
566 stdThreadLocalLog.memLogFunctions!(ll).logImplf!(line, file, funcName,
567 prettyFuncName, moduleName)(condition, msg, args);
572 /** This function logs data to the $(D sharedLog) in a $(D printf)-style
575 In order for the resulting log message to be logged the $(D LogLevel) must
576 be greater or equal than the $(D LogLevel) of the $(D sharedLog) and
577 must be greater or equal than the global $(D LogLevel).
578 Additionally the $(D LogLevel) must be greater or equal than the $(D LogLevel)
579 of the $(D stdSharedLogger).
582 msg = The $(D printf)-style string.
583 args = The data that should be logged.
587 tracef("is number %d", 1);
588 infof("is number %d", 2);
589 errorf("is number %d", 3);
590 criticalf("is number %d", 4);
591 fatalf("is number %d", 5);
594 The second version of the function logs data to the $(D sharedLog) in a $(D
595 printf)-style manner.
597 In order for the resulting log message to be logged the $(D LogLevel) must
598 be greater or equal than the $(D LogLevel) of the $(D sharedLog) and
599 must be greater or equal than the global $(D LogLevel).
600 Additionally the $(D LogLevel) must be greater or equal than the $(D LogLevel)
601 of the $(D stdSharedLogger).
604 condition = The condition must be $(D true) for the data to be logged.
605 msg = The $(D printf)-style string.
606 args = The data that should be logged.
610 tracef(false, "is number %d", 1);
611 infof(false, "is number %d", 2);
612 errorf(true, "is number %d", 3);
613 criticalf(true, "is number %d", 4);
614 fatalf(someFunct(), "is number %d", 5);
617 alias tracef = defaultLogFunctionf!(LogLevel.trace);
619 alias infof = defaultLogFunctionf!(LogLevel.info);
621 alias warningf = defaultLogFunctionf!(LogLevel.warning);
623 alias errorf = defaultLogFunctionf!(LogLevel.error);
625 alias criticalf = defaultLogFunctionf!(LogLevel.critical);
627 alias fatalf = defaultLogFunctionf!(LogLevel.fatal);
629 private struct MsgRange
631 import std.traits : isSomeString, isSomeChar;
635 this(Logger log) @safe
640 void put(T)(T msg) @safe
646 void put(dchar elem) @safe
648 import std.utf : encode;
650 size_t len = encode(buffer, elem);
651 log.logMsgPart(buffer[0 .. len]);
655 private void formatString(A...)(MsgRange oRange, A args)
657 import std.format : formattedWrite;
661 formattedWrite(oRange, "%s", arg);
669 auto tl = new TestLogger();
670 auto dst = MsgRange(tl);
671 formatString(dst, "aaa", "bbb");
678 There are eight usable logging level. These level are $(I all), $(I trace),
679 $(I info), $(I warning), $(I error), $(I critical), $(I fatal), and $(I off).
680 If a log function with $(D LogLevel.fatal) is called the shutdown handler of
681 that logger is called.
683 enum LogLevel : ubyte
685 all = 1, /** Lowest possible assignable $(D LogLevel). */
686 trace = 32, /** $(D LogLevel) for tracing the execution of the program. */
687 info = 64, /** This level is used to display information about the
689 warning = 96, /** warnings about the program should be displayed with this
691 error = 128, /** Information about errors should be logged with this
693 critical = 160, /** Messages that inform about critical errors should be
694 logged with this level. */
695 fatal = 192, /** Log messages that describe fatal errors should use this
697 off = ubyte.max /** Highest possible $(D LogLevel). */
700 /** This class is the base of every logger. In order to create a new kind of
701 logger a deriving class needs to implement the $(D writeLogMsg) method. By
702 default this is not thread-safe.
704 It is also possible to $(D override) the three methods $(D beginLogMsg),
705 $(D logMsgPart) and $(D finishLogMsg) together, this option gives more
708 abstract class Logger
710 import std.array : appender, Appender;
711 import std.concurrency : thisTid, Tid;
713 /** LogEntry is a aggregation combining all information associated
714 with a log message. This aggregation will be passed to the method
717 protected struct LogEntry
719 /// the filename the log function was called from
721 /// the line number the log function was called from
723 /// the name of the function the log function was called from
725 /// the pretty formatted name of the function the log function was
727 string prettyFuncName;
728 /// the name of the module the log message is coming from
730 /// the $(D LogLevel) associated with the log message
732 /// thread id of the log message
734 /// the time the message was logged
736 /// the message of the log message
738 /// A refernce to the $(D Logger) used to create this $(D LogEntry)
743 Every subclass of `Logger` has to call this constructor from their
744 constructor. It sets the `LogLevel`, and creates a fatal handler. The fatal
745 handler will throw an `Error` if a log call is made with level
749 lv = `LogLevel` to use for this `Logger` instance.
751 this(LogLevel lv) @safe
754 this.fatalHandler_ = delegate() {
755 throw new Error("A fatal log message was logged");
758 this.mutex = new Mutex();
761 /** A custom logger must implement this method in order to work in a
762 $(D MultiLogger) and $(D ArrayLogger).
765 payload = All information associated with call to log function.
767 See_Also: beginLogMsg, logMsgPart, finishLogMsg
769 abstract protected void writeLogMsg(ref LogEntry payload) @safe;
771 /* The default implementation will use an $(D std.array.appender)
772 internally to construct the message string. This means dynamic,
773 GC memory allocation. A logger can avoid this allocation by
774 reimplementing $(D beginLogMsg), $(D logMsgPart) and $(D finishLogMsg).
775 $(D beginLogMsg) is always called first, followed by any number of calls
776 to $(D logMsgPart) and one call to $(D finishLogMsg).
778 As an example for such a custom $(D Logger) compare this:
780 class CLogger : Logger
782 override void beginLogMsg(string file, int line, string funcName,
783 string prettyFuncName, string moduleName, LogLevel logLevel,
784 Tid threadId, SysTime timestamp)
789 override void logMsgPart(const(char)[] msg)
794 override void finishLogMsg()
799 void writeLogMsg(ref LogEntry payload)
801 this.beginLogMsg(payload.file, payload.line, payload.funcName,
802 payload.prettyFuncName, payload.moduleName, payload.logLevel,
803 payload.threadId, payload.timestamp, payload.logger);
805 this.logMsgPart(payload.msg);
811 protected void beginLogMsg(string file, int line, string funcName,
812 string prettyFuncName, string moduleName, LogLevel logLevel,
813 Tid threadId, SysTime timestamp, Logger logger)
816 static if (isLoggingActive)
818 msgAppender = appender!string();
819 header = LogEntry(file, line, funcName, prettyFuncName,
820 moduleName, logLevel, threadId, timestamp, null, logger);
824 /** Logs a part of the log message. */
825 protected void logMsgPart(const(char)[] msg) @safe
827 static if (isLoggingActive)
829 msgAppender.put(msg);
833 /** Signals that the message has been written and no more calls to
834 $(D logMsgPart) follow. */
835 protected void finishLogMsg() @safe
837 static if (isLoggingActive)
839 header.msg = msgAppender.data;
840 this.writeLogMsg(header);
844 /** The $(D LogLevel) determines if the log call are processed or dropped
845 by the $(D Logger). In order for the log call to be processed the
846 $(D LogLevel) of the log call must be greater or equal to the $(D LogLevel)
849 These two methods set and get the $(D LogLevel) of the used $(D Logger).
853 auto f = new FileLogger(stdout);
854 f.logLevel = LogLevel.info;
855 assert(f.logLevel == LogLevel.info);
858 @property final LogLevel logLevel() const pure @safe @nogc
860 return trustedLoad(this.logLevel_);
864 @property final void logLevel(const LogLevel lv) @safe @nogc
866 synchronized (mutex) this.logLevel_ = lv;
869 /** This $(D delegate) is called in case a log message with
870 $(D LogLevel.fatal) gets logged.
872 By default an $(D Error) will be thrown.
874 @property final void delegate() fatalHandler() @safe @nogc
876 synchronized (mutex) return this.fatalHandler_;
880 @property final void fatalHandler(void delegate() @safe fh) @safe @nogc
882 synchronized (mutex) this.fatalHandler_ = fh;
885 /** This method allows forwarding log entries from one logger to another.
887 $(D forwardMsg) will ensure proper synchronization and then call
888 $(D writeLogMsg). This is an API for implementing your own loggers and
889 should not be called by normal user code. A notable difference from other
890 logging functions is that the $(D globalLogLevel) wont be evaluated again
891 since it is assumed that the caller already checked that.
893 void forwardMsg(ref LogEntry payload) @trusted
895 static if (isLoggingActive) synchronized (mutex)
897 if (isLoggingEnabled(payload.logLevel, this.logLevel_,
900 this.writeLogMsg(payload);
902 if (payload.logLevel == LogLevel.fatal)
903 this.fatalHandler_();
908 /** This template provides the log functions for the $(D Logger) $(D class)
909 with the $(D LogLevel) encoded in the function name.
911 For further information see the the two functions defined inside of this
914 The aliases following this template create the public names of these log
917 template memLogFunctions(LogLevel ll)
919 /** This function logs data to the used $(D Logger).
921 In order for the resulting log message to be logged the $(D LogLevel)
922 must be greater or equal than the $(D LogLevel) of the used $(D Logger)
923 and must be greater or equal than the global $(D LogLevel).
926 args = The data that should be logged.
930 auto s = new FileLogger(stdout);
931 s.trace(1337, "is number");
932 s.info(1337, "is number");
933 s.error(1337, "is number");
934 s.critical(1337, "is number");
935 s.fatal(1337, "is number");
938 void logImpl(int line = __LINE__, string file = __FILE__,
939 string funcName = __FUNCTION__,
940 string prettyFuncName = __PRETTY_FUNCTION__,
941 string moduleName = __MODULE__, A...)(lazy A args)
942 if (args.length == 0 || (args.length > 0 && !is(A[0] : bool)))
944 static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName)
947 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
949 this.beginLogMsg(file, line, funcName, prettyFuncName,
950 moduleName, ll, thisTid, Clock.currTime, this);
952 auto writer = MsgRange(this);
953 formatString(writer, args);
957 static if (ll == LogLevel.fatal)
958 this.fatalHandler_();
963 /** This function logs data to the used $(D Logger) depending on a
966 In order for the resulting log message to be logged the $(D LogLevel) must
967 be greater or equal than the $(D LogLevel) of the used $(D Logger) and
968 must be greater or equal than the global $(D LogLevel) additionally the
969 condition passed must be $(D true).
972 condition = The condition must be $(D true) for the data to be logged.
973 args = The data that should be logged.
977 auto s = new FileLogger(stdout);
978 s.trace(true, 1337, "is number");
979 s.info(false, 1337, "is number");
980 s.error(true, 1337, "is number");
981 s.critical(false, 1337, "is number");
982 s.fatal(true, 1337, "is number");
985 void logImpl(int line = __LINE__, string file = __FILE__,
986 string funcName = __FUNCTION__,
987 string prettyFuncName = __PRETTY_FUNCTION__,
988 string moduleName = __MODULE__, A...)(lazy bool condition,
991 static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName)
994 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel,
997 this.beginLogMsg(file, line, funcName, prettyFuncName,
998 moduleName, ll, thisTid, Clock.currTime, this);
1000 auto writer = MsgRange(this);
1001 formatString(writer, args);
1003 this.finishLogMsg();
1005 static if (ll == LogLevel.fatal)
1006 this.fatalHandler_();
1011 /** This function logs data to the used $(D Logger) in a
1012 $(D printf)-style manner.
1014 In order for the resulting log message to be logged the $(D LogLevel)
1015 must be greater or equal than the $(D LogLevel) of the used $(D Logger)
1016 and must be greater or equal than the global $(D LogLevel) additionally
1017 the passed condition must be $(D true).
1020 condition = The condition must be $(D true) for the data to be logged.
1021 msg = The $(D printf)-style string.
1022 args = The data that should be logged.
1025 --------------------
1026 auto s = new FileLogger(stderr);
1027 s.tracef(true, "is number %d", 1);
1028 s.infof(true, "is number %d", 2);
1029 s.errorf(false, "is number %d", 3);
1030 s.criticalf(someFunc(), "is number %d", 4);
1031 s.fatalf(true, "is number %d", 5);
1032 --------------------
1034 void logImplf(int line = __LINE__, string file = __FILE__,
1035 string funcName = __FUNCTION__,
1036 string prettyFuncName = __PRETTY_FUNCTION__,
1037 string moduleName = __MODULE__, A...)(lazy bool condition,
1038 lazy string msg, lazy A args)
1040 static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName)
1041 synchronized (mutex)
1043 import std.format : formattedWrite;
1045 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel,
1048 this.beginLogMsg(file, line, funcName, prettyFuncName,
1049 moduleName, ll, thisTid, Clock.currTime, this);
1051 auto writer = MsgRange(this);
1052 formattedWrite(writer, msg, args);
1054 this.finishLogMsg();
1056 static if (ll == LogLevel.fatal)
1057 this.fatalHandler_();
1062 /** This function logs data to the used $(D Logger) in a
1063 $(D printf)-style manner.
1065 In order for the resulting log message to be logged the $(D LogLevel) must
1066 be greater or equal than the $(D LogLevel) of the used $(D Logger) and
1067 must be greater or equal than the global $(D LogLevel).
1070 msg = The $(D printf)-style string.
1071 args = The data that should be logged.
1074 --------------------
1075 auto s = new FileLogger(stderr);
1076 s.tracef("is number %d", 1);
1077 s.infof("is number %d", 2);
1078 s.errorf("is number %d", 3);
1079 s.criticalf("is number %d", 4);
1080 s.fatalf("is number %d", 5);
1081 --------------------
1083 void logImplf(int line = __LINE__, string file = __FILE__,
1084 string funcName = __FUNCTION__,
1085 string prettyFuncName = __PRETTY_FUNCTION__,
1086 string moduleName = __MODULE__, A...)(lazy string msg, lazy A args)
1088 static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName)
1089 synchronized (mutex)
1091 import std.format : formattedWrite;
1093 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
1095 this.beginLogMsg(file, line, funcName, prettyFuncName,
1096 moduleName, ll, thisTid, Clock.currTime, this);
1098 auto writer = MsgRange(this);
1099 formattedWrite(writer, msg, args);
1101 this.finishLogMsg();
1103 static if (ll == LogLevel.fatal)
1104 this.fatalHandler_();
1111 alias trace = memLogFunctions!(LogLevel.trace).logImpl;
1113 alias tracef = memLogFunctions!(LogLevel.trace).logImplf;
1115 alias info = memLogFunctions!(LogLevel.info).logImpl;
1117 alias infof = memLogFunctions!(LogLevel.info).logImplf;
1119 alias warning = memLogFunctions!(LogLevel.warning).logImpl;
1121 alias warningf = memLogFunctions!(LogLevel.warning).logImplf;
1123 alias error = memLogFunctions!(LogLevel.error).logImpl;
1125 alias errorf = memLogFunctions!(LogLevel.error).logImplf;
1127 alias critical = memLogFunctions!(LogLevel.critical).logImpl;
1129 alias criticalf = memLogFunctions!(LogLevel.critical).logImplf;
1131 alias fatal = memLogFunctions!(LogLevel.fatal).logImpl;
1133 alias fatalf = memLogFunctions!(LogLevel.fatal).logImplf;
1135 /** This method logs data with the $(D LogLevel) of the used $(D Logger).
1137 This method takes a $(D bool) as first argument. In order for the
1138 data to be processed the $(D bool) must be $(D true) and the $(D LogLevel)
1139 of the Logger must be greater or equal to the global $(D LogLevel).
1142 args = The data that should be logged.
1143 condition = The condition must be $(D true) for the data to be logged.
1144 args = The data that is to be logged.
1146 Returns: The logger used by the logging function as reference.
1149 --------------------
1150 auto l = new StdioLogger();
1152 --------------------
1154 void log(int line = __LINE__, string file = __FILE__,
1155 string funcName = __FUNCTION__,
1156 string prettyFuncName = __PRETTY_FUNCTION__,
1157 string moduleName = __MODULE__, A...)(const LogLevel ll,
1158 lazy bool condition, lazy A args)
1159 if (args.length != 1)
1161 static if (isLoggingActive) synchronized (mutex)
1163 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition))
1165 this.beginLogMsg(file, line, funcName, prettyFuncName,
1166 moduleName, ll, thisTid, Clock.currTime, this);
1168 auto writer = MsgRange(this);
1169 formatString(writer, args);
1171 this.finishLogMsg();
1173 if (ll == LogLevel.fatal)
1174 this.fatalHandler_();
1180 void log(T, string moduleName = __MODULE__)(const LogLevel ll,
1181 lazy bool condition, lazy T args, int line = __LINE__,
1182 string file = __FILE__, string funcName = __FUNCTION__,
1183 string prettyFuncName = __PRETTY_FUNCTION__)
1185 static if (isLoggingActive) synchronized (mutex)
1187 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel,
1188 condition) && ll >= moduleLogLevel!moduleName)
1190 this.beginLogMsg(file, line, funcName, prettyFuncName,
1191 moduleName, ll, thisTid, Clock.currTime, this);
1192 auto writer = MsgRange(this);
1193 formatString(writer, args);
1195 this.finishLogMsg();
1197 if (ll == LogLevel.fatal)
1198 this.fatalHandler_();
1203 /** This function logs data to the used $(D Logger) with a specific
1206 In order for the resulting log message to be logged the $(D LogLevel)
1207 must be greater or equal than the $(D LogLevel) of the used $(D Logger)
1208 and must be greater or equal than the global $(D LogLevel).
1211 ll = The specific $(D LogLevel) used for logging the log message.
1212 args = The data that should be logged.
1215 --------------------
1216 auto s = new FileLogger(stdout);
1217 s.log(LogLevel.trace, 1337, "is number");
1218 s.log(LogLevel.info, 1337, "is number");
1219 s.log(LogLevel.warning, 1337, "is number");
1220 s.log(LogLevel.error, 1337, "is number");
1221 s.log(LogLevel.fatal, 1337, "is number");
1222 --------------------
1224 void log(int line = __LINE__, string file = __FILE__,
1225 string funcName = __FUNCTION__,
1226 string prettyFuncName = __PRETTY_FUNCTION__,
1227 string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args)
1228 if ((args.length > 1 && !is(Unqual!(A[0]) : bool)) || args.length == 0)
1230 static if (isLoggingActive) synchronized (mutex)
1232 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
1234 this.beginLogMsg(file, line, funcName, prettyFuncName,
1235 moduleName, ll, thisTid, Clock.currTime, this);
1237 auto writer = MsgRange(this);
1238 formatString(writer, args);
1240 this.finishLogMsg();
1242 if (ll == LogLevel.fatal)
1243 this.fatalHandler_();
1249 void log(T)(const LogLevel ll, lazy T args, int line = __LINE__,
1250 string file = __FILE__, string funcName = __FUNCTION__,
1251 string prettyFuncName = __PRETTY_FUNCTION__,
1252 string moduleName = __MODULE__)
1254 static if (isLoggingActive) synchronized (mutex)
1256 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
1258 this.beginLogMsg(file, line, funcName, prettyFuncName,
1259 moduleName, ll, thisTid, Clock.currTime, this);
1260 auto writer = MsgRange(this);
1261 formatString(writer, args);
1263 this.finishLogMsg();
1265 if (ll == LogLevel.fatal)
1266 this.fatalHandler_();
1271 /** This function logs data to the used $(D Logger) depending on a
1272 explicitly passed condition with the $(D LogLevel) of the used
1275 In order for the resulting log message to be logged the $(D LogLevel)
1276 of the used $(D Logger) must be greater or equal than the global
1277 $(D LogLevel) and the condition must be $(D true).
1280 condition = The condition must be $(D true) for the data to be logged.
1281 args = The data that should be logged.
1284 --------------------
1285 auto s = new FileLogger(stdout);
1286 s.log(true, 1337, "is number");
1287 s.log(true, 1337, "is number");
1288 s.log(true, 1337, "is number");
1289 s.log(false, 1337, "is number");
1290 s.log(false, 1337, "is number");
1291 --------------------
1293 void log(int line = __LINE__, string file = __FILE__,
1294 string funcName = __FUNCTION__,
1295 string prettyFuncName = __PRETTY_FUNCTION__,
1296 string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args)
1297 if (args.length != 1)
1299 static if (isLoggingActive) synchronized (mutex)
1301 if (isLoggingEnabled(this.logLevel_, this.logLevel_,
1302 globalLogLevel, condition))
1304 this.beginLogMsg(file, line, funcName, prettyFuncName,
1305 moduleName, this.logLevel_, thisTid, Clock.currTime, this);
1307 auto writer = MsgRange(this);
1308 formatString(writer, args);
1310 this.finishLogMsg();
1312 if (this.logLevel_ == LogLevel.fatal)
1313 this.fatalHandler_();
1319 void log(T)(lazy bool condition, lazy T args, int line = __LINE__,
1320 string file = __FILE__, string funcName = __FUNCTION__,
1321 string prettyFuncName = __PRETTY_FUNCTION__,
1322 string moduleName = __MODULE__)
1324 static if (isLoggingActive) synchronized (mutex)
1326 if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel,
1329 this.beginLogMsg(file, line, funcName, prettyFuncName,
1330 moduleName, this.logLevel_, thisTid, Clock.currTime, this);
1331 auto writer = MsgRange(this);
1332 formatString(writer, args);
1334 this.finishLogMsg();
1336 if (this.logLevel_ == LogLevel.fatal)
1337 this.fatalHandler_();
1342 /** This function logs data to the used $(D Logger) with the $(D LogLevel)
1343 of the used $(D Logger).
1345 In order for the resulting log message to be logged the $(D LogLevel)
1346 of the used $(D Logger) must be greater or equal than the global
1350 args = The data that should be logged.
1353 --------------------
1354 auto s = new FileLogger(stdout);
1355 s.log(1337, "is number");
1356 s.log(info, 1337, "is number");
1357 s.log(1337, "is number");
1358 s.log(1337, "is number");
1359 s.log(1337, "is number");
1360 --------------------
1362 void log(int line = __LINE__, string file = __FILE__,
1363 string funcName = __FUNCTION__,
1364 string prettyFuncName = __PRETTY_FUNCTION__,
1365 string moduleName = __MODULE__, A...)(lazy A args)
1366 if ((args.length > 1
1367 && !is(Unqual!(A[0]) : bool)
1368 && !is(Unqual!(A[0]) == LogLevel))
1369 || args.length == 0)
1371 static if (isLoggingActive) synchronized (mutex)
1373 if (isLoggingEnabled(this.logLevel_, this.logLevel_,
1376 this.beginLogMsg(file, line, funcName, prettyFuncName,
1377 moduleName, this.logLevel_, thisTid, Clock.currTime, this);
1378 auto writer = MsgRange(this);
1379 formatString(writer, args);
1381 this.finishLogMsg();
1383 if (this.logLevel_ == LogLevel.fatal)
1384 this.fatalHandler_();
1390 void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__,
1391 string funcName = __FUNCTION__,
1392 string prettyFuncName = __PRETTY_FUNCTION__,
1393 string moduleName = __MODULE__)
1395 static if (isLoggingActive) synchronized (mutex)
1397 if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel))
1399 this.beginLogMsg(file, line, funcName, prettyFuncName,
1400 moduleName, this.logLevel_, thisTid, Clock.currTime, this);
1401 auto writer = MsgRange(this);
1402 formatString(writer, arg);
1404 this.finishLogMsg();
1406 if (this.logLevel_ == LogLevel.fatal)
1407 this.fatalHandler_();
1412 /** This function logs data to the used $(D Logger) with a specific
1413 $(D LogLevel) and depending on a condition in a $(D printf)-style manner.
1415 In order for the resulting log message to be logged the $(D LogLevel)
1416 must be greater or equal than the $(D LogLevel) of the used $(D Logger)
1417 and must be greater or equal than the global $(D LogLevel) and the
1418 condition must be $(D true).
1421 ll = The specific $(D LogLevel) used for logging the log message.
1422 condition = The condition must be $(D true) for the data to be logged.
1423 msg = The format string used for this log call.
1424 args = The data that should be logged.
1427 --------------------
1428 auto s = new FileLogger(stdout);
1429 s.logf(LogLevel.trace, true ,"%d %s", 1337, "is number");
1430 s.logf(LogLevel.info, true ,"%d %s", 1337, "is number");
1431 s.logf(LogLevel.warning, true ,"%d %s", 1337, "is number");
1432 s.logf(LogLevel.error, false ,"%d %s", 1337, "is number");
1433 s.logf(LogLevel.fatal, true ,"%d %s", 1337, "is number");
1434 --------------------
1436 void logf(int line = __LINE__, string file = __FILE__,
1437 string funcName = __FUNCTION__,
1438 string prettyFuncName = __PRETTY_FUNCTION__,
1439 string moduleName = __MODULE__, A...)(const LogLevel ll,
1440 lazy bool condition, lazy string msg, lazy A args)
1442 static if (isLoggingActive) synchronized (mutex)
1444 import std.format : formattedWrite;
1446 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition))
1448 this.beginLogMsg(file, line, funcName, prettyFuncName,
1449 moduleName, ll, thisTid, Clock.currTime, this);
1451 auto writer = MsgRange(this);
1452 formattedWrite(writer, msg, args);
1454 this.finishLogMsg();
1456 if (ll == LogLevel.fatal)
1457 this.fatalHandler_();
1462 /** This function logs data to the used $(D Logger) with a specific
1463 $(D LogLevel) in a $(D printf)-style manner.
1465 In order for the resulting log message to be logged the $(D LogLevel)
1466 must be greater or equal than the $(D LogLevel) of the used $(D Logger)
1467 and must be greater or equal than the global $(D LogLevel).
1470 ll = The specific $(D LogLevel) used for logging the log message.
1471 msg = The format string used for this log call.
1472 args = The data that should be logged.
1475 --------------------
1476 auto s = new FileLogger(stdout);
1477 s.logf(LogLevel.trace, "%d %s", 1337, "is number");
1478 s.logf(LogLevel.info, "%d %s", 1337, "is number");
1479 s.logf(LogLevel.warning, "%d %s", 1337, "is number");
1480 s.logf(LogLevel.error, "%d %s", 1337, "is number");
1481 s.logf(LogLevel.fatal, "%d %s", 1337, "is number");
1482 --------------------
1484 void logf(int line = __LINE__, string file = __FILE__,
1485 string funcName = __FUNCTION__,
1486 string prettyFuncName = __PRETTY_FUNCTION__,
1487 string moduleName = __MODULE__, A...)(const LogLevel ll,
1488 lazy string msg, lazy A args)
1490 static if (isLoggingActive) synchronized (mutex)
1492 import std.format : formattedWrite;
1494 if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
1496 this.beginLogMsg(file, line, funcName, prettyFuncName,
1497 moduleName, ll, thisTid, Clock.currTime, this);
1499 auto writer = MsgRange(this);
1500 formattedWrite(writer, msg, args);
1502 this.finishLogMsg();
1504 if (ll == LogLevel.fatal)
1505 this.fatalHandler_();
1510 /** This function logs data to the used $(D Logger) depending on a
1511 condition with the $(D LogLevel) of the used $(D Logger) in a
1512 $(D printf)-style manner.
1514 In order for the resulting log message to be logged the $(D LogLevel)
1515 of the used $(D Logger) must be greater or equal than the global
1516 $(D LogLevel) and the condition must be $(D true).
1519 condition = The condition must be $(D true) for the data to be logged.
1520 msg = The format string used for this log call.
1521 args = The data that should be logged.
1524 --------------------
1525 auto s = new FileLogger(stdout);
1526 s.logf(true ,"%d %s", 1337, "is number");
1527 s.logf(true ,"%d %s", 1337, "is number");
1528 s.logf(true ,"%d %s", 1337, "is number");
1529 s.logf(false ,"%d %s", 1337, "is number");
1530 s.logf(true ,"%d %s", 1337, "is number");
1531 --------------------
1533 void logf(int line = __LINE__, string file = __FILE__,
1534 string funcName = __FUNCTION__,
1535 string prettyFuncName = __PRETTY_FUNCTION__,
1536 string moduleName = __MODULE__, A...)(lazy bool condition,
1537 lazy string msg, lazy A args)
1539 static if (isLoggingActive) synchronized (mutex)
1541 import std.format : formattedWrite;
1543 if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel,
1546 this.beginLogMsg(file, line, funcName, prettyFuncName,
1547 moduleName, this.logLevel_, thisTid, Clock.currTime, this);
1549 auto writer = MsgRange(this);
1550 formattedWrite(writer, msg, args);
1552 this.finishLogMsg();
1554 if (this.logLevel_ == LogLevel.fatal)
1555 this.fatalHandler_();
1560 /** This method logs data to the used $(D Logger) with the $(D LogLevel)
1561 of the this $(D Logger) in a $(D printf)-style manner.
1563 In order for the data to be processed the $(D LogLevel) of the $(D Logger)
1564 must be greater or equal to the global $(D LogLevel).
1567 msg = The format string used for this log call.
1568 args = The data that should be logged.
1571 --------------------
1572 auto s = new FileLogger(stdout);
1573 s.logf("%d %s", 1337, "is number");
1574 s.logf("%d %s", 1337, "is number");
1575 s.logf("%d %s", 1337, "is number");
1576 s.logf("%d %s", 1337, "is number");
1577 s.logf("%d %s", 1337, "is number");
1578 --------------------
1580 void logf(int line = __LINE__, string file = __FILE__,
1581 string funcName = __FUNCTION__,
1582 string prettyFuncName = __PRETTY_FUNCTION__,
1583 string moduleName = __MODULE__, A...)(lazy string msg, lazy A args)
1585 static if (isLoggingActive) synchronized (mutex)
1587 import std.format : formattedWrite;
1589 if (isLoggingEnabled(this.logLevel_, this.logLevel_,
1592 this.beginLogMsg(file, line, funcName, prettyFuncName,
1593 moduleName, this.logLevel_, thisTid, Clock.currTime, this);
1595 auto writer = MsgRange(this);
1596 formattedWrite(writer, msg, args);
1598 this.finishLogMsg();
1600 if (this.logLevel_ == LogLevel.fatal)
1601 this.fatalHandler_();
1606 private void delegate() @safe fatalHandler_;
1607 private shared LogLevel logLevel_ = LogLevel.info;
1608 private Mutex mutex;
1610 protected Appender!string msgAppender;
1611 protected LogEntry header;
1616 private __gshared Logger stdSharedDefaultLogger;
1617 private shared Logger stdSharedLogger;
1618 private shared LogLevel stdLoggerGlobalLogLevel = LogLevel.all;
1620 /* This method returns the global default Logger.
1621 * Marked @trusted because of excessive reliance on __gshared data
1623 private @property Logger defaultSharedLoggerImpl() @trusted
1625 import std.conv : emplace;
1626 import std.stdio : stderr;
1628 static __gshared align(FileLogger.alignof) void[__traits(classInstanceSize, FileLogger)] _buffer;
1630 import std.concurrency : initOnce;
1631 initOnce!stdSharedDefaultLogger({
1632 auto buffer = cast(ubyte[]) _buffer;
1633 return emplace!FileLogger(buffer, stderr, LogLevel.all);
1636 return stdSharedDefaultLogger;
1639 /** This property sets and gets the default $(D Logger).
1643 sharedLog = new FileLogger(yourFile);
1645 The example sets a new $(D FileLogger) as new $(D sharedLog).
1647 If at some point you want to use the original default logger again, you can
1648 use $(D sharedLog = null;). This will put back the original.
1651 While getting and setting $(D sharedLog) is thread-safe, it has to be considered
1652 that the returned reference is only a current snapshot and in the following
1653 code, you must make sure no other thread reassigns to it between reading and
1654 writing $(D sharedLog).
1656 $(D sharedLog) is only thread-safe if the the used $(D Logger) is thread-safe.
1657 The default $(D Logger) is thread-safe.
1659 if (sharedLog !is myLogger)
1660 sharedLog = new myLogger;
1663 @property Logger sharedLog() @safe
1665 static auto trustedLoad(ref shared Logger logger) @trusted
1667 import core.atomic : atomicLoad, MemoryOrder;
1668 return cast() atomicLoad!(MemoryOrder.acq)(logger);
1669 //FIXME: Casting shared away here. Not good. See issue 16232.
1672 // If we have set up our own logger use that
1673 if (auto logger = trustedLoad(stdSharedLogger))
1679 // Otherwise resort to the default logger
1680 return defaultSharedLoggerImpl;
1685 @property void sharedLog(Logger logger) @trusted
1687 import core.atomic : atomicStore, MemoryOrder;
1688 atomicStore!(MemoryOrder.rel)(stdSharedLogger, cast(shared) logger);
1691 /** This methods get and set the global $(D LogLevel).
1693 Every log message with a $(D LogLevel) lower as the global $(D LogLevel)
1694 will be discarded before it reaches $(D writeLogMessage) method of any
1697 /* Implementation note:
1698 For any public logging call, the global log level shall only be queried once on
1699 entry. Otherwise when another threads changes the level, we would work with
1700 different levels at different spots in the code.
1702 @property LogLevel globalLogLevel() @safe @nogc
1704 return trustedLoad(stdLoggerGlobalLogLevel);
1708 @property void globalLogLevel(LogLevel ll) @safe
1710 trustedStore(stdLoggerGlobalLogLevel, ll);
1715 /** The $(D StdForwardLogger) will always forward anything to the sharedLog.
1717 The $(D StdForwardLogger) will not throw if data is logged with $(D
1720 class StdForwardLogger : Logger
1722 /** The default constructor for the $(D StdForwardLogger).
1725 lv = The $(D LogLevel) for the $(D MultiLogger). By default the $(D
1726 LogLevel) is $(D all).
1728 this(const LogLevel lv = LogLevel.all) @safe
1731 this.fatalHandler = delegate() {};
1734 override protected void writeLogMsg(ref LogEntry payload)
1736 sharedLog.forwardMsg(payload);
1743 auto nl1 = new StdForwardLogger(LogLevel.all);
1746 /** This $(D LogLevel) is unqiue to every thread.
1748 The thread local $(D Logger) will use this $(D LogLevel) to filter log calls
1749 every same way as presented earlier.
1751 //public LogLevel threadLogLevel = LogLevel.all;
1752 private Logger stdLoggerThreadLogger;
1753 private Logger stdLoggerDefaultThreadLogger;
1755 /* This method returns the thread local default Logger.
1757 private @property Logger stdThreadLocalLogImpl() @trusted
1759 import std.conv : emplace;
1761 static void*[(__traits(classInstanceSize, StdForwardLogger) - 1) / (void*).sizeof + 1] _buffer;
1763 auto buffer = cast(ubyte[]) _buffer;
1765 if (stdLoggerDefaultThreadLogger is null)
1767 stdLoggerDefaultThreadLogger = emplace!StdForwardLogger(buffer, LogLevel.all);
1769 return stdLoggerDefaultThreadLogger;
1772 /** This function returns a thread unique $(D Logger), that by default
1773 propergates all data logged to it to the $(D sharedLog).
1775 These properties can be used to set and get this $(D Logger). Every
1776 modification to this $(D Logger) will only be visible in the thread the
1777 modification has been done from.
1779 This $(D Logger) is called by the free standing log functions. This allows to
1780 create thread local redirections and still use the free standing log
1783 @property Logger stdThreadLocalLog() @safe
1785 // If we have set up our own logger use that
1786 if (auto logger = stdLoggerThreadLogger)
1789 // Otherwise resort to the default logger
1790 return stdThreadLocalLogImpl;
1794 @property void stdThreadLocalLog(Logger logger) @safe
1796 stdLoggerThreadLogger = logger;
1802 import std.experimental.logger.filelogger : FileLogger;
1803 import std.file : deleteme, remove;
1804 Logger l = stdThreadLocalLog;
1805 stdThreadLocalLog = new FileLogger(deleteme ~ "-someFile.log");
1806 scope(exit) remove(deleteme ~ "-someFile.log");
1808 auto tempLog = stdThreadLocalLog;
1809 stdThreadLocalLog = l;
1815 LogLevel ll = globalLogLevel;
1816 globalLogLevel = LogLevel.fatal;
1817 assert(globalLogLevel == LogLevel.fatal);
1818 globalLogLevel = ll;
1821 package class TestLogger : Logger
1826 string prettyFunc = null;
1830 this(const LogLevel lv = LogLevel.all) @safe
1835 override protected void writeLogMsg(ref LogEntry payload) @safe
1837 this.line = payload.line;
1838 this.file = payload.file;
1839 this.func = payload.funcName;
1840 this.prettyFunc = payload.prettyFuncName;
1841 this.lvl = payload.logLevel;
1842 this.msg = payload.msg;
1846 version (unittest) private void testFuncNames(Logger logger) @safe
1848 string s = "I'm here";
1854 auto tl1 = new TestLogger();
1856 assert(tl1.func == "std.experimental.logger.core.testFuncNames", tl1.func);
1857 assert(tl1.prettyFunc ==
1858 "void std.experimental.logger.core.testFuncNames(Logger logger) @safe",
1860 assert(tl1.msg == "I'm here", tl1.msg);
1865 auto tl1 = new TestLogger(LogLevel.all);
1867 assert(tl1.line == __LINE__ - 1);
1869 assert(tl1.line == __LINE__ - 1);
1871 assert(tl1.line == __LINE__ - 3);
1872 tl1.log(LogLevel.info);
1873 assert(tl1.line == __LINE__ - 1);
1874 tl1.log(LogLevel.off);
1875 assert(tl1.line == __LINE__ - 3);
1876 tl1.log(LogLevel.info, true);
1877 assert(tl1.line == __LINE__ - 1);
1878 tl1.log(LogLevel.info, false);
1879 assert(tl1.line == __LINE__ - 3);
1881 auto oldunspecificLogger = sharedLog;
1883 sharedLog = oldunspecificLogger;
1889 assert(tl1.line == __LINE__ - 1);
1892 assert(tl1.line == __LINE__ - 1);
1895 assert(tl1.line == __LINE__ - 1);
1897 log(LogLevel.warning, true);
1898 assert(tl1.line == __LINE__ - 1);
1901 assert(tl1.line == __LINE__ - 1);
1906 import std.experimental.logger.multilogger : MultiLogger;
1908 auto tl1 = new TestLogger;
1909 auto tl2 = new TestLogger;
1911 auto ml = new MultiLogger();
1912 ml.insertLogger("one", tl1);
1913 ml.insertLogger("two", tl2);
1915 string msg = "Hello Logger World";
1917 int lineNumber = __LINE__ - 1;
1918 assert(tl1.msg == msg);
1919 assert(tl1.line == lineNumber);
1920 assert(tl2.msg == msg);
1921 assert(tl2.line == lineNumber);
1923 ml.removeLogger("one");
1924 ml.removeLogger("two");
1925 auto n = ml.removeLogger("one");
1931 bool errorThrown = false;
1932 auto tl = new TestLogger;
1933 auto dele = delegate() {
1936 tl.fatalHandler = dele;
1938 assert(errorThrown);
1943 import std.conv : to;
1944 import std.exception : assertThrown, assertNotThrown;
1945 import std.format : format;
1947 auto l = new TestLogger(LogLevel.all);
1948 string msg = "Hello Logger World";
1950 int lineNumber = __LINE__ - 1;
1951 assert(l.msg == msg);
1952 assert(l.line == lineNumber);
1953 assert(l.logLevel == LogLevel.all);
1956 lineNumber = __LINE__ - 1;
1957 assert(l.msg == msg, l.msg);
1958 assert(l.line == lineNumber);
1959 assert(l.logLevel == LogLevel.all);
1962 assert(l.msg == msg);
1963 assert(l.line == lineNumber, to!string(l.line));
1964 assert(l.logLevel == LogLevel.all);
1966 msg = "%s Another message";
1968 lineNumber = __LINE__ - 1;
1969 assert(l.msg == msg.format("Yet"));
1970 assert(l.line == lineNumber);
1971 assert(l.logLevel == LogLevel.all);
1973 l.logf(true, msg, "Yet");
1974 lineNumber = __LINE__ - 1;
1975 assert(l.msg == msg.format("Yet"));
1976 assert(l.line == lineNumber);
1977 assert(l.logLevel == LogLevel.all);
1979 l.logf(false, msg, "Yet");
1980 assert(l.msg == msg.format("Yet"));
1981 assert(l.line == lineNumber);
1982 assert(l.logLevel == LogLevel.all);
1985 assertThrown!Throwable(l.logf(LogLevel.fatal, msg, "Yet"));
1987 lineNumber = __LINE__ - 2;
1988 assert(l.msg == msg.format("Yet"));
1989 assert(l.line == lineNumber);
1990 assert(l.logLevel == LogLevel.all);
1993 assertThrown!Throwable(l.logf(LogLevel.fatal, true, msg, "Yet"));
1995 lineNumber = __LINE__ - 2;
1996 assert(l.msg == msg.format("Yet"));
1997 assert(l.line == lineNumber);
1998 assert(l.logLevel == LogLevel.all);
2000 assertNotThrown(l.logf(LogLevel.fatal, false, msg, "Yet"));
2001 assert(l.msg == msg.format("Yet"));
2002 assert(l.line == lineNumber);
2003 assert(l.logLevel == LogLevel.all);
2005 auto oldunspecificLogger = sharedLog;
2007 assert(oldunspecificLogger.logLevel == LogLevel.all,
2008 to!string(oldunspecificLogger.logLevel));
2010 assert(l.logLevel == LogLevel.all);
2012 assert(globalLogLevel == LogLevel.all,
2013 to!string(globalLogLevel));
2017 sharedLog = oldunspecificLogger;
2020 assert(sharedLog.logLevel == LogLevel.all);
2021 assert(stdThreadLocalLog.logLevel == LogLevel.all);
2022 assert(globalLogLevel == LogLevel.all);
2024 msg = "Another message";
2026 lineNumber = __LINE__ - 1;
2027 assert(l.logLevel == LogLevel.all);
2028 assert(l.line == lineNumber, to!string(l.line));
2029 assert(l.msg == msg, l.msg);
2032 lineNumber = __LINE__ - 1;
2033 assert(l.msg == msg);
2034 assert(l.line == lineNumber);
2035 assert(l.logLevel == LogLevel.all);
2038 assert(l.msg == msg);
2039 assert(l.line == lineNumber);
2040 assert(l.logLevel == LogLevel.all);
2042 msg = "%s Another message";
2044 lineNumber = __LINE__ - 1;
2045 assert(l.msg == msg.format("Yet"));
2046 assert(l.line == lineNumber);
2047 assert(l.logLevel == LogLevel.all);
2049 logf(true, msg, "Yet");
2050 lineNumber = __LINE__ - 1;
2051 assert(l.msg == msg.format("Yet"));
2052 assert(l.line == lineNumber);
2053 assert(l.logLevel == LogLevel.all);
2055 logf(false, msg, "Yet");
2056 assert(l.msg == msg.format("Yet"));
2057 assert(l.line == lineNumber);
2058 assert(l.logLevel == LogLevel.all);
2060 msg = "%s Another message";
2062 assertThrown!Throwable(logf(LogLevel.fatal, msg, "Yet"));
2064 lineNumber = __LINE__ - 2;
2065 assert(l.msg == msg.format("Yet"));
2066 assert(l.line == lineNumber);
2067 assert(l.logLevel == LogLevel.all);
2070 assertThrown!Throwable(logf(LogLevel.fatal, true, msg, "Yet"));
2072 lineNumber = __LINE__ - 2;
2073 assert(l.msg == msg.format("Yet"));
2074 assert(l.line == lineNumber);
2075 assert(l.logLevel == LogLevel.all);
2077 assertNotThrown(logf(LogLevel.fatal, false, msg, "Yet"));
2078 assert(l.msg == msg.format("Yet"));
2079 assert(l.line == lineNumber);
2080 assert(l.logLevel == LogLevel.all);
2083 @system unittest // default logger
2085 import std.file : deleteme, exists, remove;
2086 import std.stdio : File;
2087 import std.string : indexOf;
2089 string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
2090 FileLogger l = new FileLogger(filename);
2091 auto oldunspecificLogger = sharedLog;
2097 assert(!exists(filename));
2098 sharedLog = oldunspecificLogger;
2099 globalLogLevel = LogLevel.all;
2102 string notWritten = "this should not be written to file";
2103 string written = "this should be written to file";
2105 globalLogLevel = LogLevel.critical;
2106 assert(globalLogLevel == LogLevel.critical);
2108 log(LogLevel.warning, notWritten);
2109 log(LogLevel.critical, written);
2114 auto file = File(filename, "r");
2117 string readLine = file.readln();
2118 assert(readLine.indexOf(written) != -1, readLine);
2119 assert(readLine.indexOf(notWritten) == -1, readLine);
2125 import std.file : deleteme, remove;
2126 import std.stdio : File;
2127 import std.string : indexOf;
2129 string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
2130 auto oldunspecificLogger = sharedLog;
2135 sharedLog = oldunspecificLogger;
2136 globalLogLevel = LogLevel.all;
2139 string notWritten = "this should not be written to file";
2140 string written = "this should be written to file";
2142 auto l = new FileLogger(filename);
2144 sharedLog.logLevel = LogLevel.critical;
2146 log(LogLevel.error, false, notWritten);
2147 log(LogLevel.critical, true, written);
2150 auto file = File(filename, "r");
2151 auto readLine = file.readln();
2152 assert(!readLine.empty, readLine);
2153 assert(readLine.indexOf(written) != -1);
2154 assert(readLine.indexOf(notWritten) == -1);
2160 import std.conv : to;
2162 auto tl = new TestLogger(LogLevel.all);
2165 assert(tl.line == l+1);
2166 assert(tl.msg == "a");
2167 assert(tl.logLevel == LogLevel.all);
2168 assert(globalLogLevel == LogLevel.all);
2171 assert(tl.msg == "b", tl.msg);
2172 assert(tl.line == l+1, to!string(tl.line));
2175 // testing possible log conditions
2178 import std.conv : to;
2179 import std.format : format;
2180 import std.string : indexOf;
2182 auto oldunspecificLogger = sharedLog;
2184 auto mem = new TestLogger;
2185 mem.fatalHandler = delegate() {};
2190 sharedLog = oldunspecificLogger;
2191 globalLogLevel = LogLevel.all;
2195 foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
2196 LogLevel.info, LogLevel.warning, LogLevel.error,
2197 LogLevel.critical, LogLevel.fatal, LogLevel.off])
2200 globalLogLevel = gll;
2202 foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
2203 LogLevel.info, LogLevel.warning, LogLevel.error,
2204 LogLevel.critical, LogLevel.fatal, LogLevel.off])
2209 foreach (cond; [true, false])
2211 foreach (condValue; [true, false])
2213 foreach (memOrG; [true, false])
2215 foreach (prntf; [true, false])
2217 foreach (ll2; [cast(LogLevel) LogLevel.all, LogLevel.trace,
2218 LogLevel.info, LogLevel.warning,
2219 LogLevel.error, LogLevel.critical,
2220 LogLevel.fatal, LogLevel.off])
2222 foreach (singleMulti; 0 .. 2)
2232 if (singleMulti == 0)
2234 mem.logf(ll2, condValue, "%s",
2236 lineCall = __LINE__;
2240 mem.logf(ll2, condValue,
2241 "%d %d", value, value);
2242 lineCall = __LINE__;
2247 if (singleMulti == 0)
2249 mem.logf(ll2, "%s", value);
2250 lineCall = __LINE__;
2254 mem.logf(ll2, "%d %d",
2256 lineCall = __LINE__;
2264 if (singleMulti == 0)
2266 mem.log(ll2, condValue,
2268 lineCall = __LINE__;
2272 mem.log(ll2, condValue,
2273 to!string(value), value);
2274 lineCall = __LINE__;
2279 if (singleMulti == 0)
2281 mem.log(ll2, to!string(value));
2282 lineCall = __LINE__;
2289 lineCall = __LINE__;
2300 if (singleMulti == 0)
2302 logf(ll2, condValue, "%s",
2304 lineCall = __LINE__;
2308 logf(ll2, condValue,
2309 "%s %d", value, value);
2310 lineCall = __LINE__;
2315 if (singleMulti == 0)
2317 logf(ll2, "%s", value);
2318 lineCall = __LINE__;
2322 logf(ll2, "%s %s", value,
2324 lineCall = __LINE__;
2332 if (singleMulti == 0)
2336 lineCall = __LINE__;
2340 log(ll2, condValue, value,
2342 lineCall = __LINE__;
2347 if (singleMulti == 0)
2349 log(ll2, to!string(value));
2350 lineCall = __LINE__;
2356 lineCall = __LINE__;
2362 string valueStr = to!string(value);
2365 bool ll2Off = (ll2 != LogLevel.off);
2366 bool gllOff = (gll != LogLevel.off);
2367 bool llOff = (ll != LogLevel.off);
2368 bool condFalse = (cond ? condValue : true);
2369 bool ll2VSgll = (ll2 >= gll);
2370 bool ll2VSll = (ll2 >= ll);
2372 bool shouldLog = ll2Off && gllOff && llOff
2373 && condFalse && ll2VSgll && ll2VSll;
2377 "go(%b) ll2o(%b) c(%b) lg(%b) ll(%b) s(%b)"
2378 , gll != LogLevel.off, ll2 != LogLevel.off,
2379 cond ? condValue : true,
2380 ll2 >= gll, ll2 >= ll, shouldLog);
2386 assert(mem.msg.indexOf(valueStr) != -1,
2388 "lineCall(%d) ll2Off(%u) gll(%u) ll(%u) ll2(%u) " ~
2389 "cond(%b) condValue(%b)" ~
2390 " memOrG(%b) shouldLog(%b) %s == %s" ~
2392 lineCall, ll2Off, gll, ll, ll2, cond,
2393 condValue, memOrG, shouldLog, mem.msg,
2394 valueStr, gllOff, llOff, condFalse,
2400 assert(mem.msg.indexOf(valueStr),
2402 "lineCall(%d) ll2Off(%u) gll(%u) ll(%u) ll2(%u) " ~
2403 "cond(%b) condValue(%b)" ~
2404 " memOrG(%b) shouldLog(%b) %s == %s" ~
2406 lineCall, ll2Off, gll, ll, ll2, cond,
2407 condValue, memOrG, shouldLog, mem.msg,
2408 valueStr, gllOff, llOff, condFalse,
2425 import std.conv : to;
2426 import std.format : format;
2427 import std.string : indexOf;
2429 auto oldunspecificLogger = sharedLog;
2431 auto mem = new TestLogger;
2432 mem.fatalHandler = delegate() {};
2437 sharedLog = oldunspecificLogger;
2438 globalLogLevel = LogLevel.all;
2442 foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
2443 LogLevel.info, LogLevel.warning, LogLevel.error,
2444 LogLevel.critical, LogLevel.fatal, LogLevel.off])
2447 globalLogLevel = gll;
2449 foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
2450 LogLevel.info, LogLevel.warning, LogLevel.error,
2451 LogLevel.critical, LogLevel.fatal, LogLevel.off])
2455 foreach (tll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
2456 LogLevel.info, LogLevel.warning, LogLevel.error,
2457 LogLevel.critical, LogLevel.fatal, LogLevel.off])
2459 stdThreadLocalLog.logLevel = tll;
2461 foreach (cond; [true, false])
2463 foreach (condValue; [true, false])
2465 foreach (memOrG; [true, false])
2467 foreach (prntf; [true, false])
2469 foreach (singleMulti; 0 .. 2)
2479 if (singleMulti == 0)
2481 mem.logf(condValue, "%s",
2483 lineCall = __LINE__;
2488 "%d %d", value, value);
2489 lineCall = __LINE__;
2494 if (singleMulti == 0)
2496 mem.logf("%s", value);
2497 lineCall = __LINE__;
2503 lineCall = __LINE__;
2511 if (singleMulti == 0)
2515 lineCall = __LINE__;
2520 to!string(value), value);
2521 lineCall = __LINE__;
2526 if (singleMulti == 0)
2528 mem.log(to!string(value));
2529 lineCall = __LINE__;
2533 mem.log(to!string(value),
2535 lineCall = __LINE__;
2546 if (singleMulti == 0)
2548 logf(condValue, "%s", value);
2549 lineCall = __LINE__;
2553 logf(condValue, "%s %d", value,
2555 lineCall = __LINE__;
2560 if (singleMulti == 0)
2563 lineCall = __LINE__;
2567 logf("%s %s", value, value);
2568 lineCall = __LINE__;
2576 if (singleMulti == 0)
2580 lineCall = __LINE__;
2584 log(condValue, value,
2586 lineCall = __LINE__;
2591 if (singleMulti == 0)
2593 log(to!string(value));
2594 lineCall = __LINE__;
2598 log(value, to!string(value));
2599 lineCall = __LINE__;
2605 string valueStr = to!string(value);
2608 bool gllOff = (gll != LogLevel.off);
2609 bool llOff = (ll != LogLevel.off);
2610 bool tllOff = (tll != LogLevel.off);
2611 bool llVSgll = (ll >= gll);
2613 (stdThreadLocalLog.logLevel >= ll);
2614 bool condFalse = (cond ? condValue : true);
2616 bool shouldLog = gllOff && llOff
2617 && (memOrG ? true : tllOff)
2620 (tll >= gll && tll >= ll))
2625 assert(mem.msg.indexOf(valueStr) != -1,
2626 format("\ngll(%s) ll(%s) tll(%s) " ~
2627 "cond(%s) condValue(%s) " ~
2628 "memOrG(%s) prntf(%s) " ~
2630 gll, ll, tll, cond, condValue,
2631 memOrG, prntf, singleMulti)
2632 ~ format(" gllOff(%s) llOff(%s) " ~
2633 "llVSgll(%s) tllVSll(%s) " ~
2634 "tllOff(%s) condFalse(%s) "
2636 gll != LogLevel.off,
2637 ll != LogLevel.off, llVSgll,
2638 tllVSll, tllOff, condFalse,
2640 ~ format("msg(%s) line(%s) " ~
2641 "lineCall(%s) valueStr(%s)",
2642 mem.msg, mem.line, lineCall,
2648 assert(mem.msg.indexOf(valueStr) == -1,
2649 format("\ngll(%s) ll(%s) tll(%s) " ~
2650 "cond(%s) condValue(%s) " ~
2651 "memOrG(%s) prntf(%s) " ~
2653 gll, ll, tll, cond, condValue,
2654 memOrG, prntf, singleMulti)
2655 ~ format(" gllOff(%s) llOff(%s) " ~
2656 "llVSgll(%s) tllVSll(%s) " ~
2657 "tllOff(%s) condFalse(%s) "
2659 gll != LogLevel.off,
2660 ll != LogLevel.off, llVSgll,
2661 tllVSll, tllOff, condFalse,
2663 ~ format("msg(%s) line(%s) " ~
2664 "lineCall(%s) valueStr(%s)",
2665 mem.msg, mem.line, lineCall,
2679 // testing more possible log conditions
2683 auto mem = new TestLogger;
2684 mem.fatalHandler = delegate() { fatalLog = true; };
2685 auto oldunspecificLogger = sharedLog;
2687 stdThreadLocalLog.logLevel = LogLevel.all;
2692 sharedLog = oldunspecificLogger;
2693 globalLogLevel = LogLevel.all;
2696 foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
2697 LogLevel.info, LogLevel.warning, LogLevel.error,
2698 LogLevel.critical, LogLevel.fatal, LogLevel.off])
2701 globalLogLevel = gll;
2703 foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
2704 LogLevel.info, LogLevel.warning, LogLevel.error,
2705 LogLevel.critical, LogLevel.fatal, LogLevel.off])
2709 foreach (tll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
2710 LogLevel.info, LogLevel.warning, LogLevel.error,
2711 LogLevel.critical, LogLevel.fatal, LogLevel.off])
2713 stdThreadLocalLog.logLevel = tll;
2715 foreach (cond; [true, false])
2717 assert(globalLogLevel == gll);
2718 assert(mem.logLevel == ll);
2720 bool gllVSll = LogLevel.trace >= globalLogLevel;
2721 bool llVSgll = ll >= globalLogLevel;
2722 bool lVSll = LogLevel.trace >= ll;
2723 bool gllOff = globalLogLevel != LogLevel.off;
2724 bool llOff = mem.logLevel != LogLevel.off;
2725 bool tllOff = stdThreadLocalLog.logLevel != LogLevel.off;
2726 bool tllVSll = tll >= ll;
2727 bool tllVSgll = tll >= gll;
2728 bool lVSgll = LogLevel.trace >= tll;
2730 bool test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
2731 bool testG = gllOff && llOff && tllOff && lVSgll && tllVSll && tllVSgll && cond;
2735 writefln("gll(%3u) ll(%3u) cond(%b) test(%b)",
2736 gll, ll, cond, test);
2737 writefln("%b %b %b %b %b %b test2(%b)", llVSgll, gllVSll, lVSll,
2738 gllOff, llOff, cond, test2);
2741 mem.trace(__LINE__); int line = __LINE__;
2742 assert(test ? mem.line == line : true); line = -1;
2744 trace(__LINE__); line = __LINE__;
2745 assert(testG ? mem.line == line : true); line = -1;
2747 mem.trace(cond, __LINE__); line = __LINE__;
2748 assert(test ? mem.line == line : true); line = -1;
2750 trace(cond, __LINE__); line = __LINE__;
2751 assert(testG ? mem.line == line : true); line = -1;
2753 mem.tracef("%d", __LINE__); line = __LINE__;
2754 assert(test ? mem.line == line : true); line = -1;
2756 tracef("%d", __LINE__); line = __LINE__;
2757 assert(testG ? mem.line == line : true); line = -1;
2759 mem.tracef(cond, "%d", __LINE__); line = __LINE__;
2760 assert(test ? mem.line == line : true); line = -1;
2762 tracef(cond, "%d", __LINE__); line = __LINE__;
2763 assert(testG ? mem.line == line : true); line = -1;
2765 llVSgll = ll >= globalLogLevel;
2766 lVSll = LogLevel.info >= ll;
2767 lVSgll = LogLevel.info >= tll;
2768 test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
2769 testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
2772 mem.info(__LINE__); line = __LINE__;
2773 assert(test ? mem.line == line : true); line = -1;
2775 info(__LINE__); line = __LINE__;
2776 assert(testG ? mem.line == line : true); line = -1;
2778 mem.info(cond, __LINE__); line = __LINE__;
2779 assert(test ? mem.line == line : true); line = -1;
2781 info(cond, __LINE__); line = __LINE__;
2782 assert(testG ? mem.line == line : true); line = -1;
2784 mem.infof("%d", __LINE__); line = __LINE__;
2785 assert(test ? mem.line == line : true); line = -1;
2787 infof("%d", __LINE__); line = __LINE__;
2788 assert(testG ? mem.line == line : true); line = -1;
2790 mem.infof(cond, "%d", __LINE__); line = __LINE__;
2791 assert(test ? mem.line == line : true); line = -1;
2793 infof(cond, "%d", __LINE__); line = __LINE__;
2794 assert(testG ? mem.line == line : true); line = -1;
2796 llVSgll = ll >= globalLogLevel;
2797 lVSll = LogLevel.warning >= ll;
2798 lVSgll = LogLevel.warning >= tll;
2799 test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
2800 testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
2803 mem.warning(__LINE__); line = __LINE__;
2804 assert(test ? mem.line == line : true); line = -1;
2806 warning(__LINE__); line = __LINE__;
2807 assert(testG ? mem.line == line : true); line = -1;
2809 mem.warning(cond, __LINE__); line = __LINE__;
2810 assert(test ? mem.line == line : true); line = -1;
2812 warning(cond, __LINE__); line = __LINE__;
2813 assert(testG ? mem.line == line : true); line = -1;
2815 mem.warningf("%d", __LINE__); line = __LINE__;
2816 assert(test ? mem.line == line : true); line = -1;
2818 warningf("%d", __LINE__); line = __LINE__;
2819 assert(testG ? mem.line == line : true); line = -1;
2821 mem.warningf(cond, "%d", __LINE__); line = __LINE__;
2822 assert(test ? mem.line == line : true); line = -1;
2824 warningf(cond, "%d", __LINE__); line = __LINE__;
2825 assert(testG ? mem.line == line : true); line = -1;
2827 llVSgll = ll >= globalLogLevel;
2828 lVSll = LogLevel.critical >= ll;
2829 lVSgll = LogLevel.critical >= tll;
2830 test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
2831 testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
2834 mem.critical(__LINE__); line = __LINE__;
2835 assert(test ? mem.line == line : true); line = -1;
2837 critical(__LINE__); line = __LINE__;
2838 assert(testG ? mem.line == line : true); line = -1;
2840 mem.critical(cond, __LINE__); line = __LINE__;
2841 assert(test ? mem.line == line : true); line = -1;
2843 critical(cond, __LINE__); line = __LINE__;
2844 assert(testG ? mem.line == line : true); line = -1;
2846 mem.criticalf("%d", __LINE__); line = __LINE__;
2847 assert(test ? mem.line == line : true); line = -1;
2849 criticalf("%d", __LINE__); line = __LINE__;
2850 assert(testG ? mem.line == line : true); line = -1;
2852 mem.criticalf(cond, "%d", __LINE__); line = __LINE__;
2853 assert(test ? mem.line == line : true); line = -1;
2855 criticalf(cond, "%d", __LINE__); line = __LINE__;
2856 assert(testG ? mem.line == line : true); line = -1;
2858 llVSgll = ll >= globalLogLevel;
2859 lVSll = LogLevel.fatal >= ll;
2860 lVSgll = LogLevel.fatal >= tll;
2861 test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
2862 testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
2865 mem.fatal(__LINE__); line = __LINE__;
2866 assert(test ? mem.line == line : true); line = -1;
2867 assert(test ? fatalLog : true);
2870 fatal(__LINE__); line = __LINE__;
2871 assert(testG ? mem.line == line : true); line = -1;
2872 assert(testG ? fatalLog : true);
2875 mem.fatal(cond, __LINE__); line = __LINE__;
2876 assert(test ? mem.line == line : true); line = -1;
2877 assert(test ? fatalLog : true);
2880 fatal(cond, __LINE__); line = __LINE__;
2881 assert(testG ? mem.line == line : true); line = -1;
2882 assert(testG ? fatalLog : true);
2885 mem.fatalf("%d", __LINE__); line = __LINE__;
2886 assert(test ? mem.line == line : true); line = -1;
2887 assert(test ? fatalLog : true);
2890 fatalf("%d", __LINE__); line = __LINE__;
2891 assert(testG ? mem.line == line : true); line = -1;
2892 assert(testG ? fatalLog : true);
2895 mem.fatalf(cond, "%d", __LINE__); line = __LINE__;
2896 assert(test ? mem.line == line : true); line = -1;
2897 assert(test ? fatalLog : true);
2900 fatalf(cond, "%d", __LINE__); line = __LINE__;
2901 assert(testG ? mem.line == line : true); line = -1;
2902 assert(testG ? fatalLog : true);
2913 import std.string : indexOf;
2915 auto oldunspecificLogger = sharedLog;
2919 sharedLog = oldunspecificLogger;
2920 globalLogLevel = LogLevel.all;
2923 auto tl = new TestLogger(LogLevel.info);
2927 assert(tl.msg.indexOf("trace") == -1);
2933 import std.experimental.logger.multilogger : MultiLogger;
2934 import std.string : indexOf;
2936 stdThreadLocalLog.logLevel = LogLevel.all;
2938 auto oldunspecificLogger = sharedLog;
2942 sharedLog = oldunspecificLogger;
2943 globalLogLevel = LogLevel.all;
2946 auto logger = new MultiLogger(LogLevel.error);
2948 auto tl = new TestLogger(LogLevel.info);
2949 logger.insertLogger("required", tl);
2953 assert(tl.msg.indexOf("trace") == -1);
2955 assert(tl.msg.indexOf("info") == -1);
2957 assert(tl.msg.indexOf("error") == 0);
2962 import std.exception : assertThrown;
2963 auto tl = new TestLogger();
2964 assertThrown!Throwable(tl.fatal("fatal"));
2967 // log objects with non-safe toString
2972 string toString() const @system
2978 auto tl = new TestLogger();
2980 assert(tl.msg == "test");
2983 // Workaround for atomics not allowed in @safe code
2984 private auto trustedLoad(T)(ref shared T value) @trusted
2986 import core.atomic : atomicLoad, MemoryOrder;
2987 return atomicLoad!(MemoryOrder.acq)(value);
2991 private void trustedStore(T)(ref shared T dst, ref T src) @trusted
2993 import core.atomic : atomicStore, MemoryOrder;
2994 atomicStore!(MemoryOrder.rel)(dst, src);
2997 // check that thread-local logging does not propagate
3001 import core.atomic, core.thread, std.concurrency;
3003 static shared logged_count = 0;
3005 class TestLog : Logger
3011 super (LogLevel.trace);
3015 override void writeLogMsg(ref LogEntry payload) @trusted
3017 assert(thisTid == this.tid);
3018 atomicOp!"+="(logged_count, 1);
3022 class IgnoredLog : Logger
3026 super (LogLevel.trace);
3029 override void writeLogMsg(ref LogEntry payload) @trusted
3035 auto oldSharedLog = sharedLog;
3038 sharedLog = oldSharedLog;
3041 sharedLog = new IgnoredLog;
3046 spawned ~= new Thread({
3047 stdThreadLocalLog = new TestLog;
3048 trace("zzzzzzzzzz");
3050 spawned[$-1].start();
3053 foreach (t; spawned)
3056 assert(atomicOp!"=="(logged_count, 4));
3061 auto dl = cast(FileLogger) sharedLog;
3062 assert(dl !is null);
3063 assert(dl.logLevel == LogLevel.all);
3064 assert(globalLogLevel == LogLevel.all);
3066 auto tl = cast(StdForwardLogger) stdThreadLocalLog;
3067 assert(tl !is null);
3068 stdThreadLocalLog.logLevel = LogLevel.all;
3074 import std.typecons : Nullable;
3077 auto l = new TestLogger();
3078 l.infof("log: %s", a);
3079 assert(l.msg == "log: 1");
3082 // Ensure @system toString methods work
3085 enum SystemToStringMsg = "SystemToString";
3086 static struct SystemToString
3088 string toString() @system
3090 return SystemToStringMsg;
3094 auto tl = new TestLogger();
3098 assert(tl.msg == SystemToStringMsg);
3104 import std.format : format;
3107 string s = format("%(%02x%)", data); // format 00
3110 auto tl = new TestLogger();
3112 tl.infof("%(%02x%)", data); // infof 000
3116 for (; i < s.length; ++i)
3118 assert(s[s.length - 1 - i] == fs[fs.length - 1 - i], fs);
3120 assert(fs.length == 2);
3126 import std.conv : to;
3127 auto tl = new TestLogger();
3128 tl.log("123456789".to!wstring);
3129 assert(tl.msg == "123456789");
3135 import std.conv : to;
3136 auto tl = new TestLogger();
3137 tl.log("123456789"d);
3138 assert(tl.msg == "123456789");
3144 import std.file : exists, remove;
3145 import std.stdio : File;
3146 import std.string : indexOf;
3148 string fn = "logfile.log";
3154 auto oldShared = sharedLog;
3157 sharedLog = oldShared;
3164 auto ts = [ "Test log 1", "Test log 2", "Test log 3"];
3166 auto fl = new FileLogger(fn);
3176 auto l = f.byLine();
3181 assert(it.indexOf(ts[idx]) != -1, it);