Skip to content

Commit

Permalink
Improve ErrorLogger to catagorise logs by ErrorLevel
Browse files Browse the repository at this point in the history
  • Loading branch information
lastbattle committed Nov 24, 2024
1 parent a34f253 commit 253c9ac
Showing 1 changed file with 74 additions and 32 deletions.
106 changes: 74 additions & 32 deletions MapleLib/Helpers/ErrorLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ namespace MapleLib.Helpers
{
public static class ErrorLogger
{
private static readonly List<Error> errorList = new List<Error>();
private static readonly object _lock = new object();
private static readonly List<Error> _errorList = new List<Error>();

public static void Log(ErrorLevel level, string message)
{
lock (errorList)
errorList.Add(new Error(level, message));
if (string.IsNullOrWhiteSpace(message))
throw new ArgumentNullException(nameof(message), "Error message cannot be null or empty.");

lock (_lock)
_errorList.Add(new Error(level, message, DateTime.UtcNow));
}

/// <summary>
Expand All @@ -38,7 +42,7 @@ public static void Log(ErrorLevel level, string message)
/// <returns></returns>
public static int NumberOfErrorsPresent()
{
return errorList.Count;
return _errorList.Count;
}

/// <summary>
Expand All @@ -47,64 +51,102 @@ public static int NumberOfErrorsPresent()
/// <returns></returns>
public static bool ErrorsPresent()
{
return errorList.Count > 0;
return _errorList.Any();
}

/// <summary>
/// Clears all errors currently in the pending queue
/// </summary>
public static void ClearErrors()
{
lock (errorList)
errorList.Clear();
lock (_lock)
_errorList.Clear();
}

/// <summary>
/// Logs all pending errors in the queue to file, and clears the queue
/// Logs all pending errors in the queue to file, grouped by error level, and clears the queue
/// </summary>
/// <param name="filename"></param>
/// <param name="filename">The path to the log file</param>
/// <exception cref="ArgumentNullException">Thrown when filename is null or empty</exception>
/// <exception cref="IOException">Thrown when there's an error writing to the file</exception>
public static void SaveToFile(string filename)
{
if (string.IsNullOrWhiteSpace(filename))
throw new ArgumentNullException(nameof(filename), "Filename cannot be null or empty.");

if (!ErrorsPresent())
return;

using (StreamWriter sw = new StreamWriter(File.Open(filename, FileMode.Append, FileAccess.Write, FileShare.Read)))
List<Error> errorsCopy;
lock (_lock)
{
sw.Write("----- Start of the error log. [");
sw.Write(DateTime.Today.ToString());
sw.Write("] -----");
sw.WriteLine();
errorsCopy = new List<Error>(_errorList);
ClearErrors();
}

List<Error> errorList_;
lock (errorList)
var groupedErrors = errorsCopy
.GroupBy(e => e.Level)
.OrderBy(g => g.Key);

var sb = new StringBuilder();
sb.AppendLine($"----- Start of the error log. [{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss.fff}] -----");

foreach (var errorGroup in groupedErrors)
{
sb.AppendLine();
sb.AppendLine($"=== {errorGroup.Key} Errors ===");

foreach (var error in errorGroup.OrderBy(e => e.Timestamp))
{
errorList_ = new List<Error>(errorList); // make a copy before writing
ClearErrors();
sb.AppendLine($"[{error.Timestamp:HH:mm:ss.fff}] : {error.Message}");
}
}

foreach (Error e in errorList_)
{
sw.Write("[");
sw.Write(e.level.ToString());
sw.Write("] : ");
sw.Write(e.message);
sb.AppendLine();
sb.AppendLine($"----- End of the error log. [{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss.fff}] -----");
sb.AppendLine();

sw.WriteLine();
}
sw.WriteLine();
// Use FileShare.ReadWrite to allow other processes to read the file while we're writing
using (var sw = new StreamWriter(File.Open(filename, FileMode.Append, FileAccess.Write, FileShare.ReadWrite)))
{
sw.Write(sb.ToString());
}
}

/// <summary>
/// Gets a snapshot of current errors grouped by error level
/// </summary>
/// <returns>Dictionary with error levels and their corresponding error messages</returns>
public static Dictionary<ErrorLevel, List<Error>> GetErrorSnapshot()
{
lock (_lock)
{
return _errorList
.GroupBy(e => e.Level)
.ToDictionary(
g => g.Key,
g => g.ToList()
);
}
}
}

public class Error
{
internal ErrorLevel level;
internal string message;
public ErrorLevel Level { get; }
public string Message { get; }
public DateTime Timestamp { get; }

internal Error(ErrorLevel level, string message, DateTime timestamp)
{
Level = level;
Message = message;
Timestamp = timestamp;
}

internal Error(ErrorLevel level, string message)
public override string ToString()
{
this.level = level;
this.message = message;
return $"[{Level}] [{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] : {Message}";
}
}

Expand Down

0 comments on commit 253c9ac

Please sign in to comment.