This commit is contained in:
2022-12-15 21:32:33 +01:00
parent f8659ad197
commit 5d3c85ffd3
10 changed files with 170 additions and 109 deletions

View File

@@ -8,7 +8,7 @@
net6.0; net6.0;
</TargetFrameworks> </TargetFrameworks>
<ApplicationIcon>icon.ico</ApplicationIcon> <ApplicationIcon>icon.ico</ApplicationIcon>
<Version>1.0.1</Version> <Version>1.0.2</Version>
<Authors>EonaCat (Jeroen Saey)</Authors> <Authors>EonaCat (Jeroen Saey)</Authors>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Company>EonaCat (Jeroen Saey)</Company> <Company>EonaCat (Jeroen Saey)</Company>

View File

@@ -1,5 +1,6 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System; using System;
namespace EonaCat.Logger.Extensions namespace EonaCat.Logger.Extensions
@@ -12,11 +13,7 @@ namespace EonaCat.Logger.Extensions
/// </summary> /// </summary>
public static class FileLoggerFactoryExtensions public static class FileLoggerFactoryExtensions
{ {
/// <summary> private static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder)
/// Adds a file logger named 'File' to the factory.
/// </summary>
/// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param>
public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder)
{ {
builder.Services.AddSingleton<ILoggerProvider, FileLoggerProvider>(); builder.Services.AddSingleton<ILoggerProvider, FileLoggerProvider>();
return builder; return builder;
@@ -26,15 +23,39 @@ namespace EonaCat.Logger.Extensions
/// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory. /// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory.
/// </summary> /// </summary>
/// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param> /// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param>
/// <param name="filenamePrefix">Sets the filename prefix to use for log files</param> /// <param name="filenamePrefix">Sets the filename prefix to use for log files (optional)</param>
public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder, string filenamePrefix) /// <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)
{ {
builder.AddEonaCatFileLogger(options => options.FileNamePrefix = filenamePrefix); if (fileLoggerOptions == null)
{
fileLoggerOptions = new FileLoggerOptions();
}
if (!string.IsNullOrWhiteSpace(filenamePrefix))
{
fileLoggerOptions.FileNamePrefix = filenamePrefix;
}
builder.AddEonaCatFileLogger(options =>
{
options.FileNamePrefix = fileLoggerOptions.FileNamePrefix;
options.FlushPeriod = fileLoggerOptions.FlushPeriod;
options.RetainedFileCountLimit = fileLoggerOptions.RetainedFileCountLimit;
options.MaxWriteTries = fileLoggerOptions.MaxWriteTries;
options.FileSizeLimit = fileLoggerOptions.FileSizeLimit;
options.LogDirectory = fileLoggerOptions.LogDirectory;
options.BatchSize = fileLoggerOptions.BatchSize;
options.FileSizeLimit = fileLoggerOptions.FileSizeLimit;
options.IsEnabled = fileLoggerOptions.IsEnabled;
options.MaxRolloverFiles = fileLoggerOptions.MaxRolloverFiles;
}
);
return builder; return builder;
} }
/// <summary> /// <summary>
/// Adds a file logger named 'File' to the factory. /// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory.
/// </summary> /// </summary>
/// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param> /// <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> /// <param name="configure">Configure an instance of the <see cref="FileLoggerOptions" /> to set logging options</param>

View File

@@ -17,7 +17,7 @@ namespace EonaCat.Logger
/// <summary> /// <summary>
/// An <see cref="ILoggerProvider" /> that writes logs to a file /// An <see cref="ILoggerProvider" /> that writes logs to a file
/// </summary> /// </summary>
[ProviderAlias("File")] [ProviderAlias("EonaCatFileLogger")]
public class FileLoggerProvider : BatchingLoggerProvider public class FileLoggerProvider : BatchingLoggerProvider
{ {
private readonly string _path; private readonly string _path;
@@ -28,7 +28,9 @@ namespace EonaCat.Logger
private readonly int _maxTries; private readonly int _maxTries;
private int _rollOverCount = 0; private int _rollOverCount = 0;
private static readonly object _writeLock = new object(); private static readonly object _writeLock = new object();
private static readonly object _rollOverLock = new object();
private string _logFile; private string _logFile;
private bool rollingOver;
/// <summary> /// <summary>
/// The file to which log messages should be appended. /// The file to which log messages should be appended.
@@ -87,27 +89,31 @@ namespace EonaCat.Logger
{ {
if (_rollOverCount < _maxRolloverFiles) if (_rollOverCount < _maxRolloverFiles)
{ {
fileInfo.CopyTo(LogFile.Replace(".log", $"_{++_rollOverCount}.log")); var rollOverFile = LogFile.Replace(".log", $"_{++_rollOverCount}.log");
if (File.Exists(rollOverFile))
{
File.Delete(rollOverFile);
}
fileInfo.CopyTo(rollOverFile);
File.WriteAllText(LogFile, string.Empty); File.WriteAllText(LogFile, string.Empty);
} }
else else
{ {
bool areFilesDeleted = false; lock (_rollOverLock)
for (int i = 0; i < _rollOverCount; i++)
{ {
File.Delete(LogFile.Replace(".log", $"_{i}.log")); rollingOver = true;
areFilesDeleted = true; MoveRolloverLogFiles();
} rollingOver = false;
if (areFilesDeleted)
{
File.Move(LogFile.Replace(".log", $"_{_rollOverCount}.log"), LogFile.Replace(".log", $"_1.log"));
_rollOverCount = 0;
} }
} }
} }
} }
while (rollingOver)
{
await Task.Delay(100);
}
lock (_writeLock) lock (_writeLock)
{ {
int tries = 0; int tries = 0;
@@ -135,9 +141,9 @@ namespace EonaCat.Logger
} }
} }
} }
}
DeleteOldLogFiles(); DeleteOldLogFiles();
}
} }
private string GetFullName((int Year, int Month, int Day) group) private string GetFullName((int Year, int Month, int Day) group)
@@ -158,6 +164,64 @@ namespace EonaCat.Logger
return (message.Timestamp.Year, message.Timestamp.Month, message.Timestamp.Day); return (message.Timestamp.Year, message.Timestamp.Month, message.Timestamp.Day);
} }
private static void MoveFile(string copyFromPath, string copyToPath)
{
var origin = new FileInfo(copyFromPath);
origin.MoveTo(copyToPath);
var destination = new FileInfo(copyToPath);
destination.CreationTime = origin.CreationTime;
destination.LastWriteTime = origin.LastWriteTime;
destination.LastAccessTime = origin.LastAccessTime;
}
/// <summary>
/// Rollover logFiles
/// </summary>
protected void MoveRolloverLogFiles()
{
if (_maxRolloverFiles > 0 && _rollOverCount >= 0)
{
if (_rollOverCount >= _maxRolloverFiles)
{
var maxRollover = _rollOverCount;
bool 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--)
{
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}");
continue;
}
if (i == files.Count() - 1)
{
// Delete the last file
File.Delete(currentFile.FullName);
continue;
}
var newFilename = Path.GetFileName(currentFile.FullName).Replace($"_{i}.log", $"_{i + 1}.log");
MoveFile(currentFile.FullName, $@"{Path.GetDirectoryName(currentFile.FullName)}\{newFilename}");
}
_rollOverCount = 0;
}
}
}
/// <summary> /// <summary>
/// Deletes old log files, keeping a number of files defined by <see cref="FileLoggerOptions.RetainedFileCountLimit" /> /// Deletes old log files, keeping a number of files defined by <see cref="FileLoggerOptions.RetainedFileCountLimit" />
/// </summary> /// </summary>

View File

@@ -1,4 +1,5 @@
using EonaCat.logger.Managers; using EonaCat.logger.Managers;
using EonaCat.Logger.Managers;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
using System.Text; using System.Text;
@@ -10,11 +11,13 @@ namespace EonaCat.Logger.Internal
public class BatchingLogger : ILogger public class BatchingLogger : ILogger
{ {
private LoggerSettings _loggerSettings;
private readonly BatchingLoggerProvider _provider; private readonly BatchingLoggerProvider _provider;
private readonly string _category; private readonly string _category;
public BatchingLogger(BatchingLoggerProvider loggerProvider, string categoryName) public BatchingLogger(BatchingLoggerProvider loggerProvider, string categoryName, LoggerSettings loggerSettings = null)
{ {
_loggerSettings = loggerSettings;
_provider = loggerProvider; _provider = loggerProvider;
_category = categoryName; _category = categoryName;
} }
@@ -33,8 +36,12 @@ namespace EonaCat.Logger.Internal
{ {
if (!IsEnabled(logLevel)) return; if (!IsEnabled(logLevel)) return;
var message = LogHelper.FormatMessageWithHeader(new Managers.LoggerSettings(), logLevel.FromLogLevel(), formatter(state, exception)) + Environment.NewLine; if (_loggerSettings == null)
{
_loggerSettings = new LoggerSettings();
}
var message = LogHelper.FormatMessageWithHeader(_loggerSettings, logLevel.FromLogLevel(), formatter(state, exception)) + Environment.NewLine;
if (exception != null) if (exception != null)
{ {
message = exception.FormatExceptionToMessage() + Environment.NewLine; message = exception.FormatExceptionToMessage() + Environment.NewLine;

View File

@@ -7,9 +7,8 @@ namespace EonaCat.Logger.Internal
public class BatchingLoggerOptions public class BatchingLoggerOptions
{ {
private int _batchSize = 32; private int _batchSize = 0;
private int _backgroundQueueSize; private TimeSpan _flushPeriod = TimeSpan.FromMilliseconds(100);
private TimeSpan _flushPeriod = TimeSpan.FromSeconds(1);
/// <summary> /// <summary>
/// Gets or sets the period after which logs will be flushed to the store. /// Gets or sets the period after which logs will be flushed to the store.
@@ -28,21 +27,6 @@ namespace EonaCat.Logger.Internal
} }
} }
/// <summary>
/// Gets or sets the maximum size of the background log message queue or less than 1 for no limit.
/// After maximum queue size is reached log event sink would start blocking.
/// Defaults to <c>0</c>.
/// </summary>
public int BackgroundQueueSize
{
get => _backgroundQueueSize;
set
{
_backgroundQueueSize = value;
}
}
/// <summary> /// <summary>
/// Gets or sets a maximum number of events to include in a single batch or less than 1 for no limit. /// Gets or sets a maximum number of events to include in a single batch or less than 1 for no limit.
/// </summary> /// </summary>

View File

@@ -1,4 +1,5 @@
using Microsoft.Extensions.Logging; using EonaCat.Logger.Helpers;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@@ -15,10 +16,9 @@ namespace EonaCat.Logger.Internal
{ {
private readonly List<LogMessage> _currentBatch = new List<LogMessage>(); private readonly List<LogMessage> _currentBatch = new List<LogMessage>();
private readonly TimeSpan _interval; private readonly TimeSpan _interval;
private readonly int _queueSize;
private readonly int _batchSize; private readonly int _batchSize;
private BlockingCollection<LogMessage> _messageQueue; private ConcurrentQueue<LogMessage> _messageQueue;
private Task _outputTask; private Task _outputTask;
private CancellationTokenSource _cancellationTokenSource; private CancellationTokenSource _cancellationTokenSource;
@@ -26,11 +26,6 @@ namespace EonaCat.Logger.Internal
{ {
BatchingLoggerOptions loggerOptions = options.Value; BatchingLoggerOptions loggerOptions = options.Value;
if (loggerOptions.BatchSize <= 0)
{
throw new ArgumentOutOfRangeException(nameof(loggerOptions.BatchSize), $"{nameof(loggerOptions.BatchSize)} must be a positive number.");
}
if (loggerOptions.FlushPeriod <= TimeSpan.Zero) 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.");
@@ -38,7 +33,6 @@ namespace EonaCat.Logger.Internal
_interval = loggerOptions.FlushPeriod; _interval = loggerOptions.FlushPeriod;
_batchSize = loggerOptions.BatchSize; _batchSize = loggerOptions.BatchSize;
_queueSize = loggerOptions.BackgroundQueueSize;
Start(); Start();
} }
@@ -47,11 +41,12 @@ namespace EonaCat.Logger.Internal
private async Task ProcessLogQueue(object state) private async Task ProcessLogQueue(object state)
{ {
await WriteMessagesAsync(new List<LogMessage> { new LogMessage { Message = $"[{DllInfo.ApplicationName}] {DllInfo.ApplicationName} started.{Environment.NewLine}", Timestamp = DateTimeOffset.Now } }, _cancellationTokenSource.Token);
while (!_cancellationTokenSource.IsCancellationRequested) while (!_cancellationTokenSource.IsCancellationRequested)
{ {
int limit = _batchSize <= 0 ? int.MaxValue : _batchSize; int limit = _batchSize <= 0 ? int.MaxValue : _batchSize;
while (limit > 0 && _messageQueue.TryTake(out LogMessage message)) while (limit > 0 && _messageQueue.TryDequeue(out LogMessage message))
{ {
_currentBatch.Add(message); _currentBatch.Add(message);
limit--; limit--;
@@ -73,6 +68,7 @@ namespace EonaCat.Logger.Internal
await IntervalAsync(_interval, _cancellationTokenSource.Token); await IntervalAsync(_interval, _cancellationTokenSource.Token);
} }
await WriteMessagesAsync(new List<LogMessage> { new LogMessage { Message = $"[{DllInfo.ApplicationName}] {DllInfo.ApplicationName} stopped.{Environment.NewLine}", Timestamp = DateTimeOffset.Now } }, _cancellationTokenSource.Token);
} }
protected virtual Task IntervalAsync(TimeSpan interval, CancellationToken cancellationToken) protected virtual Task IntervalAsync(TimeSpan interval, CancellationToken cancellationToken)
@@ -82,24 +78,12 @@ namespace EonaCat.Logger.Internal
internal void AddMessage(DateTimeOffset timestamp, string message) internal void AddMessage(DateTimeOffset timestamp, string message)
{ {
if (!_messageQueue.IsAddingCompleted) _messageQueue.Enqueue(new LogMessage { Message = message, Timestamp = timestamp });
{
try
{
_messageQueue.Add(new LogMessage { Message = message, Timestamp = timestamp }, _cancellationTokenSource.Token);
}
catch
{
// cancellation token canceled or CompleteAdding called
}
}
} }
private void Start() private void Start()
{ {
_messageQueue = _queueSize == 0 ? _messageQueue = new ConcurrentQueue<LogMessage>();
new BlockingCollection<LogMessage>(new ConcurrentQueue<LogMessage>()) :
new BlockingCollection<LogMessage>(new ConcurrentQueue<LogMessage>(), _queueSize);
_cancellationTokenSource = new CancellationTokenSource(); _cancellationTokenSource = new CancellationTokenSource();
_outputTask = Task.Factory.StartNew( _outputTask = Task.Factory.StartNew(
@@ -111,7 +95,6 @@ namespace EonaCat.Logger.Internal
private void Stop() private void Stop()
{ {
_cancellationTokenSource.Cancel(); _cancellationTokenSource.Cancel();
_messageQueue.CompleteAdding();
try try
{ {

View File

@@ -4,6 +4,7 @@ using EonaCat.Logger.Syslog;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -207,9 +208,34 @@ namespace EonaCat.logger.Managers
{ {
try try
{ {
System.IO.StreamWriter file = new System.IO.StreamWriter($"{settings.FileLoggerOptions.LogDirectory}{Path.DirectorySeparatorChar}{settings.FileLoggerOptions.FileNamePrefix}_{DateTime.Now.ToString("yyyyMMdd")}.log", true); if (logType == ELogType.CRITICAL)
file.Write(message + Environment.NewLine); {
file.Close(); logger.LogCritical(message);
}
else if (logType == ELogType.DEBUG)
{
logger.LogDebug(message);
}
else if (logType == ELogType.ERROR)
{
logger.LogError(message);
}
else if (logType == ELogType.INFO)
{
logger.LogInformation(message);
}
else if (logType == ELogType.TRACE)
{
logger.LogTrace(message);
}
else if (logType == ELogType.TRAFFIC)
{
logger.LogTrace($"[TRAFFIC] {message}");
}
else if (logType == ELogType.WARNING)
{
logger.LogWarning(message);
}
completed = true; completed = true;
} }
catch (Exception exc) catch (Exception exc)
@@ -220,36 +246,6 @@ namespace EonaCat.logger.Managers
throw; throw;
} }
} }
return;
if (logType == ELogType.CRITICAL)
{
logger.LogCritical(message);
}
else if (logType == ELogType.DEBUG)
{
logger.LogDebug(message);
}
else if (logType == ELogType.ERROR)
{
logger.LogError(message);
}
else if (logType == ELogType.INFO)
{
logger.LogInformation(message);
}
else if (logType == ELogType.TRACE)
{
logger.LogTrace(message);
}
else if (logType == ELogType.TRAFFIC)
{
logger.LogTrace($"[TRAFFIC] {message}");
}
else if (logType == ELogType.WARNING)
{
logger.LogWarning(message);
}
} }
} }
@@ -257,10 +253,13 @@ namespace EonaCat.logger.Managers
{ {
if (settings == null) return; if (settings == null) return;
if (!settings.SendToSyslogServers) return; if (!settings.SendToSyslogServers) return;
if (settings.SysLogServers == null) return;
if (!settings.SysLogServers.Any()) return;
if (string.IsNullOrWhiteSpace(message)) return; if (string.IsNullOrWhiteSpace(message)) return;
if (settings.SysLogServers == null || !settings.SysLogServers.Any())
{
settings.SysLogServers = new List<SyslogServer> { new SyslogServer("127.0.0.1", 514) };
}
byte[] data = Encoding.UTF8.GetBytes(message); byte[] data = Encoding.UTF8.GetBytes(message);
var sysLogServers = settings.SysLogServers.ToList(); var sysLogServers = settings.SysLogServers.ToList();

View File

@@ -189,8 +189,10 @@ namespace EonaCat.Logger.Managers
if (string.IsNullOrEmpty(serverIp)) throw new ArgumentNullException(nameof(serverIp)); if (string.IsNullOrEmpty(serverIp)) throw new ArgumentNullException(nameof(serverIp));
if (serverPort < 0) throw new ArgumentException("Server port must be zero or greater."); if (serverPort < 0) throw new ArgumentException("Server port must be zero or greater.");
settings.SysLogServers = new List<SyslogServer>(); settings.SysLogServers = new List<SyslogServer>
settings.SysLogServers.Add(new SyslogServer(serverIp, serverPort)); {
new SyslogServer(serverIp, serverPort)
};
Id = id; Id = id;
Settings = settings; Settings = settings;

View File

@@ -98,7 +98,7 @@ namespace EonaCat.Logger.Managers
public bool SendToSyslogServers { get; set; } public bool SendToSyslogServers { get; set; }
public List<SyslogServer> SysLogServers { get; set; } = new List<SyslogServer> { new SyslogServer("127.0.0.1", 514) }; public List<SyslogServer> SysLogServers { get; set; }
/// <summary> /// <summary>
/// Determines if the fileLogging is enabled /// Determines if the fileLogging is enabled

View File

@@ -19,6 +19,7 @@ namespace EonaCat.Logger.Syslog
{ {
return _Hostname; return _Hostname;
} }
set set
{ {
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname)); if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname));