This commit is contained in:
Jeroen Saey
2025-02-13 15:59:51 +01:00
parent 8d902100de
commit b18f291629
4 changed files with 173 additions and 82 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.4</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.4+{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

@@ -77,6 +77,8 @@ public class GrayLogServer
/// </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

@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using EonaCat.Json; using EonaCat.Json;
@@ -144,33 +145,36 @@ internal static class LogHelper
public static async Task SendToSplunkServersAsync(LoggerSettings settings, SplunkPayload splunkPayload, bool sendToSplunkServer) public static async Task SendToSplunkServersAsync(LoggerSettings settings, SplunkPayload splunkPayload, bool sendToSplunkServer)
{ {
if (settings == null || !sendToSplunkServer || splunkPayload == null) return; if (settings == null || !sendToSplunkServer || splunkPayload == null)
foreach (var splunkServer in settings.SplunkServers ?? Enumerable.Empty<SplunkServer.SplunkServer>())
{ {
if (!splunkServer.HasHecUrl || !splunkServer.HasHecToken) return;
{
OnException?.Invoke(null, new ErrorMessage { Message = $"Invalid Splunk server configuration for '{splunkServer.SplunkHecUrl}'" });
continue;
} }
var tasks = settings.SplunkServers?
.Where(splunkServer => splunkServer.HasHecUrl && splunkServer.HasHecToken)
.Select(async splunkServer =>
{
try try
{ {
var response = await splunkServer.SendAsync(splunkPayload); var response = await splunkServer.SendAsync(splunkPayload);
if (!response.IsSuccessStatusCode) if (!response.IsSuccessStatusCode)
{ {
OnException?.Invoke(null, new ErrorMessage { Message = $"Failed to send log to Splunk '{splunkServer.SplunkHecUrl}'. Status code: {response.StatusCode}" }); LogError($"Failed to send log to Splunk '{splunkServer.SplunkHecUrl}'. Status code: {response.StatusCode}");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
OnException?.Invoke(null, new ErrorMessage { Exception = ex, Message = $"Error logging to Splunk '{splunkServer.SplunkHecUrl}': {ex.Message}" }); LogError($"Error logging to Splunk '{splunkServer.SplunkHecUrl}': {ex.Message}", ex);
}
} }
}) ?? new List<Task>();
await Task.WhenAll(tasks);
} }
public static async Task SendToSplunkServersAsync(LoggerSettings settings, string logType, string message, /// <summary>
bool sendToSplunkServer) /// Overload for sending a simple log message to Splunk.
/// </summary>
public static async Task SendToSplunkServersAsync(LoggerSettings settings, string logType, string message, bool sendToSplunkServer)
{ {
if (settings == null || !sendToSplunkServer || string.IsNullOrWhiteSpace(message)) if (settings == null || !sendToSplunkServer || string.IsNullOrWhiteSpace(message))
{ {
@@ -187,6 +191,15 @@ internal static class LogHelper
await SendToSplunkServersAsync(settings, splunkPayload, sendToSplunkServer); await SendToSplunkServersAsync(settings, splunkPayload, sendToSplunkServer);
} }
/// <summary>
/// Logs an error using the OnException event.
/// </summary>
private static void LogError(string message, Exception ex = null)
{
OnException?.Invoke(null, new ErrorMessage { Message = message, Exception = ex });
}
internal static async Task SendToGrayLogServersAsync(LoggerSettings settings, string message, ELogType logLevel, internal static async Task SendToGrayLogServersAsync(LoggerSettings settings, string message, ELogType logLevel,
string facility, string source, bool sendToGrayLogServer, string version = "1.1") string facility, string source, bool sendToGrayLogServer, string version = "1.1")
{ {
@@ -195,10 +208,8 @@ internal static class LogHelper
return; return;
} }
foreach (var grayLogServer in settings.GrayLogServers ?? new List<GrayLogServer> { new("127.0.0.1") }) const int MaxUdpPacketSize = 4096;
{
try
{
var gelfMessage = new var gelfMessage = new
{ {
version, version,
@@ -211,62 +222,138 @@ internal static class LogHelper
}; };
var messageBytes = Encoding.UTF8.GetBytes(JsonHelper.ToJson(gelfMessage)); var messageBytes = Encoding.UTF8.GetBytes(JsonHelper.ToJson(gelfMessage));
var tasks = settings.GrayLogServers?
.Where(server => !string.IsNullOrWhiteSpace(server.Hostname) && server.Port >= 0)
.Select(async grayLogServer =>
{
try
{
if (messageBytes.Length <= MaxUdpPacketSize)
{
// Send via UDP (single packet)
await grayLogServer.Udp.SendAsync(messageBytes, messageBytes.Length); await grayLogServer.Udp.SendAsync(messageBytes, messageBytes.Length);
} }
catch (Exception exception) else if (grayLogServer.SupportsTcp)
{ {
OnException?.Invoke(null, // Send via TCP if supported
new ErrorMessage await SendViaTcpAsync(grayLogServer, messageBytes);
}
else
{ {
Exception = exception, // Chunk large messages for UDP
Message = await SendUdpInChunksAsync(grayLogServer, messageBytes, MaxUdpPacketSize);
$"Error while logging to GrayLog Server '{grayLogServer.Hostname}': {exception.Message}" }
}
catch (Exception ex)
{
OnException?.Invoke(null, new ErrorMessage
{
Exception = ex,
Message = $"Error logging to GrayLog Server '{grayLogServer.Hostname}': {ex.Message}"
}); });
} }
}) ?? new List<Task>();
await Task.WhenAll(tasks);
}
/// <summary>
/// Sends a message via TCP to a GrayLog server.
/// </summary>
private static async Task SendViaTcpAsync(GrayLogServer 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(GrayLogServer 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 async Task SendToSysLogServersAsync(LoggerSettings settings, string message, internal static async Task SendToSysLogServersAsync(LoggerSettings settings, string message, bool sendToSyslogServers)
bool sendToSyslogServers)
{ {
if (settings == null || !sendToSyslogServers || string.IsNullOrWhiteSpace(message)) if (settings == null || !sendToSyslogServers || string.IsNullOrWhiteSpace(message))
{ {
return; return;
} }
foreach (var server in settings.SysLogServers ?? new List<SyslogServer> { new("127.0.0.1") }) const int MaxUdpPacketSize = 4096;
var tasks = settings.SysLogServers?
.Where(server => !string.IsNullOrWhiteSpace(server.Hostname) && server.Port >= 0)
.Select(async server =>
{ {
try try
{ {
if (string.IsNullOrWhiteSpace(server.Hostname))
{
OnException?.Invoke(null,
new ErrorMessage { Message = "Server hostname not specified, skipping SysLog Server" });
continue;
}
if (server.Port < 0)
{
OnException?.Invoke(null,
new ErrorMessage { Message = "Server port must be zero or greater, skipping SysLog Server" });
continue;
}
var data = Encoding.UTF8.GetBytes(message); var data = Encoding.UTF8.GetBytes(message);
if (data.Length <= MaxUdpPacketSize)
{
// Send via UDP (single packet)
await server.Udp.SendAsync(data, data.Length); await server.Udp.SendAsync(data, data.Length);
} }
catch (Exception exception) else if (server.SupportsTcp)
{ {
OnException?.Invoke(null, // Send via TCP if supported
new ErrorMessage await SendViaTcpAsync(server, data);
}
else
{ {
Exception = exception, // Chunk large messages for UDP
Message = $"Error while logging to SysLog Server '{server.Hostname}': {exception.Message}" 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() internal static string GetStartupMessage()
{ {
return $"{DllInfo.ApplicationName} started."; return $"{DllInfo.ApplicationName} started.";

View File

@@ -77,6 +77,8 @@ public class SyslogServer
/// </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;