Compare commits

...
This repository has been archived on 2026-06-12. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.

45 commits
legacy ... main

Author SHA1 Message Date
Lala Sabathil
e742f21154
Merge pull request #7 from Fortunevale/dependabot/nuget/Microsoft.Extensions.Logging.Abstractions-8.0.1
Bump Microsoft.Extensions.Logging.Abstractions from 8.0.0 to 8.0.1
2024-03-13 15:07:49 +01:00
dependabot[bot]
f3d8fdfe04
Bump Microsoft.Extensions.Logging.Abstractions from 8.0.0 to 8.0.1
Bumps [Microsoft.Extensions.Logging.Abstractions](https://github.com/dotnet/runtime) from 8.0.0 to 8.0.1.
- [Release notes](https://github.com/dotnet/runtime/releases)
- [Commits](https://github.com/dotnet/runtime/compare/v8.0.0...v8.0.1)

---
updated-dependencies:
- dependency-name: Microsoft.Extensions.Logging.Abstractions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-13 07:21:48 +00:00
Mira
ee2719ec67
chore: Update to net8 2023-11-20 09:14:59 +01:00
Mira
4d1f63fbc1
Merge pull request #6 from Fortunevale/dependabot/nuget/Microsoft.Extensions.Logging.Abstractions-8.0.0
Bump Microsoft.Extensions.Logging.Abstractions from 7.0.1 to 8.0.0
2023-11-20 09:07:26 +01:00
dependabot[bot]
f045bd7dbc
Bump Microsoft.Extensions.Logging.Abstractions from 7.0.1 to 8.0.0
Bumps [Microsoft.Extensions.Logging.Abstractions](https://github.com/dotnet/runtime) from 7.0.1 to 8.0.0.
- [Release notes](https://github.com/dotnet/runtime/releases)
- [Commits](https://github.com/dotnet/runtime/compare/v7.0.1...v8.0.0)

---
updated-dependencies:
- dependency-name: Microsoft.Extensions.Logging.Abstractions
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-20 07:29:50 +00:00
Mira
b43fd3d24b
Merge pull request #5 from Fortunevale/dependabot/nuget/Microsoft.Extensions.Logging.Abstractions-7.0.1
Bump Microsoft.Extensions.Logging.Abstractions from 7.0.0 to 7.0.1
2023-11-20 07:56:28 +01:00
Mira
f8e254440b
fix: RunAnalyzersDuringBuild to False 2023-10-03 13:44:23 +02:00
Mira
a2ed87f7ce
Update LoggerClient.cs 2023-08-23 04:26:20 +02:00
Mira
f1a061edc1
refactor: Apply .editorconfig 2023-08-06 19:14:08 +02:00
Mira
e18aa2ef51
Update LoggerClient.cs 2023-06-18 21:43:23 +02:00
Mira
40e8632a77
Update LoggerClient.cs 2023-06-18 20:43:10 +02:00
dependabot[bot]
25898aff51
Bump Microsoft.Extensions.Logging.Abstractions from 7.0.0 to 7.0.1
Bumps [Microsoft.Extensions.Logging.Abstractions](https://github.com/dotnet/runtime) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/dotnet/runtime/releases)
- [Commits](https://github.com/dotnet/runtime/compare/v7.0.0...v7.0.1)

---
updated-dependencies:
- dependency-name: Microsoft.Extensions.Logging.Abstractions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-14 08:03:04 +00:00
Mira
7c1ff06fa5
Seal + Docs 2023-06-08 03:27:52 +02:00
Mira
332b6172ae
Target x64 2023-06-07 20:31:34 +02:00
Mira
2cb5b1a169
Refactor 2023-06-07 00:10:00 +02:00
Mira
8969bb00cc
Update Logger.cs 2023-05-31 17:09:02 +02:00
Mira
53596d9b8a
Update Logger.cs 2023-05-30 22:02:03 +02:00
Mira
a7e5672e18
Update Logger.cs 2023-05-30 21:18:04 +02:00
Mira
03355fe27b
fuck this 2023-05-26 11:09:31 +02:00
Mira
933fe1f5e9
Update Logger.cs 2023-05-26 11:05:17 +02:00
Mira
7b5dfa673a
fix nre 2023-05-26 10:57:47 +02:00
Mira
a3b7eccc3d
chore(deps): Update to .NET 7.0 2023-05-09 15:06:03 +02:00
Lala Sabathil
3bfe19dd65
Merge pull request #4 from Fortunevale/dependabot/nuget/Newtonsoft.Json-13.0.3
Bump Newtonsoft.Json from 13.0.2 to 13.0.3
2023-03-08 09:04:41 +01:00
dependabot[bot]
f03407e9b7
Bump Newtonsoft.Json from 13.0.2 to 13.0.3
Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 13.0.2 to 13.0.3.
- [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases)
- [Commits](https://github.com/JamesNK/Newtonsoft.Json/commits)

---
updated-dependencies:
- dependency-name: Newtonsoft.Json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-08 08:02:09 +00:00
Mira
a4608d4a74
fix: Create destination directory if not exist 2023-01-29 21:51:12 +01:00
Mira
9bc5bd0308
fix: Change colors to make them visible in linux 2023-01-29 21:47:51 +01:00
Mira
997108f3d0
Update README.md 2023-01-29 18:13:47 +01:00
Mira
249148a69d
Merge pull request #3 from Fortunevale/dependabot/nuget/Microsoft.Extensions.Logging.Abstractions-7.0.0
Bump Microsoft.Extensions.Logging.Abstractions from 6.0.2 to 7.0.0
2023-01-29 18:13:21 +01:00
Mira
311650f29a
Update Logger.cs 2022-11-29 09:11:22 +01:00
Mira
9cb981e86d
fix: dont add empty strings to builder 2022-11-29 09:06:58 +01:00
Mira
1e4bcb07b0
fix: '{0}{1}' is now parsed correctly 2022-11-29 09:04:46 +01:00
dependabot[bot]
18795d104f
Bump Microsoft.Extensions.Logging.Abstractions from 6.0.2 to 7.0.0
Bumps [Microsoft.Extensions.Logging.Abstractions](https://github.com/dotnet/runtime) from 6.0.2 to 7.0.0.
- [Release notes](https://github.com/dotnet/runtime/releases)
- [Commits](https://github.com/dotnet/runtime/commits)

---
updated-dependencies:
- dependency-name: Microsoft.Extensions.Logging.Abstractions
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-29 07:15:15 +00:00
Mira
c21f2dae76
fix: No longer the need to rely on string interpolation 2022-11-29 08:11:51 +01:00
Mira
cc976c86a1
Update Logger.cs 2022-10-12 14:44:32 +02:00
Mira
7cdc1b35ea
Merge pull request #2 from Fortunevale/dependabot/nuget/Microsoft.Extensions.Logging.Abstractions-6.0.2
Bump Microsoft.Extensions.Logging.Abstractions from 6.0.1 to 6.0.2
2022-09-14 10:00:06 +02:00
dependabot[bot]
667aa66b8e
Bump Microsoft.Extensions.Logging.Abstractions from 6.0.1 to 6.0.2
Bumps [Microsoft.Extensions.Logging.Abstractions](https://github.com/dotnet/runtime) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/dotnet/runtime/releases)
- [Commits](https://github.com/dotnet/runtime/compare/v6.0.1...v6.0.2)

---
updated-dependencies:
- dependency-name: Microsoft.Extensions.Logging.Abstractions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-14 07:42:22 +00:00
Mira
c2bb8f6398
Update Logger.cs 2022-09-04 19:41:45 +02:00
Mira
538808ec42
Update Logger.cs 2022-08-19 20:21:18 +02:00
Mira
3ec20d05d3
Fix msg 2022-07-20 06:50:25 +02:00
Mira
d0272b5761
Merge pull request #1 from Fortunevale/dependabot/nuget/Microsoft.Extensions.Logging.Abstractions-6.0.1
Bump Microsoft.Extensions.Logging.Abstractions from 6.0.0 to 6.0.1
2022-07-12 23:20:40 +02:00
dependabot[bot]
c78d869938
Bump Microsoft.Extensions.Logging.Abstractions from 6.0.0 to 6.0.1
Bumps [Microsoft.Extensions.Logging.Abstractions](https://github.com/dotnet/runtime) from 6.0.0 to 6.0.1.
- [Release notes](https://github.com/dotnet/runtime/releases)
- [Commits](https://github.com/dotnet/runtime/compare/v6.0.0...v6.0.1)

---
updated-dependencies:
- dependency-name: Microsoft.Extensions.Logging.Abstractions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-12 21:18:57 +00:00
Mira
912ce15848
Create dependabot.yml 2022-07-12 23:18:36 +02:00
Mira
fdca37ec62
Modify LoggerProvider to work with non static 2022-06-13 11:44:01 +02:00
Mira
9e9d10a282
Smart™ 2022-06-13 10:38:08 +02:00
Mira
1e7355fe8f
No more static 2022-06-13 10:27:27 +02:00
14 changed files with 810 additions and 456 deletions

11
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "nuget" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"

View file

@ -1,9 +1,33 @@
namespace Xorog.Logger.Entities;
namespace Xorog.Logger;
public class LogEntry
{
public DateTime TimeOfEvent { get; set; }
public LogLevel LogLevel { get; set; }
public string Message { get; set; }
public Exception? Exception { get; set; }
internal LogEntry() { }
internal string RawMessage { get; set; }
/// <summary>
/// The time of the event.
/// </summary>
public DateTime TimeOfEvent { get; internal set; }
/// <summary>
/// The severity of the event.
/// </summary>
public CustomLogLevel LogLevel { get; internal set; }
/// <summary>
/// The message describing the event.
/// </summary>
public string Message { get; internal set; }
/// <summary>
/// Any objects involved in creating the event message.
/// </summary>
public object[] Args { get; internal set; } = Array.Empty<object>();
/// <summary>
/// The exception that's been caused.
/// </summary>
public Exception? Exception { get; internal set; }
}

13
Entities/StringPart.cs Normal file
View file

@ -0,0 +1,13 @@
namespace Xorog.Logger;
internal class StringPart : IDisposable
{
internal string String { get; set; }
internal ConsoleColor? Color { get; set; }
public void Dispose()
{
String = "";
Color = null;
}
}

14
Enums/CustomLogLevel.cs Normal file
View file

@ -0,0 +1,14 @@
namespace Xorog.Logger;
public enum CustomLogLevel
{
None,
Fatal,
Error,
Warn,
Info,
Debug,
Debug2,
Trace,
Trace2
}

View file

@ -1,14 +0,0 @@
namespace Xorog.Logger.Enums;
public enum LogLevel
{
NONE,
FATAL,
ERROR,
WARN,
INFO,
DEBUG,
DEBUG2,
TRACE,
TRACE2
}

View file

@ -1,7 +1,6 @@
namespace Xorog.Logger;
namespace Xorog.Logger.EventArgs;
public class LogMessageEventArgs : EventArgs
public class LogMessageEventArgs : System.EventArgs
{
public LogEntry LogEntry { get; set; }
}

View file

@ -4,7 +4,4 @@ global using System.Collections.Generic;
global using System.IO;
global using System.Linq;
global using System.Text;
global using System.Threading.Tasks;
global using Xorog.Logger.Entities;
global using Xorog.Logger.Enums;
global using LogLevel = Xorog.Logger.Enums.LogLevel;
global using System.Threading.Tasks;

8
GlobalSuppressions.cs Normal file
View file

@ -0,0 +1,8 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>", Scope = "member", Target = "~P:Xorog.Logger.LoggerProvider._logger")]

407
Logger.cs
View file

@ -1,407 +0,0 @@
namespace Xorog.Logger;
public class Logger : ILogger
{
internal Logger() { }
private bool loggerStarted = false;
private LogLevel maxLogLevel = LogLevel.DEBUG;
private string FileName = "";
private FileStream OpenedFile { get; set; }
internal List<LogEntry> LogsToPost = new();
internal List<string> Blacklist = new();
internal List<LogLevel> FileBlackList = new();
private Task RunningLogger = null;
public event EventHandler<LogMessageEventArgs> LogRaised;
/// <summary>
/// Starts the logger with specified settings
/// </summary>
/// <param name="filePath">Where the current logs should be saved to, leave blank if logs shouldnt be saved</param>
/// <param name="level">The loglevel that should be displayed in the console, does not affect whats written to file</param>
/// <param name="cleanUpBefore">Clean up old logs before a datetime</param>
/// <returns>A bool stating if the logger was started</returns>
public static Logger StartLogger(string filePath = "", LogLevel level = LogLevel.DEBUG, DateTime cleanUpBefore = new DateTime(), bool ThrowOnFailedDeletion = false)
{
var handler = new Logger();
if (handler.loggerStarted)
throw new Exception($"The logger is already started");
if (filePath is not "")
{
handler.FileName = filePath;
handler.OpenedFile = File.Open(handler.FileName, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.Read);
}
handler.loggerStarted = true;
handler.maxLogLevel = level;
if (cleanUpBefore != new DateTime())
{
foreach (var b in Directory.GetFiles(new FileInfo(filePath).Directory.FullName))
{
try
{
FileInfo fi = new(b);
if (fi.CreationTimeUtc < cleanUpBefore)
{
fi.Delete();
handler.LogDebug($"{fi.Name} deleted");
}
}
catch (Exception ex)
{
if (!ThrowOnFailedDeletion)
handler.LogError( $"Couldn't delete log file {b}", ex);
else
throw new Exception($"Failed to delete {b}: {ex}");
}
}
}
handler.RunningLogger = Task.Run(async () =>
{
while (handler.loggerStarted)
{
try
{
while (handler.LogsToPost.Count == 0)
{
Thread.Sleep(10);
}
for (int i = 0; i < handler.LogsToPost.Count; i++)
{
var currentLog = handler.LogsToPost[0];
handler.LogsToPost.Remove(currentLog);
if (currentLog is null)
{
continue;
}
string LogLevelText = currentLog.LogLevel.ToString();
if (LogLevelText.Length < 6)
LogLevelText += new string(' ', 6 - LogLevelText.Length);
ConsoleColor LogLevelColor;
ConsoleColor BackgroundColor;
LogLevelColor = currentLog.LogLevel switch
{
LogLevel.TRACE => ConsoleColor.Gray,
LogLevel.DEBUG2 => ConsoleColor.Gray,
LogLevel.DEBUG => ConsoleColor.Gray,
LogLevel.INFO => ConsoleColor.Green,
LogLevel.WARN => ConsoleColor.Yellow,
LogLevel.ERROR => ConsoleColor.Red,
LogLevel.FATAL => ConsoleColor.Black,
_ => ConsoleColor.Gray
};
BackgroundColor = currentLog.LogLevel switch
{
LogLevel.FATAL => ConsoleColor.DarkRed,
_ => ConsoleColor.Black
};
string LogMessage = currentLog.Message;
foreach (var blacklistobject in handler.Blacklist)
LogMessage = LogMessage.Replace(blacklistobject, new String('*', blacklistobject.Length), StringComparison.CurrentCultureIgnoreCase);
if (handler.maxLogLevel >= currentLog.LogLevel)
{
Console.ResetColor(); Console.Write($"[{currentLog.TimeOfEvent:dd.MM.yyyy HH:mm:ss}] ");
Console.ForegroundColor = LogLevelColor; Console.BackgroundColor = BackgroundColor; Console.Write($"[{LogLevelText}]");
Console.ResetColor(); Console.WriteLine($" {LogMessage}");
if (currentLog.Exception is not null)
Console.WriteLine(currentLog.Exception.ToString());
}
_ = Task.Run(() =>
{
handler.LogRaised?.Invoke(null, new LogMessageEventArgs() { LogEntry = currentLog });
});
try
{
if (!handler.FileBlackList.Contains(currentLog.LogLevel))
{
Byte[] FileWrite = Encoding.UTF8.GetBytes($"[{currentLog.TimeOfEvent:dd.MM.yyyy HH:mm:ss}] [{LogLevelText}] {LogMessage}\n{(currentLog.Exception is not null ? $"{currentLog.Exception}\n" : "")}");
if (handler.OpenedFile != null)
{
await handler.OpenedFile.WriteAsync(FileWrite.AsMemory(0, FileWrite.Length));
handler.OpenedFile.Flush();
}
}
}
catch (Exception ex)
{
handler.LogFatal($"Couldn't write log to file: {ex}");
}
}
}
catch (Exception ex)
{
handler.LogError("An exception occured while trying to display a log message", ex);
await Task.Delay(1000);
continue;
}
}
});
return new Logger();
}
/// <summary>
/// Stops the logger
/// </summary>
public void StopLogger()
{
loggerStarted = false;
maxLogLevel = LogLevel.DEBUG;
FileName = "";
Thread.Sleep(500);
if (RunningLogger is not null)
RunningLogger.Dispose();
RunningLogger = null;
if (OpenedFile is not null)
OpenedFile.Close();
}
/// <summary>
/// Add blacklisted string to censor automatically
/// </summary>
/// <param name="blacklist"></param>
public void AddBlacklist(string blacklist)
{
Blacklist.Add(blacklist);
}
/// <summary>
/// Add blacklisted log level to not save
/// </summary>
/// <param name="blacklist"></param>
public void AddLogLevelBlacklist(LogLevel level)
{
FileBlackList.Add(level);
}
/// <summary>
/// Changes the log level
/// </summary>
/// <param name="level"></param>
public void ChangeLogLevel(LogLevel level)
{
maxLogLevel = level;
}
/// <summary>
/// Log with none log level
/// </summary>
/// <param name="sender"></param>
/// <param name="message"></param>
public void LogNone(string message, Exception? exception = null)
{
LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = LogLevel.NONE,
Message = message,
Exception = exception
});
}
/// <summary>
/// Log with trace log level
/// </summary>
/// <param name="sender"></param>
/// <param name="message"></param>
public void LogTrace(string message, Exception? exception = null)
{
LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = LogLevel.TRACE,
Message = message,
Exception = exception
});
}
/// <summary>
/// Log with debug2 log level
/// </summary>
/// <param name="sender"></param>
/// <param name="message"></param>
public void LogDebug2(string message, Exception? exception = null)
{
LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = LogLevel.DEBUG2,
Message = message,
Exception = exception
});
}
/// <summary>
/// Log with debug log level
/// </summary>
/// <param name="sender"></param>
/// <param name="message"></param>
public void LogDebug(string message, Exception? exception = null)
{
LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = LogLevel.DEBUG,
Message = message,
Exception = exception
});
}
/// <summary>
/// Log with info log level
/// </summary>
/// <param name="sender"></param>
/// <param name="message"></param>
public void LogInfo(string message, Exception? exception = null)
{
LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = LogLevel.INFO,
Message = message,
Exception = exception
});
}
/// <summary>
/// Log with warn log level
/// </summary>
/// <param name="sender"></param>
/// <param name="message"></param>
public void LogWarn(string message, Exception? exception = null)
{
LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = LogLevel.WARN,
Message = message,
Exception = exception
});
}
/// <summary>
/// Log with error log level
/// </summary>
/// <param name="sender"></param>
/// <param name="message"></param>
public void LogError(string message, Exception? exception = null)
{
LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = LogLevel.ERROR,
Message = message,
Exception = exception
});
}
/// <summary>
/// Log with fatal log level
/// </summary>
/// <param name="sender"></param>
/// <param name="message"></param>
public void LogFatal(string message, Exception? exception = null)
{
LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = LogLevel.FATAL,
Message = message,
Exception = exception
});
}
/// <summary>
/// Log with standard Microsoft.Extensions.Logging format
/// </summary>
/// <typeparam name="TState"></typeparam>
/// <param name="logLevel"></param>
/// <param name="eventId"></param>
/// <param name="state"></param>
/// <param name="exception"></param>
/// <param name="formatter"></param>
public void Log<TState>(Microsoft.Extensions.Logging.LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = logLevel switch
{
Microsoft.Extensions.Logging.LogLevel.Debug => LogLevel.DEBUG2,
Microsoft.Extensions.Logging.LogLevel.Trace => LogLevel.TRACE2,
Microsoft.Extensions.Logging.LogLevel.Information => LogLevel.INFO,
Microsoft.Extensions.Logging.LogLevel.Warning => LogLevel.WARN,
Microsoft.Extensions.Logging.LogLevel.Error => LogLevel.ERROR,
Microsoft.Extensions.Logging.LogLevel.Critical => LogLevel.FATAL,
Microsoft.Extensions.Logging.LogLevel.None => LogLevel.NONE,
_ => throw new NotImplementedException()
},
Message = $"[{eventId.Id,2}] {formatter(state, exception)}",
Exception = exception
});
}
public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel)
{
return loggerStarted;
}
public IDisposable BeginScope<TState>(TState state)
{
return default!;
}
}

696
LoggerClient.cs Normal file
View file

@ -0,0 +1,696 @@
using Newtonsoft.Json;
using Xorog.Logger.EventArgs;
namespace Xorog.Logger;
public sealed class LoggerClient : ILogger
{
internal LoggerClient() { }
/// <summary>
/// The <see cref="ILoggerProvider"/>.
/// </summary>
public LoggerProvider Provider { get; internal set; }
private bool LoggerStarted = false;
private CustomLogLevel MaxLogLevel = CustomLogLevel.Debug;
private string FileName = "";
private FileStream OpenedFile { get; set; }
internal List<LogEntry> LogsToPost = new();
internal List<string> Blacklist = new();
internal List<CustomLogLevel> FileBlackList = new();
private Task RunningLogger = null;
/// <summary>
/// Fired when a log message has been sent.
/// </summary>
public event EventHandler<LogMessageEventArgs> LogRaised;
/// <summary>
/// Starts the logger with specified settings
/// </summary>
/// <param name="filePath">Where the current logs should be saved to, leave blank if logs shouldnt be saved</param>
/// <param name="level">The loglevel that should be displayed in the console, does not affect whats written to file</param>
/// <param name="cleanUpBefore">Clean up old logs before a datetime</param>
/// <returns>A bool stating if the logger was started</returns>
public static LoggerClient StartLogger(string filePath = "", CustomLogLevel level = CustomLogLevel.Debug, DateTime cleanUpBefore = new DateTime(), bool ThrowOnFailedDeletion = false)
{
DirectoryInfo directoryInfo = new(new FileInfo(filePath).DirectoryName);
if (!directoryInfo.Exists)
directoryInfo.Create();
filePath = filePath.Replace("\\", "/");
var handler = new LoggerClient();
handler.Provider = new(handler);
if (handler.LoggerStarted)
throw new Exception($"The logger is already started");
if (filePath is not "")
{
if (filePath.Contains('/'))
{
var dirPath = filePath[..filePath.LastIndexOf('/')];
if (!Directory.Exists(dirPath))
_ = Directory.CreateDirectory(dirPath);
}
handler.FileName = filePath;
handler.OpenedFile = File.Open(handler.FileName, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.Read);
}
handler.LoggerStarted = true;
handler.MaxLogLevel = level;
if (cleanUpBefore != new DateTime())
{
foreach (var b in Directory.GetFiles(new FileInfo(filePath).Directory.FullName))
{
try
{
FileInfo fi = new(b);
if (fi.CreationTimeUtc < cleanUpBefore)
{
fi.Delete();
handler.LogDebug($"{fi.Name} deleted");
}
}
catch (Exception ex)
{
if (!ThrowOnFailedDeletion)
handler.LogError( $"Couldn't delete log file {b}", ex);
else
throw new Exception($"Failed to delete {b}: {ex}");
}
}
}
handler.RunningLogger = Task.Run(async () =>
{
while (handler.LoggerStarted)
{
try
{
while (handler.LogsToPost.Count == 0)
{
Thread.Sleep(10);
}
for (var i = 0; i < handler.LogsToPost.Count; i++)
{
var currentLog = handler.LogsToPost[0];
_ = handler.LogsToPost.Remove(currentLog);
if (currentLog is null || currentLog.RawMessage is null)
{
continue;
}
var LogLevelText = $"{currentLog.LogLevel,-6}";
ConsoleColor LogLevelColor;
ConsoleColor BackgroundColor;
LogLevelColor = currentLog.LogLevel switch
{
CustomLogLevel.Trace => ConsoleColor.Gray,
CustomLogLevel.Debug2 => ConsoleColor.Gray,
CustomLogLevel.Debug => ConsoleColor.Gray,
CustomLogLevel.Info => ConsoleColor.Cyan,
CustomLogLevel.Warn => ConsoleColor.Yellow,
CustomLogLevel.Error => ConsoleColor.Red,
CustomLogLevel.Fatal => ConsoleColor.Black,
_ => ConsoleColor.Gray
};
BackgroundColor = currentLog.LogLevel switch
{
CustomLogLevel.Fatal => ConsoleColor.DarkRed,
_ => ConsoleColor.Black
};
var leftOver = currentLog.RawMessage;
foreach (var blacklistobject in handler.Blacklist)
leftOver = leftOver.Replace(blacklistobject, new String('*', blacklistobject.Length), StringComparison.CurrentCultureIgnoreCase);
var currentArg = 0;
var inTemplate = false;
var attemptedParsing = false;
List<StringPart> builder = new();
while (leftOver.Length > 0)
{
if (inTemplate)
{
attemptedParsing = true;
if (currentLog.Args?.Length >= currentArg && currentLog.Args?.Length != 0)
{
try
{
var endIndex = leftOver.IndexOf('}');
if (currentArg > currentLog.Args.Length)
continue;
var objectToAdd = currentLog.Args[currentArg];
currentArg++;
if (objectToAdd is null)
continue;
if (objectToAdd.GetType() == typeof(int))
builder.Add(new StringPart { String = objectToAdd.ToString(), Color = ConsoleColor.Magenta });
else if (objectToAdd.GetType() == typeof(long))
builder.Add(new StringPart { String = objectToAdd.ToString(), Color = ConsoleColor.Magenta });
else if (objectToAdd.GetType() == typeof(uint))
builder.Add(new StringPart { String = objectToAdd.ToString(), Color = ConsoleColor.Magenta });
else if (objectToAdd.GetType() == typeof(ulong))
builder.Add(new StringPart { String = objectToAdd.ToString(), Color = ConsoleColor.Magenta });
else
builder.Add(new StringPart { String = objectToAdd.ToString(), Color = ConsoleColor.Cyan });
inTemplate = false;
leftOver = leftOver[(endIndex + 1)..];
attemptedParsing = false;
}
catch (Exception)
{
currentArg++;
continue;
}
continue;
}
}
inTemplate = false;
var placeholderIndex = leftOver.IndexOf('{');
if (placeholderIndex != -1)
inTemplate = true;
if (placeholderIndex == -1 || placeholderIndex > leftOver.Length)
placeholderIndex = leftOver.Length;
if (placeholderIndex == 0 && attemptedParsing)
placeholderIndex = leftOver.Length;
var str = leftOver[..placeholderIndex];
if (!string.IsNullOrEmpty(str))
builder.Add(new StringPart { String = str });
leftOver = leftOver[placeholderIndex..];
attemptedParsing = false;
}
if (handler.MaxLogLevel >= currentLog.LogLevel)
{
Console.ResetColor(); Console.Write($"[{currentLog.TimeOfEvent:dd.MM.yyyy HH:mm:ss:fff}] ");
Console.ForegroundColor = LogLevelColor; Console.BackgroundColor = BackgroundColor; Console.Write($"[{LogLevelText}]"); Console.ResetColor(); Console.Write(" ");
foreach (var part in builder)
{
Console.ForegroundColor = part.Color ?? ConsoleColor.White;
Console.BackgroundColor = ConsoleColor.Black;
Console.Write($"{part.String}");
}
Console.ResetColor();
Console.WriteLine();
if (currentLog.Exception is not null)
try
{
Console.WriteLine(JsonConvert.SerializeObject(currentLog.Exception, Formatting.Indented, new JsonSerializerSettings()
{
NullValueHandling = NullValueHandling.Ignore,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
Error = (serializer, err) => err.ErrorContext.Handled = true
}));
}
catch (Exception)
{
Console.WriteLine(currentLog.Exception);
}
}
currentLog.Message = string.Join("", builder.Select(x => x.String));
for (var i1 = 0; i1 < builder.Count; i1++)
{
builder[i1].Dispose();
}
builder.Clear();
_ = Task.Run(() => handler.LogRaised?.Invoke(null, new LogMessageEventArgs() { LogEntry = currentLog }));
try
{
if (!handler.FileBlackList.Contains(currentLog.LogLevel))
{
var FileWrite = Encoding.UTF8.GetBytes($"[{currentLog.TimeOfEvent:dd.MM.yyyy HH:mm:ss:fff}] [{LogLevelText}] {currentLog.Message}\n{(currentLog.Exception is not null ? $"{currentLog.Exception}\n" : "")}");
if (handler.OpenedFile != null)
{
await handler.OpenedFile.WriteAsync(FileWrite.AsMemory(0, FileWrite.Length));
handler.OpenedFile.Flush();
}
}
}
catch (Exception ex)
{
handler.LogFatal($"Couldn't write log to file: {ex}");
}
}
}
catch (Exception ex)
{
handler.LogError("An exception occurred while trying to display a log message", ex);
await Task.Delay(1000);
continue;
}
}
});
return handler;
}
/// <summary>
/// Stops the logger
/// </summary>
public void StopLogger()
{
LoggerStarted = false;
MaxLogLevel = CustomLogLevel.Debug;
FileName = "";
Thread.Sleep(500);
RunningLogger?.Dispose();
RunningLogger = null;
this.OpenedFile?.Close();
}
/// <summary>
/// Add strings automatically censor on output to console and file.
/// </summary>
/// <param name="blacklist">The strings to censor</param>
public void AddBlacklist(params string[] blacklist)
{
for (var i = 0; i < blacklist.Length; i++)
{
if (!string.IsNullOrWhiteSpace(blacklist[i]))
Blacklist.Add(blacklist[i]);
}
}
/// <summary>
/// Add blacklisted log level to not save
/// </summary>
/// <param name="levels">The log levels not to save to the log file</param>
public void AddLogLevelBlacklist(params CustomLogLevel[] levels)
{
for (var i = 0; i < levels.Length; i++)
{
FileBlackList.Add(levels[i]);
}
}
/// <summary>
/// Changes the log level
/// </summary>
/// <param name="level">The new log level to apply</param>
public void ChangeLogLevel(CustomLogLevel level) => MaxLogLevel = level;
/// <summary>
/// Log with none log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="exception">The exception that was caused</param>
/// <param name="args">The objects involved in the event</param>
public void LogNone(string message, Exception? exception = null, params object[] args) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.None,
RawMessage = message,
Args = args,
Exception = exception
});
/// <summary>
/// Log with none log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="exception">The exception that was caused</param>
public void LogNone(string message, Exception? exception = null) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.None,
RawMessage = message,
Exception = exception
});
/// <summary>
/// Log with none log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="args">The objects involved in the event</param>
public void LogNone(string message, params object[] args) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.None,
RawMessage = message,
Args = args
});
/// <summary>
/// Log with trace log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="exception">The exception that was caused</param>
/// <param name="args">The objects involved in the event</param>
public void LogTrace(string message, Exception? exception = null, params object[] args) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Trace,
RawMessage = message,
Args = args,
Exception = exception
});
/// <summary>
/// Log with trace log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="exception">The exception that was caused</param>
public void LogTrace(string message, Exception? exception = null) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Trace,
RawMessage = message,
Exception = exception
});
/// <summary>
/// Log with trace log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="args">The objects involved in the event</param>
public void LogTrace(string message, params object[] args) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Trace,
RawMessage = message,
Args = args,
});
/// <summary>
/// Log with debug2 log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="exception">The exception that was caused</param>
/// <param name="args">The objects involved in the event</param>
public void LogDebug2(string message, Exception? exception = null, params object[] args) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Debug2,
RawMessage = message,
Args = args,
Exception = exception
});
/// <summary>
/// Log with debug2 log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="exception">The exception that was caused</param>
public void LogDebug2(string message, Exception? exception = null) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Debug2,
RawMessage = message,
Exception = exception
});
/// <summary>
/// Log with debug2 log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="args">The objects involved in the event</param>
public void LogDebug2(string message, params object[] args) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Debug2,
RawMessage = message,
Args = args
});
/// <summary>
/// Log with debug log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="exception">The exception that was caused</param>
/// <param name="args">The objects involved in the event</param>
public void LogDebug(string message, Exception? exception = null, params object[] args) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Debug,
RawMessage = message,
Args = args,
Exception = exception
});
/// <summary>
/// Log with debug log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="exception">The exception that was caused</param>
public void LogDebug(string message, Exception? exception = null) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Debug,
RawMessage = message,
Exception = exception
});
/// <summary>
/// Log with debug log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="args">The objects involved in the event</param>
public void LogDebug(string message, params object[] args) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Debug,
RawMessage = message,
Args = args
});
/// <summary>
/// Log with info log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="exception">The exception that was caused</param>
/// <param name="args">The objects involved in the event</param>
public void LogInfo(string message, Exception? exception = null, params object[] args) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Info,
RawMessage = message,
Args = args,
Exception = exception
});
/// <summary>
/// Log with info log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="exception">The exception that was caused</param>
public void LogInfo(string message, Exception? exception = null) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Info,
RawMessage = message,
Exception = exception
});
/// <summary>
/// Log with info log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="args">The objects involved in the event</param>
public void LogInfo(string message, params object[] args) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Info,
RawMessage = message,
Args = args
});
/// <summary>
/// Log with warn log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="exception">The exception that was caused</param>
/// <param name="args">The objects involved in the event</param>
public void LogWarn(string message, Exception? exception = null, params object[] args) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Warn,
RawMessage = message,
Args = args,
Exception = exception
});
/// <summary>
/// Log with warn log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="exception">The exception that was caused</param>
public void LogWarn(string message, Exception? exception = null) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Warn,
RawMessage = message,
Exception = exception
});
/// <summary>
/// Log with warn log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="args">The objects involved in the event</param>
public void LogWarn(string message, params object[] args) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Warn,
RawMessage = message,
Args = args
});
/// <summary>
/// Log with error log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="exception">The exception that was caused</param>
/// <param name="args">The objects involved in the event</param>
public void LogError(string message, Exception? exception = null, params object[] args) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Error,
RawMessage = message,
Args = args,
Exception = exception
});
/// <summary>
/// Log with error log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="exception">The exception that was caused</param>
public void LogError(string message, Exception? exception = null) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Error,
RawMessage = message,
Exception = exception
});
/// <summary>
/// Log with error log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="args">The objects involved in the event</param>
public void LogError(string message, params object[] args) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Error,
RawMessage = message,
Args = args
});
/// <summary>
/// Log with fatal log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="exception">The exception that was caused</param>
/// <param name="args">The objects involved in the event</param>
public void LogFatal(string message, Exception? exception = null, params object[] args) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Fatal,
RawMessage = message,
Args = args,
Exception = exception
});
/// <summary>
/// Log with fatal log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="exception">The exception that was caused</param>
public void LogFatal(string message, Exception? exception = null) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Fatal,
RawMessage = message,
Exception = exception
});
/// <summary>
/// Log with fatal log level
/// </summary>
/// <param name="message">The message to display</param>
/// <param name="args">The objects involved in the event</param>
public void LogFatal(string message, params object[] args) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = CustomLogLevel.Fatal,
RawMessage = message,
Args = args
});
/// <summary>
/// Log with standard Microsoft.Extensions.Logging format
/// </summary>
/// <typeparam name="TState"></typeparam>
/// <param name="logLevel"></param>
/// <param name="eventId"></param>
/// <param name="state"></param>
/// <param name="exception"></param>
/// <param name="formatter"></param>
public void Log<TState>(Microsoft.Extensions.Logging.LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) => LogsToPost.Add(new LogEntry
{
TimeOfEvent = DateTime.Now,
LogLevel = logLevel switch
{
Microsoft.Extensions.Logging.LogLevel.Debug => CustomLogLevel.Debug2,
Microsoft.Extensions.Logging.LogLevel.Trace => CustomLogLevel.Trace2,
Microsoft.Extensions.Logging.LogLevel.Information => CustomLogLevel.Info,
Microsoft.Extensions.Logging.LogLevel.Warning => CustomLogLevel.Warn,
Microsoft.Extensions.Logging.LogLevel.Error => CustomLogLevel.Error,
Microsoft.Extensions.Logging.LogLevel.Critical => CustomLogLevel.Fatal,
Microsoft.Extensions.Logging.LogLevel.None => CustomLogLevel.None,
_ => CustomLogLevel.None,
},
RawMessage = $"[{eventId.Id}] {formatter(state, exception)}",
Exception = exception
});
public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel)
=> LoggerStarted;
public IDisposable BeginScope<TState>(TState state)
=> default!;
}

View file

@ -1,24 +1,16 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Xorog.Logger;
namespace Xorog.Logger;
public class LoggerProvider : ILoggerProvider
public sealed class LoggerProvider : ILoggerProvider
{
private readonly ConcurrentDictionary<string, Logger> _loggers = new(StringComparer.OrdinalIgnoreCase);
internal LoggerProvider(LoggerClient logger)
=> this._logger = logger;
public ILogger CreateLogger(string categoryName)
{
return _loggers.GetOrAdd(categoryName, name => new Logger());
}
public void Dispose()
{
_loggers.Clear();
GC.SuppressFinalize(this);
}
private LoggerClient _logger { get; set; }
public ILogger CreateLogger(string categoryName)
=> this._logger;
public void Dispose()
=> GC.SuppressFinalize(this);
}

View file

@ -0,0 +1,7 @@
{
"profiles": {
"Xorog.Logger": {
"commandName": "Project"
}
}
}

View file

@ -1 +1,9 @@
# Xorog.Logger
# Xorog.Logger
The logger used in projects of Fortunevale.
## Used in
- [BeatRecorder](https://github.com/TheXorog/BeatRecorder)
<!-- - [Project Ichigo](https://github.com/Fortunevale/ProjectIchigo) -->

View file

@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>annotations</Nullable>
<Configurations>Debug;Release;x64</Configurations>
<Platforms>AnyCPU;x64</Platforms>
<Platforms>x64</Platforms>
<RunAnalyzersDuringBuild>False</RunAnalyzersDuringBuild>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@ -14,10 +15,12 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>embedded</DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64|AnyCPU'">
<DebugType>embedded</DebugType>
<Optimize>False</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -26,14 +29,17 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DebugType>embedded</DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x64|x64'">
<DebugType>embedded</DebugType>
<Optimize>False</Optimize>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>