This commit is contained in:
2024-04-25 22:05:17 +02:00
parent 44468ae920
commit 6c48b43a20
41 changed files with 2276 additions and 2515 deletions

View File

@@ -1,5 +1,4 @@
namespace EonaCat.Logger
{
namespace EonaCat.Logger;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
@@ -10,4 +9,3 @@
public static string LOGGING { get; } = "yyyy-MM-dd HH:mm:ss";
}
}
}

View File

@@ -1,5 +1,4 @@
namespace EonaCat.Logger
{
namespace EonaCat.Logger;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
@@ -10,7 +9,7 @@
static DllInfo()
{
bool isDebug = false;
var isDebug = false;
#if DEBUG
isDebug = true;
#endif
@@ -21,4 +20,3 @@
public static string ApplicationName { get; internal set; } = "EonaCatLogger";
}
}

View File

@@ -1,7 +1,6 @@
using Microsoft.Extensions.Logging;
namespace EonaCat.Logger
{
namespace EonaCat.Logger;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
@@ -160,29 +159,34 @@ namespace EonaCat.Logger
/// Debug messages.
/// </summary>
Debug = 0,
/// <summary>
/// Informational messages.
/// </summary>
Info = 1,
/// <summary>
/// Warning messages.
/// </summary>
Warn = 2,
/// <summary>
/// Error messages.
/// </summary>
Error = 3,
/// <summary>
/// Alert messages.
/// </summary>
Alert = 4,
/// <summary>
/// Critical messages.
/// </summary>
Critical = 5,
/// <summary>
/// Emergency messages.
/// </summary>
Emergency = 6
}
}

View File

@@ -3,7 +3,7 @@
<TargetFrameworks>.netstandard2.1; net6.0; net7.0; net8.0; net4.8;</TargetFrameworks>
<ApplicationIcon>icon.ico</ApplicationIcon>
<LangVersion>latest</LangVersion>
<Version>1.2.5</Version>
<FileVersion>1.2.5</FileVersion>
<Authors>EonaCat (Jeroen Saey)</Authors>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Company>EonaCat (Jeroen Saey)</Company>
@@ -24,7 +24,7 @@
</PropertyGroup>
<PropertyGroup>
<EVRevisionFormat>1.0.0.0+{chash:10}.{c:ymd}</EVRevisionFormat>
<EVRevisionFormat>1.2.5+{chash:10}.{c:ymd}</EVRevisionFormat>
<EVDefault>true</EVDefault>
<EVInfo>true</EVInfo>
<EVTagMatch>v[0-9]*</EVTagMatch>

View File

@@ -2,8 +2,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace EonaCat.Logger.EonaCatCoreLogger.Extensions
{
namespace EonaCat.Logger.EonaCatCoreLogger.Extensions;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
@@ -24,17 +23,12 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Extensions
/// <param name="builder">The <see cref="ILoggingBuilder" /> to use.</param>
/// <param name="filenamePrefix">Sets the filename prefix to use for log files (optional)</param>
/// <param name="fileLoggerOptions">the options for the fileLogger that needs to be used (optional)</param>
public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder, string filenamePrefix = null, FileLoggerOptions fileLoggerOptions = null)
public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder, string filenamePrefix = null,
FileLoggerOptions fileLoggerOptions = null)
{
if (fileLoggerOptions == null)
{
fileLoggerOptions = new FileLoggerOptions();
}
if (fileLoggerOptions == null) fileLoggerOptions = new FileLoggerOptions();
if (!string.IsNullOrWhiteSpace(filenamePrefix))
{
fileLoggerOptions.FileNamePrefix = filenamePrefix;
}
if (!string.IsNullOrWhiteSpace(filenamePrefix)) fileLoggerOptions.FileNamePrefix = filenamePrefix;
builder.AddEonaCatFileLogger(options =>
{
options.FileNamePrefix = fileLoggerOptions.FileNamePrefix;
@@ -49,7 +43,6 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Extensions
options.MaxRolloverFiles = fileLoggerOptions.MaxRolloverFiles;
options.UseLocalTime = fileLoggerOptions.UseLocalTime;
}
);
return builder;
}
@@ -59,16 +52,13 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Extensions
/// </summary>
/// <param name="builder">The <see cref="ILoggingBuilder" /> to use.</param>
/// <param name="configure">Configure an instance of the <see cref="FileLoggerOptions" /> to set logging options</param>
public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder, Action<FileLoggerOptions> configure)
public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder,
Action<FileLoggerOptions> configure)
{
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
if (configure == null) throw new ArgumentNullException(nameof(configure));
builder.AddEonaCatFileLogger();
builder.Services.Configure(configure);
return builder;
}
}
}

View File

@@ -2,8 +2,7 @@
using System.IO;
using EonaCat.Logger.EonaCatCoreLogger.Internal;
namespace EonaCat.Logger.EonaCatCoreLogger
{
namespace EonaCat.Logger.EonaCatCoreLogger;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
@@ -13,11 +12,11 @@ namespace EonaCat.Logger.EonaCatCoreLogger
public class FileLoggerOptions : BatchingLoggerOptions
{
private int _fileSizeLimit = 200 * 1024 * 1024;
private int _retainedFileCountLimit = 50;
private int _maxRolloverFiles = 10;
private int _maxTries = 3;
private int _retainedFileCountLimit = 50;
public static string DefaultPath => AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
public static string DefaultPath =>
AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
/// <summary>
/// Gets or sets a strictly positive value representing the maximum log size in bytes or null for no limit.
@@ -31,9 +30,7 @@ namespace EonaCat.Logger.EonaCatCoreLogger
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FileSizeLimit)} must be positive.");
}
_fileSizeLimit = value;
}
}
@@ -49,9 +46,8 @@ namespace EonaCat.Logger.EonaCatCoreLogger
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(RetainedFileCountLimit)} must be positive.");
}
throw new ArgumentOutOfRangeException(nameof(value),
$"{nameof(RetainedFileCountLimit)} must be positive.");
_retainedFileCountLimit = value;
}
}
@@ -60,15 +56,7 @@ namespace EonaCat.Logger.EonaCatCoreLogger
/// Gets or sets the max times to try to write to the file.
/// Defaults 3.
/// </summary>
public int MaxWriteTries
{
get => _maxTries;
set
{
_maxTries = value;
}
}
public int MaxWriteTries { get; set; } = 3;
/// <summary>
/// Determines if we need to use the local time in the logging or UTC (default:false)
@@ -86,9 +74,7 @@ namespace EonaCat.Logger.EonaCatCoreLogger
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(MaxRolloverFiles)} must be positive.");
}
_maxRolloverFiles = value;
}
}
@@ -106,4 +92,3 @@ namespace EonaCat.Logger.EonaCatCoreLogger
/// <returns></returns>
public string LogDirectory { get; set; } = Path.Combine(DefaultPath, "logs");
}
}

View File

@@ -8,8 +8,7 @@ using EonaCat.Logger.EonaCatCoreLogger.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace EonaCat.Logger.EonaCatCoreLogger
{
namespace EonaCat.Logger.EonaCatCoreLogger;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
@@ -19,44 +18,17 @@ namespace EonaCat.Logger.EonaCatCoreLogger
[ProviderAlias("EonaCatFileLogger")]
public class FileLoggerProvider : BatchingLoggerProvider
{
private readonly string _path;
private static readonly object WriteLock = new();
private static readonly object RollOverLock = new();
private readonly string _fileNamePrefix;
private readonly int _maxFileSize;
private readonly int _maxRetainedFiles;
private readonly int _maxRolloverFiles;
private readonly int _maxTries;
private int _rollOverCount = 0;
private static readonly object WriteLock = new object();
private static readonly object RollOverLock = new object();
private readonly string _path;
private string _logFile;
private bool _rollingOver;
/// <summary>
/// The file to which log messages should be appended.
/// </summary>
public string LogFile
{
get
{
return _logFile;
}
set
{
_logFile = value;
if (!string.IsNullOrEmpty(_logFile))
{
string dir = Path.GetDirectoryName(_logFile);
if (!string.IsNullOrEmpty(dir))
{
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
}
}
}
}
private int _rollOverCount;
/// <summary>
/// Creates an instance of the <see cref="FileLoggerProvider" />
@@ -64,7 +36,7 @@ namespace EonaCat.Logger.EonaCatCoreLogger
/// <param name="options">The options object controlling the logger</param>
public FileLoggerProvider(IOptions<FileLoggerOptions> options) : base(options)
{
FileLoggerOptions loggerOptions = options.Value;
var loggerOptions = options.Value;
_path = loggerOptions.LogDirectory;
_fileNamePrefix = loggerOptions.FileNamePrefix;
_maxFileSize = loggerOptions.FileSizeLimit;
@@ -73,26 +45,43 @@ namespace EonaCat.Logger.EonaCatCoreLogger
_maxTries = loggerOptions.MaxWriteTries;
}
/// <summary>
/// The file to which log messages should be appended.
/// </summary>
public string LogFile
{
get => _logFile;
set
{
_logFile = value;
if (!string.IsNullOrEmpty(_logFile))
{
var dir = Path.GetDirectoryName(_logFile);
if (!string.IsNullOrEmpty(dir))
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
}
}
}
/// <inheritdoc />
protected override async Task WriteMessagesAsync(IEnumerable<LogMessage> messages, CancellationToken cancellationToken)
protected override async Task WriteMessagesAsync(IEnumerable<LogMessage> messages,
CancellationToken cancellationToken)
{
Directory.CreateDirectory(_path);
foreach (IGrouping<(int Year, int Month, int Day), LogMessage> group in messages.GroupBy(GetGrouping))
foreach (var group in messages.GroupBy(GetGrouping))
{
LogFile = GetFullName(group.Key);
FileInfo fileInfo = new FileInfo(LogFile);
var fileInfo = new FileInfo(LogFile);
if (_maxFileSize > 0 && fileInfo.Exists && fileInfo.Length > _maxFileSize)
{
if (_maxRolloverFiles > 0 && _rollOverCount >= 0)
{
if (_rollOverCount < _maxRolloverFiles)
{
var rollOverFile = LogFile.Replace(".log", $"_{++_rollOverCount}.log");
if (File.Exists(rollOverFile))
{
File.Delete(rollOverFile);
}
if (File.Exists(rollOverFile)) File.Delete(rollOverFile);
fileInfo.CopyTo(rollOverFile);
File.WriteAllText(LogFile, string.Empty);
}
@@ -106,27 +95,19 @@ namespace EonaCat.Logger.EonaCatCoreLogger
}
}
}
}
while (_rollingOver)
{
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
}
while (_rollingOver) await Task.Delay(100, cancellationToken).ConfigureAwait(false);
lock (WriteLock)
{
int tries = 0;
bool completed = false;
var tries = 0;
var completed = false;
while (!completed)
{
try
{
System.IO.StreamWriter file = new System.IO.StreamWriter(LogFile, true);
foreach (LogMessage item in group)
{
file.Write(item.Message);
}
var file = new StreamWriter(LogFile, true);
foreach (var item in group) file.Write(item.Message);
file.Close();
completed = true;
}
@@ -138,7 +119,6 @@ namespace EonaCat.Logger.EonaCatCoreLogger
throw;
}
}
}
DeleteOldLogFiles();
}
@@ -146,16 +126,11 @@ namespace EonaCat.Logger.EonaCatCoreLogger
private string GetFullName((int Year, int Month, int Day) group)
{
bool hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix);
var hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix);
if (hasPrefix)
{
return Path.Combine(_path, $"{_fileNamePrefix}_{group.Year:0000}{group.Month:00}{group.Day:00}.log");
}
else
{
return Path.Combine(_path, $"{group.Year:0000}{group.Month:00}{group.Day:00}.log");
}
}
private (int Year, int Month, int Day) GetGrouping(LogMessage message)
{
@@ -179,29 +154,25 @@ namespace EonaCat.Logger.EonaCatCoreLogger
protected void MoveRolloverLogFiles()
{
if (_maxRolloverFiles > 0 && _rollOverCount >= 0)
{
if (_rollOverCount >= _maxRolloverFiles)
{
var maxRollover = _rollOverCount;
bool hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix);
var hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix);
IEnumerable<FileInfo> files;
if (hasPrefix)
{
files = new DirectoryInfo(_path).GetFiles(_fileNamePrefix + "*").OrderBy(x => x.CreationTime);
}
else
{
files = new DirectoryInfo(_path).GetFiles("*").OrderBy(x => x.CreationTime);
}
for (int i = files.Count() -1; i >= 0; i--)
for (var i = files.Count() - 1; i >= 0; i--)
{
var currentFile = files.ElementAt(i);
if (i == 0)
{
// Temporary move first file
var newFilename2 = Path.GetFileName(currentFile.FullName).Replace($".log", $"_{i + 1}.log");
MoveFile(currentFile.FullName, $@"{Path.GetDirectoryName(currentFile.FullName)}\{newFilename2}");
var newFilename2 = Path.GetFileName(currentFile.FullName).Replace(".log", $"_{i + 1}.log");
MoveFile(currentFile.FullName,
$@"{Path.GetDirectoryName(currentFile.FullName)}\{newFilename2}");
continue;
}
@@ -215,10 +186,10 @@ namespace EonaCat.Logger.EonaCatCoreLogger
var newFilename = Path.GetFileName(currentFile.FullName).Replace($"_{i}.log", $"_{i + 1}.log");
MoveFile(currentFile.FullName, $@"{Path.GetDirectoryName(currentFile.FullName)}\{newFilename}");
}
_rollOverCount = 0;
}
}
}
/// <summary>
/// Deletes old log files, keeping a number of files defined by <see cref="FileLoggerOptions.RetainedFileCountLimit" />
@@ -227,25 +198,17 @@ namespace EonaCat.Logger.EonaCatCoreLogger
{
if (_maxRetainedFiles > 0)
{
bool hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix);
var hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix);
IEnumerable<FileInfo> files = null;
if (hasPrefix)
{
files = new DirectoryInfo(_path).GetFiles(_fileNamePrefix + "*");
}
else
{
files = new DirectoryInfo(_path).GetFiles("*");
}
files = files.OrderByDescending(file => file.Name).Skip(_maxRetainedFiles);
foreach (FileInfo item in files)
{
item.Delete();
}
}
foreach (var item in files) item.Delete();
}
}
}

View File

@@ -4,18 +4,15 @@ using EonaCat.Logger.Extensions;
using EonaCat.Logger.Managers;
using Microsoft.Extensions.Logging;
namespace EonaCat.Logger.EonaCatCoreLogger.Internal
{
namespace EonaCat.Logger.EonaCatCoreLogger.Internal;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class BatchingLogger : ILogger
{
private LoggerSettings _loggerSettings;
private readonly BatchingLoggerProvider _provider;
private readonly string _category;
private DateTimeOffset CurrentDateTimeOffset => _loggerSettings.UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
private DateTime CurrentDateTme => _loggerSettings.UseLocalTime ? DateTime.Now : DateTime.UtcNow;
private readonly BatchingLoggerProvider _provider;
private LoggerSettings _loggerSettings;
public BatchingLogger(BatchingLoggerProvider loggerProvider, string categoryName, LoggerSettings loggerSettings)
{
@@ -24,6 +21,11 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
_category = categoryName;
}
private DateTimeOffset CurrentDateTimeOffset =>
_loggerSettings.UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
private DateTime CurrentDateTme => _loggerSettings.UseLocalTime ? DateTime.Now : DateTime.UtcNow;
public IDisposable BeginScope<TState>(TState state)
{
return null;
@@ -34,20 +36,22 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
return logLevel != LogLevel.None;
}
public void Log<TState>(DateTimeOffset timestamp, LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception,
Func<TState, Exception, string> formatter)
{
Log(CurrentDateTimeOffset, logLevel, eventId, state, exception, formatter);
}
public void Log<TState>(DateTimeOffset timestamp, LogLevel logLevel, EventId eventId, TState state,
Exception exception, Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel)) return;
if (_loggerSettings == null)
{
_loggerSettings = new LoggerSettings();
}
if (_loggerSettings == null) _loggerSettings = new LoggerSettings();
var message = LogHelper.FormatMessageWithHeader(_loggerSettings, logLevel.FromLogLevel(), formatter(state, exception), timestamp.DateTime) + Environment.NewLine;
if (exception != null)
{
message = exception.FormatExceptionToMessage() + Environment.NewLine;
}
var message = LogHelper.FormatMessageWithHeader(_loggerSettings, logLevel.FromLogLevel(),
formatter(state, exception), timestamp.DateTime) + Environment.NewLine;
if (exception != null) message = exception.FormatExceptionToMessage() + Environment.NewLine;
_provider.AddMessage(timestamp, message);
@@ -58,14 +62,9 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
LogType = logLevel.FromLogLevel()
};
if (_loggerSettings == null) return;
currentMessage.Origin = string.IsNullOrWhiteSpace(_loggerSettings.LogOrigin) ? "BatchingLogger" : _loggerSettings.LogOrigin;
currentMessage.Origin = string.IsNullOrWhiteSpace(_loggerSettings.LogOrigin)
? "BatchingLogger"
: _loggerSettings.LogOrigin;
_loggerSettings?.OnLogEvent(currentMessage);
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
Log(CurrentDateTimeOffset, logLevel, eventId, state, exception, formatter);
}
}
}

View File

@@ -1,13 +1,11 @@
using System;
namespace EonaCat.Logger.EonaCatCoreLogger.Internal
{
namespace EonaCat.Logger.EonaCatCoreLogger.Internal;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class BatchingLoggerOptions
{
private int _batchSize = 0;
private TimeSpan _flushPeriod = TimeSpan.FromMilliseconds(100);
/// <summary>
@@ -20,9 +18,7 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
set
{
if (value <= TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FlushPeriod)} must be positive.");
}
_flushPeriod = value;
}
}
@@ -30,19 +26,10 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
/// <summary>
/// Gets or sets a maximum number of events to include in a single batch or less than 1 for no limit.
/// </summary>
public int BatchSize
{
get => _batchSize;
set
{
_batchSize = value;
}
}
public int BatchSize { get; set; } = 0;
/// <summary>
/// Gets or sets value indicating if logger accepts and queues writes.
/// </summary>
public bool IsEnabled { get; set; }
}
}

View File

@@ -7,47 +7,40 @@ using EonaCat.Logger.Managers;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace EonaCat.Logger.EonaCatCoreLogger.Internal
{
namespace EonaCat.Logger.EonaCatCoreLogger.Internal;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
{
protected DateTimeOffset CurrentDateTimeOffset => UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
protected DateTime CurrentDateTme => UseLocalTime ? DateTime.Now : DateTime.UtcNow;
private readonly List<LogMessage> _currentBatch = new List<LogMessage>();
private readonly TimeSpan _interval;
private readonly int _batchSize;
private ConcurrentQueue<LogMessage> _messageQueue;
private Task _outputTask;
private readonly List<LogMessage> _currentBatch = new();
private CancellationTokenSource _cancellationTokenSource;
private LoggerSettings _loggerSettings;
protected bool UseLocalTime { get; set; }
private ConcurrentQueue<LogMessage> _messageQueue;
private Task _outputTask;
protected BatchingLoggerProvider(IOptions<BatchingLoggerOptions> options)
{
BatchingLoggerOptions loggerOptions = options.Value;
var loggerOptions = options.Value;
if (loggerOptions.FlushPeriod <= TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(loggerOptions.FlushPeriod), $"{nameof(loggerOptions.FlushPeriod)} must be longer than zero.");
}
throw new ArgumentOutOfRangeException(nameof(loggerOptions.FlushPeriod),
$"{nameof(loggerOptions.FlushPeriod)} must be longer than zero.");
if (options.Value is FileLoggerOptions fileLoggerOptions)
{
UseLocalTime = fileLoggerOptions.UseLocalTime;
}
_interval = loggerOptions.FlushPeriod;
if (options.Value is FileLoggerOptions fileLoggerOptions) UseLocalTime = fileLoggerOptions.UseLocalTime;
_batchSize = loggerOptions.BatchSize;
Start();
}
protected DateTimeOffset CurrentDateTimeOffset => UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
protected DateTime CurrentDateTme => UseLocalTime ? DateTime.Now : DateTime.UtcNow;
protected bool UseLocalTime { get; set; }
protected LoggerSettings LoggerSettings
{
get
@@ -62,54 +55,54 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
set => _loggerSettings = value;
}
public bool IsStarted { get; set; }
public void Dispose()
{
while (!_messageQueue.IsEmpty) Task.Delay(10);
StopAsync().GetAwaiter().GetResult();
GC.SuppressFinalize(this);
}
public ILogger CreateLogger(string categoryName)
{
return new BatchingLogger(this, categoryName, LoggerSettings);
}
protected abstract Task WriteMessagesAsync(IEnumerable<LogMessage> messages, CancellationToken token);
private async Task ProcessLogQueueAsync(object state)
{
var startupMessage = $"{DllInfo.ApplicationName} started.{Environment.NewLine}";
startupMessage = LogHelper.FormatMessageWithHeader(LoggerSettings, ELogType.INFO, startupMessage, CurrentDateTme);
startupMessage =
LogHelper.FormatMessageWithHeader(LoggerSettings, ELogType.INFO, startupMessage, CurrentDateTme);
AddMessage(DateTimeOffset.Now, startupMessage);
while (!_cancellationTokenSource.IsCancellationRequested)
{
int limit = _batchSize <= 0 ? int.MaxValue : _batchSize;
while (limit > 0 && _messageQueue.TryDequeue(out LogMessage message))
var limit = _batchSize <= 0 ? int.MaxValue : _batchSize;
while (limit > 0 && _messageQueue.TryDequeue(out var message))
{
_currentBatch.Add(message);
limit--;
}
if (_currentBatch.Count > 0)
{
bool isBatchWritten = false;
try
{
await WriteMessagesAsync(_currentBatch, _cancellationTokenSource.Token).ConfigureAwait(false);
isBatchWritten = true;
_currentBatch.Clear();
}
catch
{
// ignored
}
if (isBatchWritten)
{
_currentBatch.Clear();
Thread.Sleep(10);
}
}
await IntervalAsync(_interval, _cancellationTokenSource.Token).ConfigureAwait(false);
}
await WriteMessagesAsync(new List<LogMessage> { new LogMessage { Message = $"[{DllInfo.ApplicationName}] {DllInfo.ApplicationName} stopped.{Environment.NewLine}", Timestamp = CurrentDateTimeOffset } }, _cancellationTokenSource.Token).ConfigureAwait(false);
}
protected virtual Task IntervalAsync(TimeSpan interval, CancellationToken cancellationToken)
{
return Task.Delay(interval, cancellationToken);
}
internal void AddMessage(DateTimeOffset timestamp, string message)
{
_messageQueue.Enqueue(new LogMessage { Message = message, Timestamp = timestamp });
@@ -117,13 +110,23 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
private void Start()
{
IsStarted = true;
_messageQueue = new ConcurrentQueue<LogMessage>();
_cancellationTokenSource = new CancellationTokenSource();
_outputTask = Task.Factory.StartNew(
ProcessLogQueueAsync,
null,
TaskCreationOptions.LongRunning);
TaskCreationOptions.LongRunning).ContinueWith(async x =>
{
var stopMessage = $"{DllInfo.ApplicationName} stopped.{Environment.NewLine}";
stopMessage = LogHelper.FormatMessageWithHeader(LoggerSettings, ELogType.INFO, stopMessage, CurrentDateTme);
await WriteMessagesAsync(
new List<LogMessage>(new List<LogMessage>
{ new() { Message = stopMessage, Timestamp = CurrentDateTme } }),
_cancellationTokenSource.Token)
.ConfigureAwait(false);
});
}
private async Task StopAsync()
@@ -131,37 +134,24 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
_cancellationTokenSource.Cancel();
try
{
while (_outputTask.Status != TaskStatus.RanToCompletion && _outputTask.Status != TaskStatus.Canceled)
{
await _outputTask.ConfigureAwait(false);
await Task.Delay(100);
}
}
catch (TaskCanceledException)
{
}
catch (AggregateException exception) when (exception.InnerExceptions.Count == 1 && exception.InnerExceptions[0] is TaskCanceledException)
catch (AggregateException exception) when (exception.InnerExceptions.Count == 1 &&
exception.InnerExceptions[0] is TaskCanceledException)
{
}
}
public void Dispose()
{
while (!_messageQueue.IsEmpty)
{
_messageQueue.TryDequeue(out _);
}
StopAsync().GetAwaiter().GetResult();
GC.SuppressFinalize(this);
}
~BatchingLoggerProvider()
{
Dispose();
}
public ILogger CreateLogger(string categoryName)
{
return new BatchingLogger(this, categoryName, LoggerSettings);
}
}
}

View File

@@ -1,7 +1,6 @@
using System;
namespace EonaCat.Logger.EonaCatCoreLogger.Internal
{
namespace EonaCat.Logger.EonaCatCoreLogger.Internal;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
@@ -10,4 +9,3 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
public DateTimeOffset Timestamp { get; set; }
public string Message { get; set; }
}
}

View File

@@ -1,8 +1,7 @@
using System;
using EonaCat.Logger.Helpers;
namespace EonaCat.Logger.EonaCatCoreLogger.Models
{
namespace EonaCat.Logger.EonaCatCoreLogger.Models;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
@@ -15,7 +14,7 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Models
public override string ToString()
{
return $"[{DateTime.ToString(Constants.DateTimeFormats.LOGGING)}] [{EnumHelper<ELogType>.ToString(LogType)}] {Message}";
}
return
$"[{DateTime.ToString(Constants.DateTimeFormats.LOGGING)}] [{EnumHelper<ELogType>.ToString(LogType)}] {Message}";
}
}

View File

@@ -1,9 +1,10 @@
using System;
namespace EonaCat.Logger.Exceptions
{
namespace EonaCat.Logger.Exceptions;
public class EonaCatLoggerAssertionException : Exception
{
public EonaCatLoggerAssertionException(string message) : base(message) { }
public EonaCatLoggerAssertionException(string message) : base(message)
{
}
}

View File

@@ -1,7 +1,7 @@
using System;
namespace EonaCat.Logger.Extensions
{
namespace EonaCat.Logger.Extensions;
public static class DateTimeExtensions
{
public static long ToUnixTimestamp(this DateTime dateTime)
@@ -9,4 +9,3 @@ namespace EonaCat.Logger.Extensions
return (long)(dateTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
}
}
}

View File

@@ -3,8 +3,8 @@ using System.Collections;
using System.Diagnostics;
using System.Text;
namespace EonaCat.Logger.Extensions
{
namespace EonaCat.Logger.Extensions;
public static class ExceptionExtensions
{
public static string FormatExceptionToMessage(this Exception exception, string module = null, string method = null)
@@ -14,10 +14,10 @@ namespace EonaCat.Logger.Extensions
var st = new StackTrace(exception, true);
var frame = st.GetFrame(0);
int fileLine = frame.GetFileLineNumber();
string filename = frame.GetFileName();
var fileLine = frame.GetFileLineNumber();
var filename = frame.GetFileName();
StringBuilder sb = new StringBuilder();
var sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine("--- Exception details provided by EonaCatLogger ---");
@@ -26,8 +26,12 @@ namespace EonaCat.Logger.Extensions
if (!string.IsNullOrEmpty(method))
sb.AppendLine(" Method : " + method);
sb.Append(" Type : ").AppendLine(exception.GetType().ToString());
sb.Append(" Data : ").AppendLine(exception.Data != null && exception.Data.Count > 0 ? FormatExceptionData(exception.Data) : "(none)");
sb.Append(" Inner : ").AppendLine(exception.InnerException != null ? FormatInnerException(exception.InnerException) : "(null)");
sb.Append(" Data : ").AppendLine(exception.Data != null && exception.Data.Count > 0
? FormatExceptionData(exception.Data)
: "(none)");
sb.Append(" Inner : ").AppendLine(exception.InnerException != null
? FormatInnerException(exception.InnerException)
: "(null)");
sb.Append(" Message : ").AppendLine(exception.Message);
sb.Append(" Source : ").AppendLine(exception.Source);
sb.Append(" StackTrace : ").AppendLine(exception.StackTrace);
@@ -41,32 +45,31 @@ namespace EonaCat.Logger.Extensions
private static string FormatExceptionData(IDictionary data)
{
StringBuilder sb = new StringBuilder();
var sb = new StringBuilder();
foreach (DictionaryEntry entry in data)
{
sb.Append(" | ")
.Append(entry.Key)
.Append(": ")
.AppendLine(entry.Value.ToString());
}
return sb.ToString();
}
private static string FormatInnerException(Exception innerException)
{
StringBuilder sb = new StringBuilder();
var sb = new StringBuilder();
sb.AppendLine(innerException.GetType().ToString())
.AppendLine(" Message : " + innerException.Message)
.AppendLine(" Source : " + innerException.Source)
.AppendLine(" StackTrace : " + innerException.StackTrace)
.AppendLine(" ToString : " + innerException.ToString())
.AppendLine(" ToString : " + innerException)
.Append(" Data : ")
.AppendLine(innerException.Data != null && innerException.Data.Count > 0 ? FormatExceptionData(innerException.Data) : "(none)");
.AppendLine(innerException.Data != null && innerException.Data.Count > 0
? FormatExceptionData(innerException.Data)
: "(none)");
return sb.ToString();
}
}
}

View File

@@ -1,8 +1,7 @@
using System;
using System.IO;
namespace EonaCat.Logger.Extensions
{
namespace EonaCat.Logger.Extensions;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
@@ -14,27 +13,17 @@ namespace EonaCat.Logger.Extensions
{
if (stream.CanSeek)
{
if (offset > stream.Length)
{
throw new EndOfStreamException();
}
if (offset > stream.Length) throw new EndOfStreamException();
BaseStreamOffset = offset;
if (length > stream.Length - offset)
{
throw new EndOfStreamException();
}
if (length > stream.Length - offset) throw new EndOfStreamException();
if (length == 0)
{
Length1 = stream.Length - offset;
}
else
{
Length1 = length;
}
}
else
{
BaseStreamOffset = 0;
@@ -46,26 +35,6 @@ namespace EonaCat.Logger.Extensions
OwnStream = ownStream;
}
protected override void Dispose(bool disposing)
{
if (Disposed)
{
return;
}
if (disposing)
{
if (OwnStream & (BaseStream != null))
{
BaseStream.Dispose();
}
}
Disposed = true;
base.Dispose(disposing);
}
public override bool CanRead => BaseStream.CanRead;
public override bool CanSeek => BaseStream.CanSeek;
@@ -80,53 +49,57 @@ namespace EonaCat.Logger.Extensions
set
{
if (value > Length1)
{
throw new EndOfStreamException();
}
if (value > Length1) throw new EndOfStreamException();
if (!BaseStream.CanSeek)
{
throw new NotSupportedException("Cannot seek stream.");
}
if (!BaseStream.CanSeek) throw new NotSupportedException("Cannot seek stream.");
Position1 = value;
}
}
public long BaseStreamOffset { get; private set; }
public Stream BaseStream { get; }
public long Length1 { get; set; }
public long Position1 { get; set; }
public bool ReadOnly { get; }
public bool Disposed { get; set; }
public bool OwnStream { get; }
protected override void Dispose(bool disposing)
{
if (Disposed) return;
if (disposing)
if (OwnStream & (BaseStream != null))
BaseStream.Dispose();
Disposed = true;
base.Dispose(disposing);
}
public override void Flush()
{
if (ReadOnly)
{
throw new IOException("OffsetStream is read only.");
}
if (ReadOnly) throw new IOException("OffsetStream is read only.");
BaseStream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
if (count < 1)
{
throw new ArgumentOutOfRangeException("Count cannot be less than 1.");
}
if (count < 1) throw new ArgumentOutOfRangeException("Count cannot be less than 1.");
if (Position1 >= Length1)
{
return 0;
}
if (Position1 >= Length1) return 0;
if (count > Length1 - Position1)
{
count = Convert.ToInt32(Length1 - Position1);
}
if (count > Length1 - Position1) count = Convert.ToInt32(Length1 - Position1);
if (BaseStream.CanSeek)
{
BaseStream.Position = BaseStreamOffset + Position1;
}
if (BaseStream.CanSeek) BaseStream.Position = BaseStreamOffset + Position1;
int bytesRead = BaseStream.Read(buffer, offset, count);
var bytesRead = BaseStream.Read(buffer, offset, count);
Position1 += bytesRead;
return bytesRead;
@@ -134,10 +107,7 @@ namespace EonaCat.Logger.Extensions
public override long Seek(long offset, SeekOrigin origin)
{
if (!BaseStream.CanSeek)
{
throw new IOException("Stream is not seekable.");
}
if (!BaseStream.CanSeek) throw new IOException("Stream is not seekable.");
long pos;
@@ -160,10 +130,7 @@ namespace EonaCat.Logger.Extensions
break;
}
if (pos < 0 || pos >= Length1)
{
throw new EndOfStreamException("OffsetStream reached begining/end of stream.");
}
if (pos < 0 || pos >= Length1) throw new EndOfStreamException("OffsetStream reached begining/end of stream.");
Position1 = pos;
@@ -172,10 +139,7 @@ namespace EonaCat.Logger.Extensions
public override void SetLength(long value)
{
if (ReadOnly)
{
throw new IOException("OffsetStream is read only.");
}
if (ReadOnly) throw new IOException("OffsetStream is read only.");
BaseStream.SetLength(BaseStreamOffset + value);
Length1 = value;
@@ -183,44 +147,20 @@ namespace EonaCat.Logger.Extensions
public override void Write(byte[] buffer, int offset, int count)
{
if (ReadOnly)
{
throw new IOException("OffsetStream is read only.");
}
if (ReadOnly) throw new IOException("OffsetStream is read only.");
if (count < 1)
{
return;
}
if (count < 1) return;
long pos = Position1 + count;
var pos = Position1 + count;
if (pos > Length1)
{
throw new EndOfStreamException("OffsetStream reached end of stream.");
}
if (pos > Length1) throw new EndOfStreamException("OffsetStream reached end of stream.");
if (BaseStream.CanSeek)
{
BaseStream.Position = BaseStreamOffset + Position1;
}
if (BaseStream.CanSeek) BaseStream.Position = BaseStreamOffset + Position1;
BaseStream.Write(buffer, offset, count);
Position1 = pos;
}
public long BaseStreamOffset { get; private set; }
public Stream BaseStream { get; }
public long Length1 { get; set; }
public long Position1 { get; set; }
public bool ReadOnly { get; }
public bool Disposed { get; set; }
public bool OwnStream { get; }
public void Reset(long offset, long length, long position)
{
BaseStreamOffset = offset;
@@ -235,17 +175,11 @@ namespace EonaCat.Logger.Extensions
public void WriteTo(Stream stream, int bufferSize)
{
if (!BaseStream.CanSeek)
{
throw new IOException("Stream is not seekable.");
}
if (!BaseStream.CanSeek) throw new IOException("Stream is not seekable.");
if (Length1 < bufferSize)
{
bufferSize = Convert.ToInt32(Length1);
}
if (Length1 < bufferSize) bufferSize = Convert.ToInt32(Length1);
long previousPosition = Position1;
var previousPosition = Position1;
Position1 = 0;
try
@@ -258,4 +192,3 @@ namespace EonaCat.Logger.Extensions
}
}
}
}

View File

@@ -1,8 +1,7 @@
using System;
using System.Net.Sockets;
namespace EonaCat.Logger.GrayLog
{
namespace EonaCat.Logger.GrayLog;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
@@ -11,58 +10,10 @@ namespace EonaCat.Logger.GrayLog
/// </summary>
public class GrayLogServer
{
/// <summary>
/// Hostname.
/// </summary>
public string Hostname
{
get
{
return _Hostname;
}
set
{
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname));
_Hostname = value;
SetUdp();
}
}
/// <summary>
/// UDP port.
/// </summary>
public int Port
{
get
{
return _Port;
}
set
{
if (value < 0) throw new ArgumentException("Port must be zero or greater.");
_Port = value;
SetUdp();
}
}
/// <summary>
/// IP:port of the server.
/// </summary>
public string IpPort
{
get
{
return _Hostname + ":" + _Port;
}
}
internal readonly object SendLock = new object();
internal UdpClient Udp = null;
internal readonly object SendLock = new();
private string _Hostname = "127.0.0.1";
private int _Port = 12201;
internal UdpClient Udp;
/// <summary>
/// Instantiate the object.
@@ -82,10 +33,45 @@ namespace EonaCat.Logger.GrayLog
Port = port;
}
/// <summary>
/// Hostname.
/// </summary>
public string Hostname
{
get => _Hostname;
set
{
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname));
_Hostname = value;
SetUdp();
}
}
/// <summary>
/// UDP port.
/// </summary>
public int Port
{
get => _Port;
set
{
if (value < 0) throw new ArgumentException("Port must be zero or greater.");
_Port = value;
SetUdp();
}
}
/// <summary>
/// IP:port of the server.
/// </summary>
public string IpPort => _Hostname + ":" + _Port;
private void SetUdp()
{
Udp = null;
Udp = new UdpClient(_Hostname, _Port);
}
}
}

View File

@@ -2,8 +2,8 @@
using System.Drawing;
using System.Globalization;
namespace EonaCat.Logger.Helpers
{
namespace EonaCat.Logger.Helpers;
public static class ColorHelper
{
public static string ColorToHexString(Color c)
@@ -13,7 +13,7 @@ namespace EonaCat.Logger.Helpers
public static string ColorToRGBString(Color c)
{
return "RGB(" + c.R.ToString() + "," + c.G.ToString() + "," + c.B.ToString() + ")";
return "RGB(" + c.R + "," + c.G + "," + c.B + ")";
}
public static Color ConsoleColorToColor(this ConsoleColor consoleColor)
@@ -73,19 +73,17 @@ namespace EonaCat.Logger.Helpers
}
}
public static Color HexStringToColor(string htmlColor, bool requireHexSpecified = false, int defaultAlpha = 0xFF) => Color.FromArgb(HexColorToArgb(htmlColor, requireHexSpecified, defaultAlpha));
public static Color HexStringToColor(string htmlColor, bool requireHexSpecified = false, int defaultAlpha = 0xFF)
{
return Color.FromArgb(HexColorToArgb(htmlColor, requireHexSpecified, defaultAlpha));
}
public static int HexColorToArgb(string htmlColor, bool requireHexSpecified = false, int defaultAlpha = 0xFF)
{
if (string.IsNullOrEmpty(htmlColor))
{
throw new ArgumentNullException(nameof(htmlColor));
}
if (string.IsNullOrEmpty(htmlColor)) throw new ArgumentNullException(nameof(htmlColor));
if (!htmlColor.StartsWith("#") && requireHexSpecified)
{
throw new ArgumentException($"Provided parameter '{htmlColor}' is not valid");
}
htmlColor = htmlColor.TrimStart('#');
@@ -96,40 +94,39 @@ namespace EonaCat.Logger.Helpers
{
case 3: // RGB short hand
{
return defaultAlpha << 24
| value & 0xF
| (value & 0xF) << 4
| (value & 0xF0) << 4
| (value & 0xF0) << 8
| (value & 0xF00) << 8
| (value & 0xF00) << 12
return (defaultAlpha << 24)
| (value & 0xF)
| ((value & 0xF) << 4)
| ((value & 0xF0) << 4)
| ((value & 0xF0) << 8)
| ((value & 0xF00) << 8)
| ((value & 0xF00) << 12)
;
}
case 4: // RGBA short hand
{
// Inline alpha swap
return (value & 0xF) << 24
| (value & 0xF) << 28
| (value & 0xF0) >> 4
| value & 0xF0
| value & 0xF00
| (value & 0xF00) << 4
| (value & 0xF000) << 4
| (value & 0xF000) << 8
return ((value & 0xF) << 24)
| ((value & 0xF) << 28)
| ((value & 0xF0) >> 4)
| (value & 0xF0)
| (value & 0xF00)
| ((value & 0xF00) << 4)
| ((value & 0xF000) << 4)
| ((value & 0xF000) << 8)
;
}
case 6: // RGB complete definition
{
return defaultAlpha << 24 | value;
return (defaultAlpha << 24) | value;
}
case 8: // RGBA complete definition
{
// Alpha swap
return (value & 0xFF) << 24 | value >> 8;
return ((value & 0xFF) << 24) | (value >> 8);
}
default:
throw new FormatException("Invalid HTML Color");
}
}
}
}

View File

@@ -1,8 +1,7 @@
using System;
using System.Collections.Generic;
namespace EonaCat.Logger.Helpers
{
namespace EonaCat.Logger.Helpers;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
@@ -11,13 +10,13 @@ namespace EonaCat.Logger.Helpers
{
static EnumHelper()
{
string[] names = Enum.GetNames(typeof(T));
T[] values = (T[])Enum.GetValues(typeof(T));
var names = Enum.GetNames(typeof(T));
var values = (T[])Enum.GetValues(typeof(T));
Names = new Dictionary<T, string>(names.Length);
Values = new Dictionary<string, T>(names.Length * 2);
for (int i = 0; i < names.Length; i++)
for (var i = 0; i < names.Length; i++)
{
Names[values[i]] = names[i];
Values[names[i]] = values[i];
@@ -31,7 +30,7 @@ namespace EonaCat.Logger.Helpers
public static string ToString(T value)
{
return Names.TryGetValue(value, out string result) ? result : Convert.ToInt64(value).ToString();
return Names.TryGetValue(value, out var result) ? result : Convert.ToInt64(value).ToString();
}
public static bool TryParse(string input, bool ignoreCase, out T value)
@@ -47,7 +46,6 @@ namespace EonaCat.Logger.Helpers
internal static T Parse(string input, bool ignoreCase, T defaultValue)
{
return TryParse(input, ignoreCase, out T result) ? result : defaultValue;
}
return TryParse(input, ignoreCase, out var result) ? result : defaultValue;
}
}

View File

@@ -1,7 +1,6 @@
using System;
namespace EonaCat.Logger.Managers
{
namespace EonaCat.Logger.Managers;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
@@ -10,40 +9,40 @@ namespace EonaCat.Logger.Managers
/// </summary>
public class ColorSchema
{
/// <summary>
/// The color to use for critical messages.
/// </summary>
public ColorScheme Critical = new(ConsoleColor.DarkRed, ConsoleColor.Black);
/// <summary>
/// The color to use for debug messages.
/// </summary>
public ColorScheme Debug = new ColorScheme(ConsoleColor.Green, ConsoleColor.Black);
/// <summary>
/// The color to use for informational messages.
/// </summary>
public ColorScheme Info = new ColorScheme(ConsoleColor.Blue, ConsoleColor.Black);
/// <summary>
/// The color to use for warning messages.
/// </summary>
public ColorScheme Warning = new ColorScheme(ConsoleColor.DarkYellow, ConsoleColor.Black);
public ColorScheme Debug = new(ConsoleColor.Green, ConsoleColor.Black);
/// <summary>
/// The color to use for error messages.
/// </summary>
public ColorScheme Error = new ColorScheme(ConsoleColor.Red, ConsoleColor.Black);
public ColorScheme Error = new(ConsoleColor.Red, ConsoleColor.Black);
/// <summary>
/// The color to use for alert messages.
/// The color to use for informational messages.
/// </summary>
public ColorScheme Traffic = new ColorScheme(ConsoleColor.DarkMagenta, ConsoleColor.Black);
/// <summary>
/// The color to use for critical messages.
/// </summary>
public ColorScheme Critical = new ColorScheme(ConsoleColor.DarkRed, ConsoleColor.Black);
public ColorScheme Info = new(ConsoleColor.Blue, ConsoleColor.Black);
/// <summary>
/// The color to use for emergency messages.
/// </summary>
public ColorScheme Trace = new ColorScheme(ConsoleColor.Cyan, ConsoleColor.Black);
public ColorScheme Trace = new(ConsoleColor.Cyan, ConsoleColor.Black);
/// <summary>
/// The color to use for alert messages.
/// </summary>
public ColorScheme Traffic = new(ConsoleColor.DarkMagenta, ConsoleColor.Black);
/// <summary>
/// The color to use for warning messages.
/// </summary>
public ColorScheme Warning = new(ConsoleColor.DarkYellow, ConsoleColor.Black);
}
/// <summary>
@@ -51,16 +50,16 @@ namespace EonaCat.Logger.Managers
/// </summary>
public class ColorScheme
{
/// <summary>
/// Foreground color.
/// </summary>
public ConsoleColor Foreground = Console.ForegroundColor;
/// <summary>
/// Background color.
/// </summary>
public ConsoleColor Background = Console.BackgroundColor;
/// <summary>
/// Foreground color.
/// </summary>
public ConsoleColor Foreground = Console.ForegroundColor;
/// <summary>
/// Instantiates a new color scheme.
/// </summary>
@@ -72,4 +71,3 @@ namespace EonaCat.Logger.Managers
Background = background;
}
}
}

View File

@@ -1,16 +1,18 @@
using System;
using System.Threading.Tasks;
namespace EonaCat.Logger.Managers
{
namespace EonaCat.Logger.Managers;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
public interface ILogManager
{
void Write(string message, ELogType logType = ELogType.INFO, bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null, string grayLogVersion = "1.1");
void Write(string message, ELogType logType = ELogType.INFO, bool? writeToConsole = null,
bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null,
bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null,
string grayLogVersion = "1.1");
void Write(Exception exception, string module = null, string method = null, bool criticalException = false,
bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null, string grayLogVersion = "1.1");
}
bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null,
string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null,
string grayLogSource = null, string grayLogVersion = "1.1");
}

View File

@@ -13,8 +13,8 @@ using Microsoft.Extensions.Logging;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
namespace EonaCat.Logger.Managers
{
namespace EonaCat.Logger.Managers;
public class ErrorMessage
{
public Exception Exception { get; set; }
@@ -25,19 +25,22 @@ namespace EonaCat.Logger.Managers
{
internal static event EventHandler<ErrorMessage> OnException;
internal static string FormatMessageWithHeader(LoggerSettings settings, ELogType logType, string currentMessage, DateTime dateTime)
internal static string FormatMessageWithHeader(LoggerSettings settings, ELogType logType, string currentMessage,
DateTime dateTime)
{
if (string.IsNullOrWhiteSpace(currentMessage))
return currentMessage;
StringBuilder sb = new StringBuilder(settings?.HeaderFormat ?? "[EonaCatLogger]");
var sb = new StringBuilder(settings?.HeaderFormat ?? "[EonaCatLogger]");
sb.Replace("{ts}", dateTime.ToString(settings?.TimestampFormat ?? "yyyy-MM-dd HH:mm:ss") + " " + (settings?.UseLocalTime ?? false ? "[LOCAL]" : "[UTC]"))
sb.Replace("{ts}",
dateTime.ToString(settings?.TimestampFormat ?? "yyyy-MM-dd HH:mm:ss") + " " +
(settings?.UseLocalTime ?? false ? "[LOCAL]" : "[UTC]"))
.Replace("{host}", $"[Host:{Dns.GetHostName()}]")
.Replace("{thread}", $"[ThreadId:{Environment.CurrentManagedThreadId}]")
.Replace("{sev}", $"[{logType}]");
if (!settings?.RemoveMessagePrefix ?? false && !currentMessage.Contains("[EonaCatLogger]"))
if (!settings?.RemoveMessagePrefix ?? (false && !currentMessage.Contains("[EonaCatLogger]")))
sb.Insert(0, "[EonaCatLogger] ");
sb.Append(" ").Append(currentMessage);
@@ -52,8 +55,8 @@ namespace EonaCat.Logger.Managers
if (settings.EnableColors && settings.Colors != null)
{
ConsoleColor prevForeground = Console.ForegroundColor;
ConsoleColor prevBackground = Console.BackgroundColor;
var prevForeground = Console.ForegroundColor;
var prevBackground = Console.BackgroundColor;
ConsoleColor foregroundColor;
ConsoleColor backgroundColor;
@@ -103,32 +106,43 @@ namespace EonaCat.Logger.Managers
}
}
internal static async Task SendToFile(ILogger logger, LoggerSettings settings, ELogType logType, string message)
internal static void SendToFile(ILogger logger, LoggerSettings settings, ELogType logType, string message)
{
if (logger == null || settings == null || !settings.EnableFileLogging || string.IsNullOrWhiteSpace(message))
return;
if (logger == null || settings == null || !settings.EnableFileLogging ||
string.IsNullOrWhiteSpace(message)) return;
LogLevel logLevel = logType.ToLogLevel();
if (logLevel >= settings.MaxLogType.ToLogLevel())
await Task.Run(() => logger.Log(logLevel, message));
var logLevel = logType.ToLogLevel();
if (IsLogLevelEnabled(settings, logLevel)) Log(logger, logLevel, message);
}
public static async Task SendToSplunkServersAsync(LoggerSettings settings, SplunkPayload splunkPayload, bool sendToSplunkServer)
public static bool IsLogLevelEnabled(LoggerSettings settings, LogLevel logLevel)
{
return settings.MinLogType.ToLogLevel() <= logLevel;
}
private static void Log(ILogger logger, LogLevel logLevel, string message)
{
logger.Log(logLevel, message);
}
public static async Task SendToSplunkServersAsync(LoggerSettings settings, SplunkPayload splunkPayload,
bool sendToSplunkServer)
{
if (settings == null || !sendToSplunkServer || splunkPayload == null)
return;
if (settings.SplunkServers == null)
{
settings.SplunkServers = new List<SplunkServer.SplunkServer>();
}
if (settings.SplunkServers == null) settings.SplunkServers = new List<SplunkServer.SplunkServer>();
foreach (var splunkServer in settings.SplunkServers)
{
if (!splunkServer.HasHecUrl || !splunkServer.HasHecToken)
{
OnException?.Invoke(null, new ErrorMessage { Message = $"Splunk server HecUrl or HecToken not specified, skipping splunkServer '{splunkServer.SplunkHecUrl}'" });
OnException?.Invoke(null,
new ErrorMessage
{
Message =
$"Splunk server HecUrl or HecToken not specified, skipping splunkServer '{splunkServer.SplunkHecUrl}'"
});
continue;
}
@@ -137,18 +151,28 @@ namespace EonaCat.Logger.Managers
var response = await splunkServer.SendAsync(splunkPayload);
if (!response.IsSuccessStatusCode)
OnException?.Invoke(null,
new ErrorMessage
{
OnException?.Invoke(null, new ErrorMessage { Message = $"Failed to send log to Splunk '{splunkServer.SplunkHecUrl}'. Status code: {response.StatusCode}" });
}
Message =
$"Failed to send log to Splunk '{splunkServer.SplunkHecUrl}'. Status code: {response.StatusCode}"
});
}
catch (Exception exception)
{
OnException?.Invoke(null, new ErrorMessage { Exception = exception, Message = $"Error while logging to Splunk Server '{splunkServer.SplunkHecUrl}': {exception.Message}" });
OnException?.Invoke(null,
new ErrorMessage
{
Exception = exception,
Message =
$"Error while logging to Splunk Server '{splunkServer.SplunkHecUrl}': {exception.Message}"
});
}
}
}
public static async Task SendToSplunkServersAsync(LoggerSettings settings, string logType, string message, bool sendToSplunkServer)
public static async Task SendToSplunkServersAsync(LoggerSettings settings, string logType, string message,
bool sendToSplunkServer)
{
if (settings == null || !sendToSplunkServer || string.IsNullOrWhiteSpace(message))
return;
@@ -163,13 +187,13 @@ namespace EonaCat.Logger.Managers
await SendToSplunkServersAsync(settings, splunkPayload, sendToSplunkServer);
}
internal static async Task SendToGrayLogServersAsync(LoggerSettings settings, string message, ELogType logLevel, string facility, string source, bool sendToGrayLogServer, string version = "1.1")
internal static async Task SendToGrayLogServersAsync(LoggerSettings settings, string message, ELogType logLevel,
string facility, string source, bool sendToGrayLogServer, string version = "1.1")
{
if (settings == null || !sendToGrayLogServer || string.IsNullOrWhiteSpace(message))
return;
foreach (var grayLogServer in settings.GrayLogServers ?? new List<GrayLogServer> { new GrayLogServer("127.0.0.1", 12201) })
{
foreach (var grayLogServer in settings.GrayLogServers ?? new List<GrayLogServer> { new("127.0.0.1") })
try
{
var gelfMessage = new
@@ -180,48 +204,60 @@ namespace EonaCat.Logger.Managers
level = logLevel.ToGrayLogLevel(),
facility,
source,
timestamp = DateTime.UtcNow.ToUnixTimestamp(),
timestamp = DateTime.UtcNow.ToUnixTimestamp()
};
var messageBytes = Encoding.UTF8.GetBytes(JsonHelper.ToJson(gelfMessage));
await grayLogServer.Udp.SendAsync(messageBytes, messageBytes.Length, new IPEndPoint(IPAddress.Parse(grayLogServer.Hostname), grayLogServer.Port));
await grayLogServer.Udp.SendAsync(messageBytes, messageBytes.Length,
new IPEndPoint(IPAddress.Parse(grayLogServer.Hostname), grayLogServer.Port));
}
catch (Exception exception)
{
OnException?.Invoke(null, new ErrorMessage { Exception = exception, Message = $"Error while logging to GrayLog Server '{grayLogServer.Hostname}': {exception.Message}" });
}
OnException?.Invoke(null,
new ErrorMessage
{
Exception = exception,
Message =
$"Error while logging to GrayLog Server '{grayLogServer.Hostname}': {exception.Message}"
});
}
}
internal static async Task SendToSysLogServersAsync(LoggerSettings settings, string message, bool sendToSyslogServers)
internal static async Task SendToSysLogServersAsync(LoggerSettings settings, string message,
bool sendToSyslogServers)
{
if (settings == null || !sendToSyslogServers || string.IsNullOrWhiteSpace(message))
return;
foreach (var server in settings.SysLogServers ?? new List<SyslogServer> { new SyslogServer("127.0.0.1", 514) })
{
foreach (var server in settings.SysLogServers ?? new List<SyslogServer> { new("127.0.0.1") })
try
{
if (string.IsNullOrWhiteSpace(server.Hostname))
{
OnException?.Invoke(null, new ErrorMessage { Message = "Server hostname not specified, skipping SysLog Server" });
OnException?.Invoke(null,
new ErrorMessage { Message = "Server hostname not specified, skipping SysLog Server" });
continue;
}
if (server.Port < 0)
{
OnException?.Invoke(null, new ErrorMessage { Message = "Server port must be zero or greater, skipping SysLog Server" });
OnException?.Invoke(null,
new ErrorMessage { Message = "Server port must be zero or greater, skipping SysLog Server" });
continue;
}
byte[] data = Encoding.UTF8.GetBytes(message);
await server.Udp.SendAsync(data, data.Length, new IPEndPoint(IPAddress.Parse(server.Hostname), server.Port));
var data = Encoding.UTF8.GetBytes(message);
await server.Udp.SendAsync(data, data.Length,
new IPEndPoint(IPAddress.Parse(server.Hostname), server.Port));
}
catch (Exception exception)
{
OnException?.Invoke(null, new ErrorMessage { Exception = exception, Message = $"Error while logging to SysLog Server '{server.Hostname}': {exception.Message}" });
}
}
OnException?.Invoke(null,
new ErrorMessage
{
Exception = exception,
Message = $"Error while logging to SysLog Server '{server.Hostname}': {exception.Message}"
});
}
}
}

View File

@@ -1,7 +1,4 @@
using EonaCat.Logger.Syslog;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
@@ -10,38 +7,99 @@ using EonaCat.Logger.EonaCatCoreLogger;
using EonaCat.Logger.EonaCatCoreLogger.Extensions;
using EonaCat.Logger.EonaCatCoreLogger.Models;
using EonaCat.Logger.Extensions;
using EonaCat.Logger.Syslog;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace EonaCat.Logger.Managers
{
namespace EonaCat.Logger.Managers;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
public partial class LogManager : ILogManager, IDisposable
public class LogManager : ILogManager, IDisposable
{
public event EventHandler<ErrorMessage> OnException;
private DateTime CurrentDateTme => Settings.UseLocalTime ? DateTime.Now : DateTime.UtcNow;
private static LogManager _instance;
private readonly CancellationTokenSource _tokenSource = new();
private DateTime _logDate;
public LogManager(LoggerSettings settings, string serverIp, int serverPort)
{
if (string.IsNullOrEmpty(serverIp))
throw new ArgumentNullException(nameof(serverIp));
if (serverPort < 0)
throw new ArgumentException("Server port must be zero or greater.");
settings.SysLogServers = new List<SyslogServer>
{
new(serverIp, serverPort)
};
Settings = settings;
SetupLogManager();
}
public LogManager(LoggerSettings settings)
{
Settings = settings;
SetupFileLogger(settings);
SetupLogManager();
LogHelper.OnException += LogHelper_OnException;
}
private DateTime CurrentDateTme => Settings.UseLocalTime ? DateTime.Now : DateTime.UtcNow;
public ILoggerProvider LoggerProvider { get; private set; }
public ILoggerFactory LoggerFactory { get; private set; }
public ILogger Logger { get; private set; }
public string CurrentLogFile => LoggerProvider is FileLoggerProvider fileLoggerProvider ? fileLoggerProvider.LogFile : string.Empty;
public string CurrentLogFile => LoggerProvider is FileLoggerProvider fileLoggerProvider
? fileLoggerProvider.LogFile
: string.Empty;
public bool IsRunning { get; private set; }
private static LogManager _instance;
private readonly CancellationTokenSource _tokenSource = new CancellationTokenSource();
public static LogManager Instance => InstanceInit();
public LoggerSettings Settings { get; set; } = CreateDefaultSettings();
public void Dispose()
{
DisposeAsync(true).GetAwaiter().GetResult();
GC.SuppressFinalize(this);
}
public void Write(Exception exception, string module = null, string method = null, bool criticalException = false,
bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null,
string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null,
string grayLogSource = null, string grayLogVersion = "1.1")
{
if (exception == null)
return;
Write(exception.FormatExceptionToMessage(module, method),
criticalException ? ELogType.CRITICAL : ELogType.ERROR, writeToConsole, sendToSysLogServers,
sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource,
grayLogVersion);
}
public void Write(string message, ELogType logType = ELogType.INFO, bool? writeToConsole = null,
bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null,
bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null,
string grayLogVersion = "1.1")
{
if (logType == ELogType.NONE)
return;
if (!IsRunning) StartNewLogAsync().ConfigureAwait(false);
InternalWriteAsync(CurrentDateTme, message, logType, writeToConsole, sendToSysLogServers, sendToSplunkServers,
customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource, grayLogVersion);
}
public event EventHandler<ErrorMessage> OnException;
private static LogManager InstanceInit()
{
if (_instance == null)
{
_instance = new LogManager(CreateDefaultSettings());
}
if (_instance == null) _instance = new LogManager(CreateDefaultSettings());
return _instance;
}
@@ -49,29 +107,20 @@ namespace EonaCat.Logger.Managers
{
var settings = new LoggerSettings();
settings.Id = "EonaCatLogger";
settings.MaxLogType = ELogType.TRACE;
settings.MinLogType = ELogType.INFO;
return settings;
}
protected virtual async Task DisposeAsync(bool disposing)
{
if (disposing)
{
await StopLoggingAsync().ConfigureAwait(false);
}
if (disposing) await StopLoggingAsync().ConfigureAwait(false);
}
public async Task StartNewLogAsync()
{
if (_tokenSource.IsCancellationRequested)
{
return;
}
if (_tokenSource.IsCancellationRequested) return;
if (IsRunning && CurrentDateTme.Date > _logDate.Date)
{
await StopLoggingAsync().ConfigureAwait(false);
}
if (IsRunning && CurrentDateTme.Date > _logDate.Date) await StopLoggingAsync().ConfigureAwait(false);
IsRunning = true;
@@ -85,11 +134,12 @@ namespace EonaCat.Logger.Managers
private void CreateLogger()
{
// Dispose of previous ServiceProvider if it exists
(LoggerProvider as IDisposable)?.Dispose();
(LoggerFactory as IDisposable)?.Dispose();
LoggerProvider?.Dispose();
LoggerFactory?.Dispose();
IServiceCollection serviceCollection = new ServiceCollection();
serviceCollection.AddLogging(builder => builder.SetMinimumLevel(Settings.MaxLogType.ToLogLevel()).AddEonaCatFileLogger(configuration =>
serviceCollection.AddLogging(builder => builder.SetMinimumLevel(Settings.MinLogType.ToLogLevel())
.AddEonaCatFileLogger(configuration =>
{
var fileLoggerOptions = Settings.FileLoggerOptions;
configuration.MaxWriteTries = fileLoggerOptions.MaxWriteTries;
@@ -108,50 +158,41 @@ namespace EonaCat.Logger.Managers
LoggerProvider = serviceProvider.GetService<ILoggerProvider>();
LoggerFactory = serviceProvider.GetService<ILoggerFactory>();
Logger = LoggerFactory.CreateLogger(Settings.Id);
// Dispose of the current ServiceProvider
(serviceProvider as IDisposable)?.Dispose();
}
private async Task InternalWriteAsync(DateTime dateTime, string message, ELogType logType = ELogType.INFO, bool? writeToConsole = null, bool? sendToSyslogServers = null, bool? sendToSplunkServers = null, string customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null, string grayLogVersion = "1.1")
private async Task InternalWriteAsync(DateTime dateTime, string message, ELogType logType = ELogType.INFO,
bool? writeToConsole = null, bool? sendToSyslogServers = null, bool? sendToSplunkServers = null,
string customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null,
string grayLogSource = null, string grayLogVersion = "1.1")
{
if (string.IsNullOrEmpty(message) || logType == ELogType.NONE || (int)logType >= (int)Settings.MaxLogType)
{
return;
}
if (string.IsNullOrEmpty(message) || logType == ELogType.NONE ||
(int)logType < (int)Settings.MinLogType) return;
string messageWithHeader = LogHelper.FormatMessageWithHeader(Settings, logType, message, dateTime);
bool writeToConsoleValue = writeToConsole ?? Settings.EnableConsole;
bool sendToSyslogServersValue = sendToSyslogServers ?? Settings.SendToSyslogServers;
bool sendToSplunkServersValue = sendToSplunkServers ?? Settings.SendToSplunkServers;
bool sendToGrayLogServersValue = sendToGrayLogServers ?? Settings.SendToGrayLogServers;
var messageWithHeader = LogHelper.FormatMessageWithHeader(Settings, logType, message, dateTime);
var writeToConsoleValue = writeToConsole ?? Settings.EnableConsole;
var sendToSyslogServersValue = sendToSyslogServers ?? Settings.SendToSyslogServers;
var sendToSplunkServersValue = sendToSplunkServers ?? Settings.SendToSplunkServers;
var sendToGrayLogServersValue = sendToGrayLogServers ?? Settings.SendToGrayLogServers;
if (writeToConsoleValue)
{
LogHelper.SendToConsole(Settings, logType, messageWithHeader, true);
}
LogHelper.SendToFile(Logger, Settings, logType, message);
if (writeToConsoleValue) LogHelper.SendToConsole(Settings, logType, messageWithHeader, true);
if (sendToSyslogServersValue || sendToSplunkServersValue || sendToGrayLogServersValue)
{
await Task.Run(async () =>
{
if (sendToSyslogServersValue)
{
await LogHelper.SendToSysLogServersAsync(Settings, messageWithHeader, true);
}
if (sendToSplunkServersValue)
{
await LogHelper.SendToSplunkServersAsync(Settings, customSplunkSourceType ?? logType.ToString(), messageWithHeader, true);
}
await LogHelper.SendToSplunkServersAsync(Settings, customSplunkSourceType ?? logType.ToString(),
messageWithHeader, true);
if (sendToGrayLogServersValue)
{
await LogHelper.SendToGrayLogServersAsync(Settings, messageWithHeader, logType, grayLogFacility, grayLogSource, true, grayLogVersion);
}
await LogHelper.SendToGrayLogServersAsync(Settings, messageWithHeader, logType, grayLogFacility,
grayLogSource, true, grayLogVersion);
});
}
var logMessage = new EonaCatLogMessage
{
@@ -164,31 +205,9 @@ namespace EonaCat.Logger.Managers
Settings.OnLogEvent(logMessage);
}
public void Reset() => Settings.ResetLogEvent();
public LogManager(LoggerSettings settings, string serverIp, int serverPort)
public void Reset()
{
if (string.IsNullOrEmpty(serverIp))
throw new ArgumentNullException(nameof(serverIp));
if (serverPort < 0)
throw new ArgumentException("Server port must be zero or greater.");
settings.SysLogServers = new List<SyslogServer>
{
new SyslogServer(serverIp, serverPort)
};
Settings = settings;
SetupLogManager();
}
public LogManager(LoggerSettings settings)
{
Settings = settings;
SetupFileLogger(settings, null, true);
SetupLogManager();
LogHelper.OnException += LogHelper_OnException;
Settings.ResetLogEvent();
}
private void LogHelper_OnException(object sender, ErrorMessage e)
@@ -228,11 +247,10 @@ namespace EonaCat.Logger.Managers
Dispose();
}
private async Task<Task> StopLoggingAsync()
private Task StopLoggingAsync()
{
IsRunning = false;
await InternalWriteAsync(CurrentDateTme, $"{DllInfo.ApplicationName} stopped.").ConfigureAwait(false);
return Task.Delay(500);
return Task.CompletedTask;
}
public void DeleteCurrentLogFile()
@@ -240,30 +258,4 @@ namespace EonaCat.Logger.Managers
if (CurrentLogFile != null)
File.Delete(CurrentLogFile);
}
public void Dispose()
{
DisposeAsync(true).GetAwaiter().GetResult();
GC.SuppressFinalize(this);
}
public void Write(Exception exception, string module = null, string method = null, bool criticalException = false, bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null, string grayLogVersion = "1.1")
{
if (exception == null)
return;
Write(exception.FormatExceptionToMessage(module, method), criticalException ? ELogType.CRITICAL : ELogType.ERROR, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource, grayLogVersion);
}
public void Write(string message, ELogType logType = ELogType.INFO, bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null, string grayLogVersion = "1.1")
{
if (logType == ELogType.NONE)
return;
if (!IsRunning)
StartNewLogAsync().ConfigureAwait(false);
InternalWriteAsync(CurrentDateTme, message, logType, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource, grayLogVersion);
}
}
}

View File

@@ -5,8 +5,7 @@ using EonaCat.Logger.EonaCatCoreLogger.Models;
using EonaCat.Logger.GrayLog;
using EonaCat.Logger.Syslog;
namespace EonaCat.Logger.Managers
{
namespace EonaCat.Logger.Managers;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
@@ -15,9 +14,17 @@ namespace EonaCat.Logger.Managers
/// </summary>
public class LoggerSettings
{
public event LogDelegate OnLog;
public delegate void LogDelegate(EonaCatLogMessage message);
private ColorSchema _colors = new();
private bool _enableConsole = true;
private FileLoggerOptions _fileLoggerOptions;
private string _headerFormat = "{ts} {host} {thread} {sev}";
private int _maxMessageLength = 1024;
private string _timestampFormat = "yyyy-MM-dd HH:mm:ss";
/// <summary>
/// Determines if we need to use the local time in the logging or UTC (default:false)
/// </summary>
@@ -32,7 +39,8 @@ namespace EonaCat.Logger.Managers
/// <summary>
/// Header format. Provide a string that specifies how the preamble of each message should be structured. You can use variables including:
/// Header format. Provide a string that specifies how the preamble of each message should be structured. You can use
/// variables including:
/// {ts}: UTC timestamp
/// {host}: Hostname
/// {thread}: Thread ID
@@ -42,10 +50,7 @@ namespace EonaCat.Logger.Managers
/// </summary>
public string HeaderFormat
{
get
{
return _headerFormat;
}
get => _headerFormat;
set
{
if (string.IsNullOrEmpty(value)) _headerFormat = "";
@@ -58,10 +63,7 @@ namespace EonaCat.Logger.Managers
/// </summary>
public string TimestampFormat
{
get
{
return _timestampFormat;
}
get => _timestampFormat;
set
{
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(HeaderFormat));
@@ -76,10 +78,7 @@ namespace EonaCat.Logger.Managers
/// </summary>
public bool EnableConsole
{
get
{
return _enableConsole;
}
get => _enableConsole;
set
{
if (value) _enableConsole = ConsoleExists();
@@ -97,21 +96,15 @@ namespace EonaCat.Logger.Managers
/// </summary>
public ColorSchema Colors
{
get
{
return _colors;
}
get => _colors;
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(Colors));
}
if (value == null) throw new ArgumentNullException(nameof(Colors));
_colors = value;
}
}
public ELogType MaxLogType { get; set; } = ELogType.TRACE;
public ELogType MinLogType { get; set; } = ELogType.INFO;
public bool SendToSyslogServers { get; set; }
@@ -130,8 +123,6 @@ namespace EonaCat.Logger.Managers
/// </summary>
public bool EnableFileLogging { get; set; } = true;
private FileLoggerOptions _fileLoggerOptions;
/// <summary>
/// FileLogger settings.
/// </summary>
@@ -139,22 +130,11 @@ namespace EonaCat.Logger.Managers
{
get
{
if (_fileLoggerOptions == null)
{
_fileLoggerOptions = CreateDefaultFileLoggerOptions();
}
if (_fileLoggerOptions == null) _fileLoggerOptions = CreateDefaultFileLoggerOptions();
return _fileLoggerOptions;
}
set
{
_fileLoggerOptions = value;
}
}
private static FileLoggerOptions CreateDefaultFileLoggerOptions()
{
return new FileLoggerOptions();
set => _fileLoggerOptions = value;
}
/// <summary>
@@ -162,19 +142,20 @@ namespace EonaCat.Logger.Managers
/// </summary>
public string LogOrigin { get; set; }
private string _headerFormat = "{ts} {host} {thread} {sev}";
private string _timestampFormat = "yyyy-MM-dd HH:mm:ss";
private bool _enableConsole = true;
private int _maxMessageLength = 1024;
private ColorSchema _colors = new ColorSchema();
public event LogDelegate OnLog;
private static FileLoggerOptions CreateDefaultFileLoggerOptions()
{
return new FileLoggerOptions();
}
private static bool ConsoleExists()
{
try
{
bool test1 = Environment.UserInteractive;
bool test2 = Console.WindowHeight > 0;
var test1 = Environment.UserInteractive;
var test2 = Console.WindowHeight > 0;
return test1 && test2;
}
catch (Exception)
@@ -193,4 +174,3 @@ namespace EonaCat.Logger.Managers
OnLog = null;
}
}
}

View File

@@ -1,9 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace EonaCat.Logger.Splunk.Models;
namespace EonaCat.Logger.Splunk.Models
{
public class SplunkPayload
{
/// <summary>
@@ -21,4 +17,3 @@ namespace EonaCat.Logger.Splunk.Models
/// </summary>
public string Host { get; set; }
}
}

View File

@@ -1,12 +1,11 @@
using EonaCat.Json;
using EonaCat.Logger.Splunk.Models;
using System;
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using EonaCat.Json;
using EonaCat.Logger.Splunk.Models;
namespace EonaCat.Logger.SplunkServer
{
namespace EonaCat.Logger.SplunkServer;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
@@ -15,22 +14,38 @@ namespace EonaCat.Logger.SplunkServer
/// </summary>
public class SplunkServer
{
internal readonly object SendLock = new();
private string _splunkHecUrl = "https://127.0.0.1:8088/services/collector/event";
/// <summary>
/// Instantiate the object.
/// </summary>
/// <param name="splunkHecUrl">splunkHecUrl.</param>
/// <param name="splunkHecToken">splunkHecToken.</param>
/// <param name="httpClientHandler">httpClientHandler. (optional)</param>
public SplunkServer(string splunkHecUrl, string splunkHecToken, HttpClientHandler httpClientHandler = null)
{
SplunkHecUrl = splunkHecUrl;
SplunkHecToken = splunkHecToken;
if (httpClientHandler == null) httpClientHandler = new HttpClientHandler();
SplunkClientHandler = httpClientHandler;
CreateHttpClient();
}
/// <summary>
/// SplunkHecUrl
/// </summary>
public string SplunkHecUrl
{
get
{
return _splunkHecUrl;
}
get => _splunkHecUrl;
set
{
if (!string.IsNullOrWhiteSpace(_splunkHecUrl) && !_splunkHecUrl.ToLower().Contains("http"))
{
value = $"http://{value}";
}
_splunkHecUrl = value;
}
}
@@ -40,25 +55,11 @@ namespace EonaCat.Logger.SplunkServer
/// <summary>
/// SplunkHecToken
/// </summary>
public string SplunkHecToken
{
get
{
return _splunkHecToken;
}
set
{
_splunkHecToken = value;
}
}
public string SplunkHecToken { get; set; } = "splunk-hec-token";
public HttpClientHandler SplunkClientHandler { get; internal set; }
public HttpClient HttpClient { get; private set; }
internal readonly object SendLock = new object();
private string _splunkHecUrl = "https://127.0.0.1:8088/services/collector/event";
private string _splunkHecToken = "splunk-hec-token";
/// <summary>
/// Determines if a HEC token is available
/// </summary>
@@ -72,28 +73,9 @@ namespace EonaCat.Logger.SplunkServer
/// <summary>
/// Determines if the splunk URL is local
/// </summary>
public bool IsLocalHost => HasHecUrl && (SplunkHecUrl.ToLower().Contains("127.0.0.1") || SplunkHecUrl.ToLower().Contains("localhost"));
/// <summary>
/// Instantiate the object.
/// </summary>
/// <param name="splunkHecUrl">splunkHecUrl.</param>
/// <param name="splunkHecToken">splunkHecToken.</param>
/// <param name="httpClientHandler">httpClientHandler. (optional)</param>
public SplunkServer(string splunkHecUrl, string splunkHecToken, HttpClientHandler httpClientHandler = null)
{
SplunkHecUrl = splunkHecUrl;
SplunkHecToken = splunkHecToken;
if (httpClientHandler == null)
{
httpClientHandler = new HttpClientHandler();
}
SplunkClientHandler = httpClientHandler;
CreateHttpClient();
}
public bool IsLocalHost => HasHecUrl &&
(SplunkHecUrl.ToLower().Contains("127.0.0.1") ||
SplunkHecUrl.ToLower().Contains("localhost"));
private void CreateHttpClient()
{
@@ -110,9 +92,9 @@ namespace EonaCat.Logger.SplunkServer
/// </summary>
public void DisableSSLValidation()
{
HttpClientHandler clientHandler = new HttpClientHandler()
var clientHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true,
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
};
SplunkClientHandler = clientHandler;
@@ -121,10 +103,7 @@ namespace EonaCat.Logger.SplunkServer
public async Task<HttpResponseMessage> SendAsync(SplunkPayload splunkPayload)
{
if (splunkPayload == null)
{
return null;
}
if (splunkPayload == null) return null;
// Create an event object with the required fields
var eventObject = new
@@ -135,20 +114,16 @@ namespace EonaCat.Logger.SplunkServer
};
// Serialize the event object to JSON
string eventJson = JsonHelper.ToJson(eventObject);
var eventJson = JsonHelper.ToJson(eventObject);
if (!HasHecToken)
{
CreateHttpClient();
}
if (!HasHecToken) CreateHttpClient();
// Create an HTTP content with the event data
var content = new StringContent(eventJson, Encoding.UTF8, "application/json");
// Send the event to Splunk
HttpResponseMessage response = await HttpClient.PostAsync("/services/collector/event", content);
var response = await HttpClient.PostAsync("/services/collector/event", content);
return response;
}
}
}

View File

@@ -1,10 +1,7 @@
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
namespace EonaCat.Logger.Syslog
{
namespace EonaCat.Logger.Syslog;
// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
@@ -13,58 +10,11 @@ namespace EonaCat.Logger.Syslog
/// </summary>
public class SyslogServer
{
/// <summary>
/// Hostname.
/// </summary>
public string Hostname
{
get
{
return _Hostname;
}
set
{
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname));
_Hostname = value;
SetUdp();
}
}
/// <summary>
/// UDP port.
/// </summary>
public int Port
{
get
{
return _Port;
}
set
{
if (value < 0) throw new ArgumentException("Port must be zero or greater.");
_Port = value;
SetUdp();
}
}
/// <summary>
/// IP:port of the server.
/// </summary>
public string IpPort
{
get
{
return _Hostname + ":" + _Port;
}
}
internal UdpClient Udp = null;
private string _Hostname = "127.0.0.1";
private int _Port = 514;
internal UdpClient Udp;
/// <summary>
/// Instantiate the object.
/// </summary>
@@ -83,10 +33,45 @@ namespace EonaCat.Logger.Syslog
Port = port;
}
/// <summary>
/// Hostname.
/// </summary>
public string Hostname
{
get => _Hostname;
set
{
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname));
_Hostname = value;
SetUdp();
}
}
/// <summary>
/// UDP port.
/// </summary>
public int Port
{
get => _Port;
set
{
if (value < 0) throw new ArgumentException("Port must be zero or greater.");
_Port = value;
SetUdp();
}
}
/// <summary>
/// IP:port of the server.
/// </summary>
public string IpPort => _Hostname + ":" + _Port;
private void SetUdp()
{
Udp = null;
Udp = new UdpClient(_Hostname, _Port);
}
}
}

View File

@@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\EonaCat.Logger\EonaCat.Logger.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,14 +0,0 @@
using EonaCat.Logger;
using EonaCat.Logger.Managers;
LogManager.Instance.Write("INFO", ELogType.INFO, true);
LogManager.Instance.Write("WARNING", ELogType.WARNING, true);
LogManager.Instance.Write("ERROR", ELogType.ERROR, true);
LogManager.Instance.Write("DEBUG", ELogType.DEBUG, true);
LogManager.Instance.Write("CRITICAL", ELogType.CRITICAL, true);
LogManager.Instance.Write("TRACE", ELogType.TRACE, true);
LogManager.Instance.Write("TRAFFIC", ELogType.TRAFFIC, true);
LogManager.Instance.Write("NONE", ELogType.NONE, true);
Console.WriteLine("Press a key to exit");
Console.ReadKey();

View File

@@ -1,15 +1,16 @@
using EonaCat.Logger.Managers;
using System.IO.Compression;
using System.IO.Compression;
using EonaCat.Logger.EonaCatCoreLogger;
using EonaCat.Logger.Extensions;
using EonaCat.Logger.Managers;
namespace EonaCat.Logger.Test.Web;
namespace EonaCat.Logger.Test.Web
{
public static class Logger
{
public static ELogType MaxLogType { get; set; }
public static bool UseLocalTime { get; set; }
private static LogManager LogManager;
public static string LogFolder => "Logs";
public static ELogType MinLogType { get; set; }
public static bool UseLocalTime { get; set; }
public static string LogFolder => Path.Combine(FileLoggerOptions.DefaultPath, "logs");
public static string CurrentLogFile => LogManager.CurrentLogFile;
public static bool IsDisabled { get; set; }
@@ -30,13 +31,14 @@ namespace EonaCat.Logger.Test.Web
{
var logFileName = logName + ".log";
await using var fS = new FileStream(Path.Combine(ConvertToAbsolutePath(LogFolder), logFileName), FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 64 * 1024, true);
await using var fS = new FileStream(Path.Combine(ConvertToAbsolutePath(LogFolder), logFileName), FileMode.Open,
FileAccess.Read, FileShare.ReadWrite, 64 * 1024, true);
var response = context.Response;
response.ContentType = "text/plain";
response.Headers.ContentDisposition = "attachment;filename=" + logFileName;
if ((limit > fS.Length) || (limit < 1))
if (limit > fS.Length || limit < 1)
limit = fS.Length;
var oFS = new OffsetStream(fS, 0, limit);
@@ -50,7 +52,8 @@ namespace EonaCat.Logger.Test.Web
}
else
{
string[] acceptEncodingParts = acceptEncoding.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
var acceptEncodingParts = acceptEncoding.Split(new[] { ',' },
StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (acceptEncodingParts.Contains("br"))
{
@@ -95,24 +98,23 @@ namespace EonaCat.Logger.Test.Web
if (IsDisabled)
return;
LogManager.Write(exception, module: message, writeToConsole: writeToConsole);
LogManager.Write(exception, message, writeToConsole: writeToConsole);
}
public static void Configure()
{
var loggerSettings = new LoggerSettings
{
Id = "EonaCatDnsLogger",
MaxLogType = ELogType.TRACE,
Id = "EonaCatTestLogger",
MinLogType = ELogType.INFO,
UseLocalTime = UseLocalTime,
FileLoggerOptions =
{
LogDirectory = "Logs",
LogDirectory = LogFolder,
FileSizeLimit = 20_000_000, // 20 MB,
UseLocalTime = UseLocalTime,
UseLocalTime = UseLocalTime
}
};
LogManager = new LogManager(loggerSettings);
}
}
}

View File

@@ -1,17 +1,13 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Diagnostics;
namespace EonaCat.Logger.Web.Pages
{
namespace EonaCat.Logger.Web.Pages;
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
private readonly ILogger<ErrorModel> _logger;
public ErrorModel(ILogger<ErrorModel> logger)
@@ -19,9 +15,12 @@ namespace EonaCat.Logger.Web.Pages
_logger = logger;
}
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
}

View File

@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace EonaCat.Logger.Web.Pages
{
namespace EonaCat.Logger.Web.Pages;
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
@@ -13,7 +13,5 @@ namespace EonaCat.Logger.Web.Pages
public void OnGet()
{
}
}
}

View File

@@ -1,8 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace EonaCat.Logger.Web.Pages;
namespace EonaCat.Logger.Web.Pages
{
public class PrivacyModel : PageModel
{
private readonly ILogger<PrivacyModel> _logger;
@@ -16,4 +15,3 @@ namespace EonaCat.Logger.Web.Pages
{
}
}
}

View File

@@ -46,6 +46,6 @@
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
@await RenderSectionAsync("Scripts", false)
</body>
</html>

View File

@@ -10,7 +10,7 @@ using EonaCat.Web.Tracer.Extensions;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
FileLoggerOptions options = new FileLoggerOptions();
var options = new FileLoggerOptions();
options.MaxRolloverFiles = 5;
options.FileSizeLimit = 1 * 1024 * 1024 / 4;
options.UseLocalTime = true;
@@ -35,6 +35,7 @@ rateOptions.AddDefaultConfiguration(config =>
);
RateLimiterMiddleware.OnLimitResponseCreated += RateLimiterMiddlewareOnLimitResponseCreatedAsync;
async void RateLimiterMiddlewareOnLimitResponseCreatedAsync(object? sender, HttpContext httpContext)
{
await httpContext.Response.WriteAsync(" THIS IS MY CUSTOM RATE LIMIT MESSAGE").ConfigureAwait(false);
@@ -61,7 +62,7 @@ builder.Services.AddMemoryCache();
var app = builder.Build();
Logger.UseLocalTime = true;
Logger.MaxLogType = ELogType.TRACE;
Logger.MinLogType = ELogType.TRACE;
Logger.Configure();
// Configure the HTTP request pipeline.
@@ -83,6 +84,7 @@ if (!app.Environment.IsDevelopment())
// await httpContext.Response.WriteAsync("THIS IS MY CUSTOM RATE LIMIT MESSAGE");
//}
WebTracer.OnLog += WebTracer_OnLog;
void WebTracer_OnLog(object? sender, string e)
{
Console.WriteLine(e);
@@ -103,10 +105,10 @@ void RunLoggingExceptionTests()
var loggerSettings = new LoggerSettings();
loggerSettings.FileLoggerOptions.UseLocalTime = true;
loggerSettings.UseLocalTime = true;
loggerSettings.MaxLogType = ELogType.TRACE;
loggerSettings.MinLogType = ELogType.INFO;
var logger = new LogManager(loggerSettings);
for (int i = 0; i < 10; i++)
for (var i = 0; i < 10; i++)
{
try
{
@@ -117,19 +119,20 @@ void RunLoggingExceptionTests()
logger.Write(exception);
Console.WriteLine($"Normal ExceptionLogged: {i}");
}
Task.Delay(1);
}
}
Task.Run(RunWebLoggerTests);
Task.Run(RunWebLoggingTests);
//Task.Run(RunWebLoggerTests);
//Task.Run(RunWebLoggingTests);
Task.Run(RunLoggingTests);
Task.Run(RunLoggingExceptionTests);
Task.Run(RunWebLoggingExceptionTests);
//Task.Run(RunLoggingExceptionTests);
//Task.Run(RunWebLoggingExceptionTests);
void RunWebLoggingExceptionTests()
{
for (int i = 0; i < 10; i++)
for (var i = 0; i < 10; i++)
{
try
{
@@ -145,18 +148,16 @@ void RunWebLoggingExceptionTests()
app.Logger.LogInformation(exception, "INFORMATION");
Console.WriteLine($"WebExceptionLogged: {i}");
}
Task.Delay(1);
}
}
void RunWebLoggingTests()
{
if (!Directory.Exists(Logger.LogFolder))
{
Directory.CreateDirectory(Logger.LogFolder);
}
if (!Directory.Exists(Logger.LogFolder)) Directory.CreateDirectory(Logger.LogFolder);
for (int i = 0; i < 9000000; i++)
for (var i = 0; i < 9000000; i++)
{
app.Logger.LogInformation($"web-test {i}");
File.AppendAllText(Path.Combine(Logger.LogFolder, "test.log"), $"WebLogged: {i}{Environment.NewLine}");
@@ -170,15 +171,15 @@ void RunLoggingTests()
var loggerSettings = new LoggerSettings();
loggerSettings.UseLocalTime = true;
loggerSettings.FileLoggerOptions.UseLocalTime = true;
loggerSettings.MaxLogType = ELogType.TRACE;
loggerSettings.MinLogType = ELogType.INFO;
loggerSettings.FileLoggerOptions.FileSizeLimit = 1024 * 1024 * 1;
loggerSettings.FileLoggerOptions.FileNamePrefix = "AllTypes";
loggerSettings.FileLoggerOptions.MaxRolloverFiles = 5;
var logger = new LogManager(loggerSettings);
for (int i = 0; i < 9000000; i++)
for (var i = 0; i < 9000000; i++)
{
logger.Write($"test to file {i} INFO", ELogType.INFO);
logger.Write($"test to file {i} INFO");
logger.Write($"test to file {i} CRITICAL", ELogType.CRITICAL);
logger.Write($"test to file {i} DEBUG", ELogType.DEBUG);
logger.Write($"test to file {i} ERROR", ELogType.ERROR);
@@ -193,9 +194,9 @@ void RunLoggingTests()
void RunWebLoggerTests()
{
for (int i = 0; i < 9000000; i++)
for (var i = 0; i < 9000000; i++)
{
Logger.Log($"test via logger {i} INFO", ELogType.INFO);
Logger.Log($"test via logger {i} INFO");
Logger.Log($"test via logger {i} CRITICAL", ELogType.CRITICAL);
Logger.Log($"test via logger {i} DEBUG", ELogType.DEBUG);
Logger.Log($"test via logger {i} ERROR", ELogType.ERROR);