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;