|
|
|
|
@@ -2,6 +2,7 @@
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Net.Sockets;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using EonaCat.Json;
|
|
|
|
|
@@ -144,33 +145,36 @@ internal static class LogHelper
|
|
|
|
|
|
|
|
|
|
public static async Task SendToSplunkServersAsync(LoggerSettings settings, SplunkPayload splunkPayload, bool sendToSplunkServer)
|
|
|
|
|
{
|
|
|
|
|
if (settings == null || !sendToSplunkServer || splunkPayload == null) return;
|
|
|
|
|
|
|
|
|
|
foreach (var splunkServer in settings.SplunkServers ?? Enumerable.Empty<SplunkServer.SplunkServer>())
|
|
|
|
|
if (settings == null || !sendToSplunkServer || splunkPayload == null)
|
|
|
|
|
{
|
|
|
|
|
if (!splunkServer.HasHecUrl || !splunkServer.HasHecToken)
|
|
|
|
|
{
|
|
|
|
|
OnException?.Invoke(null, new ErrorMessage { Message = $"Invalid Splunk server configuration for '{splunkServer.SplunkHecUrl}'" });
|
|
|
|
|
continue;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var tasks = settings.SplunkServers?
|
|
|
|
|
.Where(splunkServer => splunkServer.HasHecUrl && splunkServer.HasHecToken)
|
|
|
|
|
.Select(async splunkServer =>
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var response = await splunkServer.SendAsync(splunkPayload);
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
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,
|
|
|
|
|
bool sendToSplunkServer)
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 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))
|
|
|
|
|
{
|
|
|
|
|
@@ -187,6 +191,15 @@ internal static class LogHelper
|
|
|
|
|
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,
|
|
|
|
|
string facility, string source, bool sendToGrayLogServer, string version = "1.1")
|
|
|
|
|
{
|
|
|
|
|
@@ -195,10 +208,8 @@ internal static class LogHelper
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var grayLogServer in settings.GrayLogServers ?? new List<GrayLogServer> { new("127.0.0.1") })
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
const int MaxUdpPacketSize = 4096;
|
|
|
|
|
|
|
|
|
|
var gelfMessage = new
|
|
|
|
|
{
|
|
|
|
|
version,
|
|
|
|
|
@@ -211,62 +222,138 @@ internal static class LogHelper
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception exception)
|
|
|
|
|
else if (grayLogServer.SupportsTcp)
|
|
|
|
|
{
|
|
|
|
|
OnException?.Invoke(null,
|
|
|
|
|
new ErrorMessage
|
|
|
|
|
// Send via TCP if supported
|
|
|
|
|
await SendViaTcpAsync(grayLogServer, messageBytes);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Exception = exception,
|
|
|
|
|
Message =
|
|
|
|
|
$"Error while logging to GrayLog Server '{grayLogServer.Hostname}': {exception.Message}"
|
|
|
|
|
// Chunk large messages for UDP
|
|
|
|
|
await SendUdpInChunksAsync(grayLogServer, messageBytes, MaxUdpPacketSize);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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,
|
|
|
|
|
bool sendToSyslogServers)
|
|
|
|
|
internal static async Task SendToSysLogServersAsync(LoggerSettings settings, string message, bool sendToSyslogServers)
|
|
|
|
|
{
|
|
|
|
|
if (settings == null || !sendToSyslogServers || string.IsNullOrWhiteSpace(message))
|
|
|
|
|
{
|
|
|
|
|
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
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
if (data.Length <= MaxUdpPacketSize)
|
|
|
|
|
{
|
|
|
|
|
// Send via UDP (single packet)
|
|
|
|
|
await server.Udp.SendAsync(data, data.Length);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception exception)
|
|
|
|
|
else if (server.SupportsTcp)
|
|
|
|
|
{
|
|
|
|
|
OnException?.Invoke(null,
|
|
|
|
|
new ErrorMessage
|
|
|
|
|
// Send via TCP if supported
|
|
|
|
|
await SendViaTcpAsync(server, data);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Exception = exception,
|
|
|
|
|
Message = $"Error while logging to SysLog Server '{server.Hostname}': {exception.Message}"
|
|
|
|
|
// 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.";
|
|
|
|
|
|