diff --git a/src/StardewModdingAPI/Framework/LogFileManager.cs b/src/StardewModdingAPI/Framework/LogFileManager.cs new file mode 100644 index 00000000..c2a2105b --- /dev/null +++ b/src/StardewModdingAPI/Framework/LogFileManager.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; + +namespace StardewModdingAPI.Framework +{ + /// Manages reading and writing to log file. + internal class LogFileManager : IDisposable + { + /********* + ** Properties + *********/ + /// The underlying stream writer. + private readonly StreamWriter Stream; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The log file to write. + public LogFileManager(string path) + { + // create log directory if needed + string logDir = Path.GetDirectoryName(path); + if (logDir == null) + throw new ArgumentException($"The log path '{path}' is not valid."); + Directory.CreateDirectory(logDir); + + // open log file stream + this.Stream = new StreamWriter(path, append: false) { AutoFlush = true }; + } + + /// Write a message to the log. + /// The message to log. + public void WriteLine(string message) + { + this.Stream.WriteLine(message); + } + + /// Release all resources. + public void Dispose() + { + this.Stream.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Framework/Monitor.cs b/src/StardewModdingAPI/Framework/Monitor.cs new file mode 100644 index 00000000..4fe1df04 --- /dev/null +++ b/src/StardewModdingAPI/Framework/Monitor.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace StardewModdingAPI.Framework +{ + /// Encapsulates monitoring and logic for a given module. + internal class Monitor : IMonitor + { + /********* + ** Properties + *********/ + /// The name of the module which logs messages using this instance. + private readonly string Source; + + /// The log file to which to write messages. + private readonly LogFileManager LogFile; + + /// The maximum length of the values. + private static readonly int MaxLevelLength = (from level in Enumerable.Cast(Enum.GetValues(typeof(LogLevel))) select level.ToString().Length).Max(); + + /// The console text color for each log level. + private static readonly Dictionary Colors = new Dictionary + { + [LogLevel.Trace] = ConsoleColor.DarkGray, + [LogLevel.Debug] = ConsoleColor.DarkGray, + [LogLevel.Info] = ConsoleColor.White, + [LogLevel.Warn] = ConsoleColor.Yellow, + [LogLevel.Error] = ConsoleColor.Red, + [LogLevel.Alert] = ConsoleColor.Magenta + }; + + + /********* + ** Accessors + *********/ + /// Whether to show trace messages in the console. + internal bool ShowTraceInConsole { get; set; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The name of the module which logs messages using this instance. + /// The log file to which to write messages. + public Monitor(string source, LogFileManager logFile) + { + // validate + if (string.IsNullOrWhiteSpace(source)) + throw new ArgumentException("The log source cannot be empty."); + if (logFile == null) + throw new ArgumentNullException(nameof(logFile), "The log file manager cannot be null."); + + // initialise + this.Source = source; + this.LogFile = logFile; + } + + /// Log a message for the player or developer. + /// The message to log. + /// The log severity level. + public void Log(string message, LogLevel level = LogLevel.Debug) + { + this.LogImpl(this.Source, message, Monitor.Colors[level], level); + } + + /// Log a message for the player or developer, using the specified console color. + /// The name of the mod logging the message. + /// The message to log. + /// The console color. + /// The log level. + [Obsolete("This method is provided for backwards compatibility and otherwise should not be used. Use " + nameof(Monitor) + "." + nameof(Monitor.Log) + " instead.")] + internal void LegacyLog(string source, string message, ConsoleColor color, LogLevel level = LogLevel.Debug) + { + this.LogImpl(source, message, color, level); + } + + + /********* + ** Private methods + *********/ + /// Write a message line to the log. + /// The name of the mod logging the message. + /// The message to log. + /// The console color. + /// The log level. + private void LogImpl(string source, string message, ConsoleColor color, LogLevel level) + { + // generate message + string levelStr = level.ToString().ToUpper().PadRight(Monitor.MaxLevelLength); + message = $"[{DateTime.Now:HH:mm:ss} {levelStr} {source}] {message}"; + + // log + if (this.ShowTraceInConsole || level != LogLevel.Trace) + { + Console.ForegroundColor = color; + Console.WriteLine(message); + Console.ResetColor(); + } + this.LogFile.WriteLine(message); + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/IMonitor.cs b/src/StardewModdingAPI/IMonitor.cs new file mode 100644 index 00000000..8d20af01 --- /dev/null +++ b/src/StardewModdingAPI/IMonitor.cs @@ -0,0 +1,14 @@ +namespace StardewModdingAPI +{ + /// Encapsulates monitoring and logging for a given module. + public interface IMonitor + { + /********* + ** Methods + *********/ + /// Log a message for the player or developer. + /// The message to log. + /// The log severity level. + void Log(string message, LogLevel level = LogLevel.Debug); + } +} diff --git a/src/StardewModdingAPI/LogLevel.cs b/src/StardewModdingAPI/LogLevel.cs new file mode 100644 index 00000000..89647876 --- /dev/null +++ b/src/StardewModdingAPI/LogLevel.cs @@ -0,0 +1,24 @@ +namespace StardewModdingAPI +{ + /// The log severity levels. + public enum LogLevel + { + /// Tracing info intended for developers. + Trace, + + /// Troubleshooting info that may be relevant to the player. + Debug, + + /// Info relevant to the player. This should be used judiciously. + Info, + + /// An issue the player should be aware of. This should be used rarely. + Warn, + + /// A message indicating something went wrong. + Error, + + /// Important information to highlight for the player when player action is needed (e.g. new version available). This should be used rarely to avoid alert fatigue. + Alert + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index ce302b95..2d6a7c7f 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -195,14 +195,18 @@ + + + +