using System; using SabreTools.Helper.Data; #if MONO using System.IO; #else using Alphaleonis.Win32.Filesystem; using FileAccess = System.IO.FileAccess; using FileMode = System.IO.FileMode; using StreamWriter = System.IO.StreamWriter; #endif namespace SabreTools.Helper { /// /// Log either to file or to the console /// /// /// TODO: Allow for "triggerable" logging done on an interval (async) /// public class Logger { // Private instance variables private bool _tofile; private bool _warnings; private bool _errors; private string _filename; private LogLevel _filter; private DateTime _start; private StreamWriter _log; // Private required variables private string _basepath = Path.Combine(Globals.ExeDir, "logs") + Path.DirectorySeparatorChar; /// /// Initialize a console-only logger object /// public Logger() { _tofile = false; _warnings = false; _errors = false; _filename = null; _filter = LogLevel.VERBOSE; Start(); } /// /// Initialize a Logger object with the given information /// /// True if file should be written to instead of console /// Filename representing log location /// Highest filtering level to be kept, default VERBOSE public Logger(bool tofile, string filename, LogLevel filter = LogLevel.VERBOSE) { _tofile = tofile; _warnings = false; _errors = false; _filename = Path.GetFileNameWithoutExtension(filename) + " (" + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + ")" + Path.GetExtension(filename); _filter = filter; if (!Directory.Exists(_basepath)) { Directory.CreateDirectory(_basepath); } Start(); } /// /// Start logging by opening output file (if necessary) /// /// True if the logging was started correctly, false otherwise public bool Start() { _start = DateTime.Now; if (!_tofile) { return true; } try { _log = new StreamWriter(File.Open(_basepath + _filename, FileMode.OpenOrCreate | FileMode.Append, FileAccess.Write)); _log.AutoFlush = true; _log.WriteLine("Logging started " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); _log.WriteLine(string.Format("Command run: {0}", Globals.CommandLineArgs)); } catch { return false; } return true; } /// /// End logging by closing output file (if necessary) /// /// True if all ending output is to be suppressed, false otherwise (default) /// True if the logging was ended correctly, false otherwise public bool Close(bool suppress = false) { if (!suppress) { if (_warnings) { Console.WriteLine("There were warnings in the last run! Check the log for more details"); } if (_errors) { Console.WriteLine("There were errors in the last run! Check the log for more details"); } TimeSpan span = DateTime.Now.Subtract(_start); // Special case for multi-day runs string total = ""; if (span >= TimeSpan.FromDays(1)) { total = span.ToString(@"d\:hh\:mm\:ss"); } else { total = span.ToString(@"hh\:mm\:ss"); } if (!_tofile) { Console.WriteLine("Total runtime: " + total); return true; } try { _log.WriteLine("Logging ended " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); _log.WriteLine("Total runtime: " + total); Console.WriteLine("Total runtime: " + total); _log.Close(); } catch { return false; } } else { try { _log.Close(); } catch { return false; } } return true; } /// /// Write the given string to the log output /// /// String to be written log /// Severity of the information being logged /// True if the level and datetime should be prepended to each statement, false otherwise /// True if the output could be written, false otherwise private bool Log(string output, LogLevel loglevel, bool appendPrefix) { // If the log level is less than the filter level, we skip it but claim we didn't if (loglevel < _filter) { return true; } // USER and ERROR writes to console if (loglevel == LogLevel.USER || loglevel == LogLevel.ERROR) { Console.WriteLine((loglevel == LogLevel.ERROR && appendPrefix ? loglevel.ToString() + " " : "") + output); } // If we're writing to file, use the existing stream if (_tofile) { try { _log.WriteLine((appendPrefix ? loglevel.ToString() + " - " + DateTime.Now + " - " : "" ) + output); } catch { Console.WriteLine("Could not write to log file!"); return false; } } return true; } /// /// Write the given exact string to the log output /// /// String to be written log /// Line number to write out to /// Column number to write out to /// True if the output could be written, false otherwise public bool WriteExact(string output, int line, int column) { // Set the cursor position (if not being redirected) if (!Console.IsOutputRedirected) { Console.CursorTop = line; Console.CursorLeft = column; } // Write out to the console Console.Write(output); // If we're writing to file, use the existing stream if (_tofile) { try { _log.Write(DateTime.Now + " - " + output); } catch { Console.WriteLine("Could not write to log file!"); return false; } } return true; } /// /// Write the given string as a verbose message to the log output /// /// String to be written log /// True if the level and datetime should be prepended to each statement (default), false otherwise /// True if the output could be written, false otherwises public bool Verbose(string output, bool appendPrefix = true) { return Log(output, LogLevel.VERBOSE, appendPrefix); } /// /// Write the given string as a user message to the log output /// /// String to be written log /// True if the level and datetime should be prepended to each statement (default), false otherwise /// True if the output could be written, false otherwise public bool User(string output, bool appendPrefix = true) { return Log(output, LogLevel.USER, appendPrefix); } /// /// Write the given string as a warning to the log output /// /// String to be written log /// True if the level and datetime should be prepended to each statement (default), false otherwise /// True if the output could be written, false otherwise public bool Warning(string output, bool appendPrefix = true) { _warnings = true; return Log(output, LogLevel.WARNING, appendPrefix); } /// /// Writes the given string as an error in the log /// /// String to be written log /// True if the level and datetime should be prepended to each statement (default), false otherwise /// True if the output could be written, false otherwise public bool Error(string output, bool appendPrefix = true) { _errors = true; return Log(output, LogLevel.ERROR, appendPrefix); } /// /// Clear lines beneath the given line in the console /// /// Line number to clear beneath /// True public bool ClearBeneath(int line) { if (!Console.IsOutputRedirected) { for (int i = line; i < Console.WindowHeight; i++) { // http://stackoverflow.com/questions/8946808/can-console-clear-be-used-to-only-clear-a-line-instead-of-whole-console Console.SetCursorPosition(0, Console.CursorTop); Console.Write(new string(' ', Console.WindowWidth)); Console.SetCursorPosition(0, i); } } return true; } } }