Improved logging.

This commit is contained in:
2023-09-26 21:46:54 +03:00
parent d00ebfd7ed
commit f58a57c6cd
25 changed files with 429 additions and 633 deletions

View File

@@ -78,16 +78,13 @@ public class Boot
CommonTasks();
await client.LoginAsync(TokenType.Bot, botToken);
await client.StartAsync();
commandServiceHandler = new CommandHandler(client, service, botPrefix);
await commandServiceHandler.InstallCommandsAsync();
await Task.Delay(2000);
Config._DiscordBotClient = this;
while (!isReady) ;
@@ -108,8 +105,7 @@ public class Boot
if (arg.Message.Contains("401"))
{
Config.AppSettings.Remove("token");
Config.Logger.Log("The token is invalid. Please restart the bot and enter a valid token.", this,
LogLevel.ERROR);
Config.Logger.Log("The token is invalid. Please restart the bot and enter a valid token.", source:typeof(Boot), type: LogType.CRITICAL);
await Config.AppSettings.SaveToFile();
await Task.Delay(4000);
Environment.Exit(0);
@@ -118,7 +114,7 @@ public class Boot
private async Task Client_LoggedOut()
{
Config.Logger.Log("Successfully Logged Out", this);
Config.Logger.Log("Successfully Logged Out", source: typeof(Boot));
await Log(new LogMessage(LogSeverity.Info, "Boot", "Successfully logged out from discord !"));
}
@@ -130,7 +126,7 @@ public class Boot
private Task LoggedIn()
{
Config.Logger.Log("Successfully Logged In", this);
Config.Logger.Log("Successfully Logged In", source: typeof(Boot));
return Task.CompletedTask;
}
@@ -140,13 +136,12 @@ public class Boot
{
case LogSeverity.Error:
case LogSeverity.Critical:
Config.Logger.Log(message.Message, this, LogLevel.ERROR);
Config.Logger.Log(message.Message, source: typeof(Boot), type: LogType.ERROR);
break;
case LogSeverity.Info:
case LogSeverity.Debug:
Config.Logger.Log(message.Message, this);
Config.Logger.Log(message.Message, source: typeof(Boot), type: LogType.INFO);
break;

View File

@@ -57,7 +57,7 @@ internal class CommandHandler
}
catch (Exception ex)
{
Config.Logger.Log(ex.Message, "CommandHandler", LogLevel.ERROR);
Config.Logger.Log(ex.Message, type: LogType.ERROR, source: typeof(CommandHandler));
}
return Task.CompletedTask;
@@ -145,7 +145,7 @@ internal class CommandHandler
}
catch (Exception ex)
{
Config.Logger.Log(ex.Message, this, LogLevel.ERROR);
Config.Logger.Log(ex.Message, type: LogType.ERROR, source: typeof(CommandHandler));
}
}
}

View File

@@ -9,8 +9,8 @@ namespace PluginManager;
public class Config
{
private static bool _isLoaded;
public static DBLogger? Logger;
private static bool _isLoaded;
public static Logger Logger;
public static SettingsDictionary<string, string> AppSettings;
internal static Boot? _DiscordBotClient;
@@ -30,15 +30,14 @@ public class Config
AppSettings = new SettingsDictionary<string, string>("./Data/Resources/config.json");
AppSettings["LogFolder"] = "./Data/Logs/Logs";
AppSettings["ErrorFolder"] = "./Data/Logs/Errors";
Logger = new DBLogger(true);
Logger = new Logger(false, true);
ArchiveManager.Initialize();
_isLoaded = true;
Logger.Log("Config initialized", LogLevel.INFO);
await Logger.Log(message: "Config initialized", source: typeof(Config));
}
}

View File

@@ -1,17 +0,0 @@
using System.Collections.Generic;
namespace PluginManager.Interfaces.Exceptions;
public interface IException
{
public List<string> Messages { get; set; }
public bool isFatal { get; }
public string GenerateFullMessage();
public void HandleException();
public IException AppendError(string message);
public IException AppendError(List<string> messages);
public IException IsFatal(bool isFatal = true);
}

View File

@@ -0,0 +1,15 @@
using System;
using PluginManager.Others;
namespace PluginManager.Interfaces.Logger;
internal interface ILog
{
string Message { get; set; }
string OutputFile { get; set; }
Type? Source { get; set; }
LogType Type { get; set; }
DateTime ThrowTime { get; set; }
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Threading.Tasks;
using PluginManager.Others;
using PluginManager.Others.Logger;
namespace PluginManager.Interfaces.Logger;
internal interface ILogger
{
bool IsEnabled { get; init; }
bool OutputToFile { get; init; }
event EventHandler<Log> OnLog;
Task Log(
string message = "", string outputFile = "", Type? source = default, LogType type = LogType.INFO,
DateTime throwTime = default);
}

View File

@@ -53,10 +53,9 @@ internal class Loader
{
Assembly.LoadFrom(file);
}
catch (Exception ex)
catch
{
Config.Logger.Log("PluginName: " + new FileInfo(file).Name.Split('.')[0] + " not loaded", this,
LogLevel.ERROR);
Config.Logger.Log("PluginName: " + new FileInfo(file).Name.Split('.')[0] + " not loaded", source: typeof(Loader), type: LogType.ERROR);
continue;
}
@@ -130,7 +129,7 @@ internal class Loader
}
catch (Exception ex)
{
Config.Logger.Log(ex.Message, this, LogLevel.ERROR);
Config.Logger.Log(ex.Message, source: typeof(Loader), type: LogType.ERROR);
return null;
}

View File

@@ -88,11 +88,10 @@ public class PluginLoader
Events = new List<DBEvent>();
SlashCommands = new List<DBSlashCommand>();
Config.Logger.Log("Starting plugin loader ... Client: " + _client.CurrentUser.Username, this,
LogLevel.INFO);
Config.Logger.Log("Starting plugin loader ... Client: " + _client.CurrentUser.Username, source: typeof(PluginLoader), type: LogType.INFO);
var loader = new Loader("./Data/Plugins", "dll");
loader.FileLoaded += args => Config.Logger.Log($"{args.PluginName} file Loaded", this, LogLevel.INFO);
loader.FileLoaded += args => Config.Logger.Log($"{args.PluginName} file Loaded", source: typeof(PluginLoader), type: LogType.INFO);
loader.PluginLoaded += Loader_PluginLoaded;
var res = loader.Load();
Events = res.Item1;
@@ -117,7 +116,7 @@ public class PluginLoader
}
catch (Exception ex)
{
Config.Logger.Log(ex.Message, this, LogLevel.ERROR);
Config.Logger.Log(ex.Message, source: typeof(PluginLoader), type: LogType.ERROR);
}
break;
@@ -151,13 +150,13 @@ public class PluginLoader
var instance = (DBEvent)Activator.CreateInstance(type);
instance.Start(client);
Events.Add(instance);
Config.Logger.Log($"[EVENT] Loaded external {type.FullName}!", LogLevel.INFO);
Config.Logger.Log($"[EVENT] Loaded external {type.FullName}!", source: typeof(PluginLoader));
}
else if (type.IsClass && typeof(DBCommand).IsAssignableFrom(type))
{
var instance = (DBCommand)Activator.CreateInstance(type);
Commands.Add(instance);
Config.Logger.Log($"[CMD] Instance: {type.FullName} loaded !", LogLevel.INFO);
Config.Logger.Log($"[CMD] Instance: {type.FullName} loaded !", source: typeof(PluginLoader));
}
else if (type.IsClass && typeof(DBSlashCommand).IsAssignableFrom(type))
{
@@ -170,13 +169,13 @@ public class PluginLoader
await client.CreateGlobalApplicationCommandAsync(builder.Build());
SlashCommands.Add(instance);
Config.Logger.Log($"[SLASH] Instance: {type.FullName} loaded !", LogLevel.INFO);
Config.Logger.Log($"[SLASH] Instance: {type.FullName} loaded !", source: typeof(PluginLoader));
}
}
catch (Exception ex)
{
//Console.WriteLine(ex.Message);
Config.Logger.Error(ex);
Config.Logger.Log(ex.Message, source: typeof(PluginLoader), type: LogType.ERROR);
}
}

View File

@@ -89,8 +89,7 @@ public class PluginsManager
}
catch (Exception exception)
{
Config.Logger.Log("Failed to execute command: listplugs\nReason: " + exception.Message, this,
LogLevel.ERROR);
Config.Logger.Log(message: "Failed to execute command: listplugs\nReason: " + exception.Message, source: typeof(PluginsManager), type: LogType.ERROR );
}
return null;
@@ -142,8 +141,7 @@ public class PluginsManager
}
catch (Exception exception)
{
Config.Logger.Log("Failed to execute command: listplugs\nReason: " + exception.Message, this,
LogLevel.ERROR);
Config.Logger.Log("Failed to execute command: plugin list\nReason: " + exception.Message, source: typeof(PluginsManager), type: LogType.ERROR);
}
return null;

View File

@@ -48,7 +48,7 @@ public class InternalActionManager
{
if (!Actions.ContainsKey(actionName))
{
Config.Logger.Log($"Action {actionName} not found", "InternalActionManager", LogLevel.WARNING, true);
Config.Logger.Log($"Action {actionName} not found", type: LogType.ERROR, source: typeof(InternalActionManager));
return "Action not found";
}
@@ -59,7 +59,7 @@ public class InternalActionManager
}
catch (Exception e)
{
Config.Logger.Log(e.Message, "InternalActionManager", LogLevel.ERROR);
Config.Logger.Log(e.Message , type: LogType.ERROR, source: typeof(InternalActionManager));
return e.Message;
}
}

View File

@@ -2,7 +2,6 @@
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading.Tasks;
namespace PluginManager.Others;
@@ -94,7 +93,7 @@ public static class ArchiveManager
}
catch (Exception ex)
{
Config.Logger.Log(ex.Message, "Archive Manager", LogLevel.ERROR); // Write the error to a file
Config.Logger.Log(message: ex.Message, source: typeof(ArchiveManager), type: LogType.ERROR); // Write the error to a file
await Task.Delay(100);
return await ReadFromPakAsync(FileName, archFile);
}
@@ -132,8 +131,7 @@ public static class ArchiveManager
}
catch (Exception ex)
{
Config.Logger.Log($"Failed to extract {entry.Name}. Exception: {ex.Message}",
"Archive Manager", LogLevel.ERROR);
Config.Logger.Log(ex.Message, source: typeof(ArchiveManager), type: LogType.ERROR);
}
currentZIPFile++;
@@ -165,8 +163,7 @@ public static class ArchiveManager
}
catch (Exception ex)
{
Config.Logger.Log($"Failed to extract {entry.Name}. Exception: {ex.Message}",
"Archive Manager", LogLevel.ERROR);
Config.Logger.Log(ex.Message, source: typeof(ArchiveManager), type: LogType.ERROR);
}
await Task.Delay(10);

View File

@@ -14,7 +14,7 @@ public enum OperatingSystem
/// <summary>
/// The output log type
/// </summary>
public enum LogLevel
public enum LogType
{
INFO,
WARNING,

View File

@@ -1,91 +0,0 @@
using System;
using System.Collections.Generic;
using PluginManager.Interfaces.Exceptions;
namespace PluginManager.Others.Exceptions;
public class ConfigFailedToLoad : IException
{
public List<string>? Messages { get; set; }
public bool isFatal { get; private set; }
public string? File { get; }
public ConfigFailedToLoad(string message, bool isFatal, string file)
{
this.isFatal = isFatal;
Messages = new List<string>() {message};
this.File = file;
}
public ConfigFailedToLoad(string message, bool isFatal)
{
this.isFatal = isFatal;
Messages = new List<string>() {message};
this.File = null;
}
public ConfigFailedToLoad(string message)
{
this.isFatal = false;
Messages = new List<string>() {message};
this.File = null;
}
public string GenerateFullMessage()
{
string messages = "";
foreach (var message in Messages)
{
messages += message + "\n";
}
return $"\nMessage: {messages}\nIsFatal: {isFatal}\nFile: {File ?? "null"}";
}
public void HandleException()
{
if (isFatal)
{
Config.Logger.Log(GenerateFullMessage(), LogLevel.CRITICAL, true);
Environment.Exit((int)ExceptionExitCode.CONFIG_FAILED_TO_LOAD);
}
Config.Logger.Log(GenerateFullMessage(), LogLevel.WARNING);
}
public IException AppendError(string message)
{
Messages.Add(message);
return this;
}
public IException AppendError(List<string> messages)
{
Messages.AddRange(messages);
return this;
}
public IException IsFatal(bool isFatal = true)
{
this.isFatal = isFatal;
return this;
}
public static ConfigFailedToLoad CreateError(string message, bool isFatal, string? file = null)
{
if (file is not null)
return new ConfigFailedToLoad(message, isFatal, file);
return new ConfigFailedToLoad(message, isFatal);
}
public static ConfigFailedToLoad CreateError(string message)
{
return new ConfigFailedToLoad(message);
}
}

View File

@@ -1,75 +0,0 @@
using System;
using System.Collections.Generic;
using PluginManager.Interfaces.Exceptions;
namespace PluginManager.Others.Exceptions;
public class ConfigNoKeyWasPresent: IException
{
public List<string> Messages { get; set; }
public bool isFatal { get; private set; }
public ConfigNoKeyWasPresent(string message, bool isFatal)
{
this.Messages = new List<string>() { message };
this.isFatal = isFatal;
}
public ConfigNoKeyWasPresent(string message)
{
this.Messages = new List<string>() { message };
this.isFatal = false;
}
public string GenerateFullMessage()
{
string messages = "";
foreach (var message in Messages)
{
messages += message + "\n";
}
return $"\nMessage: {messages}\nIsFatal: {isFatal}";
}
public void HandleException()
{
if (isFatal)
{
Config.Logger.Log(GenerateFullMessage(), LogLevel.CRITICAL, true);
Environment.Exit((int)ExceptionExitCode.CONFIG_KEY_NOT_FOUND);
}
Config.Logger.Log(GenerateFullMessage(), LogLevel.WARNING);
}
public IException AppendError(string message)
{
Messages.Add(message);
return this;
}
public IException AppendError(List<string> messages)
{
Messages.AddRange(messages);
return this;
}
public IException IsFatal(bool isFatal = true)
{
this.isFatal = isFatal;
return this;
}
public static ConfigNoKeyWasPresent CreateError(string message)
{
return new ConfigNoKeyWasPresent(message);
}
public static ConfigNoKeyWasPresent CreateError(string message, bool isFatal)
{
return new ConfigNoKeyWasPresent(message, isFatal);
}
}

View File

@@ -1,95 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace PluginManager.Others.Logger;
public class DBLogger
{
public delegate void LogHandler(string message, LogLevel logType, bool isInternal = false);
private readonly string _errFolder;
private readonly string _logFolder;
private readonly List<LogMessage> ErrorHistory = new();
private readonly List<LogMessage> LogHistory = new();
private readonly bool _continuousSave;
private readonly bool _LogErrorsOnly;
public DBLogger(bool continuousSave = true, bool logErrorsOnly = true)
{
_logFolder = Config.AppSettings["LogFolder"];
_errFolder = Config.AppSettings["ErrorFolder"];
_continuousSave = continuousSave;
_LogErrorsOnly = logErrorsOnly;
}
public IReadOnlyList<LogMessage> Logs => LogHistory;
public IReadOnlyList<LogMessage> Errors => ErrorHistory;
public event LogHandler? LogEvent;
public void Log(string message, LogLevel type = LogLevel.INFO)
{
Log(new LogMessage(message, type));
}
public void Log(string message, LogLevel type= LogLevel.INFO, bool isInternal = false)
{
Log(new LogMessage(message, type,"unknown", isInternal));
}
public void Log(string message, string sender = "unknown", LogLevel type = LogLevel.INFO, bool isInternal = false)
{
Log(new LogMessage(message, type,sender,isInternal));
}
public void Log(string message, string sender = "unknown", LogLevel type = LogLevel.INFO)
{
Log(new LogMessage(message, type, sender));
}
public void Error(Exception? e)
{
Log(e.Message, e.Source, LogLevel.ERROR);
}
private async void Log(LogMessage message)
{
LogEvent?.Invoke(message.Message, message.Type);
if (message.Type != LogLevel.ERROR && message.Type != LogLevel.CRITICAL)
LogHistory.Add(message);
else
ErrorHistory.Add(message);
if (_continuousSave)
await SaveToFile();
}
public void Log(string message, object sender, LogLevel type = LogLevel.INFO)
{
Log(message, sender.GetType().Name, type);
}
public async Task SaveToFile()
{
await SaveToTxt();
}
private async Task SaveToTxt()
{
if (!_LogErrorsOnly)
{
var logFile = new LogFile(_logFolder + $"/{DateTime.Today.ToShortDateString().Replace('/', '_')}_log.txt");
foreach (var logMessage in LogHistory)
logFile.Write(logMessage);
}
var errFile = new LogFile(_errFolder + $"/{DateTime.Today.ToShortDateString().Replace('/', '_')}_err.txt");
foreach (var logMessage in ErrorHistory)
errFile.Write(logMessage);
}
}

View File

@@ -0,0 +1,78 @@
using System;
using System.Linq;
using PluginManager.Interfaces.Logger;
namespace PluginManager.Others.Logger;
public class Log : ILog
{
public string Message { get; set; }
public string OutputFile { get; set; }
public Type? Source { get; set; }
public LogType Type { get; set; }
public DateTime ThrowTime { get; set; }
public Log(string message, string outputFile, Type? source, LogType type, DateTime throwTime)
{
Message = message;
OutputFile = outputFile;
Source = source;
Type = type;
ThrowTime = throwTime;
}
public Log(string message, string outputFile, Type? source, LogType type)
{
Message = message;
OutputFile = outputFile;
Source = source;
Type = type;
ThrowTime = DateTime.Now;
}
public Log(string message, string outputFile, Type? source)
{
Message = message;
OutputFile = outputFile;
Source = source;
Type = LogType.INFO;
ThrowTime = DateTime.Now;
}
public Log(string message, string outputFile)
{
Message = message;
OutputFile = outputFile;
Source = typeof(Log);
Type = LogType.INFO;
ThrowTime = DateTime.Now;
}
public Log(string message)
{
Message = message;
OutputFile = "";
Source = typeof(Log);
Type = LogType.INFO;
ThrowTime = DateTime.Now;
}
public static implicit operator Log(string message) => new (message);
public static implicit operator string(Log log) => $"[{log.ThrowTime}] {log.Message}";
public string AsLongString()
{
return $"[{ThrowTime}] [{Source}] [{Type}] {Message}";
}
public string AsShortString()
{
return this;
}
public string FormatedLongString()
{
return $"[{ThrowTime}]\t[{Source}]\t\t\t[{Type}]\t{Message}";
}
}

View File

@@ -1,36 +0,0 @@
using System.IO;
namespace PluginManager.Others.Logger;
public class LogFile
{
public FileInfo File { get; set; }
public LogFile(string path)
{
File = new FileInfo(path);
}
public void Write(string message)
{
using var sw = File.AppendText();
sw.WriteLine(message);
}
public void Write(string message, LogLevel type)
{
using var sw = File.AppendText();
sw.WriteLine($"[{type}] {message}");
}
public void Write(string message, string sender, LogLevel type)
{
using var sw = File.AppendText();
sw.WriteLine($"[{type}] [{sender}] {message}");
}
public void Write(LogMessage logMessage)
{
using var sw = File.AppendText();
sw.WriteLine(logMessage.ToString());
}
}

View File

@@ -1,51 +0,0 @@
using System;
namespace PluginManager.Others.Logger;
public class LogMessage
{
public LogMessage(string message, LogLevel type)
{
Message = message;
Type = type;
Time = DateTime.Now.ToString("HH:mm:ss");
isInternal = false;
}
public LogMessage(string message, LogLevel type, string sender, bool isInternal) : this(message, type)
{
Sender = sender;
this.isInternal = isInternal;
}
public LogMessage(string message, LogLevel type, string sender) : this (message, type, sender, false)
{
}
public string Message { get; set; }
public LogLevel Type { get; set; }
public string Time { get; set; }
public string Sender { get; set; }
public bool isInternal { get; set; }
public override string ToString()
{
return $"[{Time}] {Message}";
}
public static explicit operator LogMessage(string message)
{
return new LogMessage(message, LogLevel.INFO);
}
public static explicit operator LogMessage((string message, LogLevel type) tuple)
{
return new LogMessage(tuple.message, tuple.type);
}
public static explicit operator LogMessage((string message, LogLevel type, string sender) tuple)
{
return new LogMessage(tuple.message, tuple.type, tuple.sender);
}
}

View File

@@ -0,0 +1,57 @@
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using PluginManager.Interfaces.Logger;
namespace PluginManager.Others.Logger;
public sealed class Logger : ILogger
{
public bool IsEnabled { get; init; }
public bool OutputToFile { get; init; }
public LogType LowestLogLevel { get; set; }
private bool UseShortVersion { get; }
public Logger(bool useShortVersion, bool outputToFile, LogType lowestLogLevel = LogType.INFO)
{
UseShortVersion = useShortVersion;
OutputToFile = outputToFile;
IsEnabled = true;
LowestLogLevel = lowestLogLevel;
}
public event EventHandler<Log>? OnLog;
private async Task Log(Log logMessage)
{
if (!IsEnabled) return;
OnLog?.Invoke(this, logMessage);
if (logMessage.Type < LowestLogLevel) return;
if (OutputToFile)
await File.AppendAllTextAsync(
logMessage.OutputFile,
(UseShortVersion ? logMessage : logMessage.AsLongString()) + "\n");
}
public async Task Log(string message = "", string outputFile = "", Type? source = default, LogType type = LogType.INFO, DateTime throwTime = default)
{
if (!IsEnabled) return;
if (type < LowestLogLevel) return;
if (string.IsNullOrEmpty(message)) return;
if (string.IsNullOrEmpty(outputFile)) outputFile = Config.AppSettings["LogFolder"] + "/" + DateTime.Now.ToString("yyyy-MM-dd") + ".log";
if(throwTime == default) throwTime = DateTime.Now;
if (source == default) source = typeof(Log);
await Log(new Log(message, outputFile, source, type, throwTime));
}
}

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using PluginManager.Others.Exceptions;
namespace PluginManager.Others;
@@ -16,10 +15,8 @@ public class SettingsDictionary<TKey, TValue> : IDictionary<TKey, TValue>
_file = file;
if (!LoadFromFile())
{
ConfigFailedToLoad.CreateError("Failed to load config")
.AppendError("The file is empty or does not exist")
.IsFatal()
.HandleException();
_dictionary = new Dictionary<TKey, TValue>();
SaveToFile();
}
}
@@ -50,10 +47,6 @@ public class SettingsDictionary<TKey, TValue> : IDictionary<TKey, TValue>
}
catch
{
ConfigFailedToLoad
.CreateError("Failed to load config")
.IsFatal()
.HandleException();
return false;
}
@@ -126,11 +119,6 @@ public class SettingsDictionary<TKey, TValue> : IDictionary<TKey, TValue>
if(this._dictionary[key] is string s && !string.IsNullOrEmpty(s) && !string.IsNullOrWhiteSpace(s))
return this._dictionary[key];
ConfigNoKeyWasPresent.CreateError($"Key {(key is string ? key : typeof(TKey).Name)} was not present in {_file ?? "config"}")
.AppendError("Deleting the file may fix this issue")
.IsFatal()
.HandleException();
return default!;
}
set => this._dictionary![key] = value;