]> git.ipfire.org Git - thirdparty/gcc.git/blob - libphobos/src/std/experimental/logger/multilogger.d
d: Import dmd b8384668f, druntime e6caaab9, phobos 5ab9ad256 (v2.098.0-beta.1)
[thirdparty/gcc.git] / libphobos / src / std / experimental / logger / multilogger.d
1 // Written in the D programming language.
2 /**
3 Source: $(PHOBOSSRC std/experimental/logger/multilogger.d)
4 */
5 module std.experimental.logger.multilogger;
6
7 import std.experimental.logger.core;
8 import std.experimental.logger.filelogger;
9
10 /** This Element is stored inside the `MultiLogger` and associates a
11 `Logger` to a `string`.
12 */
13 struct MultiLoggerEntry
14 {
15 string name; /// The name if the `Logger`
16 Logger logger; /// The stored `Logger`
17 }
18
19 /** MultiLogger logs to multiple `Logger`. The `Logger`s are stored in an
20 `Logger[]` in their order of insertion.
21
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
26 be removed.
27 */
28 class MultiLogger : Logger
29 {
30 /** A constructor for the `MultiLogger` Logger.
31
32 Params:
33 lv = The `LogLevel` for the `MultiLogger`. By default the
34 `LogLevel` for `MultiLogger` is `LogLevel.all`.
35
36 Example:
37 -------------
38 auto l1 = new MultiLogger(LogLevel.trace);
39 -------------
40 */
41 this(const LogLevel lv = LogLevel.all) @safe
42 {
43 super(lv);
44 }
45
46 /** This member holds all `Logger`s stored in the `MultiLogger`.
47
48 When inheriting from `MultiLogger` this member can be used to gain
49 access to the stored `Logger`.
50 */
51 protected MultiLoggerEntry[] logger;
52
53 /** This method inserts a new Logger into the `MultiLogger`.
54
55 Params:
56 name = The name of the `Logger` to insert.
57 newLogger = The `Logger` to insert.
58 */
59 void insertLogger(string name, Logger newLogger) @safe
60 {
61 this.logger ~= MultiLoggerEntry(name, newLogger);
62 }
63
64 /** This method removes a Logger from the `MultiLogger`.
65
66 Params:
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.
70
71 Returns: The removed `Logger`.
72 */
73 Logger removeLogger(in char[] toRemove) @safe
74 {
75 import std.algorithm.mutation : copy;
76 import std.range.primitives : back, popBack;
77 for (size_t i = 0; i < this.logger.length; ++i)
78 {
79 if (this.logger[i].name == toRemove)
80 {
81 Logger ret = this.logger[i].logger;
82 this.logger[i] = this.logger.back;
83 this.logger.popBack();
84
85 return ret;
86 }
87 }
88
89 return null;
90 }
91
92 /* The override to pass the payload to all children of the
93 `MultiLoggerBase`.
94 */
95 override protected void writeLogMsg(ref LogEntry payload) @safe
96 {
97 foreach (it; this.logger)
98 {
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).
103 */
104 it.logger.forwardMsg(payload);
105 }
106 }
107 }
108
109 @safe unittest
110 {
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);
118
119 auto n0_1 = a.removeLogger("zero");
120 assert(n0_1 is n0);
121 auto n = a.removeLogger("zero");
122 assert(n is null);
123
124 auto n1_1 = a.removeLogger("one");
125 assert(n1_1 is n1);
126 n = a.removeLogger("one");
127 assert(n is null);
128 }
129
130 @safe unittest
131 {
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);
137
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);
143 }
144
145 // Issue #16
146 @system unittest
147 {
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");
153 scope(exit)
154 {
155 import std.file : remove;
156 logFileOutput.close();
157 remove(logName);
158 }
159 auto traceLog = new FileLogger(logFileOutput, LogLevel.all);
160 auto infoLog = new TestLogger(LogLevel.info);
161
162 auto root = new MultiLogger(LogLevel.all);
163 root.insertLogger("fileLogger", traceLog);
164 root.insertLogger("stdoutLogger", infoLog);
165
166 string tMsg = "A trace message";
167 root.trace(tMsg); int line1 = __LINE__;
168
169 assert(infoLog.line != line1);
170 assert(infoLog.msg != tMsg);
171
172 string iMsg = "A info message";
173 root.info(iMsg); int line2 = __LINE__;
174
175 assert(infoLog.line == line2);
176 assert(infoLog.msg == iMsg, infoLog.msg ~ ":" ~ iMsg);
177
178 logFileOutput.close();
179 logFileOutput = File(logName, "r");
180 assert(logFileOutput.isOpen);
181 assert(!logFileOutput.eof);
182
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);
188 }
189
190 @safe unittest
191 {
192 auto dl = cast(FileLogger) sharedLog;
193 assert(dl !is null);
194 assert(dl.logLevel == LogLevel.all);
195 assert(globalLogLevel == LogLevel.all);
196
197 auto tl = cast(StdForwardLogger) stdThreadLocalLog;
198 assert(tl !is null);
199 stdThreadLocalLog.logLevel = LogLevel.all;
200 }