Added LocalTime or UTC time display

This commit is contained in:
2023-07-13 10:40:50 +02:00
parent 9dea272b5e
commit bfb2b8a999
10 changed files with 105 additions and 118 deletions

View File

@@ -7,7 +7,7 @@
net7.0; net7.0;
</TargetFrameworks> </TargetFrameworks>
<ApplicationIcon>icon.ico</ApplicationIcon> <ApplicationIcon>icon.ico</ApplicationIcon>
<Version>1.1.5</Version> <Version>1.1.6</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>
@@ -43,6 +43,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
</ItemGroup> </ItemGroup>

View File

@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp110</s:String></wpf:ResourceDictionary>

View File

@@ -47,6 +47,7 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Extensions
options.FileSizeLimit = fileLoggerOptions.FileSizeLimit; options.FileSizeLimit = fileLoggerOptions.FileSizeLimit;
options.IsEnabled = fileLoggerOptions.IsEnabled; options.IsEnabled = fileLoggerOptions.IsEnabled;
options.MaxRolloverFiles = fileLoggerOptions.MaxRolloverFiles; options.MaxRolloverFiles = fileLoggerOptions.MaxRolloverFiles;
options.UseLocalTime = fileLoggerOptions.UseLocalTime;
} }
); );

View File

@@ -70,6 +70,11 @@ namespace EonaCat.Logger.EonaCatCoreLogger
} }
} }
/// <summary>
/// Determines if we need to use the local time in the logging or UTC (default:false)
/// </summary>
public bool UseLocalTime { get; set; }
/// <summary> /// <summary>
/// Gets or sets a strictly positive value representing the maximum retained file rollovers or null for no limit. /// Gets or sets a strictly positive value representing the maximum retained file rollovers or null for no limit.
/// Defaults to <c>10</c>. /// Defaults to <c>10</c>.

View File

@@ -14,8 +14,10 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
private LoggerSettings _loggerSettings; private LoggerSettings _loggerSettings;
private readonly BatchingLoggerProvider _provider; private readonly BatchingLoggerProvider _provider;
private readonly string _category; private readonly string _category;
private DateTimeOffset CurrentDateTimeOffset => _loggerSettings.UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
private DateTime CurrentDateTme => _loggerSettings.UseLocalTime ? DateTime.Now : DateTime.UtcNow;
public BatchingLogger(BatchingLoggerProvider loggerProvider, string categoryName, LoggerSettings loggerSettings = null) public BatchingLogger(BatchingLoggerProvider loggerProvider, string categoryName, LoggerSettings loggerSettings)
{ {
_loggerSettings = loggerSettings; _loggerSettings = loggerSettings;
_provider = loggerProvider; _provider = loggerProvider;
@@ -41,7 +43,7 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
_loggerSettings = new LoggerSettings(); _loggerSettings = new LoggerSettings();
} }
var message = LogHelper.FormatMessageWithHeader(_loggerSettings, logLevel.FromLogLevel(), formatter(state, exception), DateTime.Now) + Environment.NewLine; var message = LogHelper.FormatMessageWithHeader(_loggerSettings, logLevel.FromLogLevel(), formatter(state, exception), timestamp.DateTime) + Environment.NewLine;
if (exception != null) if (exception != null)
{ {
message = exception.FormatExceptionToMessage() + Environment.NewLine; message = exception.FormatExceptionToMessage() + Environment.NewLine;
@@ -63,7 +65,7 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{ {
Log(DateTimeOffset.Now, logLevel, eventId, state, exception, formatter); Log(CurrentDateTimeOffset, logLevel, eventId, state, exception, formatter);
} }
} }
} }

View File

@@ -14,6 +14,9 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
public abstract class BatchingLoggerProvider : ILoggerProvider public abstract class BatchingLoggerProvider : ILoggerProvider
{ {
protected DateTimeOffset CurrentDateTimeOffset => UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
protected DateTime CurrentDateTme => UseLocalTime ? DateTime.Now : DateTime.UtcNow;
private readonly List<LogMessage> _currentBatch = new List<LogMessage>(); private readonly List<LogMessage> _currentBatch = new List<LogMessage>();
private readonly TimeSpan _interval; private readonly TimeSpan _interval;
private readonly int _batchSize; private readonly int _batchSize;
@@ -21,6 +24,9 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
private ConcurrentQueue<LogMessage> _messageQueue; private ConcurrentQueue<LogMessage> _messageQueue;
private Task _outputTask; private Task _outputTask;
private CancellationTokenSource _cancellationTokenSource; private CancellationTokenSource _cancellationTokenSource;
private LoggerSettings _loggerSettings;
protected bool UseLocalTime { get; set; }
protected BatchingLoggerProvider(IOptions<BatchingLoggerOptions> options) protected BatchingLoggerProvider(IOptions<BatchingLoggerOptions> options)
{ {
@@ -31,18 +37,37 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
throw new ArgumentOutOfRangeException(nameof(loggerOptions.FlushPeriod), $"{nameof(loggerOptions.FlushPeriod)} must be longer than zero."); throw new ArgumentOutOfRangeException(nameof(loggerOptions.FlushPeriod), $"{nameof(loggerOptions.FlushPeriod)} must be longer than zero.");
} }
if (options is FileLoggerOptions fileLoggerOptions)
{
UseLocalTime = fileLoggerOptions.UseLocalTime;
}
_interval = loggerOptions.FlushPeriod; _interval = loggerOptions.FlushPeriod;
_batchSize = loggerOptions.BatchSize; _batchSize = loggerOptions.BatchSize;
Start(); Start();
} }
protected LoggerSettings LoggerSettings
{
get
{
if (_loggerSettings != null) return _loggerSettings;
_loggerSettings = new LoggerSettings();
_loggerSettings.UseLocalTime = UseLocalTime;
return _loggerSettings;
}
set => _loggerSettings = value;
}
protected abstract Task WriteMessagesAsync(IEnumerable<LogMessage> messages, CancellationToken token); protected abstract Task WriteMessagesAsync(IEnumerable<LogMessage> messages, CancellationToken token);
private async Task ProcessLogQueueAsync(object state) private async Task ProcessLogQueueAsync(object state)
{ {
var startupMessage = $"{DllInfo.ApplicationName} started.{Environment.NewLine}"; var startupMessage = $"{DllInfo.ApplicationName} started.{Environment.NewLine}";
startupMessage = LogHelper.FormatMessageWithHeader(new LoggerSettings(), ELogType.INFO, startupMessage, DateTime.Now); startupMessage = LogHelper.FormatMessageWithHeader(LoggerSettings, ELogType.INFO, startupMessage, CurrentDateTme);
AddMessage(DateTimeOffset.Now, startupMessage); AddMessage(DateTimeOffset.Now, startupMessage);
@@ -77,7 +102,7 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
await IntervalAsync(_interval, _cancellationTokenSource.Token).ConfigureAwait(false); await IntervalAsync(_interval, _cancellationTokenSource.Token).ConfigureAwait(false);
} }
await WriteMessagesAsync(new List<LogMessage> { new LogMessage { Message = $"[{DllInfo.ApplicationName}] {DllInfo.ApplicationName} stopped.{Environment.NewLine}", Timestamp = DateTimeOffset.Now } }, _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) protected virtual Task IntervalAsync(TimeSpan interval, CancellationToken cancellationToken)
@@ -132,7 +157,7 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
public ILogger CreateLogger(string categoryName) public ILogger CreateLogger(string categoryName)
{ {
return new BatchingLogger(this, categoryName); return new BatchingLogger(this, categoryName, LoggerSettings);
} }
} }
} }

View File

@@ -22,7 +22,7 @@ namespace EonaCat.Logger.Managers
if (settings != null) if (settings != null)
{ {
if (sb.ToString().Contains("{ts}")) if (sb.ToString().Contains("{ts}"))
sb.Replace("{ts}", dateTime.ToString(settings.TimestampFormat)); sb.Replace("{ts}", dateTime.ToString(settings.TimestampFormat) + " " + (settings.UseLocalTime ? "[LOCAL]" : "[UTC]"));
if (sb.ToString().Contains("{host}")) if (sb.ToString().Contains("{host}"))
sb.Replace("{host}", $"[Host:{Dns.GetHostName()}]"); sb.Replace("{host}", $"[Host:{Dns.GetHostName()}]");

View File

@@ -4,7 +4,6 @@ using Microsoft.Extensions.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using EonaCat.Logger.EonaCatCoreLogger; using EonaCat.Logger.EonaCatCoreLogger;
@@ -16,6 +15,7 @@ namespace EonaCat.Logger.Managers
{ {
public partial class LogManager : ILogManager, IDisposable public partial class LogManager : ILogManager, IDisposable
{ {
private DateTime CurrentDateTme => Settings.UseLocalTime ? DateTime.Now : DateTime.UtcNow;
private DateTime _logDate; private DateTime _logDate;
public ILoggerProvider LoggerProvider { get; private set; } public ILoggerProvider LoggerProvider { get; private set; }
public ILoggerFactory LoggerFactory { get; private set; } public ILoggerFactory LoggerFactory { get; private set; }
@@ -30,7 +30,7 @@ namespace EonaCat.Logger.Managers
public static LogManager Instance => InstanceInit(); public static LogManager Instance => InstanceInit();
public LoggerSettings Settings { get; } = CreateDefaultSettings(); public LoggerSettings Settings { get; set; } = CreateDefaultSettings();
private static LogManager InstanceInit() private static LogManager InstanceInit()
{ {
@@ -62,9 +62,7 @@ namespace EonaCat.Logger.Managers
if (_tokenSource.IsCancellationRequested) if (_tokenSource.IsCancellationRequested)
return; return;
DateTime now = DateTime.UtcNow; if (IsRunning && CurrentDateTme.Date > _logDate.Date)
if (IsRunning && now.Date > _logDate.Date)
await StopLoggingAsync().ConfigureAwait(false); await StopLoggingAsync().ConfigureAwait(false);
IsRunning = true; IsRunning = true;
@@ -73,7 +71,7 @@ namespace EonaCat.Logger.Managers
Directory.CreateDirectory(Settings.FileLoggerOptions.LogDirectory); Directory.CreateDirectory(Settings.FileLoggerOptions.LogDirectory);
_logDate = now; _logDate = CurrentDateTme;
} }
private void CreateLogger() private void CreateLogger()
@@ -91,6 +89,7 @@ namespace EonaCat.Logger.Managers
configuration.LogDirectory = fileLoggerOptions.LogDirectory; configuration.LogDirectory = fileLoggerOptions.LogDirectory;
configuration.FileNamePrefix = fileLoggerOptions.FileNamePrefix; configuration.FileNamePrefix = fileLoggerOptions.FileNamePrefix;
configuration.MaxRolloverFiles = fileLoggerOptions.MaxRolloverFiles; configuration.MaxRolloverFiles = fileLoggerOptions.MaxRolloverFiles;
configuration.UseLocalTime = Settings.UseLocalTime;
})); }));
var serviceProvider = serviceCollection.BuildServiceProvider(); var serviceProvider = serviceCollection.BuildServiceProvider();
@@ -172,7 +171,7 @@ namespace EonaCat.Logger.Managers
{ {
AppDomain.CurrentDomain.ProcessExit += ProcessExit; AppDomain.CurrentDomain.ProcessExit += ProcessExit;
Console.CancelKeyPress += Console_CancelKeyPress; Console.CancelKeyPress += Console_CancelKeyPress;
_logDate = DateTime.UtcNow; _logDate = CurrentDateTme;
} }
private void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) private void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
@@ -188,7 +187,7 @@ namespace EonaCat.Logger.Managers
private Task StopLoggingAsync() private Task StopLoggingAsync()
{ {
IsRunning = false; IsRunning = false;
InternalWriteAsync(DateTime.UtcNow, $"{DllInfo.ApplicationName} stopped."); InternalWriteAsync(CurrentDateTme, $"{DllInfo.ApplicationName} stopped.");
return Task.Delay(500); return Task.Delay(500);
} }
@@ -217,12 +216,10 @@ namespace EonaCat.Logger.Managers
if (logType == ELogType.NONE) if (logType == ELogType.NONE)
return; return;
var now = DateTime.UtcNow;
if (!IsRunning) if (!IsRunning)
StartNewLogAsync().ConfigureAwait(false); StartNewLogAsync().ConfigureAwait(false);
InternalWriteAsync(now, message, logType, writeToConsole); InternalWriteAsync(CurrentDateTme, message, logType, writeToConsole);
} }
} }
} }

View File

@@ -14,6 +14,11 @@ namespace EonaCat.Logger.Managers
public event LogDelegate OnLog; public event LogDelegate OnLog;
public delegate void LogDelegate(EonaCatLogMessage message); public delegate void LogDelegate(EonaCatLogMessage message);
/// <summary>
/// Determines if we need to use the local time in the logging or UTC (default:false)
/// </summary>
public bool UseLocalTime { get; set; }
public string Id { get; set; } = "EonaCatLogger"; public string Id { get; set; } = "EonaCatLogger";
/// <summary> /// <summary>
@@ -135,7 +140,7 @@ namespace EonaCat.Logger.Managers
} }
} }
private FileLoggerOptions CreateDefaultFileLoggerOptions() private static FileLoggerOptions CreateDefaultFileLoggerOptions()
{ {
return new FileLoggerOptions(); return new FileLoggerOptions();
} }

View File

@@ -1,7 +1,6 @@
using EonaCat.Logger.Managers; using EonaCat.Logger.Managers;
using Microsoft.AspNetCore.Http.Features; using System.IO.Compression;
using Microsoft.AspNetCore.Mvc; using EonaCat.Logger.Extensions;
using System.Net;
namespace EonaCat.Logger.Test.Web namespace EonaCat.Logger.Test.Web
{ {
@@ -30,114 +29,64 @@ namespace EonaCat.Logger.Test.Web
LogManager.DeleteCurrentLogFile(); LogManager.DeleteCurrentLogFile();
} }
internal static IActionResult DownloadLogAsync(HttpResponse response, string logFile) private static string ConvertToAbsolutePath(string path)
{ {
if (IsDisabled) return Path.IsPathRooted(path) ? path : Path.Combine(LogFolder, path);
{
response.StatusCode = (int)HttpStatusCode.BadRequest;
return null;
} }
// Check if the file exists public static async Task DownloadLogAsync(HttpContext context, string logName, long limit)
if (!File.Exists(logFile))
{ {
response.StatusCode = (int)HttpStatusCode.BadRequest; var logFileName = logName + ".log";
return null;
await using var fS = new FileStream(Path.Combine(ConvertToAbsolutePath(LogFolder), logFileName), FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 64 * 1024, true);
var response = context.Response;
response.ContentType = "text/plain";
response.Headers.ContentDisposition = "attachment;filename=" + logFileName;
if ((limit > fS.Length) || (limit < 1))
limit = fS.Length;
var oFS = new OffsetStream(fS, 0, limit);
var request = context.Request;
Stream s;
string acceptEncoding = request.Headers["Accept-Encoding"];
if (string.IsNullOrEmpty(acceptEncoding))
{
s = response.Body;
} }
else
{
string[] acceptEncodingParts = acceptEncoding.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
// Create a cancellation token source to handle cancellation if (acceptEncodingParts.Contains("br"))
var cancellationTokenSource = new CancellationTokenSource(); {
response.Headers.ContentEncoding = "br";
// Start streaming the file content to the response s = new BrotliStream(response.Body, CompressionMode.Compress);
Task.Run(() => StreamFileContentAsync(response, logFile, response.Body, cancellationTokenSource.Token), cancellationTokenSource.Token);
// Return an empty result to indicate that streaming has started
return new EmptyResult();
} }
else if (acceptEncodingParts.Contains("gzip"))
private static void DisableResponseBuffering(HttpResponse response)
{ {
var httpContext = response.HttpContext; response.Headers.ContentEncoding = "gzip";
var responseBodyFeature = httpContext.Features.Get<IHttpResponseBodyFeature>(); s = new GZipStream(response.Body, CompressionMode.Compress);
if (responseBodyFeature != null) }
else if (acceptEncodingParts.Contains("deflate"))
{ {
responseBodyFeature.DisableBuffering(); response.Headers.ContentEncoding = "deflate";
s = new DeflateStream(response.Body, CompressionMode.Compress);
}
else
{
s = response.Body;
} }
} }
private static async Task StreamFileContentAsync(HttpResponse response, string filePath, Stream outputStream, CancellationToken cancellationToken) await using (s)
{ {
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, FileOptions.Asynchronous | FileOptions.SequentialScan)) await oFS.CopyToAsync(s).ConfigureAwait(false);
{
try
{
// Clear the response first
response.Clear();
// Disable response buffering if (fS.Length > limit)
DisableResponseBuffering(response); await s.WriteAsync("\r\n####__EONACATLOGGER_TRUNCATED___####"u8.ToArray()).ConfigureAwait(false);
// Set the response headers
response.Headers.Add("Content-Disposition", $"attachment; filename={Path.GetFileName(filePath)}");
response.Headers.Add("Content-Type", "application/octet-stream");
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) > 0)
{
if (cancellationToken.IsCancellationRequested)
{
break;
}
// Check if the response is completed
if (response.HasStarted)
{
// The response is already completed, so we can't write further
break;
}
try
{
await outputStream.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
}
catch (Exception)
{
// Do nothing, handle any specific error if needed
}
// Check if the file has been modified
if (File.GetLastWriteTimeUtc(filePath) > File.GetLastWriteTimeUtc(fileStream.Name))
{
// Close the existing stream and reopen the file with updated contents
fileStream.Close();
await StreamFileContentAsync(response, filePath, outputStream, cancellationToken).ConfigureAwait(false);
return;
}
}
}
catch (OperationCanceledException)
{
// The operation was canceled, clean up if necessary
fileStream.Close();
}
finally
{
try
{
// Flush and close the output stream
await outputStream.FlushAsync(cancellationToken).ConfigureAwait(false);
}
catch (ObjectDisposedException e)
{
Log(e, "StreamFileContentAsync");
}
finally
{
outputStream?.Close();
}
}
} }
} }