diff --git a/ExtremelySimpleLogger/DirectorySink.cs b/ExtremelySimpleLogger/DirectorySink.cs new file mode 100644 index 0000000..386c43f --- /dev/null +++ b/ExtremelySimpleLogger/DirectorySink.cs @@ -0,0 +1,90 @@ +using System; +using System.IO; +using System.Linq; + +namespace ExtremelySimpleLogger { + /// + /// A that writes log output to a set of directories. + /// This sink differs from in that it manages multiple log files in a directory, where a new file will be created every time the sink is created. + /// Additionally, this sink will automatically delete the oldest log files if the amount of files exceeds a set limit. + /// + public class DirectorySink : Sink { + + private const string DefaultDateFormat = "yy-MM-dd_HH-mm-ss"; + private readonly FileInfo file; + private readonly StreamWriter writer; + private readonly bool reopenOnWrite; + + /// + /// Creates a new directory sink with the given settings. + /// + /// The directory that this sink should operate in + /// The maximum amount of files that can exist in the directory before the oldest one gets deleted. 10 by default. + /// Whether this sink should reopen the file every time it logs to it. If this is false, the file will be kept open by this sink. + /// The way the name of the current log file gets formatted. yy-MM-dd_HH-mm-ss by default. + public DirectorySink(string directory, int maxFiles = 10, bool reopenOnWrite = false, string dateFormat = DefaultDateFormat) : + this(new DirectoryInfo(directory), maxFiles, reopenOnWrite, dateFormat) { + } + + /// + /// Creates a new directory sink with the given settings. + /// + /// The directory that this sink should operate in + /// The maximum amount of files that can exist in the directory before the oldest one gets deleted. 10 by default. + /// Whether this sink should reopen the file every time it logs to it. If this is false, the file will be kept open by this sink. + /// The way the name of the current log file gets formatted. yy-MM-dd_HH-mm-ss by default. + public DirectorySink(DirectoryInfo directory, int maxFiles = 10, bool reopenOnWrite = false, string dateFormat = DefaultDateFormat) { + this.reopenOnWrite = reopenOnWrite; + + if (!directory.Exists) + directory.Create(); + + // delete old files + var files = directory.GetFiles(); + if (files.Length >= maxFiles) { + // order files by their creation time so that older files are deleted first + var ordered = files.OrderBy(f => f.CreationTime).ToList(); + while (ordered.Count >= maxFiles) { + ordered[0].Delete(); + ordered.RemoveAt(0); + } + } + + var date = DateTime.Now.ToString(dateFormat); + this.file = new FileInfo(Path.Combine(directory.FullName, $"{date}.txt")); + if (!reopenOnWrite) { + this.writer = this.file.AppendText(); + this.writer.AutoFlush = true; + } + } + + /// + /// Logs the given message, which has already been formatted using . + /// + /// The logger that the message was passed to + /// The importance level of this message + /// The message to log + protected override void Log(Logger logger, LogLevel level, string s) { + lock (this.file) { + if (this.reopenOnWrite) { + using (var w = this.file.AppendText()) + w.WriteLine(s); + } else { + this.writer.WriteLine(s); + } + } + } + + /// + /// Disposes this sink, freeing all of the resources it uses. + /// + public override void Dispose() { + base.Dispose(); + lock (this.file) { + if (!this.reopenOnWrite) + this.writer.Dispose(); + } + } + + } +} \ No newline at end of file diff --git a/ExtremelySimpleLogger/Logger.cs b/ExtremelySimpleLogger/Logger.cs index dc6243b..2614083 100644 --- a/ExtremelySimpleLogger/Logger.cs +++ b/ExtremelySimpleLogger/Logger.cs @@ -10,7 +10,7 @@ namespace ExtremelySimpleLogger { /// /// All of the instances that this logger logs to. - /// By default, , and are available. + /// By default, , , and are available. /// public List Sinks { get; set; } = new List(); /// diff --git a/README.md b/README.md index e2314c3..b4d6516 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ var logger = new Logger { Since there are multiple ways for logging data to be processed, the logger needs to receive a set of `Sink` instances. By default, the following sinks are available: - `FileSink`, which outputs logging data to a file - `ConsoleSink`, which outputs logging data to the default console +- `DirectorySink`, which outputs logging data to a set of files and automatically manages how many old logs are kept - `StringSink`, which stores logging data in a string There are multiple ways to easily log messages with your newly created logger: