aboutsummaryrefslogtreecommitdiff
path: root/libphobos/src/std/experimental/logger/multilogger.d
diff options
context:
space:
mode:
Diffstat (limited to 'libphobos/src/std/experimental/logger/multilogger.d')
-rw-r--r--libphobos/src/std/experimental/logger/multilogger.d197
1 files changed, 197 insertions, 0 deletions
diff --git a/libphobos/src/std/experimental/logger/multilogger.d b/libphobos/src/std/experimental/logger/multilogger.d
new file mode 100644
index 0000000..ed9cfd9
--- /dev/null
+++ b/libphobos/src/std/experimental/logger/multilogger.d
@@ -0,0 +1,197 @@
+///
+module std.experimental.logger.multilogger;
+
+import std.experimental.logger.core;
+import std.experimental.logger.filelogger;
+
+/** This Element is stored inside the $(D MultiLogger) and associates a
+$(D Logger) to a $(D string).
+*/
+struct MultiLoggerEntry
+{
+ string name; /// The name if the $(D Logger)
+ Logger logger; /// The stored $(D Logger)
+}
+
+/** MultiLogger logs to multiple $(D Logger). The $(D Logger)s are stored in an
+$(D Logger[]) in their order of insertion.
+
+Every data logged to this $(D MultiLogger) will be distributed to all the $(D
+Logger)s inserted into it. This $(D MultiLogger) implementation can
+hold multiple $(D Logger)s with the same name. If the method $(D removeLogger)
+is used to remove a $(D Logger) only the first occurrence with that name will
+be removed.
+*/
+class MultiLogger : Logger
+{
+ /** A constructor for the $(D MultiLogger) Logger.
+
+ Params:
+ lv = The $(D LogLevel) for the $(D MultiLogger). By default the
+ $(D LogLevel) for $(D MultiLogger) is $(D LogLevel.all).
+
+ Example:
+ -------------
+ auto l1 = new MultiLogger(LogLevel.trace);
+ -------------
+ */
+ this(const LogLevel lv = LogLevel.all) @safe
+ {
+ super(lv);
+ }
+
+ /** This member holds all $(D Logger)s stored in the $(D MultiLogger).
+
+ When inheriting from $(D MultiLogger) this member can be used to gain
+ access to the stored $(D Logger).
+ */
+ protected MultiLoggerEntry[] logger;
+
+ /** This method inserts a new Logger into the $(D MultiLogger).
+
+ Params:
+ name = The name of the $(D Logger) to insert.
+ newLogger = The $(D Logger) to insert.
+ */
+ void insertLogger(string name, Logger newLogger) @safe
+ {
+ this.logger ~= MultiLoggerEntry(name, newLogger);
+ }
+
+ /** This method removes a Logger from the $(D MultiLogger).
+
+ Params:
+ toRemove = The name of the $(D Logger) to remove. If the $(D Logger)
+ is not found $(D null) will be returned. Only the first occurrence of
+ a $(D Logger) with the given name will be removed.
+
+ Returns: The removed $(D Logger).
+ */
+ Logger removeLogger(in char[] toRemove) @safe
+ {
+ import std.algorithm.mutation : copy;
+ import std.range.primitives : back, popBack;
+ for (size_t i = 0; i < this.logger.length; ++i)
+ {
+ if (this.logger[i].name == toRemove)
+ {
+ Logger ret = this.logger[i].logger;
+ this.logger[i] = this.logger.back;
+ this.logger.popBack();
+
+ return ret;
+ }
+ }
+
+ return null;
+ }
+
+ /* The override to pass the payload to all children of the
+ $(D MultiLoggerBase).
+ */
+ override protected void writeLogMsg(ref LogEntry payload) @safe
+ {
+ foreach (it; this.logger)
+ {
+ /* We don't perform any checks here to avoid race conditions.
+ Instead the child will check on its own if its log level matches
+ and assume LogLevel.all for the globalLogLevel (since we already
+ know the message passes this test).
+ */
+ it.logger.forwardMsg(payload);
+ }
+ }
+}
+
+@safe unittest
+{
+ import std.exception : assertThrown;
+ import std.experimental.logger.nulllogger;
+ auto a = new MultiLogger;
+ auto n0 = new NullLogger();
+ auto n1 = new NullLogger();
+ a.insertLogger("zero", n0);
+ a.insertLogger("one", n1);
+
+ auto n0_1 = a.removeLogger("zero");
+ assert(n0_1 is n0);
+ auto n = a.removeLogger("zero");
+ assert(n is null);
+
+ auto n1_1 = a.removeLogger("one");
+ assert(n1_1 is n1);
+ n = a.removeLogger("one");
+ assert(n is null);
+}
+
+@safe unittest
+{
+ auto a = new MultiLogger;
+ auto n0 = new TestLogger;
+ auto n1 = new TestLogger;
+ a.insertLogger("zero", n0);
+ a.insertLogger("one", n1);
+
+ a.log("Hello TestLogger"); int line = __LINE__;
+ assert(n0.msg == "Hello TestLogger");
+ assert(n0.line == line);
+ assert(n1.msg == "Hello TestLogger");
+ assert(n1.line == line);
+}
+
+// Issue #16
+@system unittest
+{
+ import std.file : deleteme;
+ import std.stdio : File;
+ import std.string : indexOf;
+ string logName = deleteme ~ __FUNCTION__ ~ ".log";
+ auto logFileOutput = File(logName, "w");
+ scope(exit)
+ {
+ import std.file : remove;
+ logFileOutput.close();
+ remove(logName);
+ }
+ auto traceLog = new FileLogger(logFileOutput, LogLevel.all);
+ auto infoLog = new TestLogger(LogLevel.info);
+
+ auto root = new MultiLogger(LogLevel.all);
+ root.insertLogger("fileLogger", traceLog);
+ root.insertLogger("stdoutLogger", infoLog);
+
+ string tMsg = "A trace message";
+ root.trace(tMsg); int line1 = __LINE__;
+
+ assert(infoLog.line != line1);
+ assert(infoLog.msg != tMsg);
+
+ string iMsg = "A info message";
+ root.info(iMsg); int line2 = __LINE__;
+
+ assert(infoLog.line == line2);
+ assert(infoLog.msg == iMsg, infoLog.msg ~ ":" ~ iMsg);
+
+ logFileOutput.close();
+ logFileOutput = File(logName, "r");
+ assert(logFileOutput.isOpen);
+ assert(!logFileOutput.eof);
+
+ auto line = logFileOutput.readln();
+ assert(line.indexOf(tMsg) != -1, line ~ ":" ~ tMsg);
+ assert(!logFileOutput.eof);
+ line = logFileOutput.readln();
+ assert(line.indexOf(iMsg) != -1, line ~ ":" ~ tMsg);
+}
+
+@safe unittest
+{
+ auto dl = cast(FileLogger) sharedLog;
+ assert(dl !is null);
+ assert(dl.logLevel == LogLevel.all);
+ assert(globalLogLevel == LogLevel.all);
+
+ auto tl = cast(StdForwardLogger) stdThreadLocalLog;
+ assert(tl !is null);
+ stdThreadLocalLog.logLevel = LogLevel.all;
+}