d: Merge upstream dmd 47871363d, druntime, c52e28b7, phobos 99e9c1b77.
[official-gcc.git] / libphobos / src / std / experimental / logger / filelogger.d
blob5112e520bc03d200069f383dfa7353abfe447db0
1 // Written in the D programming language.
2 /**
3 Source: $(PHOBOSSRC std/experimental/logger/filelogger.d)
4 */
5 module std.experimental.logger.filelogger;
7 import std.experimental.logger.core;
8 import std.stdio;
10 import std.typecons : Flag;
12 /** An option to create $(LREF FileLogger) directory if it is non-existent.
14 alias CreateFolder = Flag!"CreateFolder";
16 /** This `Logger` implementation writes log messages to the associated
17 file. The name of the file has to be passed on construction time. If the file
18 is already present new log messages will be append at its end.
20 class FileLogger : Logger
22 import std.concurrency : Tid;
23 import std.datetime.systime : SysTime;
24 import std.format.write : formattedWrite;
26 /** A constructor for the `FileLogger` Logger.
28 Params:
29 fn = The filename of the output file of the `FileLogger`. If that
30 file can not be opened for writting an exception will be thrown.
31 lv = The `LogLevel` for the `FileLogger`. By default the
33 Example:
34 -------------
35 auto l1 = new FileLogger("logFile");
36 auto l2 = new FileLogger("logFile", LogLevel.fatal);
37 auto l3 = new FileLogger("logFile", LogLevel.fatal, CreateFolder.yes);
38 -------------
40 this(const string fn, const LogLevel lv = LogLevel.all) @safe
42 this(fn, lv, CreateFolder.yes);
45 /** A constructor for the `FileLogger` Logger that takes a reference to
46 a `File`.
48 The `File` passed must be open for all the log call to the
49 `FileLogger`. If the `File` gets closed, using the `FileLogger`
50 for logging will result in undefined behaviour.
52 Params:
53 fn = The file used for logging.
54 lv = The `LogLevel` for the `FileLogger`. By default the
55 `LogLevel` for `FileLogger` is `LogLevel.all`.
56 createFileNameFolder = if yes and fn contains a folder name, this
57 folder will be created.
59 Example:
60 -------------
61 auto file = File("logFile.log", "w");
62 auto l1 = new FileLogger(file);
63 auto l2 = new FileLogger(file, LogLevel.fatal);
64 -------------
66 this(const string fn, const LogLevel lv, CreateFolder createFileNameFolder) @safe
68 import std.file : exists, mkdirRecurse;
69 import std.path : dirName;
70 import std.conv : text;
72 super(lv);
73 this.filename = fn;
75 if (createFileNameFolder)
77 auto d = dirName(this.filename);
78 mkdirRecurse(d);
79 assert(exists(d), text("The folder the FileLogger should have",
80 " created in '", d,"' could not be created."));
83 this.file_.open(this.filename, "a");
86 /** A constructor for the `FileLogger` Logger that takes a reference to
87 a `File`.
89 The `File` passed must be open for all the log call to the
90 `FileLogger`. If the `File` gets closed, using the `FileLogger`
91 for logging will result in undefined behaviour.
93 Params:
94 file = The file used for logging.
95 lv = The `LogLevel` for the `FileLogger`. By default the
96 `LogLevel` for `FileLogger` is `LogLevel.all`.
98 Example:
99 -------------
100 auto file = File("logFile.log", "w");
101 auto l1 = new FileLogger(file);
102 auto l2 = new FileLogger(file, LogLevel.fatal);
103 -------------
105 this(File file, const LogLevel lv = LogLevel.all) @safe
107 super(lv);
108 this.file_ = file;
111 /** If the `FileLogger` is managing the `File` it logs to, this
112 method will return a reference to this File.
114 @property File file() @safe
116 return this.file_;
119 /* This method overrides the base class method in order to log to a file
120 without requiring heap allocated memory. Additionally, the `FileLogger`
121 local mutex is logged to serialize the log calls.
123 override protected void beginLogMsg(string file, int line, string funcName,
124 string prettyFuncName, string moduleName, LogLevel logLevel,
125 Tid threadId, SysTime timestamp, Logger logger)
126 @safe
128 import std.string : lastIndexOf;
129 ptrdiff_t fnIdx = file.lastIndexOf('/') + 1;
130 ptrdiff_t funIdx = funcName.lastIndexOf('.') + 1;
132 auto lt = this.file_.lockingTextWriter();
133 systimeToISOString(lt, timestamp);
134 import std.conv : to;
135 formattedWrite(lt, " [%s] %s:%u:%s ", logLevel.to!string,
136 file[fnIdx .. $], line, funcName[funIdx .. $]);
139 /* This methods overrides the base class method and writes the parts of
140 the log call directly to the file.
142 override protected void logMsgPart(scope const(char)[] msg)
144 formattedWrite(this.file_.lockingTextWriter(), "%s", msg);
147 /* This methods overrides the base class method and finalizes the active
148 log call. This requires flushing the `File` and releasing the
149 `FileLogger` local mutex.
151 override protected void finishLogMsg()
153 this.file_.lockingTextWriter().put("\n");
154 this.file_.flush();
157 /* This methods overrides the base class method and delegates the
158 `LogEntry` data to the actual implementation.
160 override protected void writeLogMsg(ref LogEntry payload)
162 this.beginLogMsg(payload.file, payload.line, payload.funcName,
163 payload.prettyFuncName, payload.moduleName, payload.logLevel,
164 payload.threadId, payload.timestamp, payload.logger);
165 this.logMsgPart(payload.msg);
166 this.finishLogMsg();
169 /** If the `FileLogger` was constructed with a filename, this method
170 returns this filename. Otherwise an empty `string` is returned.
172 string getFilename()
174 return this.filename;
177 /** The `File` log messages are written to. */
178 protected File file_;
180 /** The filename of the `File` log messages are written to. */
181 protected string filename;
184 @system unittest
186 import std.array : empty;
187 import std.file : deleteme, remove;
188 import std.string : indexOf;
190 string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
191 auto l = new FileLogger(filename);
193 scope(exit)
195 remove(filename);
198 string notWritten = "this should not be written to file";
199 string written = "this should be written to file";
201 l.logLevel = LogLevel.critical;
202 l.log(LogLevel.warning, notWritten);
203 l.log(LogLevel.critical, written);
204 destroy(l);
206 auto file = File(filename, "r");
207 string readLine = file.readln();
208 assert(readLine.indexOf(written) != -1, readLine);
209 readLine = file.readln();
210 assert(readLine.indexOf(notWritten) == -1, readLine);
213 @safe unittest
215 import std.file : rmdirRecurse, exists, deleteme;
216 import std.path : dirName;
218 const string tmpFolder = dirName(deleteme);
219 const string filepath = tmpFolder ~ "/bug15771/minas/oops/";
220 const string filename = filepath ~ "output.txt";
221 assert(!exists(filepath));
223 auto f = new FileLogger(filename, LogLevel.all, CreateFolder.yes);
224 scope(exit) () @trusted { rmdirRecurse(tmpFolder ~ "/bug15771"); }();
226 f.log("Hello World!");
227 assert(exists(filepath));
228 f.file.close();
231 @system unittest
233 import std.array : empty;
234 import std.file : deleteme, remove;
235 import std.string : indexOf;
237 string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
238 auto file = File(filename, "w");
239 auto l = new FileLogger(file);
241 scope(exit)
243 remove(filename);
246 string notWritten = "this should not be written to file";
247 string written = "this should be written to file";
249 l.logLevel = LogLevel.critical;
250 l.log(LogLevel.warning, notWritten);
251 l.log(LogLevel.critical, written);
252 file.close();
254 file = File(filename, "r");
255 string readLine = file.readln();
256 assert(readLine.indexOf(written) != -1, readLine);
257 readLine = file.readln();
258 assert(readLine.indexOf(notWritten) == -1, readLine);
259 file.close();
262 @safe unittest
264 auto dl = cast(FileLogger) sharedLog;
265 assert(dl !is null);
266 assert(dl.logLevel == LogLevel.info);
267 assert(globalLogLevel == LogLevel.all);
269 auto tl = cast(StdForwardLogger) stdThreadLocalLog;
270 assert(tl !is null);
271 stdThreadLocalLog.logLevel = LogLevel.all;