1 // Written in the D programming language.
3 Source: $(PHOBOSSRC std/experimental/logger/multilogger.d)
5 module std.experimental.logger.multilogger;
7 import std.experimental.logger.core;
8 import std.experimental.logger.filelogger;
10 /** This Element is stored inside the `MultiLogger` and associates a
11 `Logger` to a `string`.
13 struct MultiLoggerEntry
15 string name; /// The name if the `Logger`
16 Logger logger; /// The stored `Logger`
19 /** MultiLogger logs to multiple `Logger`. The `Logger`s are stored in an
20 `Logger[]` in their order of insertion.
22 Every data logged to this `MultiLogger` will be distributed to all the $(D
23 Logger)s inserted into it. This `MultiLogger` implementation can
24 hold multiple `Logger`s with the same name. If the method `removeLogger`
25 is used to remove a `Logger` only the first occurrence with that name will
28 class MultiLogger : Logger
30 /** A constructor for the `MultiLogger` Logger.
33 lv = The `LogLevel` for the `MultiLogger`. By default the
34 `LogLevel` for `MultiLogger` is `LogLevel.all`.
38 auto l1 = new MultiLogger(LogLevel.trace);
41 this(const LogLevel lv = LogLevel.all) @safe
46 /** This member holds all `Logger`s stored in the `MultiLogger`.
48 When inheriting from `MultiLogger` this member can be used to gain
49 access to the stored `Logger`.
51 protected MultiLoggerEntry[] logger;
53 /** This method inserts a new Logger into the `MultiLogger`.
56 name = The name of the `Logger` to insert.
57 newLogger = The `Logger` to insert.
59 void insertLogger(string name, Logger newLogger) @safe
61 this.logger ~= MultiLoggerEntry(name, newLogger);
64 /** This method removes a Logger from the `MultiLogger`.
67 toRemove = The name of the `Logger` to remove. If the `Logger`
68 is not found `null` will be returned. Only the first occurrence of
69 a `Logger` with the given name will be removed.
71 Returns: The removed `Logger`.
73 Logger removeLogger(in char[] toRemove) @safe
75 import std.algorithm.mutation : copy;
76 import std.range.primitives : back, popBack;
77 for (size_t i = 0; i < this.logger.length; ++i)
79 if (this.logger[i].name == toRemove)
81 Logger ret = this.logger[i].logger;
82 this.logger[i] = this.logger.back;
83 this.logger.popBack();
92 /* The override to pass the payload to all children of the
95 override protected void writeLogMsg(ref LogEntry payload) @safe
97 foreach (it; this.logger)
99 /* We don't perform any checks here to avoid race conditions.
100 Instead the child will check on its own if its log level matches
101 and assume LogLevel.all for the globalLogLevel (since we already
102 know the message passes this test).
104 it.logger.forwardMsg(payload);
111 import std.exception : assertThrown;
112 import std.experimental.logger.nulllogger;
113 auto a = new MultiLogger;
114 auto n0 = new NullLogger();
115 auto n1 = new NullLogger();
116 a.insertLogger("zero", n0);
117 a.insertLogger("one", n1);
119 auto n0_1 = a.removeLogger("zero");
121 auto n = a.removeLogger("zero");
124 auto n1_1 = a.removeLogger("one");
126 n = a.removeLogger("one");
132 auto a = new MultiLogger;
133 auto n0 = new TestLogger;
134 auto n1 = new TestLogger;
135 a.insertLogger("zero", n0);
136 a.insertLogger("one", n1);
138 a.log("Hello TestLogger"); int line = __LINE__;
139 assert(n0.msg == "Hello TestLogger");
140 assert(n0.line == line);
141 assert(n1.msg == "Hello TestLogger");
142 assert(n1.line == line);
148 import std.file : deleteme;
149 import std.stdio : File;
150 import std.string : indexOf;
151 string logName = deleteme ~ __FUNCTION__ ~ ".log";
152 auto logFileOutput = File(logName, "w");
155 import std.file : remove;
156 logFileOutput.close();
159 auto traceLog = new FileLogger(logFileOutput, LogLevel.all);
160 auto infoLog = new TestLogger(LogLevel.info);
162 auto root = new MultiLogger(LogLevel.all);
163 root.insertLogger("fileLogger", traceLog);
164 root.insertLogger("stdoutLogger", infoLog);
166 string tMsg = "A trace message";
167 root.trace(tMsg); int line1 = __LINE__;
169 assert(infoLog.line != line1);
170 assert(infoLog.msg != tMsg);
172 string iMsg = "A info message";
173 root.info(iMsg); int line2 = __LINE__;
175 assert(infoLog.line == line2);
176 assert(infoLog.msg == iMsg, infoLog.msg ~ ":" ~ iMsg);
178 logFileOutput.close();
179 logFileOutput = File(logName, "r");
180 assert(logFileOutput.isOpen);
181 assert(!logFileOutput.eof);
183 auto line = logFileOutput.readln();
184 assert(line.indexOf(tMsg) != -1, line ~ ":" ~ tMsg);
185 assert(!logFileOutput.eof);
186 line = logFileOutput.readln();
187 assert(line.indexOf(iMsg) != -1, line ~ ":" ~ tMsg);
192 auto dl = cast(FileLogger) sharedLog;
194 assert(dl.logLevel == LogLevel.all);
195 assert(globalLogLevel == LogLevel.all);
197 auto tl = cast(StdForwardLogger) stdThreadLocalLog;
199 stdThreadLocalLog.logLevel = LogLevel.all;