Updated (added more header options)
This commit is contained in:
@@ -25,7 +25,7 @@
|
|||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="EonaCat.Logger" Version="1.5.5" />
|
<PackageReference Include="EonaCat.Logger" Version="1.5.7" />
|
||||||
<PackageReference Include="System.Net.Http.Json" Version="10.0.1" />
|
<PackageReference Include="System.Net.Http.Json" Version="10.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
<Copyright>EonaCat (Jeroen Saey)</Copyright>
|
<Copyright>EonaCat (Jeroen Saey)</Copyright>
|
||||||
<PackageTags>EonaCat;Logger;EonaCatLogger;Log;Writer;Jeroen;Saey</PackageTags>
|
<PackageTags>EonaCat;Logger;EonaCatLogger;Log;Writer;Jeroen;Saey</PackageTags>
|
||||||
<PackageIconUrl />
|
<PackageIconUrl />
|
||||||
<Version>1.5.7</Version>
|
<Version>1.5.8</Version>
|
||||||
<FileVersion>1.5.7</FileVersion>
|
<FileVersion>1.5.8</FileVersion>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<EVRevisionFormat>1.5.7+{chash:10}.{c:ymd}</EVRevisionFormat>
|
<EVRevisionFormat>1.5.8+{chash:10}.{c:ymd}</EVRevisionFormat>
|
||||||
<EVDefault>true</EVDefault>
|
<EVDefault>true</EVDefault>
|
||||||
<EVInfo>true</EVInfo>
|
<EVInfo>true</EVInfo>
|
||||||
<EVTagMatch>v[0-9]*</EVTagMatch>
|
<EVTagMatch>v[0-9]*</EVTagMatch>
|
||||||
|
|||||||
@@ -53,7 +53,9 @@ namespace EonaCat.Logger.EonaCatCoreLogger
|
|||||||
Exception exception, Func<TState, Exception, string> formatter)
|
Exception exception, Func<TState, Exception, string> formatter)
|
||||||
{
|
{
|
||||||
if (!IsEnabled(logLevel) || formatter == null)
|
if (!IsEnabled(logLevel) || formatter == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (IncludeCorrelationId)
|
if (IncludeCorrelationId)
|
||||||
{
|
{
|
||||||
@@ -71,17 +73,25 @@ namespace EonaCat.Logger.EonaCatCoreLogger
|
|||||||
};
|
};
|
||||||
|
|
||||||
foreach (var kvp in _context.GetAll())
|
foreach (var kvp in _context.GetAll())
|
||||||
|
{
|
||||||
logParts.Add($"`{kvp.Key}`: {kvp.Value}");
|
logParts.Add($"`{kvp.Key}`: {kvp.Value}");
|
||||||
|
}
|
||||||
|
|
||||||
if (exception != null)
|
if (exception != null)
|
||||||
|
{
|
||||||
logParts.Add($"Exception: {exception}");
|
logParts.Add($"Exception: {exception}");
|
||||||
|
}
|
||||||
|
|
||||||
// Limit queue size to prevent memory growth
|
// Limit queue size to prevent memory growth
|
||||||
if (_messageQueue.Count < 1000)
|
if (_messageQueue.Count < 1000)
|
||||||
|
{
|
||||||
_messageQueue.Enqueue(string.Join("\n", logParts));
|
_messageQueue.Enqueue(string.Join("\n", logParts));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
OnException?.Invoke(this, new Exception("DiscordLogger queue overflow"));
|
OnException?.Invoke(this, new Exception("DiscordLogger queue overflow"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task FlushLoopAsync(CancellationToken token)
|
private async Task FlushLoopAsync(CancellationToken token)
|
||||||
{
|
{
|
||||||
@@ -107,7 +117,9 @@ namespace EonaCat.Logger.EonaCatCoreLogger
|
|||||||
private async Task FlushBufferAsync(CancellationToken token)
|
private async Task FlushBufferAsync(CancellationToken token)
|
||||||
{
|
{
|
||||||
if (!await _flushLock.WaitAsync(0, token).ConfigureAwait(false))
|
if (!await _flushLock.WaitAsync(0, token).ConfigureAwait(false))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -54,8 +54,7 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
|
|||||||
? exception.FormatExceptionToMessage() + Environment.NewLine
|
? exception.FormatExceptionToMessage() + Environment.NewLine
|
||||||
: formatter(state, exception);
|
: formatter(state, exception);
|
||||||
|
|
||||||
message = LogHelper.FormatMessageWithHeader(_loggerSettings, logLevel.FromLogLevel(), message, timestamp.DateTime, category)
|
message = LogHelper.FormatMessageWithHeader(_loggerSettings, logLevel.FromLogLevel(), message, timestamp.DateTime, category) + Environment.NewLine;
|
||||||
+ Environment.NewLine;
|
|
||||||
|
|
||||||
var currentMessage = new EonaCatLogMessage
|
var currentMessage = new EonaCatLogMessage
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
using System;
|
using EonaCat.Logger.Managers;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.SymbolStore;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using EonaCat.Logger.Managers;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
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.
|
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
||||||
@@ -14,21 +13,22 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal;
|
|||||||
|
|
||||||
public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
|
public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
|
||||||
{
|
{
|
||||||
private readonly int _batchSize;
|
private int _batchSize;
|
||||||
|
|
||||||
|
private readonly Channel<LogMessage> _channel =
|
||||||
|
Channel.CreateUnbounded<LogMessage>(new UnboundedChannelOptions
|
||||||
|
{
|
||||||
|
SingleReader = true,
|
||||||
|
SingleWriter = false
|
||||||
|
});
|
||||||
|
|
||||||
private readonly List<LogMessage> _currentBatch = new();
|
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
private LoggerSettings _loggerSettings;
|
|
||||||
|
|
||||||
private ConcurrentQueue<LogMessage> _messageQueue;
|
|
||||||
private Task _outputTask;
|
private Task _outputTask;
|
||||||
private object _writeLock = new object();
|
private bool _isDisposed;
|
||||||
private bool _isDisposing;
|
|
||||||
protected string Category;
|
|
||||||
|
|
||||||
protected BatchingLoggerProvider(IOptions<BatchingLoggerOptions> options)
|
protected BatchingLoggerProvider(IOptions<BatchingLoggerOptions> options)
|
||||||
{
|
{
|
||||||
var loggerOptions = options.Value;
|
var loggerOptions = options.Value ?? throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
if (loggerOptions.FlushPeriod <= TimeSpan.Zero)
|
if (loggerOptions.FlushPeriod <= TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
@@ -42,9 +42,8 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
|
|||||||
UseMask = fileLoggerOptions.UseMask;
|
UseMask = fileLoggerOptions.UseMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
_batchSize = loggerOptions.BatchSize;
|
_batchSize = loggerOptions.BatchSize > 0 ? loggerOptions.BatchSize : 100;
|
||||||
|
StartAsync();
|
||||||
StartAsync().ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DateTimeOffset CurrentDateTimeOffset => UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
|
protected DateTimeOffset CurrentDateTimeOffset => UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
|
||||||
@@ -70,73 +69,56 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
|
|||||||
set => _loggerSettings = value;
|
set => _loggerSettings = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SemaphoreSlim _writeSemaphore = new SemaphoreSlim(1, 1);
|
|
||||||
|
|
||||||
public bool IsStarted { get; set; }
|
public bool IsStarted { get; set; }
|
||||||
public bool UseMask { get; set; }
|
public bool UseMask { get; set; }
|
||||||
|
|
||||||
public async void Dispose()
|
private LoggerSettings _loggerSettings;
|
||||||
{
|
|
||||||
while (!_messageQueue.IsEmpty)
|
|
||||||
{
|
|
||||||
// Do nothing, just wait for the queue to be empty
|
|
||||||
await Task.Delay(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
_isDisposing = true;
|
|
||||||
await StopAsync().ConfigureAwait(false);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ILogger CreateLogger(string categoryName)
|
public ILogger CreateLogger(string categoryName)
|
||||||
{
|
{
|
||||||
Category = categoryName;
|
|
||||||
return new BatchingLogger(this, categoryName, LoggerSettings);
|
return new BatchingLogger(this, categoryName, LoggerSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract Task WriteMessagesAsync(IEnumerable<LogMessage> messages, CancellationToken token);
|
protected abstract Task WriteMessagesAsync(IEnumerable<LogMessage> messages, CancellationToken token);
|
||||||
private async Task ProcessLogQueueAsync(object state)
|
|
||||||
{
|
|
||||||
while (!_cancellationTokenSource.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
var limit = _batchSize <= 0 ? int.MaxValue : _batchSize;
|
|
||||||
while (limit > 0 && _messageQueue.TryDequeue(out var message))
|
|
||||||
{
|
|
||||||
_currentBatch.Add(message);
|
|
||||||
limit--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_currentBatch.Count > 0)
|
private async Task ProcessLogQueueAsync()
|
||||||
{
|
{
|
||||||
|
var batchSize = _batchSize > 0 ? _batchSize : 100;
|
||||||
|
var batch = new List<LogMessage>(batchSize);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_isDisposing)
|
var token = _cancellationTokenSource.Token;
|
||||||
|
while (await _channel.Reader.WaitToReadAsync(token).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
return;
|
batch.Clear();
|
||||||
|
|
||||||
|
while (batch.Count < batchSize && _channel.Reader.TryRead(out var message))
|
||||||
|
{
|
||||||
|
batch.Add(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _writeSemaphore.WaitAsync();
|
if (batch.Count > 0)
|
||||||
await WriteMessagesAsync(_currentBatch, _cancellationTokenSource.Token).ConfigureAwait(false);
|
|
||||||
_currentBatch.Clear();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
{
|
||||||
// ignored
|
await WriteMessagesAsync(batch, token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
_writeSemaphore.Release();
|
// normal shutdown
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await Task.Delay(500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected async Task WriteStartMessage()
|
protected async Task WriteStartMessage()
|
||||||
{
|
{
|
||||||
var message = LogHelper.GetStartupMessage();
|
var message = LogHelper.GetStartupMessage();
|
||||||
await WriteMessagesAsync(new List<LogMessage> { CreateLoggerMessage(message, CurrentDateTimeOffset) }, _cancellationTokenSource.Token).ConfigureAwait(false);
|
var token = _cancellationTokenSource?.Token ?? CancellationToken.None;
|
||||||
|
await WriteMessagesAsync(new List<LogMessage> { CreateLoggerMessage(message, CurrentDateTimeOffset) }, token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LogMessage CreateLoggerMessage(string message, DateTimeOffset currentDateTimeOffset)
|
private LogMessage CreateLoggerMessage(string message, DateTimeOffset currentDateTimeOffset)
|
||||||
@@ -320,46 +302,93 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
|
|||||||
internal string AddMessage(DateTimeOffset timestamp, string message)
|
internal string AddMessage(DateTimeOffset timestamp, string message)
|
||||||
{
|
{
|
||||||
var result = CreateLoggerMessage(message, timestamp);
|
var result = CreateLoggerMessage(message, timestamp);
|
||||||
_messageQueue.Enqueue(result);
|
if (!_channel.Writer.TryWrite(result))
|
||||||
|
{
|
||||||
|
_ = _channel.Writer.WriteAsync(result);
|
||||||
|
}
|
||||||
|
|
||||||
return result.Message;
|
return result.Message;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task StartAsync()
|
private void StartAsync()
|
||||||
{
|
{
|
||||||
IsStarted = true;
|
if (_cancellationTokenSource != null)
|
||||||
_messageQueue = new ConcurrentQueue<LogMessage>();
|
{
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_outputTask = Task.Factory.StartNew(
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
ProcessLogQueueAsync,
|
_outputTask = Task.Run(ProcessLogQueueAsync);
|
||||||
null,
|
IsStarted = true;
|
||||||
TaskCreationOptions.LongRunning);
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StopAsync()
|
private async Task StopAsync()
|
||||||
{
|
{
|
||||||
_cancellationTokenSource.Cancel();
|
if (_cancellationTokenSource == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (_outputTask.Status != TaskStatus.RanToCompletion && _outputTask.Status != TaskStatus.Canceled)
|
_cancellationTokenSource.Cancel();
|
||||||
|
_channel.Writer.Complete();
|
||||||
|
|
||||||
|
if (_outputTask != null)
|
||||||
{
|
{
|
||||||
await _outputTask.ConfigureAwait(false);
|
await _outputTask.ConfigureAwait(false);
|
||||||
await Task.Delay(100);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
|
// expected on shutdown
|
||||||
}
|
}
|
||||||
catch (AggregateException exception) when (exception.InnerExceptions.Count == 1 &&
|
catch (AggregateException exception) when (exception.InnerExceptions.Count == 1 && exception.InnerExceptions[0] is TaskCanceledException)
|
||||||
exception.InnerExceptions[0] is TaskCanceledException)
|
|
||||||
{
|
{
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_cancellationTokenSource.Dispose();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
_cancellationTokenSource = null;
|
||||||
|
_outputTask = null;
|
||||||
|
IsStarted = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~BatchingLoggerProvider()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose();
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_isDisposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDisposed = true;
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StopAsync().GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ public static class ExceptionExtensions
|
|||||||
var sb = new StringBuilderChill();
|
var sb = new StringBuilderChill();
|
||||||
|
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
sb.AppendLine($"--- Exception details provided by {DllInfo.ApplicationName} ---");
|
sb.AppendLine($"--- Exception details provided by {DllInfo.ApplicationName} on {Environment.MachineName} ---");
|
||||||
if (!string.IsNullOrEmpty(module))
|
if (!string.IsNullOrEmpty(module))
|
||||||
{
|
{
|
||||||
sb.AppendLine(" Module : " + module);
|
sb.AppendLine(" Module : " + module);
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
using System;
|
using EonaCat.Json;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using EonaCat.Json;
|
|
||||||
using EonaCat.Logger.Extensions;
|
using EonaCat.Logger.Extensions;
|
||||||
using EonaCat.Logger.Servers.GrayLog;
|
using EonaCat.Logger.Servers.GrayLog;
|
||||||
using EonaCat.Logger.Servers.Splunk.Models;
|
using EonaCat.Logger.Servers.Splunk.Models;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
// 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.
|
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
||||||
@@ -23,6 +25,34 @@ public class ErrorMessage
|
|||||||
|
|
||||||
public static class LogHelper
|
public static class LogHelper
|
||||||
{
|
{
|
||||||
|
public sealed class HeaderContext
|
||||||
|
{
|
||||||
|
public DateTime Timestamp { get; set; }
|
||||||
|
public string TimestampFormat { get; set; }
|
||||||
|
public string HostName { get; set; }
|
||||||
|
public string Category { get; set; }
|
||||||
|
public ELogType LogType { get; set; }
|
||||||
|
public LoggerSettings Settings { get; set; }
|
||||||
|
public string EnvironmentName { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Dictionary<string, Func<HeaderContext, string>> TokenResolvers =
|
||||||
|
new(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
["ts"] = ctx => ctx.Timestamp.ToString(ctx.TimestampFormat),
|
||||||
|
["host"] = ctx => ctx.HostName,
|
||||||
|
["machine"] = ctx => Environment.MachineName,
|
||||||
|
["category"] = ctx => ctx.Category,
|
||||||
|
["thread"] = ctx => Environment.CurrentManagedThreadId.ToString(),
|
||||||
|
["process"] = ctx => Process.GetCurrentProcess().ProcessName,
|
||||||
|
["pid"] = ctx => Process.GetCurrentProcess().Id.ToString(),
|
||||||
|
["sev"] = ctx => ctx.LogType.ToString(),
|
||||||
|
["user"] = ctx => Environment.UserName,
|
||||||
|
["env"] = ctx => ctx.EnvironmentName,
|
||||||
|
["newline"] = _ => Environment.NewLine
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
internal static event EventHandler<ErrorMessage> OnException;
|
internal static event EventHandler<ErrorMessage> OnException;
|
||||||
|
|
||||||
internal static event EventHandler<ErrorMessage> OnLogLevelDisabled;
|
internal static event EventHandler<ErrorMessage> OnLogLevelDisabled;
|
||||||
@@ -73,34 +103,71 @@ public static class LogHelper
|
|||||||
return syslogMessage;
|
return syslogMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string ResolveHeader(string format, HeaderContext ctx)
|
||||||
internal static string FormatMessageWithHeader(LoggerSettings settings, ELogType logType, string currentMessage,
|
|
||||||
DateTime dateTime, string category = null)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(currentMessage))
|
if (string.IsNullOrWhiteSpace(format))
|
||||||
{
|
{
|
||||||
return currentMessage;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
category ??= "General";
|
return Regex.Replace(format, @"\{([^}]+)\}", match =>
|
||||||
var sb = new StringBuilder(settings?.HeaderFormat);
|
|
||||||
var timestamp = dateTime.ToString(settings?.TimestampFormat ?? "yyyy-MM-dd HH:mm:ss");
|
|
||||||
var timeLabel = settings?.UseLocalTime ?? false ? "[LOCAL]" : "[UTC]";
|
|
||||||
|
|
||||||
sb.Replace("{ts}", $"{timestamp} {timeLabel}")
|
|
||||||
.Replace("{host}", $"[Host:{HostName}]")
|
|
||||||
.Replace("{category}", $"[Category:{category}]")
|
|
||||||
.Replace("{thread}", $"[ThreadId:{Environment.CurrentManagedThreadId}]")
|
|
||||||
.Replace("{sev}", $"[{logType}]");
|
|
||||||
|
|
||||||
if (sb.Length > 0)
|
|
||||||
{
|
{
|
||||||
sb.Append(" ");
|
var token = match.Groups[1].Value;
|
||||||
|
var parts = token.Split(new[] { ':' }, 2);
|
||||||
|
var key = parts[0];
|
||||||
|
|
||||||
|
if (!TokenResolvers.TryGetValue(key, out var resolver))
|
||||||
|
{
|
||||||
|
return match.Value;
|
||||||
}
|
}
|
||||||
sb.Append(currentMessage);
|
|
||||||
return sb.ToString();
|
if (key.Equals("ts", StringComparison.OrdinalIgnoreCase) && parts.Length == 2)
|
||||||
|
{
|
||||||
|
ctx.TimestampFormat = parts[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return resolver(ctx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
internal static string FormatMessageWithHeader(
|
||||||
|
LoggerSettings settings,
|
||||||
|
ELogType logType,
|
||||||
|
string message,
|
||||||
|
DateTime dateTime,
|
||||||
|
string category = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(message))
|
||||||
|
{
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ctx = new HeaderContext
|
||||||
|
{
|
||||||
|
Timestamp = dateTime,
|
||||||
|
TimestampFormat = settings?.TimestampFormat ?? "yyyy-MM-dd HH:mm:ss",
|
||||||
|
HostName = HostName,
|
||||||
|
Category = category ?? "General",
|
||||||
|
LogType = logType,
|
||||||
|
Settings = settings,
|
||||||
|
EnvironmentName = settings?.EnvironmentName
|
||||||
|
};
|
||||||
|
|
||||||
|
string header = settings?.CustomHeaderFormatter != null
|
||||||
|
? settings.CustomHeaderFormatter(ctx)
|
||||||
|
: ResolveHeader(settings?.HeaderFormat, ctx);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(header))
|
||||||
|
{
|
||||||
|
header += " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
return header + message;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
internal static void SendToConsole(LoggerSettings settings, ELogType logType, string message)
|
internal static void SendToConsole(LoggerSettings settings, ELogType logType, string message)
|
||||||
{
|
{
|
||||||
if (settings == null || string.IsNullOrWhiteSpace(message))
|
if (settings == null || string.IsNullOrWhiteSpace(message))
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
using System;
|
using EonaCat.Json.Linq;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using EonaCat.Json.Linq;
|
|
||||||
using EonaCat.Logger.EonaCatCoreLogger;
|
using EonaCat.Logger.EonaCatCoreLogger;
|
||||||
using EonaCat.Logger.EonaCatCoreLogger.Models;
|
using EonaCat.Logger.EonaCatCoreLogger.Models;
|
||||||
using EonaCat.Logger.Servers.GrayLog;
|
using EonaCat.Logger.Servers.GrayLog;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using static EonaCat.Logger.Managers.LogHelper;
|
||||||
|
|
||||||
namespace EonaCat.Logger.Managers;
|
namespace EonaCat.Logger.Managers;
|
||||||
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
||||||
@@ -60,6 +61,10 @@ public class LoggerSettings
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Func<HeaderContext, string> CustomHeaderFormatter { get; set; }
|
||||||
|
|
||||||
|
public string EnvironmentName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Timestamp format.
|
/// Timestamp format.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -38,6 +38,16 @@ public class Logger
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
LoggerSettings.CustomHeaderFormatter = ctx =>
|
||||||
|
{
|
||||||
|
if (ctx.LogType == ELogType.Error)
|
||||||
|
{
|
||||||
|
return $"{ctx.Timestamp:HH:mm:ss} [{ctx.LogType}]";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"{ctx.Timestamp:HH:mm:ss} [{ctx.LogType}]";
|
||||||
|
};
|
||||||
|
|
||||||
_logManager = new LogManager(LoggerSettings);
|
_logManager = new LogManager(LoggerSettings);
|
||||||
_logManager.Settings.TypesToLog.Clear();
|
_logManager.Settings.TypesToLog.Clear();
|
||||||
_logManager.Settings.LogInfo();
|
_logManager.Settings.LogInfo();
|
||||||
|
|||||||
Reference in New Issue
Block a user