From 240468ad228a66dd0548a1049fe158a017d6b85f Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Sun, 7 Mar 2021 14:23:26 +0800 Subject: [PATCH] Implement log to file logger (#1494) * Implement log to file Logger moved to it's own namespace * Add lock to log file --- MinecraftClient/Logger/FileLogLogger.cs | 113 ++++++++++++++ MinecraftClient/Logger/FilteredLogger.cs | 74 +++++++++ MinecraftClient/{ => Logger}/ILogger.cs | 2 +- MinecraftClient/Logger/LoggerBase.cs | 99 +++++++++++++ MinecraftClient/MCLogger.cs | 140 ------------------ MinecraftClient/McClient.cs | 5 +- MinecraftClient/MinecraftClient.csproj | 6 +- .../Protocol/Handlers/Protocol18.cs | 1 + .../Protocol/IMinecraftComHandler.cs | 1 + .../Resources/config/MinecraftClient.ini | 3 + MinecraftClient/Settings.cs | 6 + 11 files changed, 306 insertions(+), 144 deletions(-) create mode 100644 MinecraftClient/Logger/FileLogLogger.cs create mode 100644 MinecraftClient/Logger/FilteredLogger.cs rename MinecraftClient/{ => Logger}/ILogger.cs (96%) create mode 100644 MinecraftClient/Logger/LoggerBase.cs delete mode 100644 MinecraftClient/MCLogger.cs diff --git a/MinecraftClient/Logger/FileLogLogger.cs b/MinecraftClient/Logger/FileLogLogger.cs new file mode 100644 index 00000000..eddf26db --- /dev/null +++ b/MinecraftClient/Logger/FileLogLogger.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace MinecraftClient.Logger +{ + public class FileLogLogger : FilteredLogger + { + private string logFile; + private bool prependTimestamp; + private object logFileLock = new object(); + + public FileLogLogger(string file, bool prependTimestamp = false) + { + logFile = file; + this.prependTimestamp = prependTimestamp; + Save("### Log started at " + GetTimestamp() + " ###"); + } + + private void LogAndSave(string msg) + { + Log(msg); + Save(msg); + } + + private void Save(string msg) + { + try + { + msg = ChatBot.GetVerbatim(msg); + if (prependTimestamp) + msg = GetTimestamp() + ' ' + msg; + + string directory = Path.GetDirectoryName(logFile); + if (!String.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + Directory.CreateDirectory(directory); + lock (logFileLock) + { + FileStream stream = new FileStream(logFile, FileMode.OpenOrCreate); + StreamWriter writer = new StreamWriter(stream); + stream.Seek(0, SeekOrigin.End); + writer.WriteLine(msg); + writer.Dispose(); + stream.Close(); + } + } + catch (Exception e) + { + // Must use base since we already failed to write log + base.Error("Cannot write to log file: " + e.Message); + base.Debug("Stack trace: \n" + e.StackTrace); + } + } + + private static string GetTimestamp() + { + DateTime time = DateTime.Now; + return String.Format("{0}-{1}-{2} {3}:{4}:{5}", + time.Year.ToString("0000"), + time.Month.ToString("00"), + time.Day.ToString("00"), + time.Hour.ToString("00"), + time.Minute.ToString("00"), + time.Second.ToString("00")); + } + + public override void Chat(string msg) + { + if (ChatEnabled) + { + if (ShouldDisplay(FilterChannel.Chat, msg)) + { + LogAndSave(msg); + } + else Debug("[Logger] One Chat message filtered: " + msg); + } + } + + public override void Debug(string msg) + { + if (DebugEnabled) + { + if (ShouldDisplay(FilterChannel.Debug, msg)) + { + LogAndSave("§8[DEBUG] " + msg); + } + } + } + + public override void Error(string msg) + { + base.Error(msg); + if (ErrorEnabled) + Save(msg); + } + + public override void Info(string msg) + { + base.Info(msg); + if (InfoEnabled) + Save(msg); + } + + public override void Warn(string msg) + { + base.Warn(msg); + if (WarnEnabled) + Save(msg); + } + } +} diff --git a/MinecraftClient/Logger/FilteredLogger.cs b/MinecraftClient/Logger/FilteredLogger.cs new file mode 100644 index 00000000..288d75f2 --- /dev/null +++ b/MinecraftClient/Logger/FilteredLogger.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace MinecraftClient.Logger +{ + public class FilteredLogger : LoggerBase + { + protected enum FilterChannel { Debug, Chat } + + protected bool ShouldDisplay(FilterChannel channel, string msg) + { + Regex regexToUse = null; + // Convert to bool for XOR later. Whitelist = 0, Blacklist = 1 + bool filterMode = Settings.FilterMode == Settings.FilterModeEnum.Blacklist ? true : false; + switch (channel) + { + case FilterChannel.Chat: regexToUse = Settings.ChatFilter; break; + case FilterChannel.Debug: regexToUse = Settings.DebugFilter; break; + } + if (regexToUse != null) + { + // IsMatch and white/blacklist result can be represented using XOR + // e.g. matched(true) ^ blacklist(true) => shouldn't log(false) + return regexToUse.IsMatch(msg) ^ filterMode; + } + else return true; + } + + public override void Debug(string msg) + { + if (DebugEnabled) + { + if (ShouldDisplay(FilterChannel.Debug, msg)) + { + Log("§8[DEBUG] " + msg); + } + // Don't write debug lines here as it could cause a stack overflow + } + } + + public override void Info(string msg) + { + if (InfoEnabled) + ConsoleIO.WriteLogLine(msg); + } + + public override void Warn(string msg) + { + if (WarnEnabled) + Log("§6[WARN] " + msg); + } + + public override void Error(string msg) + { + if (ErrorEnabled) + Log("§c[ERROR] " + msg); + } + + public override void Chat(string msg) + { + if (ChatEnabled) + { + if (ShouldDisplay(FilterChannel.Chat, msg)) + { + Log(msg); + } + else Debug("[Logger] One Chat message filtered: " + msg); + } + } + } +} diff --git a/MinecraftClient/ILogger.cs b/MinecraftClient/Logger/ILogger.cs similarity index 96% rename from MinecraftClient/ILogger.cs rename to MinecraftClient/Logger/ILogger.cs index 283bfc7c..98b810d2 100644 --- a/MinecraftClient/ILogger.cs +++ b/MinecraftClient/Logger/ILogger.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; -namespace MinecraftClient +namespace MinecraftClient.Logger { public interface ILogger { diff --git a/MinecraftClient/Logger/LoggerBase.cs b/MinecraftClient/Logger/LoggerBase.cs new file mode 100644 index 00000000..1ea31c6d --- /dev/null +++ b/MinecraftClient/Logger/LoggerBase.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient.Logger +{ + /// + /// Abstract class providing basic implementation of the ILogger interface + /// + public abstract class LoggerBase : ILogger + { + private bool debugEnabled = false; + private bool warnEnabled = true; + private bool infoEnabled = true; + private bool errorEnabled = true; + private bool chatEnabled = true; + public bool DebugEnabled { get { return debugEnabled; } set { debugEnabled = value; } } + public bool WarnEnabled { get { return warnEnabled; } set { warnEnabled = value; } } + public bool InfoEnabled { get { return infoEnabled; } set { infoEnabled = value; } } + public bool ErrorEnabled { get { return errorEnabled; } set { errorEnabled = value; } } + public bool ChatEnabled { get { return chatEnabled; } set { chatEnabled = value; } } + + public abstract void Chat(string msg); + + public void Chat(string msg, params object[] args) + { + Chat(string.Format(msg, args)); + } + + public void Chat(object msg) + { + Chat(msg.ToString()); + } + + public abstract void Debug(string msg); + + public void Debug(string msg, params object[] args) + { + Debug(string.Format(msg, args)); + } + + public void Debug(object msg) + { + Debug(msg.ToString()); + } + + public abstract void Error(string msg); + + public void Error(string msg, params object[] args) + { + Error(string.Format(msg, args)); + } + + public void Error(object msg) + { + Error(msg.ToString()); + } + + public abstract void Info(string msg); + + public void Info(string msg, params object[] args) + { + Info(string.Format(msg, args)); + } + + public void Info(object msg) + { + Info(msg.ToString()); + } + + public abstract void Warn(string msg); + + public void Warn(string msg, params object[] args) + { + Warn(string.Format(msg, args)); + } + + public void Warn(object msg) + { + Warn(msg.ToString()); + } + + protected virtual void Log(object msg) + { + ConsoleIO.WriteLineFormatted(msg.ToString()); + } + + protected virtual void Log(string msg) + { + ConsoleIO.WriteLineFormatted(msg); + } + + protected virtual void Log(string msg, params object[] args) + { + ConsoleIO.WriteLineFormatted(string.Format(msg, args)); + } + } +} diff --git a/MinecraftClient/MCLogger.cs b/MinecraftClient/MCLogger.cs deleted file mode 100644 index 6f42d08e..00000000 --- a/MinecraftClient/MCLogger.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace MinecraftClient -{ - public class MCLogger : ILogger - { - private bool debugEnabled = false; - private bool warnEnabled = true; - private bool infoEnabled = true; - private bool errorEnabled = true; - private bool chatEnabled = true; - public bool DebugEnabled { get { return debugEnabled; } set { debugEnabled = value; } } - public bool WarnEnabled { get { return warnEnabled; } set { warnEnabled = value; } } - public bool InfoEnabled { get { return infoEnabled; } set { infoEnabled = value; } } - public bool ErrorEnabled { get { return errorEnabled; } set { errorEnabled = value; } } - public bool ChatEnabled { get { return chatEnabled; } set { chatEnabled = value; } } - - public void Debug(string msg) - { - if (debugEnabled) - { - if (Settings.DebugFilter != null) - { - var shouldLog = Settings.DebugFilter.IsMatch(msg); // assumed whitelist mode - if (Settings.FilterMode == Settings.FilterModeEnum.Blacklist) - shouldLog = !shouldLog; // blacklist mode so flip result - if (!shouldLog) - return; - // Don't write debug lines here as it could cause a stack overflow - } - Log("§8[DEBUG] " + msg); - } - } - - public void Debug(string msg, params object[] args) - { - Debug(string.Format(msg, args)); - } - - public void Debug(object msg) - { - Debug(msg.ToString()); - } - - public void Info(object msg) - { - if (infoEnabled) - ConsoleIO.WriteLogLine(msg.ToString()); - } - - public void Info(string msg) - { - if (infoEnabled) - ConsoleIO.WriteLogLine(msg); - } - - public void Info(string msg, params object[] args) - { - if (infoEnabled) - ConsoleIO.WriteLogLine(string.Format(msg, args)); - } - - public void Warn(string msg) - { - if (warnEnabled) - Log("§6[WARN] " + msg); - } - - public void Warn(string msg, params object[] args) - { - Warn(string.Format(msg, args)); - } - - public void Warn(object msg) - { - Warn(msg.ToString()); - } - - public void Error(string msg) - { - if (errorEnabled) - Log("§c[ERROR] " + msg); - } - - public void Error(string msg, params object[] args) - { - Error(string.Format(msg, args)); - } - - public void Error(object msg) - { - Error(msg.ToString()); - } - - public void Chat(string msg) - { - if (chatEnabled) - { - if (Settings.ChatFilter != null) - { - var shouldLog = Settings.ChatFilter.IsMatch(msg); // assumed whitelist mode - if (Settings.FilterMode == Settings.FilterModeEnum.Blacklist) - shouldLog = !shouldLog; // blacklist mode so flip result - if (shouldLog) - Log(msg); - else Debug("[Logger] One Chat message filtered: " + msg); - } - else Log(msg); - } - } - - public void Chat(string msg, params object[] args) - { - Chat(string.Format(msg, args)); - } - - public void Chat(object msg) - { - Chat(msg.ToString()); - } - - private void Log(object msg) - { - ConsoleIO.WriteLineFormatted(msg.ToString()); - } - - private void Log(string msg) - { - ConsoleIO.WriteLineFormatted(msg); - } - - private void Log(string msg, params object[] args) - { - ConsoleIO.WriteLineFormatted(string.Format(msg, args)); - } - } -} diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index e6f66ca8..1c07258f 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -12,6 +12,7 @@ using MinecraftClient.Proxy; using MinecraftClient.Protocol.Handlers.Forge; using MinecraftClient.Mapping; using MinecraftClient.Inventory; +using MinecraftClient.Logger; namespace MinecraftClient { @@ -176,7 +177,9 @@ namespace MinecraftClient this.port = port; this.protocolversion = protocolversion; - this.Log = new MCLogger(); + this.Log = Settings.LogToFile + ? new FileLogLogger(Settings.ExpandVars(Settings.LogFile), Settings.PrependTimestamp) + : new FilteredLogger(); Log.DebugEnabled = Settings.DebugMessages; Log.InfoEnabled = Settings.InfoMessages; Log.ChatEnabled = Settings.ChatMessages; diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index b2599159..2799afe6 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -119,7 +119,8 @@ True DefaultConfigResource.resx - + + @@ -136,6 +137,7 @@ + @@ -158,7 +160,7 @@ - + diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index dee3cb52..4af6da08 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -17,6 +17,7 @@ using System.Data.SqlClient; using System.Diagnostics; using MinecraftClient.Inventory.ItemPalettes; using MinecraftClient.Protocol.Handlers.PacketPalettes; +using MinecraftClient.Logger; namespace MinecraftClient.Protocol.Handlers { diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index 5c044ad9..eb8b208e 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using MinecraftClient.Mapping; using MinecraftClient.Inventory; +using MinecraftClient.Logger; namespace MinecraftClient.Protocol { diff --git a/MinecraftClient/Resources/config/MinecraftClient.ini b/MinecraftClient/Resources/config/MinecraftClient.ini index 5573a4f8..1e27a8c9 100644 --- a/MinecraftClient/Resources/config/MinecraftClient.ini +++ b/MinecraftClient/Resources/config/MinecraftClient.ini @@ -50,6 +50,9 @@ infomessages=true # Informative messages (i.e Most of the messa #chatfilter= # Regex for filtering chat message #debugfilter= # Regex for filtering debug message filtermode=blacklist # blacklist OR whitelist. Blacklist hide message match regex. Whitelist show message match regex +logtofile=false # Write log messages to file +logfile=console-log-%username%-%serverip%.txt # Log file name +prependtimestamp=false # Prepend timestamp to messages in log file [AppVars] # yourvar=yourvalue diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index 79a92657..c5c8921e 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -110,6 +110,9 @@ namespace MinecraftClient public static Regex ChatFilter = null; public static Regex DebugFilter = null; public static FilterModeEnum FilterMode = FilterModeEnum.Blacklist; + public static bool LogToFile = false; + public static string LogFile = "console-log.txt"; + public static bool PrependTimestamp = false; //AntiAFK Settings public static bool AntiAFK_Enabled = false; @@ -428,6 +431,9 @@ namespace MinecraftClient else FilterMode = FilterModeEnum.Blacklist; break; + case "logtofile": LogToFile = str2bool(argValue); break; + case "logfile": LogFile = argValue; break; + case "prependtimestamp": PrependTimestamp = str2bool(argValue); break; } break;