Fortran: Fix PR 47485.
[gcc.git] / libphobos / src / std / logger / filelogger.d
blob754175c7dc823dd087135430198e7918fd15e2c3
1 // Written in the D programming language.
2 /**
3 Source: $(PHOBOSSRC std/logger/filelogger.d)
4 */
5 module std.logger.filelogger;
7 import std.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(this This)(const string fn, const LogLevel lv = LogLevel.all)
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(this This)(const string fn, const LogLevel lv, CreateFolder createFileNameFolder)
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 // Cast away `shared` when the constructor is inferred shared.
84 () @trusted { (cast() this.file_).open(this.filename, "a"); }();
87 /** A constructor for the `FileLogger` Logger that takes a reference to
88 a `File`.
90 The `File` passed must be open for all the log call to the
91 `FileLogger`. If the `File` gets closed, using the `FileLogger`
92 for logging will result in undefined behaviour.
94 Params:
95 file = The file used for logging.
96 lv = The `LogLevel` for the `FileLogger`. By default the
97 `LogLevel` for `FileLogger` is `LogLevel.all`.
99 Example:
100 -------------
101 auto file = File("logFile.log", "w");
102 auto l1 = new FileLogger(file);
103 auto l2 = new FileLogger(file, LogLevel.fatal);
104 -------------
106 this(File file, const LogLevel lv = LogLevel.all) @safe
108 super(lv);
109 this.file_ = file;
112 /** If the `FileLogger` is managing the `File` it logs to, this
113 method will return a reference to this File.
115 @property File file() @safe
117 return this.file_;
120 /* This method overrides the base class method in order to log to a file
121 without requiring heap allocated memory. Additionally, the `FileLogger`
122 local mutex is logged to serialize the log calls.
124 override protected void beginLogMsg(string file, int line, string funcName,
125 string prettyFuncName, string moduleName, LogLevel logLevel,
126 Tid threadId, SysTime timestamp, Logger logger)
127 @safe
129 import std.string : lastIndexOf;
130 ptrdiff_t fnIdx = file.lastIndexOf('/') + 1;
131 ptrdiff_t funIdx = funcName.lastIndexOf('.') + 1;
133 auto lt = this.file_.lockingTextWriter();
134 systimeToISOString(lt, timestamp);
135 import std.conv : to;
136 formattedWrite(lt, " [%s] %s:%u:%s ", logLevel.to!string,
137 file[fnIdx .. $], line, funcName[funIdx .. $]);
140 /* This methods overrides the base class method and writes the parts of
141 the log call directly to the file.
143 override protected void logMsgPart(scope const(char)[] msg)
145 formattedWrite(this.file_.lockingTextWriter(), "%s", msg);
148 /* This methods overrides the base class method and finalizes the active
149 log call. This requires flushing the `File` and releasing the
150 `FileLogger` local mutex.
152 override protected void finishLogMsg()
154 this.file_.lockingTextWriter().put("\n");
155 this.file_.flush();
158 /* This methods overrides the base class method and delegates the
159 `LogEntry` data to the actual implementation.
161 override protected void writeLogMsg(ref LogEntry payload)
163 this.beginLogMsg(payload.file, payload.line, payload.funcName,
164 payload.prettyFuncName, payload.moduleName, payload.logLevel,
165 payload.threadId, payload.timestamp, payload.logger);
166 this.logMsgPart(payload.msg);
167 this.finishLogMsg();
170 /** If the `FileLogger` was constructed with a filename, this method
171 returns this filename. Otherwise an empty `string` is returned.
173 string getFilename()
175 return this.filename;
178 /** The `File` log messages are written to. */
179 protected File file_;
181 /** The filename of the `File` log messages are written to. */
182 protected string filename;
185 @system unittest
187 import std.array : empty;
188 import std.file : deleteme, remove;
189 import std.string : indexOf;
191 string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
192 auto l = new FileLogger(filename);
194 scope(exit)
196 remove(filename);
199 string notWritten = "this should not be written to file";
200 string written = "this should be written to file";
202 l.logLevel = LogLevel.critical;
203 l.log(LogLevel.warning, notWritten);
204 l.log(LogLevel.critical, written);
205 destroy(l);
207 auto file = File(filename, "r");
208 string readLine = file.readln();
209 assert(readLine.indexOf(written) != -1, readLine);
210 readLine = file.readln();
211 assert(readLine.indexOf(notWritten) == -1, readLine);
214 @safe unittest
216 import std.file : rmdirRecurse, exists, deleteme;
217 import std.path : dirName;
219 const string tmpFolder = dirName(deleteme);
220 const string filepath = tmpFolder ~ "/bug15771/minas/oops/";
221 const string filename = filepath ~ "output.txt";
222 assert(!exists(filepath));
224 auto f = new FileLogger(filename, LogLevel.all, CreateFolder.yes);
225 scope(exit) () @trusted { rmdirRecurse(tmpFolder ~ "/bug15771"); }();
227 f.log("Hello World!");
228 assert(exists(filepath));
229 f.file.close();
232 @system unittest
234 import std.array : empty;
235 import std.file : deleteme, remove;
236 import std.string : indexOf;
238 string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
239 auto file = File(filename, "w");
240 auto l = new FileLogger(file);
242 scope(exit)
244 remove(filename);
247 string notWritten = "this should not be written to file";
248 string written = "this should be written to file";
250 l.logLevel = LogLevel.critical;
251 l.log(LogLevel.warning, notWritten);
252 l.log(LogLevel.critical, written);
253 file.close();
255 file = File(filename, "r");
256 string readLine = file.readln();
257 assert(readLine.indexOf(written) != -1, readLine);
258 readLine = file.readln();
259 assert(readLine.indexOf(notWritten) == -1, readLine);
260 file.close();
263 @system unittest
265 auto dl = cast(FileLogger) sharedLog;
266 assert(dl !is null);
267 assert(dl.logLevel == LogLevel.info);
268 assert(globalLogLevel == LogLevel.all);
270 auto tl = cast(StdForwardLogger) stdThreadLocalLog;
271 assert(tl !is null);
272 stdThreadLocalLog.logLevel = LogLevel.all;
275 @safe unittest
277 // we don't need to actually run the code, only make sure
278 // it compiles
279 static void _() {
280 auto l = new shared FileLogger("");