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