This commit is contained in:
2025-02-16 03:45:05 +01:00
7 changed files with 740 additions and 638 deletions

View File

@@ -3,7 +3,7 @@
<TargetFrameworks>.netstandard2.1; net6.0; net7.0; net8.0; net4.8;</TargetFrameworks> <TargetFrameworks>.netstandard2.1; net6.0; net7.0; net8.0; net4.8;</TargetFrameworks>
<ApplicationIcon>icon.ico</ApplicationIcon> <ApplicationIcon>icon.ico</ApplicationIcon>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<FileVersion>1.3.2</FileVersion> <FileVersion>1.3.5</FileVersion>
<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>
@@ -24,7 +24,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<EVRevisionFormat>1.3.2+{chash:10}.{c:ymd}</EVRevisionFormat> <EVRevisionFormat>1.3.5+{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>

View File

@@ -63,10 +63,17 @@ public static class ExceptionExtensions
foreach (DictionaryEntry entry in data) foreach (DictionaryEntry entry in data)
{ {
sb.Append(" | ") if (entry.Key != null)
.Append(entry.Key) {
.Append(": ") sb.Append(" | ")
.AppendLine(entry.Value.ToString()); .Append(entry.Key);
}
if (entry.Value != null)
{
sb.Append(": ")
.AppendLine(entry.Value.ToString());
}
} }
return sb.ToString(); return sb.ToString();

View File

@@ -75,8 +75,10 @@ public class GrayLogServer
/// <summary> /// <summary>
/// IP:port of the server. /// IP:port of the server.
/// </summary> /// </summary>
public string IpPort => _Hostname + ":" + _Port; public string IpPort => _Hostname + ":" + _Port;
public bool SupportsTcp { get; set; }
private void SetUdp() private void SetUdp()
{ {
Udp = null; Udp = null;

View File

@@ -1,306 +1,366 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Linq;
using System.Text; using System.Net;
using System.Threading.Tasks; using System.Net.Sockets;
using EonaCat.Json; using System.Text;
using EonaCat.Logger.Extensions; using System.Threading.Tasks;
using EonaCat.Logger.GrayLog; using EonaCat.Json;
using EonaCat.Logger.Splunk.Models; using EonaCat.Logger.Extensions;
using EonaCat.Logger.Syslog; using EonaCat.Logger.GrayLog;
using Microsoft.Extensions.Logging; using EonaCat.Logger.Splunk.Models;
using EonaCat.Logger.Syslog;
// This file is part of the EonaCat project(s) which is released under the Apache License. using Microsoft.Extensions.Logging;
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
// This file is part of the EonaCat project(s) which is released under the Apache License.
namespace EonaCat.Logger.Managers; // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class ErrorMessage namespace EonaCat.Logger.Managers;
{
public Exception Exception { get; set; } public class ErrorMessage
public string Message { get; set; } {
} public Exception Exception { get; set; }
public string Message { get; set; }
internal static class LogHelper }
{
internal static event EventHandler<ErrorMessage> OnException; internal static class LogHelper
{
internal static string FormatMessageWithHeader(LoggerSettings settings, ELogType logType, string currentMessage, internal static event EventHandler<ErrorMessage> OnException;
DateTime dateTime, string category = null)
{ internal static event EventHandler<ErrorMessage> OnLogLevelDisabled;
if (string.IsNullOrWhiteSpace(currentMessage))
{ private static readonly string MachineName = Environment.MachineName;
return currentMessage; private static readonly string HostName = Dns.GetHostName();
}
internal static string FormatMessageWithHeader(LoggerSettings settings, ELogType logType, string currentMessage,
if (string.IsNullOrWhiteSpace(category)) DateTime dateTime, string category = null)
{ {
category = "General"; if (string.IsNullOrWhiteSpace(currentMessage)) return currentMessage;
}
category ??= "General";
var sb = new StringBuilder(settings?.HeaderFormat ?? "[EonaCatLogger]"); var sb = new StringBuilder(settings?.HeaderFormat ?? "[EonaCatLogger]");
var timestamp = dateTime.ToString(settings?.TimestampFormat ?? "yyyy-MM-dd HH:mm:ss");
sb.Replace("{ts}", var timeLabel = settings?.UseLocalTime ?? false ? "[LOCAL]" : "[UTC]";
dateTime.ToString(settings?.TimestampFormat ?? "yyyy-MM-dd HH:mm:ss") + " " +
(settings?.UseLocalTime ?? false ? "[LOCAL]" : "[UTC]")) sb.Replace("{ts}", $"{timestamp} {timeLabel}")
.Replace("{host}", $"[Host:{Dns.GetHostName()}]") .Replace("{host}", $"[Host:{HostName}]")
.Replace("{category}", $"[Category:{category}]") .Replace("{category}", $"[Category:{category}]")
.Replace("{thread}", $"[ThreadId:{Environment.CurrentManagedThreadId}]") .Replace("{thread}", $"[ThreadId:{Environment.CurrentManagedThreadId}]")
.Replace("{sev}", $"[{logType}]"); .Replace("{sev}", $"[{logType}]");
if (!settings?.RemoveMessagePrefix ?? (false && !currentMessage.Contains("[EonaCatLogger]"))) if (!(settings?.RemoveMessagePrefix ?? false) && !currentMessage.Contains("[EonaCatLogger]"))
{ {
sb.Insert(0, "[EonaCatLogger] "); sb.Insert(0, "[EonaCatLogger] ");
} }
sb.Append(" ").Append(currentMessage); sb.Append(" ").Append(currentMessage);
return sb.ToString();
return sb.ToString(); }
}
internal static void SendToConsole(LoggerSettings settings, ELogType logType, string message, bool writeToConsole)
internal static void SendToConsole(LoggerSettings settings, ELogType logType, string message, bool writeToConsole) {
{ if (settings == null || !writeToConsole || string.IsNullOrWhiteSpace(message)) return;
if (settings == null || !writeToConsole || string.IsNullOrWhiteSpace(message))
{ if (settings.EnableColors && settings.Colors != null)
return; {
} var prevForeground = Console.ForegroundColor;
var prevBackground = Console.BackgroundColor;
if (settings.EnableColors && settings.Colors != null)
{ if (TryGetLogColor(settings.Colors, logType, out var foregroundColor, out var backgroundColor))
var prevForeground = Console.ForegroundColor; {
var prevBackground = Console.BackgroundColor; Console.ForegroundColor = foregroundColor;
Console.BackgroundColor = backgroundColor;
ConsoleColor foregroundColor; }
ConsoleColor backgroundColor;
switch (logType) Console.WriteLine(message);
{ Console.ForegroundColor = prevForeground;
case ELogType.DEBUG: Console.BackgroundColor = prevBackground;
foregroundColor = settings.Colors.Debug.Foreground; }
backgroundColor = settings.Colors.Debug.Background; else
break; {
case ELogType.INFO: Console.WriteLine(message);
foregroundColor = settings.Colors.Info.Foreground; }
backgroundColor = settings.Colors.Info.Background; }
break;
case ELogType.WARNING: private static bool TryGetLogColor(ColorSchema colors, ELogType logType, out ConsoleColor foreground, out ConsoleColor background)
foregroundColor = settings.Colors.Warning.Foreground; {
backgroundColor = settings.Colors.Warning.Background; foreground = default;
break; background = default;
case ELogType.ERROR:
foregroundColor = settings.Colors.Error.Foreground; return logType switch
backgroundColor = settings.Colors.Error.Background; {
break; ELogType.DEBUG => TrySetColors(colors.Debug, out foreground, out background),
case ELogType.TRAFFIC: ELogType.INFO => TrySetColors(colors.Info, out foreground, out background),
foregroundColor = settings.Colors.Traffic.Foreground; ELogType.WARNING => TrySetColors(colors.Warning, out foreground, out background),
backgroundColor = settings.Colors.Traffic.Background; ELogType.ERROR => TrySetColors(colors.Error, out foreground, out background),
break; ELogType.TRAFFIC => TrySetColors(colors.Traffic, out foreground, out background),
case ELogType.CRITICAL: ELogType.CRITICAL => TrySetColors(colors.Critical, out foreground, out background),
foregroundColor = settings.Colors.Critical.Foreground; ELogType.TRACE => TrySetColors(colors.Trace, out foreground, out background),
backgroundColor = settings.Colors.Critical.Background; _ => false,
break; };
case ELogType.TRACE: }
foregroundColor = settings.Colors.Trace.Foreground;
backgroundColor = settings.Colors.Trace.Background; private static bool TrySetColors(ColorScheme color, out ConsoleColor foreground, out ConsoleColor background)
break; {
default: foreground = color.Foreground;
return; background = color.Background;
} return true;
}
Console.ForegroundColor = foregroundColor;
Console.BackgroundColor = backgroundColor; internal static void SendToFile(ILogger logger, LoggerSettings settings, ELogType logType, string message)
Console.WriteLine(message); {
Console.ForegroundColor = prevForeground; if (logger == null || settings == null || !settings.EnableFileLogging || string.IsNullOrWhiteSpace(message)) return;
Console.BackgroundColor = prevBackground;
} if (IsLogLevelEnabled(settings, logType))
else {
{ Log(logger, logType.ToLogLevel(), message);
Console.WriteLine(message); }
} }
} private static bool IsLogLevelEnabled(LoggerSettings loggerSettings, ELogType logType)
{
internal static void SendToFile(ILogger logger, LoggerSettings settings, ELogType logType, string message) if (loggerSettings == null)
{ {
if (logger == null || settings == null || !settings.EnableFileLogging || OnLogLevelDisabled?.Invoke(null, new ErrorMessage { Message = "Settings is null." });
string.IsNullOrWhiteSpace(message)) return false;
{ }
return;
} var isEnabled = loggerSettings.MaxLogType != ELogType.NONE && logType <= loggerSettings.MaxLogType;
if (!isEnabled)
var logLevel = logType.ToLogLevel(); {
if (IsLogLevelEnabled(settings, logType)) OnLogLevelDisabled?.Invoke(null, new ErrorMessage { Message = $"Logtype '{logType}' is not enabled." });
{ }
Log(logger, logLevel, message);
} return isEnabled;
} }
private static bool IsLogLevelEnabled(LoggerSettings settings, ELogType logType) public static void SetLogLevel(LoggerSettings settings, ELogType logType)
{ {
return settings.MaxLogType != ELogType.NONE && logType <= settings.MaxLogType; settings.MaxLogType = logType;
} }
private static void Log(ILogger logger, LogLevel logLevel, string message) private static void Log(ILogger logger, LogLevel logLevel, string message)
{ {
logger.Log(logLevel, message); logger.Log(logLevel, message);
} }
public static async Task SendToSplunkServersAsync(LoggerSettings settings, SplunkPayload splunkPayload, public static async Task SendToSplunkServersAsync(LoggerSettings settings, SplunkPayload splunkPayload, bool sendToSplunkServer)
bool sendToSplunkServer) {
{ if (settings == null || !sendToSplunkServer || splunkPayload == null)
if (settings == null || !sendToSplunkServer || splunkPayload == null) {
{ return;
return; }
}
var tasks = settings.SplunkServers?
if (settings.SplunkServers == null) .Where(splunkServer => splunkServer.HasHecUrl && splunkServer.HasHecToken)
{ .Select(async splunkServer =>
settings.SplunkServers = new List<SplunkServer.SplunkServer>(); {
} try
{
foreach (var splunkServer in settings.SplunkServers) var response = await splunkServer.SendAsync(splunkPayload);
{ if (!response.IsSuccessStatusCode)
if (!splunkServer.HasHecUrl || !splunkServer.HasHecToken) {
{ LogError($"Failed to send log to Splunk '{splunkServer.SplunkHecUrl}'. Status code: {response.StatusCode}");
OnException?.Invoke(null, }
new ErrorMessage }
{ catch (Exception ex)
Message = {
$"Splunk server HecUrl or HecToken not specified, skipping splunkServer '{splunkServer.SplunkHecUrl}'" LogError($"Error logging to Splunk '{splunkServer.SplunkHecUrl}': {ex.Message}", ex);
}); }
continue; }) ?? new List<Task>();
}
await Task.WhenAll(tasks);
try }
{
var response = await splunkServer.SendAsync(splunkPayload); /// <summary>
/// Overload for sending a simple log message to Splunk.
if (!response.IsSuccessStatusCode) /// </summary>
{ public static async Task SendToSplunkServersAsync(LoggerSettings settings, string logType, string message, bool sendToSplunkServer)
OnException?.Invoke(null, {
new ErrorMessage if (settings == null || !sendToSplunkServer || string.IsNullOrWhiteSpace(message))
{ {
Message = return;
$"Failed to send log to Splunk '{splunkServer.SplunkHecUrl}'. Status code: {response.StatusCode}" }
});
} var splunkPayload = new SplunkPayload
} {
catch (Exception exception) Host = Environment.MachineName,
{ EventData = message,
OnException?.Invoke(null, SourceType = logType
new ErrorMessage };
{
Exception = exception, await SendToSplunkServersAsync(settings, splunkPayload, sendToSplunkServer);
Message = }
$"Error while logging to Splunk Server '{splunkServer.SplunkHecUrl}': {exception.Message}"
}); /// <summary>
} /// Logs an error using the OnException event.
} /// </summary>
} private static void LogError(string message, Exception ex = null)
{
public static async Task SendToSplunkServersAsync(LoggerSettings settings, string logType, string message, OnException?.Invoke(null, new ErrorMessage { Message = message, Exception = ex });
bool sendToSplunkServer) }
{
if (settings == null || !sendToSplunkServer || string.IsNullOrWhiteSpace(message))
{ internal static async Task SendToGrayLogServersAsync(LoggerSettings settings, string message, ELogType logLevel,
return; string facility, string source, bool sendToGrayLogServer, string version = "1.1")
} {
if (settings == null || !sendToGrayLogServer || string.IsNullOrWhiteSpace(message))
var splunkPayload = new SplunkPayload {
{ return;
Host = Environment.MachineName, }
EventData = message,
SourceType = logType const int MaxUdpPacketSize = 4096;
};
var gelfMessage = new
await SendToSplunkServersAsync(settings, splunkPayload, sendToSplunkServer); {
} version,
host = MachineName,
internal static async Task SendToGrayLogServersAsync(LoggerSettings settings, string message, ELogType logLevel, short_message = message,
string facility, string source, bool sendToGrayLogServer, string version = "1.1") level = logLevel.ToGrayLogLevel(),
{ facility,
if (settings == null || !sendToGrayLogServer || string.IsNullOrWhiteSpace(message)) source,
{ timestamp = DateTime.UtcNow.ToUnixTimestamp()
return; };
}
var messageBytes = Encoding.UTF8.GetBytes(JsonHelper.ToJson(gelfMessage));
foreach (var grayLogServer in settings.GrayLogServers ?? new List<GrayLogServer> { new("127.0.0.1") })
{ var tasks = settings.GrayLogServers?
try .Where(server => !string.IsNullOrWhiteSpace(server.Hostname) && server.Port >= 0)
{ .Select(async grayLogServer =>
var gelfMessage = new {
{ try
version, {
host = Environment.MachineName, if (messageBytes.Length <= MaxUdpPacketSize)
short_message = message, {
level = logLevel.ToGrayLogLevel(), // Send via UDP (single packet)
facility, await grayLogServer.Udp.SendAsync(messageBytes, messageBytes.Length);
source, }
timestamp = DateTime.UtcNow.ToUnixTimestamp() else if (grayLogServer.SupportsTcp)
}; {
// Send via TCP if supported
var messageBytes = Encoding.UTF8.GetBytes(JsonHelper.ToJson(gelfMessage)); await SendViaTcpAsync(grayLogServer, messageBytes);
await grayLogServer.Udp.SendAsync(messageBytes, messageBytes.Length); }
} else
catch (Exception exception) {
{ // Chunk large messages for UDP
OnException?.Invoke(null, await SendUdpInChunksAsync(grayLogServer, messageBytes, MaxUdpPacketSize);
new ErrorMessage }
{ }
Exception = exception, catch (Exception ex)
Message = {
$"Error while logging to GrayLog Server '{grayLogServer.Hostname}': {exception.Message}" OnException?.Invoke(null, new ErrorMessage
}); {
} Exception = ex,
} Message = $"Error logging to GrayLog Server '{grayLogServer.Hostname}': {ex.Message}"
} });
}
internal static async Task SendToSysLogServersAsync(LoggerSettings settings, string message, }) ?? new List<Task>();
bool sendToSyslogServers)
{ await Task.WhenAll(tasks);
if (settings == null || !sendToSyslogServers || string.IsNullOrWhiteSpace(message)) }
{
return;
} /// <summary>
/// Sends a message via TCP to a GrayLog server.
foreach (var server in settings.SysLogServers ?? new List<SyslogServer> { new("127.0.0.1") }) /// </summary>
{ private static async Task SendViaTcpAsync(GrayLogServer server, byte[] data)
try {
{ using var tcpClient = new TcpClient();
if (string.IsNullOrWhiteSpace(server.Hostname)) await tcpClient.ConnectAsync(server.Hostname, server.Port);
{ using var stream = tcpClient.GetStream();
OnException?.Invoke(null, await stream.WriteAsync(data, 0, data.Length);
new ErrorMessage { Message = "Server hostname not specified, skipping SysLog Server" }); await stream.FlushAsync();
continue; }
}
/// <summary>
if (server.Port < 0) /// Sends large messages in chunks over UDP.
{ /// </summary>
OnException?.Invoke(null, private static async Task SendUdpInChunksAsync(GrayLogServer server, byte[] data, int chunkSize)
new ErrorMessage { Message = "Server port must be zero or greater, skipping SysLog Server" }); {
continue; for (int i = 0; i < data.Length; i += chunkSize)
} {
var chunk = data.Skip(i).Take(chunkSize).ToArray();
var data = Encoding.UTF8.GetBytes(message); await server.Udp.SendAsync(chunk, chunk.Length);
await server.Udp.SendAsync(data, data.Length); }
} }
catch (Exception exception)
{ internal static async Task SendToSysLogServersAsync(LoggerSettings settings, string message, bool sendToSyslogServers)
OnException?.Invoke(null, {
new ErrorMessage if (settings == null || !sendToSyslogServers || string.IsNullOrWhiteSpace(message))
{ {
Exception = exception, return;
Message = $"Error while logging to SysLog Server '{server.Hostname}': {exception.Message}" }
});
} const int MaxUdpPacketSize = 4096;
}
} var tasks = settings.SysLogServers?
.Where(server => !string.IsNullOrWhiteSpace(server.Hostname) && server.Port >= 0)
internal static string GetStartupMessage() .Select(async server =>
{ {
return $"{DllInfo.ApplicationName} started."; try
} {
var data = Encoding.UTF8.GetBytes(message);
internal static string GetStopMessage()
{ if (data.Length <= MaxUdpPacketSize)
return $"{DllInfo.ApplicationName} stopped."; {
} // Send via UDP (single packet)
await server.Udp.SendAsync(data, data.Length);
}
else if (server.SupportsTcp)
{
// Send via TCP if supported
await SendViaTcpAsync(server, data);
}
else
{
// Chunk large messages for UDP
await SendUdpInChunksAsync(server, data, MaxUdpPacketSize);
}
}
catch (Exception ex)
{
OnException?.Invoke(null, new ErrorMessage
{
Exception = ex,
Message = $"Error logging to SysLog Server '{server.Hostname}': {ex.Message}"
});
}
}) ?? new List<Task>();
await Task.WhenAll(tasks);
}
/// <summary>
/// Sends a message via TCP to a syslog server.
/// </summary>
private static async Task SendViaTcpAsync(SyslogServer server, byte[] data)
{
using var tcpClient = new TcpClient();
await tcpClient.ConnectAsync(server.Hostname, server.Port);
using var stream = tcpClient.GetStream();
await stream.WriteAsync(data, 0, data.Length);
await stream.FlushAsync();
}
/// <summary>
/// Sends large messages in chunks over UDP.
/// </summary>
private static async Task SendUdpInChunksAsync(SyslogServer server, byte[] data, int chunkSize)
{
for (int i = 0; i < data.Length; i += chunkSize)
{
var chunk = data.Skip(i).Take(chunkSize).ToArray();
await server.Udp.SendAsync(chunk, chunk.Length);
}
}
internal static string GetStartupMessage()
{
return $"{DllInfo.ApplicationName} started.";
}
internal static string GetStopMessage()
{
return $"{DllInfo.ApplicationName} stopped.";
}
} }

View File

@@ -1,324 +1,344 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.IO;
using System.IO; using System.Threading;
using System.Threading; using System.Threading.Tasks;
using System.Threading.Tasks; using EonaCat.Logger.EonaCatCoreLogger;
using EonaCat.Logger.EonaCatCoreLogger; using EonaCat.Logger.EonaCatCoreLogger.Extensions;
using EonaCat.Logger.EonaCatCoreLogger.Extensions; using EonaCat.Logger.EonaCatCoreLogger.Models;
using EonaCat.Logger.EonaCatCoreLogger.Models; using EonaCat.Logger.Extensions;
using EonaCat.Logger.Extensions; using EonaCat.Logger.Syslog;
using EonaCat.Logger.Syslog; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging;
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. // 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.
public class LogManager : ILogManager, IDisposable
public class LogManager : ILogManager, IDisposable {
{ private static readonly Lazy<LogManager> _instance = new(() => new LogManager(CreateDefaultSettings()));
private static readonly Lazy<LogManager> _instance = new(() => new LogManager(CreateDefaultSettings())); private readonly CancellationTokenSource _tokenSource = new();
private readonly CancellationTokenSource _tokenSource = new(); private DateTime _logDate;
private DateTime _logDate; private bool _isDisposing;
private bool _isDisposing; private string _category;
private string _category;
public LogManager(LoggerSettings settings, string serverIp, int serverPort)
public LogManager(LoggerSettings settings, string serverIp, int serverPort) {
{ if (string.IsNullOrEmpty(serverIp))
if (string.IsNullOrEmpty(serverIp)) {
{ throw new ArgumentNullException(nameof(serverIp));
throw new ArgumentNullException(nameof(serverIp)); }
}
if (serverPort < 0)
if (serverPort < 0) {
{ throw new ArgumentException("Server port must be zero or greater.");
throw new ArgumentException("Server port must be zero or greater."); }
}
settings.SysLogServers = new List<SyslogServer>
settings.SysLogServers = new List<SyslogServer> {
{ new(serverIp, serverPort)
new(serverIp, serverPort) };
};
Settings = settings;
Settings = settings; SetupLogManager();
SetupLogManager(); }
}
public LogManager(LoggerSettings settings, string category = null)
public LogManager(LoggerSettings settings, string category = null) {
{ _category = category;
_category = category; if (string.IsNullOrWhiteSpace(category))
if (string.IsNullOrWhiteSpace(category)) {
{ _category = "General";
_category = "General"; }
} Settings = settings;
Settings = settings; SetupFileLogger(settings);
SetupFileLogger(settings); SetupLogManager();
SetupLogManager(); LogHelper.OnException += LogHelper_OnException;
LogHelper.OnException += LogHelper_OnException; LogHelper.OnLogLevelDisabled += LogHelper_OnLogLevelDisabled;
} }
private DateTime CurrentDateTime => Settings.UseLocalTime ? DateTime.Now : DateTime.UtcNow; private void LogHelper_OnLogLevelDisabled(object sender, ErrorMessage e)
public ILoggerProvider LoggerProvider { get; private set; } {
public ILoggerFactory LoggerFactory { get; private set; } OnLogLevelDisabled?.Invoke(sender, e);
public ILogger Logger { get; private set; } }
public string CurrentLogFile => LoggerProvider is FileLoggerProvider fileLoggerProvider private DateTime CurrentDateTime => Settings.UseLocalTime ? DateTime.Now : DateTime.UtcNow;
? fileLoggerProvider.LogFile public ILoggerProvider LoggerProvider { get; private set; }
: string.Empty; public ILoggerFactory LoggerFactory { get; private set; }
public ILogger Logger { get; private set; }
public bool IsRunning { get; private set; }
public string CurrentLogFile => LoggerProvider is FileLoggerProvider fileLoggerProvider
public static LogManager Instance => _instance.Value; ? fileLoggerProvider.LogFile
: string.Empty;
public LoggerSettings Settings { get; set; } = CreateDefaultSettings();
public bool IsRunning { get; private set; }
public void Dispose()
{ public static LogManager Instance => _instance.Value;
DisposeAsync(true).GetAwaiter().GetResult();
GC.SuppressFinalize(this); public LoggerSettings Settings { get; set; } = CreateDefaultSettings();
}
public void Dispose()
public async Task WriteAsync(Exception exception, string module = null, string method = null, {
bool criticalException = false, DisposeAsync(true).GetAwaiter().GetResult();
bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, GC.SuppressFinalize(this);
string customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null, }
string grayLogSource = null, string grayLogVersion = "1.1")
{ public async Task WriteAsync(Exception exception, string module = null, string method = null,
if (exception == null) bool criticalException = false,
{ bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null,
return; string customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null,
} string grayLogSource = null, string grayLogVersion = "1.1")
{
await WriteAsync(exception.FormatExceptionToMessage(module, method), if (exception == null)
criticalException ? ELogType.CRITICAL : ELogType.ERROR, writeToConsole, sendToSysLogServers, {
sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource, return;
grayLogVersion); }
}
await WriteAsync(exception.FormatExceptionToMessage(module, method),
public async Task WriteAsync(string message, ELogType logType = ELogType.INFO, bool? writeToConsole = null, criticalException ? ELogType.CRITICAL : ELogType.ERROR, writeToConsole, sendToSysLogServers,
bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string customSplunkSourceType = null, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource,
bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null, grayLogVersion);
string grayLogVersion = "1.1") }
{
if (logType == ELogType.NONE) public async Task WriteAsync(string message, ELogType logType = ELogType.INFO, bool? writeToConsole = null,
{ bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string customSplunkSourceType = null,
return; bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null,
} string grayLogVersion = "1.1")
{
await InternalWriteAsync(CurrentDateTime, message, logType, writeToConsole, sendToSysLogServers, sendToSplunkServers, if (logType == ELogType.NONE)
customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource, grayLogVersion); {
} return;
}
public event EventHandler<ErrorMessage> OnException;
await InternalWriteAsync(CurrentDateTime, message, logType, writeToConsole, sendToSysLogServers, sendToSplunkServers,
private static LoggerSettings CreateDefaultSettings() customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource, grayLogVersion);
{ }
var settings = new LoggerSettings
{ /// <summary>
Id = "EonaCatLogger", /// Gets fired when an exception occurs during logging
MaxLogType = ELogType.INFO /// </summary>
}; public event EventHandler<ErrorMessage> OnException;
return settings;
} /// <summary>
/// Gets fired when the log level is disabled and the user tries to log a message
protected virtual async Task DisposeAsync(bool disposing) /// </summary>
{ public event EventHandler<ErrorMessage> OnLogLevelDisabled;
if (disposing)
{ private static LoggerSettings CreateDefaultSettings()
_isDisposing = true; {
await StopLoggingAsync(); var settings = new LoggerSettings
await Task.Delay(100); {
} Id = "EonaCatLogger",
} MaxLogType = ELogType.INFO
};
public async Task StartNewLogAsync() return settings;
{ }
if (_tokenSource.IsCancellationRequested)
{ protected virtual async Task DisposeAsync(bool disposing)
return; {
} if (disposing)
{
if (IsRunning && CurrentDateTime.Date > _logDate.Date) _isDisposing = true;
{ await StopLoggingAsync();
await StopLoggingAsync(); await Task.Delay(100);
} }
}
IsRunning = true;
public async Task StartNewLogAsync()
CreateLogger(); {
if (_tokenSource.IsCancellationRequested)
Directory.CreateDirectory(Settings.FileLoggerOptions.LogDirectory); {
return;
_logDate = CurrentDateTime; }
}
if (IsRunning && CurrentDateTime.Date > _logDate.Date)
private void CreateLogger() {
{ await StopLoggingAsync();
// Dispose of previous ServiceProvider if it exists }
LoggerProvider?.Dispose();
LoggerFactory?.Dispose(); IsRunning = true;
IServiceCollection serviceCollection = new ServiceCollection(); CreateLogger();
serviceCollection.AddLogging(builder => builder.SetMinimumLevel(Settings.MaxLogType.ToLogLevel())
.AddEonaCatFileLogger(configuration => Directory.CreateDirectory(Settings.FileLoggerOptions.LogDirectory);
{
var fileLoggerOptions = Settings.FileLoggerOptions; _logDate = CurrentDateTime;
configuration.MaxWriteTries = fileLoggerOptions.MaxWriteTries; }
configuration.RetainedFileCountLimit = fileLoggerOptions.RetainedFileCountLimit;
configuration.FlushPeriod = fileLoggerOptions.FlushPeriod; private void CreateLogger()
configuration.IsEnabled = fileLoggerOptions.IsEnabled; {
configuration.BatchSize = fileLoggerOptions.BatchSize; // Dispose of previous ServiceProvider if it exists
configuration.FileSizeLimit = fileLoggerOptions.FileSizeLimit; LoggerProvider?.Dispose();
configuration.LogDirectory = fileLoggerOptions.LogDirectory; LoggerFactory?.Dispose();
configuration.FileNamePrefix = fileLoggerOptions.FileNamePrefix;
configuration.MaxRolloverFiles = fileLoggerOptions.MaxRolloverFiles; IServiceCollection serviceCollection = new ServiceCollection();
configuration.UseLocalTime = Settings.UseLocalTime; serviceCollection.AddLogging(builder => builder.SetMinimumLevel(Settings.MaxLogType.ToLogLevel())
configuration.UseMask = Settings.UseMask; .AddEonaCatFileLogger(configuration =>
configuration.Mask = fileLoggerOptions.Mask; {
configuration.UseDefaultMasking = Settings.UseDefaultMasking; var fileLoggerOptions = Settings.FileLoggerOptions;
configuration.MaskedKeywords = fileLoggerOptions.MaskedKeywords; configuration.MaxWriteTries = fileLoggerOptions.MaxWriteTries;
})); configuration.RetainedFileCountLimit = fileLoggerOptions.RetainedFileCountLimit;
configuration.FlushPeriod = fileLoggerOptions.FlushPeriod;
var serviceProvider = serviceCollection.BuildServiceProvider(); configuration.IsEnabled = fileLoggerOptions.IsEnabled;
LoggerProvider = serviceProvider.GetService<ILoggerProvider>(); configuration.BatchSize = fileLoggerOptions.BatchSize;
LoggerFactory = serviceProvider.GetService<ILoggerFactory>(); configuration.FileSizeLimit = fileLoggerOptions.FileSizeLimit;
Logger = LoggerFactory.CreateLogger(Settings.Id); configuration.LogDirectory = fileLoggerOptions.LogDirectory;
LogHelper.SendToFile(Logger, Settings, ELogType.INFO, LogHelper.GetStartupMessage()); configuration.FileNamePrefix = fileLoggerOptions.FileNamePrefix;
} configuration.MaxRolloverFiles = fileLoggerOptions.MaxRolloverFiles;
configuration.UseLocalTime = Settings.UseLocalTime;
private async Task InternalWriteAsync(DateTime dateTime, string message, ELogType logType = ELogType.INFO, configuration.UseMask = Settings.UseMask;
bool? writeToConsole = null, bool? sendToSyslogServers = null, bool? sendToSplunkServers = null, configuration.Mask = fileLoggerOptions.Mask;
string customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null, configuration.UseDefaultMasking = Settings.UseDefaultMasking;
string grayLogSource = null, string grayLogVersion = "1.1") configuration.MaskedKeywords = fileLoggerOptions.MaskedKeywords;
{ }));
if (string.IsNullOrEmpty(message) || logType == ELogType.NONE ||
(int)logType > (int)Settings.MaxLogType) var serviceProvider = serviceCollection.BuildServiceProvider();
{ LoggerProvider = serviceProvider.GetService<ILoggerProvider>();
return; LoggerFactory = serviceProvider.GetService<ILoggerFactory>();
} Logger = LoggerFactory.CreateLogger(Settings.Id);
LogHelper.SendToFile(Logger, Settings, ELogType.INFO, LogHelper.GetStartupMessage());
if (_isDisposing) }
{
return; private async Task InternalWriteAsync(DateTime dateTime, string message, ELogType logType = ELogType.INFO,
} bool? writeToConsole = null, bool? sendToSyslogServers = null, bool? sendToSplunkServers = null,
string customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null,
if (!IsRunning) string grayLogSource = null, string grayLogVersion = "1.1")
{ {
await StartNewLogAsync(); if (string.IsNullOrWhiteSpace(message) || logType == ELogType.NONE || !IsLogLevelEnabled(logType) || _isDisposing)
} {
return;
var messageWithHeader = LogHelper.FormatMessageWithHeader(Settings, logType, message, dateTime, _category); }
var writeToConsoleValue = writeToConsole ?? Settings.EnableConsole;
var sendToSyslogServersValue = sendToSyslogServers ?? Settings.SendToSyslogServers; if (!IsRunning)
var sendToSplunkServersValue = sendToSplunkServers ?? Settings.SendToSplunkServers; {
var sendToGrayLogServersValue = sendToGrayLogServers ?? Settings.SendToGrayLogServers; await StartNewLogAsync().ConfigureAwait(false);
}
LogHelper.SendToFile(Logger, Settings, logType, message);
var messageWithHeader = LogHelper.FormatMessageWithHeader(Settings, logType, message, dateTime, _category);
if (writeToConsoleValue) var writeToConsoleValue = writeToConsole.GetValueOrDefault(Settings.EnableConsole);
{ var sendToSyslogServersValue = sendToSyslogServers.GetValueOrDefault(Settings.SendToSyslogServers);
LogHelper.SendToConsole(Settings, logType, messageWithHeader, true); var sendToSplunkServersValue = sendToSplunkServers.GetValueOrDefault(Settings.SendToSplunkServers);
} var sendToGrayLogServersValue = sendToGrayLogServers.GetValueOrDefault(Settings.SendToGrayLogServers);
var tasks = new List<Task>(); LogHelper.SendToFile(Logger, Settings, logType, message);
if (sendToSyslogServersValue || sendToSplunkServersValue || sendToGrayLogServersValue)
{ if (writeToConsoleValue)
if (sendToSyslogServersValue) {
{ LogHelper.SendToConsole(Settings, logType, messageWithHeader, true);
tasks.Add(LogHelper.SendToSysLogServersAsync(Settings, messageWithHeader, true)); }
}
var tasks = new List<Task>(3);
if (sendToSplunkServersValue) if (sendToSyslogServersValue)
{ {
tasks.Add(LogHelper.SendToSplunkServersAsync(Settings, customSplunkSourceType ?? logType.ToString(), tasks.Add(LogHelper.SendToSysLogServersAsync(Settings, messageWithHeader, true));
messageWithHeader, true)); }
} if (sendToSplunkServersValue)
{
if (sendToGrayLogServersValue) tasks.Add(LogHelper.SendToSplunkServersAsync(Settings, customSplunkSourceType ?? logType.ToString(),
{ messageWithHeader, true));
tasks.Add(LogHelper.SendToGrayLogServersAsync(Settings, messageWithHeader, logType, grayLogFacility, }
grayLogSource, true, grayLogVersion)); if (sendToGrayLogServersValue)
} {
tasks.Add(LogHelper.SendToGrayLogServersAsync(Settings, messageWithHeader, logType, grayLogFacility,
await Task.WhenAll(tasks); grayLogSource, true, grayLogVersion));
} }
var logMessage = new EonaCatLogMessage if (tasks.Count > 0)
{ {
DateTime = dateTime, await Task.WhenAll(tasks).ConfigureAwait(false);
Message = message, }
LogType = logType,
Origin = string.IsNullOrWhiteSpace(Settings.LogOrigin) ? "LogManager" : Settings.LogOrigin var logMessage = new EonaCatLogMessage
}; {
DateTime = dateTime,
Settings.OnLogEvent(logMessage); Message = message,
} LogType = logType,
Origin = string.IsNullOrEmpty(Settings.LogOrigin) ? "LogManager" : Settings.LogOrigin
public void Reset() };
{ Settings.OnLogEvent(logMessage);
Settings.ResetLogEvent(); }
}
private bool IsLogLevelEnabled(ELogType logType)
private void LogHelper_OnException(object sender, ErrorMessage e) {
{ if (Settings == null)
OnException?.Invoke(sender, e); {
} OnLogLevelDisabled?.Invoke(this, new ErrorMessage { Message = "Settings is null." });
return false;
private void SetupFileLogger(LoggerSettings settings = null, string logFolder = null, bool defaultPrefix = true) }
{
if (settings == null) var isEnabled = Settings.MaxLogType != ELogType.NONE && logType <= Settings.MaxLogType;
{ if (!isEnabled)
settings = Settings; {
} OnLogLevelDisabled?.Invoke(this, new ErrorMessage { Message = $"Logtype '{logType}' is not enabled, cannot log message" });
}
if (!settings.EnableFileLogging) return isEnabled;
{ }
return;
} public void Reset()
{
if (logFolder != null) Settings.ResetLogEvent();
{ }
settings.FileLoggerOptions.LogDirectory = logFolder;
} private void LogHelper_OnException(object sender, ErrorMessage e)
{
if (string.IsNullOrWhiteSpace(settings.FileLoggerOptions.FileNamePrefix)) OnException?.Invoke(sender, e);
{ }
settings.FileLoggerOptions.FileNamePrefix = defaultPrefix ? "EonaCat" : string.Empty;
} private void SetupFileLogger(LoggerSettings settings = null, string logFolder = null, bool defaultPrefix = true)
} {
if (settings == null)
private void SetupLogManager() {
{ settings = Settings;
AppDomain.CurrentDomain.ProcessExit += ProcessExit; }
_logDate = CurrentDateTime;
} if (!settings.EnableFileLogging)
{
private void ProcessExit(object sender, EventArgs e) return;
{ }
Dispose();
} if (logFolder != null)
{
private Task StopLoggingAsync() settings.FileLoggerOptions.LogDirectory = logFolder;
{ }
WriteStopMessage();
IsRunning = false; if (string.IsNullOrWhiteSpace(settings.FileLoggerOptions.FileNamePrefix))
return Task.CompletedTask; {
} settings.FileLoggerOptions.FileNamePrefix = defaultPrefix ? "EonaCat" : string.Empty;
}
private void WriteStopMessage() }
{
var stopMessage = $"{DllInfo.ApplicationName} stopped.{Environment.NewLine}"; private void SetupLogManager()
LogHelper.SendToFile(Logger, Settings, ELogType.INFO, stopMessage); {
} AppDomain.CurrentDomain.ProcessExit += ProcessExit;
_logDate = CurrentDateTime;
public void DeleteCurrentLogFile() }
{
if (!string.IsNullOrEmpty(CurrentLogFile)) private void ProcessExit(object sender, EventArgs e)
{ {
File.Delete(CurrentLogFile); Dispose();
} }
}
} private Task StopLoggingAsync()
{
WriteStopMessage();
IsRunning = false;
return Task.CompletedTask;
}
private void WriteStopMessage()
{
var stopMessage = $"{DllInfo.ApplicationName} stopped.{Environment.NewLine}";
LogHelper.SendToFile(Logger, Settings, ELogType.INFO, stopMessage);
}
public void DeleteCurrentLogFile()
{
if (!string.IsNullOrEmpty(CurrentLogFile))
{
File.Delete(CurrentLogFile);
}
}
}
} }

View File

@@ -75,8 +75,10 @@ public class SyslogServer
/// <summary> /// <summary>
/// IP:port of the server. /// IP:port of the server.
/// </summary> /// </summary>
public string IpPort => _Hostname + ":" + _Port; public string IpPort => _Hostname + ":" + _Port;
public bool SupportsTcp { get; set; }
private void SetUdp() private void SetUdp()
{ {
Udp = null; Udp = null;

View File

@@ -95,6 +95,7 @@ namespace EonaCat.Logger.Advanced
// Create and configure a LogManager for logging. // Create and configure a LogManager for logging.
_logManager = new LogManager(new LoggerSettings { RemoveMessagePrefix = true }); _logManager = new LogManager(new LoggerSettings { RemoveMessagePrefix = true });
_logManager.OnException += _logManager_OnException; _logManager.OnException += _logManager_OnException;
_logManager.OnLogLevelDisabled += _logManager_OnLogLevelDisabled;
_logManager.Settings.FileLoggerOptions.FileNamePrefix = "advanced"; _logManager.Settings.FileLoggerOptions.FileNamePrefix = "advanced";
_logManager.Settings.UseLocalTime = true; _logManager.Settings.UseLocalTime = true;
_logManager.Settings.FileLoggerOptions.UseLocalTime = true; _logManager.Settings.FileLoggerOptions.UseLocalTime = true;
@@ -106,6 +107,16 @@ namespace EonaCat.Logger.Advanced
// Event handler for LogManager exceptions, writes messages to the console. // Event handler for LogManager exceptions, writes messages to the console.
private static void _logManager_OnException(object? sender, ErrorMessage e) private static void _logManager_OnException(object? sender, ErrorMessage e)
{
Console.WriteLine(e.Message);
if (e.Exception != null)
{
Console.WriteLine(e.Exception);
}
}
// Event handler for LogManager loglevel disabled notifications, writes messages to the console.
private static void _logManager_OnLogLevelDisabled(object? sender, ErrorMessage e)
{ {
Console.WriteLine(e.Message); Console.WriteLine(e.Message);
if (e.Exception != null) if (e.Exception != null)