mirror of
https://github.com/dlang/phobos.git
synced 2025-04-27 13:40:20 +03:00
200 lines
5.5 KiB
D
200 lines
5.5 KiB
D
// Written in the D programming language.
|
|
/**
|
|
Source: $(PHOBOSSRC std/logger/multilogger.d)
|
|
*/
|
|
module std.logger.multilogger;
|
|
|
|
import std.logger.core;
|
|
import std.logger.filelogger;
|
|
|
|
/** This Element is stored inside the `MultiLogger` and associates a
|
|
`Logger` to a `string`.
|
|
*/
|
|
struct MultiLoggerEntry
|
|
{
|
|
string name; /// The name if the `Logger`
|
|
Logger logger; /// The stored `Logger`
|
|
}
|
|
|
|
/** MultiLogger logs to multiple `Logger`. The `Logger`s are stored in an
|
|
`Logger[]` in their order of insertion.
|
|
|
|
Every data logged to this `MultiLogger` will be distributed to all the $(D
|
|
Logger)s inserted into it. This `MultiLogger` implementation can
|
|
hold multiple `Logger`s with the same name. If the method `removeLogger`
|
|
is used to remove a `Logger` only the first occurrence with that name will
|
|
be removed.
|
|
*/
|
|
class MultiLogger : Logger
|
|
{
|
|
/** A constructor for the `MultiLogger` Logger.
|
|
|
|
Params:
|
|
lv = The `LogLevel` for the `MultiLogger`. By default the
|
|
`LogLevel` for `MultiLogger` is `LogLevel.all`.
|
|
|
|
Example:
|
|
-------------
|
|
auto l1 = new MultiLogger(LogLevel.trace);
|
|
-------------
|
|
*/
|
|
this(const LogLevel lv = LogLevel.all) @safe
|
|
{
|
|
super(lv);
|
|
}
|
|
|
|
/** This member holds all `Logger`s stored in the `MultiLogger`.
|
|
|
|
When inheriting from `MultiLogger` this member can be used to gain
|
|
access to the stored `Logger`.
|
|
*/
|
|
protected MultiLoggerEntry[] logger;
|
|
|
|
/** This method inserts a new Logger into the `MultiLogger`.
|
|
|
|
Params:
|
|
name = The name of the `Logger` to insert.
|
|
newLogger = The `Logger` to insert.
|
|
*/
|
|
void insertLogger(string name, Logger newLogger) @safe
|
|
{
|
|
this.logger ~= MultiLoggerEntry(name, newLogger);
|
|
}
|
|
|
|
/** This method removes a Logger from the `MultiLogger`.
|
|
|
|
Params:
|
|
toRemove = The name of the `Logger` to remove. If the `Logger`
|
|
is not found `null` will be returned. Only the first occurrence of
|
|
a `Logger` with the given name will be removed.
|
|
|
|
Returns: The removed `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
|
|
`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.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);
|
|
}
|
|
|
|
@system unittest
|
|
{
|
|
auto dl = cast(FileLogger) sharedLog;
|
|
assert(dl !is null);
|
|
assert(dl.logLevel == LogLevel.info);
|
|
assert(globalLogLevel == LogLevel.all);
|
|
|
|
auto tl = cast(StdForwardLogger) stdThreadLocalLog;
|
|
assert(tl !is null);
|
|
stdThreadLocalLog.logLevel = LogLevel.all;
|
|
}
|