Updated
This commit is contained in:
@@ -2,40 +2,34 @@
|
||||
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.
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for adding the <see cref="FileLoggerProvider" /> to the <see cref="ILoggingBuilder" />
|
||||
/// </summary>
|
||||
public static class FileLoggerFactoryExtensions
|
||||
{
|
||||
// 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.
|
||||
private static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder)
|
||||
{
|
||||
builder.Services.AddSingleton<ILoggerProvider, FileLoggerProvider>();
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for adding the <see cref="FileLoggerProvider" /> to the <see cref="ILoggingBuilder" />
|
||||
/// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory.
|
||||
/// </summary>
|
||||
public static class FileLoggerFactoryExtensions
|
||||
/// <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)
|
||||
{
|
||||
private static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder)
|
||||
{
|
||||
builder.Services.AddSingleton<ILoggerProvider, FileLoggerProvider>();
|
||||
return builder;
|
||||
}
|
||||
if (fileLoggerOptions == null) fileLoggerOptions = new FileLoggerOptions();
|
||||
|
||||
/// <summary>
|
||||
/// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory.
|
||||
/// </summary>
|
||||
/// <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)
|
||||
{
|
||||
if (fileLoggerOptions == null)
|
||||
{
|
||||
fileLoggerOptions = new FileLoggerOptions();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(filenamePrefix))
|
||||
{
|
||||
fileLoggerOptions.FileNamePrefix = filenamePrefix;
|
||||
}
|
||||
builder.AddEonaCatFileLogger(options =>
|
||||
if (!string.IsNullOrWhiteSpace(filenamePrefix)) fileLoggerOptions.FileNamePrefix = filenamePrefix;
|
||||
builder.AddEonaCatFileLogger(options =>
|
||||
{
|
||||
options.FileNamePrefix = fileLoggerOptions.FileNamePrefix;
|
||||
options.FlushPeriod = fileLoggerOptions.FlushPeriod;
|
||||
@@ -49,26 +43,22 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Extensions
|
||||
options.MaxRolloverFiles = fileLoggerOptions.MaxRolloverFiles;
|
||||
options.UseLocalTime = fileLoggerOptions.UseLocalTime;
|
||||
}
|
||||
);
|
||||
return builder;
|
||||
}
|
||||
|
||||
);
|
||||
return builder;
|
||||
}
|
||||
/// <summary>
|
||||
/// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory.
|
||||
/// </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)
|
||||
{
|
||||
if (configure == null) throw new ArgumentNullException(nameof(configure));
|
||||
builder.AddEonaCatFileLogger();
|
||||
builder.Services.Configure(configure);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory.
|
||||
/// </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)
|
||||
{
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
builder.AddEonaCatFileLogger();
|
||||
builder.Services.Configure(configure);
|
||||
|
||||
return builder;
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -2,108 +2,93 @@
|
||||
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.
|
||||
|
||||
/// <summary>
|
||||
/// Options for file logging.
|
||||
/// </summary>
|
||||
public class FileLoggerOptions : BatchingLoggerOptions
|
||||
{
|
||||
// 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.
|
||||
private int _fileSizeLimit = 200 * 1024 * 1024;
|
||||
private int _maxRolloverFiles = 10;
|
||||
private int _retainedFileCountLimit = 50;
|
||||
|
||||
public static string DefaultPath =>
|
||||
AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
|
||||
|
||||
/// <summary>
|
||||
/// Options for file logging.
|
||||
/// Gets or sets a strictly positive value representing the maximum log size in bytes or null for no limit.
|
||||
/// Once the log is full, no more messages will be appended.
|
||||
/// Defaults to <c>200MB</c>.
|
||||
/// </summary>
|
||||
public class FileLoggerOptions : BatchingLoggerOptions
|
||||
public int FileSizeLimit
|
||||
{
|
||||
private int _fileSizeLimit = 200 * 1024 * 1024;
|
||||
private int _retainedFileCountLimit = 50;
|
||||
private int _maxRolloverFiles = 10;
|
||||
private int _maxTries = 3;
|
||||
get => _fileSizeLimit;
|
||||
|
||||
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.
|
||||
/// Once the log is full, no more messages will be appended.
|
||||
/// Defaults to <c>200MB</c>.
|
||||
/// </summary>
|
||||
public int FileSizeLimit
|
||||
set
|
||||
{
|
||||
get => _fileSizeLimit;
|
||||
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FileSizeLimit)} must be positive.");
|
||||
}
|
||||
_fileSizeLimit = value;
|
||||
}
|
||||
if (value <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FileSizeLimit)} must be positive.");
|
||||
_fileSizeLimit = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a strictly positive value representing the maximum retained file count or null for no limit.
|
||||
/// Defaults to <c>50</c>.
|
||||
/// </summary>
|
||||
public int RetainedFileCountLimit
|
||||
{
|
||||
get => _retainedFileCountLimit;
|
||||
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(RetainedFileCountLimit)} must be positive.");
|
||||
}
|
||||
_retainedFileCountLimit = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the max times to try to write to the file.
|
||||
/// Defaults 3.
|
||||
/// </summary>
|
||||
public int MaxWriteTries
|
||||
{
|
||||
get => _maxTries;
|
||||
|
||||
set
|
||||
{
|
||||
_maxTries = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if we need to use the local time in the logging or UTC (default:false)
|
||||
/// </summary>
|
||||
public bool UseLocalTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a strictly positive value representing the maximum retained file rollovers or null for no limit.
|
||||
/// Defaults to <c>10</c>.
|
||||
/// </summary>
|
||||
public int MaxRolloverFiles
|
||||
{
|
||||
get => _maxRolloverFiles;
|
||||
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(MaxRolloverFiles)} must be positive.");
|
||||
}
|
||||
_maxRolloverFiles = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the filename prefix to use for log files.
|
||||
/// Defaults to <c>EonaCat_</c>.
|
||||
/// </summary>
|
||||
public string FileNamePrefix { get; set; } = "EonaCat";
|
||||
|
||||
/// <summary>
|
||||
/// The directory in which log files will be written, relative to the app process.
|
||||
/// Default to <c>executablePath\logs</c>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string LogDirectory { get; set; } = Path.Combine(DefaultPath, "logs");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a strictly positive value representing the maximum retained file count or null for no limit.
|
||||
/// Defaults to <c>50</c>.
|
||||
/// </summary>
|
||||
public int RetainedFileCountLimit
|
||||
{
|
||||
get => _retainedFileCountLimit;
|
||||
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(value),
|
||||
$"{nameof(RetainedFileCountLimit)} must be positive.");
|
||||
_retainedFileCountLimit = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the max times to try to write to the file.
|
||||
/// Defaults 3.
|
||||
/// </summary>
|
||||
public int MaxWriteTries { get; set; } = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if we need to use the local time in the logging or UTC (default:false)
|
||||
/// </summary>
|
||||
public bool UseLocalTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a strictly positive value representing the maximum retained file rollovers or null for no limit.
|
||||
/// Defaults to <c>10</c>.
|
||||
/// </summary>
|
||||
public int MaxRolloverFiles
|
||||
{
|
||||
get => _maxRolloverFiles;
|
||||
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(MaxRolloverFiles)} must be positive.");
|
||||
_maxRolloverFiles = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the filename prefix to use for log files.
|
||||
/// Defaults to <c>EonaCat_</c>.
|
||||
/// </summary>
|
||||
public string FileNamePrefix { get; set; } = "EonaCat";
|
||||
|
||||
/// <summary>
|
||||
/// The directory in which log files will be written, relative to the app process.
|
||||
/// Default to <c>executablePath\logs</c>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string LogDirectory { get; set; } = Path.Combine(DefaultPath, "logs");
|
||||
}
|
||||
@@ -8,244 +8,207 @@ 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.
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="ILoggerProvider" /> that writes logs to a file
|
||||
/// </summary>
|
||||
[ProviderAlias("EonaCatFileLogger")]
|
||||
public class FileLoggerProvider : BatchingLoggerProvider
|
||||
{
|
||||
// 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.
|
||||
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 readonly string _path;
|
||||
private string _logFile;
|
||||
private bool _rollingOver;
|
||||
private int _rollOverCount;
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="ILoggerProvider" /> that writes logs to a file
|
||||
/// Creates an instance of the <see cref="FileLoggerProvider" />
|
||||
/// </summary>
|
||||
[ProviderAlias("EonaCatFileLogger")]
|
||||
public class FileLoggerProvider : BatchingLoggerProvider
|
||||
/// <param name="options">The options object controlling the logger</param>
|
||||
public FileLoggerProvider(IOptions<FileLoggerOptions> options) : base(options)
|
||||
{
|
||||
private readonly string _path;
|
||||
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 string _logFile;
|
||||
private bool _rollingOver;
|
||||
var loggerOptions = options.Value;
|
||||
_path = loggerOptions.LogDirectory;
|
||||
_fileNamePrefix = loggerOptions.FileNamePrefix;
|
||||
_maxFileSize = loggerOptions.FileSizeLimit;
|
||||
_maxRetainedFiles = loggerOptions.RetainedFileCountLimit;
|
||||
_maxRolloverFiles = loggerOptions.MaxRolloverFiles;
|
||||
_maxTries = loggerOptions.MaxWriteTries;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The file to which log messages should be appended.
|
||||
/// </summary>
|
||||
public string LogFile
|
||||
/// <summary>
|
||||
/// The file to which log messages should be appended.
|
||||
/// </summary>
|
||||
public string LogFile
|
||||
{
|
||||
get => _logFile;
|
||||
set
|
||||
{
|
||||
get
|
||||
_logFile = value;
|
||||
|
||||
if (!string.IsNullOrEmpty(_logFile))
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of the <see cref="FileLoggerProvider" />
|
||||
/// </summary>
|
||||
/// <param name="options">The options object controlling the logger</param>
|
||||
public FileLoggerProvider(IOptions<FileLoggerOptions> options) : base(options)
|
||||
{
|
||||
FileLoggerOptions loggerOptions = options.Value;
|
||||
_path = loggerOptions.LogDirectory;
|
||||
_fileNamePrefix = loggerOptions.FileNamePrefix;
|
||||
_maxFileSize = loggerOptions.FileSizeLimit;
|
||||
_maxRetainedFiles = loggerOptions.RetainedFileCountLimit;
|
||||
_maxRolloverFiles = loggerOptions.MaxRolloverFiles;
|
||||
_maxTries = loggerOptions.MaxWriteTries;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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))
|
||||
{
|
||||
LogFile = GetFullName(group.Key);
|
||||
FileInfo 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);
|
||||
}
|
||||
fileInfo.CopyTo(rollOverFile);
|
||||
File.WriteAllText(LogFile, string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (RollOverLock)
|
||||
{
|
||||
_rollingOver = true;
|
||||
MoveRolloverLogFiles();
|
||||
_rollingOver = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (_rollingOver)
|
||||
{
|
||||
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
lock (WriteLock)
|
||||
{
|
||||
int tries = 0;
|
||||
bool completed = false;
|
||||
|
||||
while (!completed)
|
||||
{
|
||||
try
|
||||
{
|
||||
System.IO.StreamWriter file = new System.IO.StreamWriter(LogFile, true);
|
||||
foreach (LogMessage item in group)
|
||||
{
|
||||
file.Write(item.Message);
|
||||
}
|
||||
file.Close();
|
||||
completed = true;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
tries++;
|
||||
Task.Delay(100);
|
||||
if (tries >= _maxTries)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DeleteOldLogFiles();
|
||||
}
|
||||
}
|
||||
|
||||
private string GetFullName((int Year, int Month, int Day) group)
|
||||
{
|
||||
bool 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)
|
||||
{
|
||||
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>
|
||||
/// Deletes old log files, keeping a number of files defined by <see cref="FileLoggerOptions.RetainedFileCountLimit" />
|
||||
/// </summary>
|
||||
protected void DeleteOldLogFiles()
|
||||
{
|
||||
if (_maxRetainedFiles > 0)
|
||||
{
|
||||
bool 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();
|
||||
}
|
||||
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)
|
||||
{
|
||||
Directory.CreateDirectory(_path);
|
||||
|
||||
foreach (var group in messages.GroupBy(GetGrouping))
|
||||
{
|
||||
LogFile = GetFullName(group.Key);
|
||||
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);
|
||||
fileInfo.CopyTo(rollOverFile);
|
||||
File.WriteAllText(LogFile, string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (RollOverLock)
|
||||
{
|
||||
_rollingOver = true;
|
||||
MoveRolloverLogFiles();
|
||||
_rollingOver = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (_rollingOver) await Task.Delay(100, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
lock (WriteLock)
|
||||
{
|
||||
var tries = 0;
|
||||
var completed = false;
|
||||
|
||||
while (!completed)
|
||||
try
|
||||
{
|
||||
var file = new StreamWriter(LogFile, true);
|
||||
foreach (var item in group) file.Write(item.Message);
|
||||
file.Close();
|
||||
completed = true;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
tries++;
|
||||
Task.Delay(100);
|
||||
if (tries >= _maxTries)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
DeleteOldLogFiles();
|
||||
}
|
||||
}
|
||||
|
||||
private string GetFullName((int Year, int Month, int Day) group)
|
||||
{
|
||||
var hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix);
|
||||
if (hasPrefix)
|
||||
return Path.Combine(_path, $"{_fileNamePrefix}_{group.Year:0000}{group.Month:00}{group.Day:00}.log");
|
||||
return Path.Combine(_path, $"{group.Year:0000}{group.Month:00}{group.Day:00}.log");
|
||||
}
|
||||
|
||||
private (int Year, int Month, int Day) GetGrouping(LogMessage message)
|
||||
{
|
||||
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;
|
||||
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 (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}");
|
||||
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>
|
||||
/// Deletes old log files, keeping a number of files defined by <see cref="FileLoggerOptions.RetainedFileCountLimit" />
|
||||
/// </summary>
|
||||
protected void DeleteOldLogFiles()
|
||||
{
|
||||
if (_maxRetainedFiles > 0)
|
||||
{
|
||||
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 (var item in files) item.Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,68 +4,67 @@ 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
|
||||
{
|
||||
// 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.
|
||||
private readonly string _category;
|
||||
private readonly BatchingLoggerProvider _provider;
|
||||
private LoggerSettings _loggerSettings;
|
||||
|
||||
public class BatchingLogger : ILogger
|
||||
public BatchingLogger(BatchingLoggerProvider loggerProvider, string categoryName, LoggerSettings loggerSettings)
|
||||
{
|
||||
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;
|
||||
_loggerSettings = loggerSettings;
|
||||
_provider = loggerProvider;
|
||||
_category = categoryName;
|
||||
}
|
||||
|
||||
public BatchingLogger(BatchingLoggerProvider loggerProvider, string categoryName, LoggerSettings loggerSettings)
|
||||
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;
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return logLevel != LogLevel.None;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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);
|
||||
|
||||
var currentMessage = new EonaCatLogMessage
|
||||
{
|
||||
_loggerSettings = loggerSettings;
|
||||
_provider = loggerProvider;
|
||||
_category = categoryName;
|
||||
}
|
||||
DateTime = timestamp.DateTime,
|
||||
Message = message,
|
||||
LogType = logLevel.FromLogLevel()
|
||||
};
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return logLevel != LogLevel.None;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
var currentMessage = new EonaCatLogMessage
|
||||
{
|
||||
DateTime = timestamp.DateTime,
|
||||
Message = message,
|
||||
LogType = logLevel.FromLogLevel()
|
||||
};
|
||||
|
||||
if (_loggerSettings == null) return;
|
||||
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);
|
||||
}
|
||||
currentMessage.Origin = string.IsNullOrWhiteSpace(_loggerSettings.LogOrigin)
|
||||
? "BatchingLogger"
|
||||
: _loggerSettings.LogOrigin;
|
||||
_loggerSettings?.OnLogEvent(currentMessage);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,35 @@
|
||||
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
|
||||
{
|
||||
// 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.
|
||||
private TimeSpan _flushPeriod = TimeSpan.FromMilliseconds(100);
|
||||
|
||||
public class BatchingLoggerOptions
|
||||
/// <summary>
|
||||
/// Gets or sets the period after which logs will be flushed to the store.
|
||||
/// </summary>
|
||||
public TimeSpan FlushPeriod
|
||||
{
|
||||
private int _batchSize = 0;
|
||||
private TimeSpan _flushPeriod = TimeSpan.FromMilliseconds(100);
|
||||
get => _flushPeriod;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the period after which logs will be flushed to the store.
|
||||
/// </summary>
|
||||
public TimeSpan FlushPeriod
|
||||
set
|
||||
{
|
||||
get => _flushPeriod;
|
||||
|
||||
set
|
||||
{
|
||||
if (value <= TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FlushPeriod)} must be positive.");
|
||||
}
|
||||
_flushPeriod = value;
|
||||
}
|
||||
if (value <= TimeSpan.Zero)
|
||||
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FlushPeriod)} must be positive.");
|
||||
_flushPeriod = value;
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets value indicating if logger accepts and queues writes.
|
||||
/// </summary>
|
||||
public bool IsEnabled { get; set; }
|
||||
}
|
||||
|
||||
/// <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; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets value indicating if logger accepts and queues writes.
|
||||
/// </summary>
|
||||
public bool IsEnabled { get; set; }
|
||||
}
|
||||
@@ -7,161 +7,151 @@ 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
|
||||
{
|
||||
// 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.
|
||||
private readonly int _batchSize;
|
||||
|
||||
public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
|
||||
private readonly List<LogMessage> _currentBatch = new();
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private LoggerSettings _loggerSettings;
|
||||
|
||||
private ConcurrentQueue<LogMessage> _messageQueue;
|
||||
private Task _outputTask;
|
||||
|
||||
protected BatchingLoggerProvider(IOptions<BatchingLoggerOptions> options)
|
||||
{
|
||||
protected DateTimeOffset CurrentDateTimeOffset => UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
|
||||
protected DateTime CurrentDateTme => UseLocalTime ? DateTime.Now : DateTime.UtcNow;
|
||||
var loggerOptions = options.Value;
|
||||
|
||||
private readonly List<LogMessage> _currentBatch = new List<LogMessage>();
|
||||
private readonly TimeSpan _interval;
|
||||
private readonly int _batchSize;
|
||||
if (loggerOptions.FlushPeriod <= TimeSpan.Zero)
|
||||
throw new ArgumentOutOfRangeException(nameof(loggerOptions.FlushPeriod),
|
||||
$"{nameof(loggerOptions.FlushPeriod)} must be longer than zero.");
|
||||
|
||||
private ConcurrentQueue<LogMessage> _messageQueue;
|
||||
private Task _outputTask;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private LoggerSettings _loggerSettings;
|
||||
if (options.Value is FileLoggerOptions fileLoggerOptions) UseLocalTime = fileLoggerOptions.UseLocalTime;
|
||||
_batchSize = loggerOptions.BatchSize;
|
||||
|
||||
protected bool UseLocalTime { get; set; }
|
||||
Start();
|
||||
}
|
||||
|
||||
protected BatchingLoggerProvider(IOptions<BatchingLoggerOptions> options)
|
||||
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
|
||||
{
|
||||
BatchingLoggerOptions loggerOptions = options.Value;
|
||||
if (_loggerSettings != null) return _loggerSettings;
|
||||
|
||||
if (loggerOptions.FlushPeriod <= TimeSpan.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;
|
||||
_batchSize = loggerOptions.BatchSize;
|
||||
|
||||
Start();
|
||||
_loggerSettings = new LoggerSettings();
|
||||
_loggerSettings.UseLocalTime = UseLocalTime;
|
||||
return _loggerSettings;
|
||||
}
|
||||
|
||||
protected LoggerSettings LoggerSettings
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_loggerSettings != null) return _loggerSettings;
|
||||
set => _loggerSettings = value;
|
||||
}
|
||||
|
||||
_loggerSettings = new LoggerSettings();
|
||||
_loggerSettings.UseLocalTime = UseLocalTime;
|
||||
return _loggerSettings;
|
||||
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);
|
||||
AddMessage(DateTimeOffset.Now, startupMessage);
|
||||
|
||||
while (!_cancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
var limit = _batchSize <= 0 ? int.MaxValue : _batchSize;
|
||||
while (limit > 0 && _messageQueue.TryDequeue(out var message))
|
||||
{
|
||||
_currentBatch.Add(message);
|
||||
limit--;
|
||||
}
|
||||
|
||||
set => _loggerSettings = value;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
AddMessage(DateTimeOffset.Now, startupMessage);
|
||||
|
||||
while (!_cancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
int limit = _batchSize <= 0 ? int.MaxValue : _batchSize;
|
||||
|
||||
while (limit > 0 && _messageQueue.TryDequeue(out LogMessage message))
|
||||
if (_currentBatch.Count > 0)
|
||||
try
|
||||
{
|
||||
_currentBatch.Add(message);
|
||||
limit--;
|
||||
await WriteMessagesAsync(_currentBatch, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
_currentBatch.Clear();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (_currentBatch.Count > 0)
|
||||
{
|
||||
bool isBatchWritten = false;
|
||||
try
|
||||
{
|
||||
await WriteMessagesAsync(_currentBatch, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
isBatchWritten = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (isBatchWritten)
|
||||
{
|
||||
_currentBatch.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
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 });
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_messageQueue = new ConcurrentQueue<LogMessage>();
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
_outputTask = Task.Factory.StartNew(
|
||||
ProcessLogQueueAsync,
|
||||
null,
|
||||
TaskCreationOptions.LongRunning);
|
||||
}
|
||||
|
||||
private async Task StopAsync()
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
|
||||
try
|
||||
{
|
||||
await _outputTask.ConfigureAwait(false);
|
||||
}
|
||||
catch (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);
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddMessage(DateTimeOffset timestamp, string message)
|
||||
{
|
||||
_messageQueue.Enqueue(new LogMessage { Message = message, Timestamp = timestamp });
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
IsStarted = true;
|
||||
_messageQueue = new ConcurrentQueue<LogMessage>();
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
_outputTask = Task.Factory.StartNew(
|
||||
ProcessLogQueueAsync,
|
||||
null,
|
||||
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()
|
||||
{
|
||||
_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)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
~BatchingLoggerProvider()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
|
||||
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.
|
||||
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 struct LogMessage
|
||||
{
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
public struct LogMessage
|
||||
{
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
@@ -1,21 +1,20 @@
|
||||
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.
|
||||
|
||||
public class EonaCatLogMessage
|
||||
{
|
||||
// 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 DateTime DateTime { get; set; }
|
||||
public string Message { get; set; }
|
||||
public ELogType LogType { get; set; }
|
||||
public string Origin { get; set; }
|
||||
|
||||
public class EonaCatLogMessage
|
||||
public override string ToString()
|
||||
{
|
||||
public DateTime DateTime { get; set; }
|
||||
public string Message { get; set; }
|
||||
public ELogType LogType { get; set; }
|
||||
public string Origin { get; set; }
|
||||
|
||||
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}";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user