Updated
This commit is contained in:
@@ -2,9 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>.netstandard2.1; net8.0; net4.8;</TargetFrameworks>
|
||||
<ApplicationIcon>icon.ico</ApplicationIcon>
|
||||
<Version>1.5.0</Version>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<FileVersion>1.5.0</FileVersion>
|
||||
<Authors>EonaCat (Jeroen Saey)</Authors>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Company>EonaCat (Jeroen Saey)</Company>
|
||||
@@ -25,7 +23,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<EVRevisionFormat>1.5.0+{chash:10}.{c:ymd}</EVRevisionFormat>
|
||||
<EVRevisionFormat>1.5.2+{chash:10}.{c:ymd}</EVRevisionFormat>
|
||||
<EVDefault>true</EVDefault>
|
||||
<EVInfo>true</EVInfo>
|
||||
<EVTagMatch>v[0-9]*</EVTagMatch>
|
||||
@@ -35,6 +33,17 @@
|
||||
<EVShowRevision>true</EVShowRevision>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>$(GeneratedVersion)</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="EVPack" BeforeTargets="Pack">
|
||||
<Message Text="EVPack: Forcing NuGet Version = $(GeneratedVersion)" Importance="High"/>
|
||||
<PropertyGroup>
|
||||
<Version>$(GeneratedVersion)</Version>
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="icon.png" />
|
||||
<None Include="..\LICENSE">
|
||||
|
||||
@@ -48,7 +48,9 @@ namespace EonaCat.Logger.EonaCatCoreLogger
|
||||
Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
if (!IsEnabled(logLevel) || formatter == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IncludeCorrelationId)
|
||||
{
|
||||
@@ -88,7 +90,9 @@ namespace EonaCat.Logger.EonaCatCoreLogger
|
||||
private async Task FlushBufferAsync()
|
||||
{
|
||||
if (!await _flushLock.WaitAsync(0))
|
||||
{
|
||||
return; // Another flush is in progress
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -98,7 +102,9 @@ namespace EonaCat.Logger.EonaCatCoreLogger
|
||||
{
|
||||
using var connection = _options.DbProviderFactory.CreateConnection();
|
||||
if (connection == null)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to create database connection.");
|
||||
}
|
||||
|
||||
connection.ConnectionString = _options.ConnectionString;
|
||||
await connection.OpenAsync();
|
||||
|
||||
@@ -52,7 +52,9 @@ namespace EonaCat.Logger.EonaCatCoreLogger
|
||||
Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
if (!IsEnabled(logLevel) || formatter == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IncludeCorrelationId)
|
||||
{
|
||||
@@ -98,7 +100,9 @@ namespace EonaCat.Logger.EonaCatCoreLogger
|
||||
private async Task FlushBufferAsync()
|
||||
{
|
||||
if (!await _flushLock.WaitAsync(0))
|
||||
{
|
||||
return; // Already flushing
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -41,7 +41,10 @@ namespace EonaCat.Logger.EonaCatCoreLogger
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
|
||||
Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
if (!IsEnabled(logLevel)) return;
|
||||
if (!IsEnabled(logLevel))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -48,7 +48,9 @@ namespace EonaCat.Logger.EonaCatCoreLogger
|
||||
Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
if (!IsEnabled(logLevel) || formatter == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -19,81 +19,63 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
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 class LogManager : ILogManager, IDisposable
|
||||
{
|
||||
private static readonly Lazy<LogManager> _instance = new(() => new LogManager(CreateDefaultSettings()));
|
||||
private readonly CancellationTokenSource _tokenSource = new();
|
||||
private CancellationTokenSource _tokenSource = new();
|
||||
private DateTime _logDate;
|
||||
private bool _isDisposing;
|
||||
private string _category;
|
||||
|
||||
public LogManager(LoggerSettings settings)
|
||||
{
|
||||
Settings = settings;
|
||||
Settings = settings ?? CreateDefaultSettings();
|
||||
SetupLogManager();
|
||||
}
|
||||
|
||||
public LogManager(LoggerSettings settings, string category = null)
|
||||
public LogManager(LoggerSettings settings, string category = null) : this(settings)
|
||||
{
|
||||
_category = category;
|
||||
if (string.IsNullOrWhiteSpace(category))
|
||||
{
|
||||
_category = "General";
|
||||
}
|
||||
Settings = settings;
|
||||
_category = string.IsNullOrWhiteSpace(category) ? "General" : category;
|
||||
SetupFileLogger(settings);
|
||||
SetupLogManager();
|
||||
|
||||
// Subscribe to static events
|
||||
LogHelper.OnException += LogHelper_OnException;
|
||||
LogHelper.OnLogLevelDisabled += LogHelper_OnLogLevelDisabled;
|
||||
}
|
||||
|
||||
private void LogHelper_OnLogLevelDisabled(object sender, ErrorMessage e)
|
||||
{
|
||||
OnLogLevelDisabled?.Invoke(sender, e);
|
||||
}
|
||||
public static LogManager Instance => _instance.Value;
|
||||
|
||||
private DateTime CurrentDateTime => Settings.UseLocalTime ? DateTime.Now : DateTime.UtcNow;
|
||||
public LoggerSettings Settings { get; private set; }
|
||||
public ILoggerProvider LoggerProvider { get; private set; }
|
||||
public ILoggerFactory LoggerFactory { get; private set; }
|
||||
public ILogger Logger { get; private set; }
|
||||
public bool IsRunning { get; private set; }
|
||||
|
||||
public string CurrentLogFile => LoggerProvider is FileLoggerProvider fileLoggerProvider
|
||||
? fileLoggerProvider.LogFile
|
||||
: string.Empty;
|
||||
|
||||
public bool IsRunning { get; private set; }
|
||||
|
||||
public static LogManager Instance => _instance.Value;
|
||||
|
||||
public LoggerSettings Settings { get; set; } = CreateDefaultSettings();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeAsync(true).GetAwaiter().GetResult();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
private DateTime CurrentDateTime => Settings.UseLocalTime ? DateTime.Now : DateTime.UtcNow;
|
||||
|
||||
public event EventHandler<ErrorMessage> OnException;
|
||||
public event EventHandler<ErrorMessage> OnLogLevelDisabled;
|
||||
public async Task WriteAsync(object currentObject, ELogType logType = ELogType.INFO, bool? writeToConsole = null,
|
||||
string customSplunkSourceType = null,
|
||||
string grayLogFacility = null, string grayLogSource = null,
|
||||
string grayLogVersion = "1.1", bool disableSplunkSSL = false, DumpFormat dumpFormat = DumpFormat.Json, bool isDetailedDump = false, int? dumpDepth = null, int? maxCollectionItems = null)
|
||||
string customSplunkSourceType = null, string grayLogFacility = null,
|
||||
string grayLogSource = null, string grayLogVersion = "1.1", bool disableSplunkSSL = false,
|
||||
DumpFormat dumpFormat = DumpFormat.Json, bool isDetailedDump = false, int? dumpDepth = null, int? maxCollectionItems = null)
|
||||
{
|
||||
if (currentObject == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await WriteAsync(currentObject.Dump(dumpFormat, isDetailedDump, dumpDepth, maxCollectionItems), logType, writeToConsole,
|
||||
customSplunkSourceType, grayLogFacility, grayLogSource,
|
||||
grayLogVersion, disableSplunkSSL);
|
||||
await WriteAsync(currentObject.Dump(dumpFormat, isDetailedDump, dumpDepth, maxCollectionItems),
|
||||
logType, writeToConsole, customSplunkSourceType, grayLogFacility, grayLogSource, grayLogVersion, disableSplunkSSL);
|
||||
}
|
||||
|
||||
public async Task WriteAsync(Exception exception, string module = null, string method = null,
|
||||
bool criticalException = false,
|
||||
bool? writeToConsole = null, string customSplunkSourceType = null, string grayLogFacility = null,
|
||||
bool criticalException = false, bool? writeToConsole = null,
|
||||
string customSplunkSourceType = null, string grayLogFacility = null,
|
||||
string grayLogSource = null, string grayLogVersion = "1.1", bool disableSplunkSSL = false)
|
||||
{
|
||||
if (exception == null)
|
||||
@@ -102,14 +84,12 @@ namespace EonaCat.Logger.Managers
|
||||
}
|
||||
|
||||
await WriteAsync(exception.FormatExceptionToMessage(module, method),
|
||||
criticalException ? ELogType.CRITICAL : ELogType.ERROR, writeToConsole,
|
||||
customSplunkSourceType, grayLogFacility, grayLogSource,
|
||||
grayLogVersion, disableSplunkSSL = false);
|
||||
criticalException ? ELogType.CRITICAL : ELogType.ERROR,
|
||||
writeToConsole, customSplunkSourceType, grayLogFacility, grayLogSource, grayLogVersion, disableSplunkSSL);
|
||||
}
|
||||
|
||||
public async Task WriteAsync(string message, ELogType logType = ELogType.INFO, bool? writeToConsole = null,
|
||||
string customSplunkSourceType = null,
|
||||
string grayLogFacility = null, string grayLogSource = null,
|
||||
string customSplunkSourceType = null, string grayLogFacility = null, string grayLogSource = null,
|
||||
string grayLogVersion = "1.1", bool disableSplunkSSL = false)
|
||||
{
|
||||
if (logType == ELogType.NONE)
|
||||
@@ -117,39 +97,10 @@ namespace EonaCat.Logger.Managers
|
||||
return;
|
||||
}
|
||||
|
||||
await InternalWriteAsync(CurrentDateTime, message, logType, writeToConsole,
|
||||
await InternalWriteAsync(CurrentDateTime, message, logType, writeToConsole,
|
||||
customSplunkSourceType, grayLogFacility, grayLogSource, grayLogVersion, disableSplunkSSL);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets fired when an exception occurs during logging
|
||||
/// </summary>
|
||||
public event EventHandler<ErrorMessage> OnException;
|
||||
|
||||
/// <summary>
|
||||
/// Gets fired when the log level is disabled and the user tries to log a message
|
||||
/// </summary>
|
||||
public event EventHandler<ErrorMessage> OnLogLevelDisabled;
|
||||
|
||||
private static LoggerSettings CreateDefaultSettings()
|
||||
{
|
||||
var settings = new LoggerSettings
|
||||
{
|
||||
Id = DllInfo.ApplicationName,
|
||||
};
|
||||
return settings;
|
||||
}
|
||||
|
||||
protected virtual async Task DisposeAsync(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_isDisposing = true;
|
||||
await StopLoggingAsync();
|
||||
await Task.Delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StartNewLogAsync()
|
||||
{
|
||||
if (_tokenSource.IsCancellationRequested)
|
||||
@@ -163,57 +114,73 @@ namespace EonaCat.Logger.Managers
|
||||
}
|
||||
|
||||
IsRunning = true;
|
||||
|
||||
CreateLogger();
|
||||
|
||||
Directory.CreateDirectory(Settings.FileLoggerOptions.LogDirectory);
|
||||
|
||||
_logDate = CurrentDateTime;
|
||||
}
|
||||
|
||||
private async Task StopLoggingAsync()
|
||||
{
|
||||
WriteStopMessage();
|
||||
IsRunning = false;
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void WriteStopMessage()
|
||||
{
|
||||
var stopMessage = $"{DllInfo.ApplicationName} stopped.{Environment.NewLine}";
|
||||
LogHelper.SendToFile(Logger, Settings, ELogType.INFO, stopMessage);
|
||||
}
|
||||
|
||||
private void CreateLogger()
|
||||
{
|
||||
// Dispose of previous ServiceProvider if it exists
|
||||
// Dispose previous providers safely
|
||||
LoggerProvider?.Dispose();
|
||||
LoggerFactory?.Dispose();
|
||||
|
||||
IServiceCollection serviceCollection = new ServiceCollection();
|
||||
|
||||
if (Settings.TypesToLog == null)
|
||||
if (Settings.TypesToLog == null || !Settings.TypesToLog.Any())
|
||||
{
|
||||
Settings.TypesToLog = new List<ELogType> { ELogType.INFO, ELogType.WARNING, ELogType.ERROR, ELogType.TRAFFIC, ELogType.DEBUG, ELogType.CRITICAL, ELogType.TRACE };
|
||||
Settings.TypesToLog = new List<ELogType>
|
||||
{
|
||||
ELogType.INFO, ELogType.WARNING, ELogType.ERROR,
|
||||
ELogType.TRAFFIC, ELogType.DEBUG, ELogType.CRITICAL, ELogType.TRACE
|
||||
};
|
||||
}
|
||||
|
||||
serviceCollection.AddLogging(builder => builder.SetMinimumLevel(Settings.TypesToLog.Max().ToLogLevel())
|
||||
.AddEonaCatFileLogger(configuration =>
|
||||
{
|
||||
var fileLoggerOptions = Settings.FileLoggerOptions;
|
||||
configuration.MaxWriteTries = fileLoggerOptions.MaxWriteTries;
|
||||
configuration.RetainedFileCountLimit = fileLoggerOptions.RetainedFileCountLimit;
|
||||
configuration.FlushPeriod = fileLoggerOptions.FlushPeriod;
|
||||
configuration.IsEnabled = fileLoggerOptions.IsEnabled;
|
||||
configuration.BatchSize = fileLoggerOptions.BatchSize;
|
||||
configuration.FileSizeLimit = fileLoggerOptions.FileSizeLimit;
|
||||
configuration.LogDirectory = fileLoggerOptions.LogDirectory;
|
||||
configuration.FileNamePrefix = fileLoggerOptions.FileNamePrefix;
|
||||
configuration.MaxRolloverFiles = fileLoggerOptions.MaxRolloverFiles;
|
||||
configuration.UseLocalTime = Settings.UseLocalTime;
|
||||
configuration.UseMask = Settings.UseMask;
|
||||
configuration.Mask = fileLoggerOptions.Mask;
|
||||
configuration.UseDefaultMasking = Settings.UseDefaultMasking;
|
||||
configuration.MaskedKeywords = fileLoggerOptions.MaskedKeywords;
|
||||
}));
|
||||
serviceCollection.AddLogging(builder =>
|
||||
builder.SetMinimumLevel(Settings.TypesToLog.Max().ToLogLevel())
|
||||
.AddEonaCatFileLogger(config =>
|
||||
{
|
||||
var options = Settings.FileLoggerOptions;
|
||||
config.MaxWriteTries = options.MaxWriteTries;
|
||||
config.RetainedFileCountLimit = options.RetainedFileCountLimit;
|
||||
config.FlushPeriod = options.FlushPeriod;
|
||||
config.IsEnabled = options.IsEnabled;
|
||||
config.BatchSize = options.BatchSize;
|
||||
config.FileSizeLimit = options.FileSizeLimit;
|
||||
config.LogDirectory = options.LogDirectory;
|
||||
config.FileNamePrefix = options.FileNamePrefix;
|
||||
config.MaxRolloverFiles = options.MaxRolloverFiles;
|
||||
config.UseLocalTime = Settings.UseLocalTime;
|
||||
config.UseMask = Settings.UseMask;
|
||||
config.Mask = options.Mask;
|
||||
config.UseDefaultMasking = Settings.UseDefaultMasking;
|
||||
config.MaskedKeywords = options.MaskedKeywords;
|
||||
}));
|
||||
|
||||
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
LoggerProvider = serviceProvider.GetService<ILoggerProvider>();
|
||||
LoggerFactory = serviceProvider.GetService<ILoggerFactory>();
|
||||
Logger = LoggerFactory.CreateLogger(Settings.Id);
|
||||
|
||||
LogHelper.SendToFile(Logger, Settings, ELogType.INFO, LogHelper.GetStartupMessage());
|
||||
}
|
||||
|
||||
private async Task InternalWriteAsync(DateTime dateTime, string message, ELogType logType = ELogType.INFO,
|
||||
bool? writeToConsole = null, string customSplunkSourceType = null, string grayLogFacility = null,
|
||||
string grayLogSource = null, string grayLogVersion = "1.1", bool disableSplunkSSL = false)
|
||||
bool? writeToConsole = null, string customSplunkSourceType = null, string grayLogFacility = null,
|
||||
string grayLogSource = null, string grayLogVersion = "1.1", bool disableSplunkSSL = false)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message) || logType == ELogType.NONE || !IsLogLevelEnabled(logType) || _isDisposing)
|
||||
{
|
||||
@@ -235,29 +202,32 @@ namespace EonaCat.Logger.Managers
|
||||
LogHelper.SendToConsole(Settings, logType, messageWithHeader);
|
||||
}
|
||||
|
||||
var tasks = new List<Task>(5)
|
||||
var tasks = new List<Task>
|
||||
{
|
||||
LogHelper.SendToSysLogServersAsync(Settings, message, logType, dateTime, _category),
|
||||
LogHelper.SendToSplunkServersAsync(Settings, customSplunkSourceType ?? logType.ToString(), messageWithHeader, disableSplunkSSL),
|
||||
LogHelper.SendToGrayLogServersAsync(Settings, messageWithHeader, logType, grayLogFacility, grayLogSource, grayLogVersion),
|
||||
LogHelper.SendToTcpLogServersAsync(Settings, messageWithHeader, logType),
|
||||
LogHelper.SendToUdpLogServersAsync(Settings, messageWithHeader, logType),
|
||||
LogHelper.SendToUdpLogServersAsync(Settings, messageWithHeader, logType)
|
||||
};
|
||||
|
||||
if (tasks.Count > 0)
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnException?.Invoke(this, new ErrorMessage { Message = ex.Message, Exception = ex });
|
||||
}
|
||||
|
||||
var logMessage = new EonaCatLogMessage
|
||||
Settings.OnLogEvent(new EonaCatLogMessage
|
||||
{
|
||||
DateTime = dateTime,
|
||||
Message = message,
|
||||
LogType = logType,
|
||||
Origin = string.IsNullOrEmpty(Settings.LogOrigin) ? "LogManager" : Settings.LogOrigin,
|
||||
Category = _category,
|
||||
};
|
||||
Settings.OnLogEvent(logMessage);
|
||||
Category = _category
|
||||
});
|
||||
}
|
||||
|
||||
private bool IsLogLevelEnabled(ELogType logType)
|
||||
@@ -268,42 +238,74 @@ namespace EonaCat.Logger.Managers
|
||||
return false;
|
||||
}
|
||||
|
||||
if (logType == ELogType.NONE)
|
||||
return logType != ELogType.NONE &&
|
||||
(Settings.TypesToLog == null || !Settings.TypesToLog.Any() || Settings.TypesToLog.Contains(logType));
|
||||
}
|
||||
|
||||
private void LogHelper_OnException(object sender, ErrorMessage e) => OnException?.Invoke(sender, e);
|
||||
private void LogHelper_OnLogLevelDisabled(object sender, ErrorMessage e) => OnLogLevelDisabled?.Invoke(sender, e);
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeAsync(true).GetAwaiter().GetResult();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual async Task DisposeAsync(bool disposing)
|
||||
{
|
||||
if (_isDisposing)
|
||||
{
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
var isEnabled = (Settings.TypesToLog == null || Settings.TypesToLog.Count == 0 || Settings.TypesToLog.Contains(logType));
|
||||
if (!isEnabled)
|
||||
if (disposing)
|
||||
{
|
||||
return false;
|
||||
_isDisposing = true;
|
||||
|
||||
await StopLoggingAsync();
|
||||
|
||||
// Unsubscribe events
|
||||
LogHelper.OnException -= LogHelper_OnException;
|
||||
LogHelper.OnLogLevelDisabled -= LogHelper_OnLogLevelDisabled;
|
||||
|
||||
// Cancel token
|
||||
_tokenSource.Cancel();
|
||||
_tokenSource.Dispose();
|
||||
_tokenSource = null;
|
||||
|
||||
LoggerProvider?.Dispose();
|
||||
LoggerFactory?.Dispose();
|
||||
|
||||
await Task.Delay(50);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
public static void Shutdown()
|
||||
{
|
||||
Settings.ResetLogEvent();
|
||||
if (_instance.IsValueCreated)
|
||||
{
|
||||
_instance.Value.Dispose();
|
||||
}
|
||||
}
|
||||
public void DeleteCurrentLogFile()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(CurrentLogFile) && File.Exists(CurrentLogFile))
|
||||
{
|
||||
File.Delete(CurrentLogFile);
|
||||
}
|
||||
}
|
||||
|
||||
private void LogHelper_OnException(object sender, ErrorMessage e)
|
||||
{
|
||||
OnException?.Invoke(sender, e);
|
||||
}
|
||||
private static LoggerSettings CreateDefaultSettings() => new() { Id = DllInfo.ApplicationName };
|
||||
|
||||
private void SetupFileLogger(LoggerSettings settings = null, string logFolder = null, bool defaultPrefix = true)
|
||||
{
|
||||
if (settings == null)
|
||||
{
|
||||
settings = Settings;
|
||||
}
|
||||
settings ??= Settings;
|
||||
|
||||
if (!settings.EnableFileLogging)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (logFolder != null)
|
||||
if (!string.IsNullOrWhiteSpace(logFolder))
|
||||
{
|
||||
settings.FileLoggerOptions.LogDirectory = logFolder;
|
||||
}
|
||||
@@ -314,50 +316,14 @@ namespace EonaCat.Logger.Managers
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupLogManager()
|
||||
{
|
||||
_logDate = CurrentDateTime;
|
||||
}
|
||||
private void SetupLogManager() => _logDate = CurrentDateTime;
|
||||
|
||||
private Task StopLoggingAsync()
|
||||
{
|
||||
WriteStopMessage();
|
||||
IsRunning = false;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void WriteStopMessage()
|
||||
{
|
||||
var stopMessage = $"{DllInfo.ApplicationName} stopped.{Environment.NewLine}";
|
||||
LogHelper.SendToFile(Logger, Settings, ELogType.INFO, stopMessage);
|
||||
}
|
||||
|
||||
public void DeleteCurrentLogFile()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(CurrentLogFile))
|
||||
{
|
||||
File.Delete(CurrentLogFile);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add Syslog server
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="nickName"></param>
|
||||
/// <param name="typesToLog"></param>
|
||||
/// <param name="convertToRfc5424"></param>
|
||||
/// <param name="convertToRfc3164"></param>
|
||||
/// <returns></returns>
|
||||
// Syslog
|
||||
public bool AddSyslogServer(string address, int port, string nickName = null, List<ELogType> typesToLog = null, bool convertToRfc5424 = false, bool convertToRfc3164 = false)
|
||||
{
|
||||
if (Settings.SysLogServers == null)
|
||||
{
|
||||
Settings.SysLogServers = new List<Servers.Syslog.Syslog>();
|
||||
}
|
||||
Settings.SysLogServers ??= new List<Syslog>();
|
||||
|
||||
if (Settings.SysLogServers.Any(x => (nickName != null && x.Nickname == nickName) || nickName == null && x.Hostname == address && x.Port == port))
|
||||
if (Settings.SysLogServers.Any(x => (nickName != null && x.Nickname == nickName) || (nickName == null && x.Hostname == address && x.Port == port)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -366,12 +332,6 @@ namespace EonaCat.Logger.Managers
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the sysLog server
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <returns></returns>
|
||||
public bool RemoveSyslogServer(string address, int port)
|
||||
{
|
||||
if (Settings.SysLogServers == null)
|
||||
@@ -379,19 +339,15 @@ namespace EonaCat.Logger.Managers
|
||||
return false;
|
||||
}
|
||||
|
||||
var delete = Settings.SysLogServers.FirstOrDefault(x => x.Hostname == address && x.Port == port);
|
||||
if (delete != null)
|
||||
var server = Settings.SysLogServers.FirstOrDefault(x => x.Hostname == address && x.Port == port);
|
||||
if (server != null)
|
||||
{
|
||||
Settings.SysLogServers.Remove(delete);
|
||||
Settings.SysLogServers.Remove(server);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the sysLog server
|
||||
/// </summary>
|
||||
/// <param name="nickName"></param>
|
||||
/// <returns></returns>
|
||||
public bool RemoveSyslogServer(string nickName)
|
||||
{
|
||||
if (Settings.SysLogServers == null)
|
||||
@@ -399,31 +355,21 @@ namespace EonaCat.Logger.Managers
|
||||
return false;
|
||||
}
|
||||
|
||||
var delete = Settings.SysLogServers.FirstOrDefault(x => x.Nickname == nickName);
|
||||
if (delete != null)
|
||||
var server = Settings.SysLogServers.FirstOrDefault(x => x.Nickname == nickName);
|
||||
if (server != null)
|
||||
{
|
||||
Settings.SysLogServers.Remove(delete);
|
||||
Settings.SysLogServers.Remove(server);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add Splunk server
|
||||
/// </summary>
|
||||
/// <param name="splunkHecUrl"></param>
|
||||
/// <param name="splunkHecToken"></param>
|
||||
/// <param name="httpClientHandler"></param>
|
||||
/// <param name="nickName"></param>
|
||||
/// <param name="typesToLog"></param>
|
||||
/// <returns></returns>
|
||||
// Splunk
|
||||
public bool AddSplunkServer(string splunkHecUrl, string splunkHecToken, HttpClientHandler httpClientHandler = null, string nickName = null, List<ELogType> typesToLog = null)
|
||||
{
|
||||
if (Settings.SplunkServers == null)
|
||||
{
|
||||
Settings.SplunkServers = new List<Splunk>();
|
||||
}
|
||||
Settings.SplunkServers ??= new List<Splunk>();
|
||||
|
||||
if (Settings.SplunkServers.Any(x => (nickName != null && x.Nickname == nickName) || nickName == null && x.SplunkHecUrl == splunkHecUrl && x.SplunkHecToken == splunkHecToken))
|
||||
if (Settings.SplunkServers.Any(x => (nickName != null && x.Nickname == nickName) || (nickName == null && x.SplunkHecUrl == splunkHecUrl && x.SplunkHecToken == splunkHecToken)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -432,12 +378,6 @@ namespace EonaCat.Logger.Managers
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the Splunk server
|
||||
/// </summary>
|
||||
/// <param name="splunkHecUrl"></param>
|
||||
/// <param name="splunkHecToken"></param>
|
||||
/// <returns></returns>
|
||||
public bool RemoveSplunkServer(string splunkHecUrl, string splunkHecToken)
|
||||
{
|
||||
if (Settings.SplunkServers == null)
|
||||
@@ -445,19 +385,15 @@ namespace EonaCat.Logger.Managers
|
||||
return false;
|
||||
}
|
||||
|
||||
var delete = Settings.SplunkServers.FirstOrDefault(x => x.SplunkHecUrl == splunkHecUrl && x.SplunkHecToken == splunkHecToken);
|
||||
if (delete != null)
|
||||
var server = Settings.SplunkServers.FirstOrDefault(x => x.SplunkHecUrl == splunkHecUrl && x.SplunkHecToken == splunkHecToken);
|
||||
if (server != null)
|
||||
{
|
||||
Settings.SplunkServers.Remove(delete);
|
||||
Settings.SplunkServers.Remove(server);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the Splunk server
|
||||
/// </summary>
|
||||
/// <param name="nickName"></param>
|
||||
/// <returns></returns>
|
||||
public bool RemoveSplunkServer(string nickName)
|
||||
{
|
||||
if (Settings.SplunkServers == null)
|
||||
@@ -465,30 +401,21 @@ namespace EonaCat.Logger.Managers
|
||||
return false;
|
||||
}
|
||||
|
||||
var delete = Settings.SplunkServers.FirstOrDefault(x => x.Nickname == nickName);
|
||||
if (delete != null)
|
||||
var server = Settings.SplunkServers.FirstOrDefault(x => x.Nickname == nickName);
|
||||
if (server != null)
|
||||
{
|
||||
Settings.SplunkServers.Remove(delete);
|
||||
Settings.SplunkServers.Remove(server);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add Graylog server
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="nickName"></param>
|
||||
/// <param name="typesToLog"></param>
|
||||
/// <returns></returns>
|
||||
// Graylog
|
||||
public bool AddGrayLogServer(string address, int port, string nickName = null, List<ELogType> typesToLog = null)
|
||||
{
|
||||
if (Settings.GrayLogServers == null)
|
||||
{
|
||||
Settings.GrayLogServers = new List<Graylog>();
|
||||
}
|
||||
Settings.GrayLogServers ??= new List<Graylog>();
|
||||
|
||||
if (Settings.GrayLogServers.Any(x => (nickName != null && x.Nickname == nickName) || nickName == null && x.Hostname == address && x.Port == port))
|
||||
if (Settings.GrayLogServers.Any(x => (nickName != null && x.Nickname == nickName) || (nickName == null && x.Hostname == address && x.Port == port)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -497,12 +424,6 @@ namespace EonaCat.Logger.Managers
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the GrayLog server
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <returns></returns>
|
||||
public bool RemoveGrayLogServer(string address, int port)
|
||||
{
|
||||
if (Settings.GrayLogServers == null)
|
||||
@@ -510,19 +431,15 @@ namespace EonaCat.Logger.Managers
|
||||
return false;
|
||||
}
|
||||
|
||||
var delete = Settings.GrayLogServers.FirstOrDefault(x => x.Hostname == address && x.Port == port);
|
||||
if (delete != null)
|
||||
var server = Settings.GrayLogServers.FirstOrDefault(x => x.Hostname == address && x.Port == port);
|
||||
if (server != null)
|
||||
{
|
||||
Settings.GrayLogServers.Remove(delete);
|
||||
Settings.GrayLogServers.Remove(server);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the GrayLog server
|
||||
/// </summary>
|
||||
/// <param name="nickName"></param>
|
||||
/// <returns></returns>
|
||||
public bool RemoveGrayLogServer(string nickName)
|
||||
{
|
||||
if (Settings.GrayLogServers == null)
|
||||
@@ -530,28 +447,19 @@ namespace EonaCat.Logger.Managers
|
||||
return false;
|
||||
}
|
||||
|
||||
var delete = Settings.GrayLogServers.FirstOrDefault(x => x.Nickname == nickName);
|
||||
if (delete != null)
|
||||
var server = Settings.GrayLogServers.FirstOrDefault(x => x.Nickname == nickName);
|
||||
if (server != null)
|
||||
{
|
||||
Settings.GrayLogServers.Remove(delete);
|
||||
Settings.GrayLogServers.Remove(server);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add Tcp Server
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="nickName"></param>
|
||||
/// <param name="typesToLog"></param>
|
||||
/// <returns></returns>
|
||||
// TCP
|
||||
public bool AddTcpLogServer(string address, int port, string nickName = null, List<ELogType> typesToLog = null)
|
||||
{
|
||||
if (Settings.TcpServers == null)
|
||||
{
|
||||
Settings.TcpServers = new List<Servers.Tcp.Tcp>();
|
||||
}
|
||||
Settings.TcpServers ??= new List<Servers.Tcp.Tcp>();
|
||||
|
||||
if (Settings.TcpServers.Any(x => (nickName != null && x.Nickname == nickName) || (nickName == null && x.Hostname == address && x.Port == port)))
|
||||
{
|
||||
@@ -562,12 +470,6 @@ namespace EonaCat.Logger.Managers
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the Tcp server
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <returns></returns>
|
||||
public bool RemoveTcpLogServer(string address, int port)
|
||||
{
|
||||
if (Settings.TcpServers == null)
|
||||
@@ -575,19 +477,15 @@ namespace EonaCat.Logger.Managers
|
||||
return false;
|
||||
}
|
||||
|
||||
var delete = Settings.TcpServers.FirstOrDefault(x => x.Hostname == address && x.Port == port);
|
||||
if (delete != null)
|
||||
var server = Settings.TcpServers.FirstOrDefault(x => x.Hostname == address && x.Port == port);
|
||||
if (server != null)
|
||||
{
|
||||
Settings.TcpServers.Remove(delete);
|
||||
Settings.TcpServers.Remove(server);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the Tcp server
|
||||
/// </summary>
|
||||
/// <param name="nickName"></param>
|
||||
/// <returns></returns>
|
||||
public bool RemoveTcpLogServer(string nickName)
|
||||
{
|
||||
if (Settings.TcpServers == null)
|
||||
@@ -595,30 +493,21 @@ namespace EonaCat.Logger.Managers
|
||||
return false;
|
||||
}
|
||||
|
||||
var delete = Settings.TcpServers.FirstOrDefault(x => x.Nickname == nickName);
|
||||
if (delete != null)
|
||||
var server = Settings.TcpServers.FirstOrDefault(x => x.Nickname == nickName);
|
||||
if (server != null)
|
||||
{
|
||||
Settings.TcpServers.Remove(delete);
|
||||
Settings.TcpServers.Remove(server);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add Udp Server
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="nickName"></param>
|
||||
/// <param name="typesToLog"></param>
|
||||
/// <returns></returns>
|
||||
// UDP
|
||||
public bool AddUdpLogServer(string address, int port, string nickName = null, List<ELogType> typesToLog = null)
|
||||
{
|
||||
if (Settings.UdpServers == null)
|
||||
{
|
||||
Settings.UdpServers = new List<Servers.Udp.Udp>();
|
||||
}
|
||||
Settings.UdpServers ??= new List<Servers.Udp.Udp>();
|
||||
|
||||
if (Settings.UdpServers.Any(x => (nickName != null && x.Nickname == nickName) || nickName == null && x.Hostname == address && x.Port == port))
|
||||
if (Settings.UdpServers.Any(x => (nickName != null && x.Nickname == nickName) || (nickName == null && x.Hostname == address && x.Port == port)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -627,12 +516,6 @@ namespace EonaCat.Logger.Managers
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the Udp server
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <returns></returns>
|
||||
public bool RemoveUdpLogServer(string address, int port)
|
||||
{
|
||||
if (Settings.UdpServers == null)
|
||||
@@ -640,19 +523,15 @@ namespace EonaCat.Logger.Managers
|
||||
return false;
|
||||
}
|
||||
|
||||
var delete = Settings.UdpServers.FirstOrDefault(x => x.Hostname == address && x.Port == port);
|
||||
if (delete != null)
|
||||
var server = Settings.UdpServers.FirstOrDefault(x => x.Hostname == address && x.Port == port);
|
||||
if (server != null)
|
||||
{
|
||||
Settings.UdpServers.Remove(delete);
|
||||
Settings.UdpServers.Remove(server);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the Udp server
|
||||
/// </summary>
|
||||
/// <param name="nickName"></param>
|
||||
/// <returns></returns>
|
||||
public bool RemoveUdpLogServer(string nickName)
|
||||
{
|
||||
if (Settings.UdpServers == null)
|
||||
@@ -660,12 +539,13 @@ namespace EonaCat.Logger.Managers
|
||||
return false;
|
||||
}
|
||||
|
||||
var delete = Settings.UdpServers.FirstOrDefault(x => x.Nickname == nickName);
|
||||
if (delete != null)
|
||||
var server = Settings.UdpServers.FirstOrDefault(x => x.Nickname == nickName);
|
||||
if (server != null)
|
||||
{
|
||||
Settings.UdpServers.Remove(delete);
|
||||
Settings.UdpServers.Remove(server);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,9 @@ namespace EonaCat.Logger.Servers.Splunk
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(SplunkHecUrl));
|
||||
}
|
||||
|
||||
_splunkHecUrl = value.StartsWith("http", StringComparison.OrdinalIgnoreCase)
|
||||
? value
|
||||
@@ -91,13 +93,19 @@ namespace EonaCat.Logger.Servers.Splunk
|
||||
try
|
||||
{
|
||||
if (payload == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!HasHecToken || !HasHecUrl)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (disableSplunkSSL)
|
||||
{
|
||||
DisableSSLValidation();
|
||||
}
|
||||
|
||||
_httpClient ??= new HttpClient(_httpClientHandler ?? new HttpClientHandler())
|
||||
{
|
||||
|
||||
@@ -42,7 +42,9 @@ namespace EonaCat.Logger.Servers.Syslog
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(Hostname));
|
||||
}
|
||||
|
||||
_hostname = value;
|
||||
SetUdp();
|
||||
@@ -55,7 +57,9 @@ namespace EonaCat.Logger.Servers.Syslog
|
||||
set
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
throw new ArgumentException("Port must be zero or greater.");
|
||||
}
|
||||
|
||||
_port = value;
|
||||
SetUdp();
|
||||
@@ -96,7 +100,9 @@ namespace EonaCat.Logger.Servers.Syslog
|
||||
public async Task WriteAsync(string message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var data = Encoding.UTF8.GetBytes(message);
|
||||
await SendAsync(data);
|
||||
@@ -105,13 +111,17 @@ namespace EonaCat.Logger.Servers.Syslog
|
||||
public async Task WriteAsync(byte[] data)
|
||||
{
|
||||
if (data is { Length: > 0 })
|
||||
{
|
||||
await SendAsync(data);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendAsync(byte[] data)
|
||||
{
|
||||
if (_udp == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.Length <= MaxUdpPacketSize)
|
||||
{
|
||||
|
||||
@@ -38,7 +38,9 @@ namespace EonaCat.Logger.Servers.Tcp
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(Hostname));
|
||||
}
|
||||
|
||||
_hostname = value;
|
||||
SetTcp();
|
||||
@@ -51,7 +53,9 @@ namespace EonaCat.Logger.Servers.Tcp
|
||||
set
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
throw new ArgumentException("Port must be zero or greater.");
|
||||
}
|
||||
|
||||
_port = value;
|
||||
SetTcp();
|
||||
@@ -91,13 +95,21 @@ namespace EonaCat.Logger.Servers.Tcp
|
||||
|
||||
public async Task WriteAsync(byte[] data)
|
||||
{
|
||||
if (data == null || data.Length == 0) return;
|
||||
if (data == null || data.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await InternalWriteAsync(data);
|
||||
}
|
||||
|
||||
public async Task WriteAsync(string data)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(data)) return;
|
||||
if (string.IsNullOrWhiteSpace(data))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(data);
|
||||
await InternalWriteAsync(bytes);
|
||||
}
|
||||
@@ -108,10 +120,14 @@ namespace EonaCat.Logger.Servers.Tcp
|
||||
try
|
||||
{
|
||||
if (!IsConnected || _tcp == null)
|
||||
{
|
||||
SetTcp();
|
||||
}
|
||||
|
||||
if (!IsConnected || _tcp == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var stream = _tcp.GetStream();
|
||||
await stream.WriteAsync(data, 0, data.Length);
|
||||
|
||||
@@ -39,7 +39,9 @@ namespace EonaCat.Logger.Servers.Udp
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(Hostname));
|
||||
}
|
||||
|
||||
_hostname = value;
|
||||
SetUdp();
|
||||
@@ -52,7 +54,9 @@ namespace EonaCat.Logger.Servers.Udp
|
||||
set
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
throw new ArgumentException("Port must be zero or greater.");
|
||||
}
|
||||
|
||||
_port = value;
|
||||
SetUdp();
|
||||
@@ -96,16 +100,22 @@ namespace EonaCat.Logger.Servers.Udp
|
||||
public async Task WriteAsync(byte[] data, bool dontFragment = false)
|
||||
{
|
||||
if (data == null || data.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _sendLock.WaitAsync();
|
||||
try
|
||||
{
|
||||
if (!IsConnected)
|
||||
{
|
||||
SetUdp();
|
||||
}
|
||||
|
||||
if (!IsConnected || _udp == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_udp.DontFragment = dontFragment;
|
||||
|
||||
@@ -120,7 +130,9 @@ namespace EonaCat.Logger.Servers.Udp
|
||||
public async Task WriteAsync(string data, bool dontFragment = false)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(data))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(data);
|
||||
await WriteAsync(bytes, dontFragment);
|
||||
|
||||
@@ -40,6 +40,17 @@
|
||||
int onLogCounter = 0;
|
||||
var defaultColor = Console.ForegroundColor;
|
||||
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
var logman = LogManager.Instance;
|
||||
var i = 0;
|
||||
while (true)
|
||||
{
|
||||
logman.WriteAsync($"Logman test {++i}", ELogType.TRACE).ConfigureAwait(false);
|
||||
Task.Delay(10).ConfigureAwait(false);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
// Add services to the container.
|
||||
Logger logger = new Logger();
|
||||
logger.UseLocalTime = true;
|
||||
@@ -209,7 +220,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
MemoryLeakTester.Start(logger);
|
||||
//MemoryLeakTester.Start(logger);
|
||||
_ = Task.Run(RunMemoryReportTask).ConfigureAwait(false);
|
||||
_ = Task.Run(RunMaskTest).ConfigureAwait(false);
|
||||
_ = Task.Run(RunWebLoggerTestsAsync).ConfigureAwait(false);
|
||||
|
||||
Reference in New Issue
Block a user