2 module std.experimental.logger.filelogger;
4 import std.experimental.logger.core;
7 import std.typecons : Flag;
9 /** An option to create $(LREF FileLogger) directory if it is non-existent.
11 alias CreateFolder = Flag!"CreateFolder";
13 /** This $(D Logger) implementation writes log messages to the associated
14 file. The name of the file has to be passed on construction time. If the file
15 is already present new log messages will be append at its end.
17 class FileLogger : Logger
19 import std.concurrency : Tid;
20 import std.datetime.systime : SysTime;
21 import std.format : formattedWrite;
23 /** A constructor for the $(D FileLogger) Logger.
26 fn = The filename of the output file of the $(D FileLogger). If that
27 file can not be opened for writting an exception will be thrown.
28 lv = The $(D LogLevel) for the $(D FileLogger). By default the
32 auto l1 = new FileLogger("logFile");
33 auto l2 = new FileLogger("logFile", LogLevel.fatal);
34 auto l3 = new FileLogger("logFile", LogLevel.fatal, CreateFolder.yes);
37 this(in string fn, const LogLevel lv = LogLevel.all) @safe
39 this(fn, lv, CreateFolder.yes);
42 /** A constructor for the $(D FileLogger) Logger that takes a reference to
45 The $(D File) passed must be open for all the log call to the
46 $(D FileLogger). If the $(D File) gets closed, using the $(D FileLogger)
47 for logging will result in undefined behaviour.
50 fn = The file used for logging.
51 lv = The $(D LogLevel) for the $(D FileLogger). By default the
52 $(D LogLevel) for $(D FileLogger) is $(D LogLevel.all).
53 createFileNameFolder = if yes and fn contains a folder name, this
54 folder will be created.
58 auto file = File("logFile.log", "w");
59 auto l1 = new FileLogger(file);
60 auto l2 = new FileLogger(file, LogLevel.fatal);
63 this(in string fn, const LogLevel lv, CreateFolder createFileNameFolder) @safe
65 import std.file : exists, mkdirRecurse;
66 import std.path : dirName;
67 import std.conv : text;
72 if (createFileNameFolder)
74 auto d = dirName(this.filename);
76 assert(exists(d), text("The folder the FileLogger should have",
77 " created in '", d,"' could not be created."));
80 this.file_.open(this.filename, "a");
83 /** A constructor for the $(D FileLogger) Logger that takes a reference to
86 The $(D File) passed must be open for all the log call to the
87 $(D FileLogger). If the $(D File) gets closed, using the $(D FileLogger)
88 for logging will result in undefined behaviour.
91 file = The file used for logging.
92 lv = The $(D LogLevel) for the $(D FileLogger). By default the
93 $(D LogLevel) for $(D FileLogger) is $(D LogLevel.all).
97 auto file = File("logFile.log", "w");
98 auto l1 = new FileLogger(file);
99 auto l2 = new FileLogger(file, LogLevel.fatal);
102 this(File file, const LogLevel lv = LogLevel.all) @safe
108 /** If the $(D FileLogger) is managing the $(D File) it logs to, this
109 method will return a reference to this File.
111 @property File file() @safe
116 /* This method overrides the base class method in order to log to a file
117 without requiring heap allocated memory. Additionally, the $(D FileLogger)
118 local mutex is logged to serialize the log calls.
120 override protected void beginLogMsg(string file, int line, string funcName,
121 string prettyFuncName, string moduleName, LogLevel logLevel,
122 Tid threadId, SysTime timestamp, Logger logger)
125 import std.string : lastIndexOf;
126 ptrdiff_t fnIdx = file.lastIndexOf('/') + 1;
127 ptrdiff_t funIdx = funcName.lastIndexOf('.') + 1;
129 auto lt = this.file_.lockingTextWriter();
130 systimeToISOString(lt, timestamp);
131 formattedWrite(lt, ":%s:%s:%u ", file[fnIdx .. $],
132 funcName[funIdx .. $], line);
135 /* This methods overrides the base class method and writes the parts of
136 the log call directly to the file.
138 override protected void logMsgPart(const(char)[] msg)
140 formattedWrite(this.file_.lockingTextWriter(), "%s", msg);
143 /* This methods overrides the base class method and finalizes the active
144 log call. This requires flushing the $(D File) and releasing the
145 $(D FileLogger) local mutex.
147 override protected void finishLogMsg()
149 this.file_.lockingTextWriter().put("\n");
153 /* This methods overrides the base class method and delegates the
154 $(D LogEntry) data to the actual implementation.
156 override protected void writeLogMsg(ref LogEntry payload)
158 this.beginLogMsg(payload.file, payload.line, payload.funcName,
159 payload.prettyFuncName, payload.moduleName, payload.logLevel,
160 payload.threadId, payload.timestamp, payload.logger);
161 this.logMsgPart(payload.msg);
165 /** If the $(D FileLogger) was constructed with a filename, this method
166 returns this filename. Otherwise an empty $(D string) is returned.
170 return this.filename;
174 private string filename;
179 import std.array : empty;
180 import std.file : deleteme, remove;
181 import std.string : indexOf;
183 string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
184 auto l = new FileLogger(filename);
191 string notWritten = "this should not be written to file";
192 string written = "this should be written to file";
194 l.logLevel = LogLevel.critical;
195 l.log(LogLevel.warning, notWritten);
196 l.log(LogLevel.critical, written);
199 auto file = File(filename, "r");
200 string readLine = file.readln();
201 assert(readLine.indexOf(written) != -1, readLine);
202 readLine = file.readln();
203 assert(readLine.indexOf(notWritten) == -1, readLine);
208 import std.file : rmdirRecurse, exists, deleteme;
209 import std.path : dirName;
211 const string tmpFolder = dirName(deleteme);
212 const string filepath = tmpFolder ~ "/bug15771/minas/oops/";
213 const string filename = filepath ~ "output.txt";
214 assert(!exists(filepath));
216 auto f = new FileLogger(filename, LogLevel.all, CreateFolder.yes);
217 scope(exit) () @trusted { rmdirRecurse(tmpFolder ~ "/bug15771"); }();
219 f.log("Hello World!");
220 assert(exists(filepath));
226 import std.array : empty;
227 import std.file : deleteme, remove;
228 import std.string : indexOf;
230 string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
231 auto file = File(filename, "w");
232 auto l = new FileLogger(file);
239 string notWritten = "this should not be written to file";
240 string written = "this should be written to file";
242 l.logLevel = LogLevel.critical;
243 l.log(LogLevel.warning, notWritten);
244 l.log(LogLevel.critical, written);
247 file = File(filename, "r");
248 string readLine = file.readln();
249 assert(readLine.indexOf(written) != -1, readLine);
250 readLine = file.readln();
251 assert(readLine.indexOf(notWritten) == -1, readLine);
257 auto dl = cast(FileLogger) sharedLog;
259 assert(dl.logLevel == LogLevel.all);
260 assert(globalLogLevel == LogLevel.all);
262 auto tl = cast(StdForwardLogger) stdThreadLocalLog;
264 stdThreadLocalLog.logLevel = LogLevel.all;