Updated
This commit is contained in:
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace EonaCat.Logger.Servers.GrayLog;
|
||||
|
||||
// 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.
|
||||
|
||||
|
||||
@@ -1,155 +1,159 @@
|
||||
using EonaCat.Json;
|
||||
|
||||
namespace EonaCat.Logger.Servers.Splunk;
|
||||
|
||||
using EonaCat.Logger.Servers.Splunk.Models;
|
||||
// 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.
|
||||
|
||||
/// <summary>
|
||||
/// Splunk Server.
|
||||
/// </summary>
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using EonaCat.Json;
|
||||
using EonaCat.Logger.Servers.Splunk.Models;
|
||||
|
||||
public class Splunk : IDisposable
|
||||
namespace EonaCat.Logger.Servers.Splunk
|
||||
{
|
||||
internal readonly object SendLock = new();
|
||||
private string _splunkHecUrl = "https://127.0.0.1:8088/services/collector/event";
|
||||
|
||||
public string SplunkHecToken { get; set; } = "splunk-hec-token";
|
||||
public HttpClientHandler SplunkClientHandler { get; private set; }
|
||||
public HttpClient HttpClient { get; private set; }
|
||||
public string Nickname { get; set; }
|
||||
|
||||
public bool HasHecToken => !string.IsNullOrWhiteSpace(SplunkHecToken) && SplunkHecToken != "splunk-hec-token";
|
||||
public bool HasHecUrl => !string.IsNullOrWhiteSpace(SplunkHecUrl);
|
||||
public bool IsHttpsHecUrl => HasHecUrl && _splunkHecUrl.ToLower().StartsWith("https");
|
||||
public bool IsLocalHost => HasHecUrl && (_splunkHecUrl.ToLower().Contains("127.0.0.1") || _splunkHecUrl.ToLower().Contains("localhost"));
|
||||
|
||||
/// <summary>
|
||||
/// Splunk Server
|
||||
/// Splunk Server.
|
||||
/// </summary>
|
||||
/// <param name="splunkHecUrl"></param>
|
||||
/// <param name="splunkHecToken"></param>
|
||||
/// <param name="httpClientHandler"></param>
|
||||
/// <param name="nickName"></param>
|
||||
/// <param name="typesToLog"></param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public Splunk(string splunkHecUrl, string splunkHecToken, HttpClientHandler httpClientHandler = null, string nickName = null, List<ELogType> typesToLog = null)
|
||||
public class Splunk : IDisposable
|
||||
{
|
||||
SplunkHecUrl = splunkHecUrl ?? throw new ArgumentNullException(nameof(splunkHecUrl));
|
||||
SplunkHecToken = splunkHecToken ?? throw new ArgumentNullException(nameof(splunkHecToken));
|
||||
private string _splunkHecUrl = "https://127.0.0.1:8088/services/collector/event";
|
||||
private HttpClient _httpClient;
|
||||
private HttpClientHandler _httpClientHandler;
|
||||
public event EventHandler<Exception> OnException;
|
||||
|
||||
Nickname = nickName ?? $"{splunkHecUrl}_{splunkHecToken}";
|
||||
SplunkClientHandler = httpClientHandler ?? new HttpClientHandler();
|
||||
TypesToLog = typesToLog;
|
||||
public string SplunkHecToken { get; set; } = "splunk-hec-token";
|
||||
public string Nickname { get; set; }
|
||||
public List<ELogType> TypesToLog { get; set; }
|
||||
|
||||
CreateHttpClient();
|
||||
}
|
||||
|
||||
public string SplunkHecUrl
|
||||
{
|
||||
get => _splunkHecUrl;
|
||||
set
|
||||
public string SplunkHecUrl
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(value) && !value.ToLower().Contains("http"))
|
||||
get => _splunkHecUrl;
|
||||
set
|
||||
{
|
||||
value = $"http://{value}";
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
throw new ArgumentNullException(nameof(SplunkHecUrl));
|
||||
|
||||
_splunkHecUrl = value.StartsWith("http", StringComparison.OrdinalIgnoreCase)
|
||||
? value
|
||||
: $"http://{value}";
|
||||
|
||||
RecreateHttpClient();
|
||||
}
|
||||
|
||||
_splunkHecUrl = value;
|
||||
CreateHttpClient();
|
||||
}
|
||||
}
|
||||
|
||||
public List<ELogType> TypesToLog { get; set; }
|
||||
|
||||
private void CreateHttpClient()
|
||||
{
|
||||
DisposeHttpClient();
|
||||
|
||||
SplunkClientHandler ??= new HttpClientHandler();
|
||||
HttpClient = new HttpClient(SplunkClientHandler)
|
||||
{
|
||||
BaseAddress = new Uri(SplunkHecUrl)
|
||||
};
|
||||
|
||||
HttpClient.DefaultRequestHeaders.Add("Authorization", $"Splunk {SplunkHecToken}");
|
||||
}
|
||||
|
||||
public void DisableSSLValidation()
|
||||
{
|
||||
DisposeHttpClient();
|
||||
|
||||
SplunkClientHandler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
|
||||
};
|
||||
|
||||
CreateHttpClient();
|
||||
}
|
||||
|
||||
public async Task<HttpResponseMessage> SendAsync(SplunkPayload splunkPayload, bool disableSplunkSSL = false)
|
||||
{
|
||||
if (splunkPayload == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var eventObject = new
|
||||
{
|
||||
@event = splunkPayload.EventData,
|
||||
sourcetype = splunkPayload.SourceType,
|
||||
host = splunkPayload.Host
|
||||
};
|
||||
public bool HasHecToken => !string.IsNullOrWhiteSpace(SplunkHecToken) && SplunkHecToken != "splunk-hec-token";
|
||||
public bool HasHecUrl => !string.IsNullOrWhiteSpace(_splunkHecUrl);
|
||||
public bool IsHttpsHecUrl => _splunkHecUrl.StartsWith("https", StringComparison.OrdinalIgnoreCase);
|
||||
public bool IsLocalHost => _splunkHecUrl.ToLower().Contains("127.0.0.1") || _splunkHecUrl.ToLower().Contains("localhost");
|
||||
|
||||
if (disableSplunkSSL)
|
||||
{
|
||||
DisableSSLValidation();
|
||||
}
|
||||
|
||||
if (HttpClient == null)
|
||||
public Splunk(string splunkHecUrl, string splunkHecToken, HttpClientHandler handler = null, string nickName = null, List<ELogType> typesToLog = null)
|
||||
{
|
||||
SplunkHecToken = splunkHecToken ?? throw new ArgumentNullException(nameof(splunkHecToken));
|
||||
SplunkHecUrl = splunkHecUrl ?? throw new ArgumentNullException(nameof(splunkHecUrl));
|
||||
Nickname = nickName ?? $"{splunkHecUrl}_{splunkHecToken}";
|
||||
TypesToLog = typesToLog;
|
||||
_httpClientHandler = handler ?? new HttpClientHandler();
|
||||
CreateHttpClient();
|
||||
}
|
||||
|
||||
var eventJson = JsonHelper.ToJson(eventObject);
|
||||
if (!HasHecToken)
|
||||
private void CreateHttpClient()
|
||||
{
|
||||
DisposeHttpClient();
|
||||
|
||||
_httpClientHandler ??= new HttpClientHandler();
|
||||
_httpClient = new HttpClient(_httpClientHandler)
|
||||
{
|
||||
BaseAddress = new Uri(SplunkHecUrl)
|
||||
};
|
||||
|
||||
if (!_httpClient.DefaultRequestHeaders.Contains("Authorization"))
|
||||
{
|
||||
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Splunk {SplunkHecToken}");
|
||||
}
|
||||
}
|
||||
|
||||
private void RecreateHttpClient()
|
||||
{
|
||||
DisposeHttpClient();
|
||||
_httpClientHandler = new HttpClientHandler();
|
||||
CreateHttpClient();
|
||||
}
|
||||
|
||||
using var content = new StringContent(eventJson, Encoding.UTF8, "application/json");
|
||||
return await HttpClient.PostAsync("/services/collector/event", content);
|
||||
}
|
||||
|
||||
internal void DisposeHttpClient()
|
||||
{
|
||||
if (HttpClient != null)
|
||||
public void DisableSSLValidation()
|
||||
{
|
||||
HttpClient.Dispose();
|
||||
HttpClient = null;
|
||||
DisposeHttpClient();
|
||||
_httpClientHandler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = (_, _, _, _) => true
|
||||
};
|
||||
CreateHttpClient();
|
||||
}
|
||||
|
||||
if (SplunkClientHandler != null)
|
||||
public async Task<HttpResponseMessage> SendAsync(SplunkPayload payload, bool disableSplunkSSL = false)
|
||||
{
|
||||
SplunkClientHandler.Dispose();
|
||||
SplunkClientHandler = null;
|
||||
try
|
||||
{
|
||||
if (payload == null)
|
||||
return null;
|
||||
|
||||
if (!HasHecToken || !HasHecUrl)
|
||||
return null;
|
||||
|
||||
if (disableSplunkSSL)
|
||||
DisableSSLValidation();
|
||||
|
||||
_httpClient ??= new HttpClient(_httpClientHandler ?? new HttpClientHandler())
|
||||
{
|
||||
BaseAddress = new Uri(SplunkHecUrl)
|
||||
};
|
||||
|
||||
var eventObject = new
|
||||
{
|
||||
@event = payload.EventData,
|
||||
sourcetype = payload.SourceType,
|
||||
host = payload.Host
|
||||
};
|
||||
|
||||
var json = JsonHelper.ToJson(eventObject);
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, "services/collector/event")
|
||||
{
|
||||
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
||||
};
|
||||
|
||||
return await _httpClient.SendAsync(request);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnException?.Invoke(this, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeHttpClient();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
internal void DisposeHttpClient()
|
||||
{
|
||||
try
|
||||
{
|
||||
_httpClient?.Dispose();
|
||||
_httpClientHandler?.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Silent fail
|
||||
}
|
||||
finally
|
||||
{
|
||||
_httpClient = null;
|
||||
_httpClientHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
~Splunk()
|
||||
{
|
||||
Dispose();
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeHttpClient();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~Splunk()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,194 +1,191 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EonaCat.Logger.Servers.Syslog;
|
||||
// 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.
|
||||
|
||||
/// <summary>
|
||||
/// Syslog server.
|
||||
/// </summary>
|
||||
public class Syslog : IDisposable
|
||||
namespace EonaCat.Logger.Servers.Syslog
|
||||
{
|
||||
const int MaxUdpPacketSize = 4096;
|
||||
private string _Hostname = "127.0.0.1";
|
||||
private int _Port = 514;
|
||||
public bool IsConnected { get; private set; }
|
||||
|
||||
internal UdpClient Udp;
|
||||
|
||||
public Syslog() { }
|
||||
|
||||
/// <summary>
|
||||
/// Syslog server
|
||||
/// </summary>
|
||||
/// <param name="hostname"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="nickName"></param>
|
||||
/// <param name="typesToLog"></param>
|
||||
/// <param name="convertToRfc5424"></param>
|
||||
/// <param name="convertToRfc3164"></param>
|
||||
public Syslog(string hostname = "127.0.0.1", int port = 514, string nickName = null, List<ELogType> typesToLog = null, bool convertToRfc5424 = false, bool convertToRfc3164 = false)
|
||||
// This file is part of the EonaCat project(s) released under the Apache License.
|
||||
public class Syslog : IDisposable
|
||||
{
|
||||
Hostname = hostname;
|
||||
Port = port;
|
||||
Nickname = nickName ?? IpPort;
|
||||
TypesToLog = typesToLog;
|
||||
ConvertToRfc5424 = convertToRfc5424;
|
||||
ConvertToRfc3164 = convertToRfc3164;
|
||||
}
|
||||
private const int MaxUdpPacketSize = 4096;
|
||||
private string _hostname = "127.0.0.1";
|
||||
private int _port = 514;
|
||||
private UdpClient _udp;
|
||||
public event EventHandler<Exception> OnException;
|
||||
|
||||
public string Hostname
|
||||
{
|
||||
get => _Hostname;
|
||||
set
|
||||
public bool IsConnected { get; private set; }
|
||||
public string Nickname { get; set; }
|
||||
public string IpPort => $"{_hostname}:{_port}";
|
||||
public bool SupportsTcp { get; set; }
|
||||
public List<ELogType> TypesToLog { get; set; }
|
||||
public bool ConvertToRfc5424 { get; set; }
|
||||
public bool ConvertToRfc3164 { get; set; }
|
||||
|
||||
public Syslog() { }
|
||||
|
||||
public Syslog(string hostname = "127.0.0.1", int port = 514, string nickName = null, List<ELogType> typesToLog = null, bool convertToRfc5424 = false, bool convertToRfc3164 = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
Hostname = hostname;
|
||||
Port = port;
|
||||
Nickname = nickName ?? IpPort;
|
||||
TypesToLog = typesToLog;
|
||||
ConvertToRfc5424 = convertToRfc5424;
|
||||
ConvertToRfc3164 = convertToRfc3164;
|
||||
}
|
||||
|
||||
public string Hostname
|
||||
{
|
||||
get => _hostname;
|
||||
set
|
||||
{
|
||||
throw new ArgumentNullException(nameof(Hostname));
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
throw new ArgumentNullException(nameof(Hostname));
|
||||
|
||||
_hostname = value;
|
||||
SetUdp();
|
||||
}
|
||||
|
||||
_Hostname = value;
|
||||
SetUdp();
|
||||
}
|
||||
}
|
||||
|
||||
public int Port
|
||||
{
|
||||
get => _Port;
|
||||
set
|
||||
public int Port
|
||||
{
|
||||
if (value < 0)
|
||||
get => _port;
|
||||
set
|
||||
{
|
||||
throw new ArgumentException("Port must be zero or greater.");
|
||||
if (value < 0)
|
||||
throw new ArgumentException("Port must be zero or greater.");
|
||||
|
||||
_port = value;
|
||||
SetUdp();
|
||||
}
|
||||
|
||||
_Port = value;
|
||||
SetUdp();
|
||||
}
|
||||
}
|
||||
|
||||
public string IpPort => _Hostname + ":" + _Port;
|
||||
public bool SupportsTcp { get; set; }
|
||||
public string Nickname { get; set; }
|
||||
public List<ELogType> TypesToLog { get; set; }
|
||||
public bool ConvertToRfc5424 { get; set; }
|
||||
public bool ConvertToRfc3164 { get; set; }
|
||||
|
||||
internal void SetUdp()
|
||||
{
|
||||
try
|
||||
internal void SetUdp()
|
||||
{
|
||||
DisposeUdp();
|
||||
Udp = new UdpClient(_Hostname, _Port);
|
||||
IsConnected = true;
|
||||
try
|
||||
{
|
||||
DisposeUdp();
|
||||
_udp = new UdpClient(_hostname, _port);
|
||||
IsConnected = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
IsConnected = false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
IsConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal void DisposeUdp()
|
||||
{
|
||||
if (Udp != null)
|
||||
internal void DisposeUdp()
|
||||
{
|
||||
IsConnected = false;
|
||||
|
||||
try
|
||||
{
|
||||
Udp?.Close();
|
||||
Udp?.Dispose();
|
||||
_udp?.Close();
|
||||
_udp?.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Do nothing
|
||||
// Swallow cleanup exceptions
|
||||
}
|
||||
Udp = null;
|
||||
|
||||
_udp = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeUdp();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public async Task WriteAsync(string message)
|
||||
{
|
||||
var data = Encoding.UTF8.GetBytes(message);
|
||||
await SendAsync(data);
|
||||
}
|
||||
|
||||
public async Task WriteAsync(byte[] data)
|
||||
{
|
||||
await SendAsync(data);
|
||||
}
|
||||
|
||||
private async Task SendAsync(byte[] data)
|
||||
{
|
||||
if (data == null)
|
||||
public async Task WriteAsync(string message)
|
||||
{
|
||||
return;
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
return;
|
||||
|
||||
var data = Encoding.UTF8.GetBytes(message);
|
||||
await SendAsync(data);
|
||||
}
|
||||
|
||||
if (Udp == null)
|
||||
public async Task WriteAsync(byte[] data)
|
||||
{
|
||||
return;
|
||||
if (data is { Length: > 0 })
|
||||
await SendAsync(data);
|
||||
}
|
||||
|
||||
if (data.Length <= MaxUdpPacketSize)
|
||||
private async Task SendAsync(byte[] data)
|
||||
{
|
||||
// Send via UDP (single packet)
|
||||
await Udp?.SendAsync(data, data.Length);
|
||||
}
|
||||
else if (SupportsTcp)
|
||||
{
|
||||
// Send via TCP if supported
|
||||
await SendViaTcpAsync(this, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Chunk large messages for UDP
|
||||
await SendUdpInChunksAsync(this, data, MaxUdpPacketSize);
|
||||
}
|
||||
}
|
||||
if (_udp == null)
|
||||
return;
|
||||
|
||||
/// <summary>
|
||||
/// Sends a message via TCP to a syslog server.
|
||||
/// </summary>
|
||||
private static async Task SendViaTcpAsync(Servers.Syslog.Syslog server, byte[] data)
|
||||
{
|
||||
if (server == null)
|
||||
{
|
||||
return;
|
||||
if (data.Length <= MaxUdpPacketSize)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _udp.SendAsync(data, data.Length);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnException?.Invoke(this, ex);
|
||||
IsConnected = false;
|
||||
}
|
||||
}
|
||||
else if (SupportsTcp)
|
||||
{
|
||||
await SendViaTcpAsync(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
await SendUdpInChunksAsync(data, MaxUdpPacketSize);
|
||||
}
|
||||
}
|
||||
|
||||
using var tcpClient = new System.Net.Sockets.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(Servers.Syslog.Syslog server, byte[] data, int chunkSize)
|
||||
{
|
||||
for (int i = 0; i < data.Length; i += chunkSize)
|
||||
private async Task SendViaTcpAsync(byte[] data)
|
||||
{
|
||||
var chunk = data.Skip(i).Take(chunkSize).ToArray();
|
||||
await server.Udp.SendAsync(chunk, chunk.Length);
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
using var tcpClient = new TcpClient();
|
||||
await tcpClient.ConnectAsync(_hostname, _port);
|
||||
|
||||
~Syslog()
|
||||
{
|
||||
Dispose();
|
||||
var stream = tcpClient.GetStream();
|
||||
await stream.WriteAsync(data, 0, data.Length);
|
||||
await stream.FlushAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// TCP failure fallback is silent here
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendUdpInChunksAsync(byte[] data, int chunkSize)
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = ArrayPool<byte>.Shared.Rent(chunkSize);
|
||||
|
||||
try
|
||||
{
|
||||
while (offset < data.Length)
|
||||
{
|
||||
int size = Math.Min(chunkSize, data.Length - offset);
|
||||
Buffer.BlockCopy(data, offset, buffer, 0, size);
|
||||
await _udp.SendAsync(buffer, size);
|
||||
offset += size;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
IsConnected = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeUdp();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~Syslog()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,35 +2,30 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EonaCat.Logger.Servers.Tcp
|
||||
{
|
||||
// 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.
|
||||
public class Tcp : IDisposable
|
||||
{
|
||||
private string _Hostname = "127.0.0.1";
|
||||
private int _Port = 514;
|
||||
internal TcpClient _tcp;
|
||||
private string _hostname = "127.0.0.1";
|
||||
private int _port = 514;
|
||||
private TcpClient _tcp;
|
||||
private readonly SemaphoreSlim _sendLock = new(1, 1);
|
||||
public event EventHandler<Exception> OnException;
|
||||
|
||||
public bool IsConnected { get; private set; }
|
||||
public string Nickname { get; set; }
|
||||
public string IpPort => _Hostname + ":" + _Port;
|
||||
public string IpPort => $"{_hostname}:{_port}";
|
||||
public List<ELogType> TypesToLog { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tcp Server
|
||||
/// </summary>
|
||||
/// <param name="hostname"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="nickname"></param>
|
||||
/// <param name="typesToLog"></param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
public Tcp(string hostname, int port, string nickname = null, List<ELogType> typesToLog = null)
|
||||
{
|
||||
_Hostname = hostname ?? throw new ArgumentNullException(nameof(hostname));
|
||||
_Port = port >= 0 ? port : throw new ArgumentException("Port must be zero or greater.");
|
||||
|
||||
_hostname = hostname ?? throw new ArgumentNullException(nameof(hostname));
|
||||
_port = port >= 0 ? port : throw new ArgumentException("Port must be zero or greater.");
|
||||
Nickname = nickname ?? IpPort;
|
||||
TypesToLog = typesToLog;
|
||||
|
||||
@@ -39,42 +34,36 @@ namespace EonaCat.Logger.Servers.Tcp
|
||||
|
||||
public string Hostname
|
||||
{
|
||||
get => _Hostname;
|
||||
get => _hostname;
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
throw new ArgumentNullException(nameof(Hostname));
|
||||
}
|
||||
|
||||
_Hostname = value;
|
||||
_hostname = value;
|
||||
SetTcp();
|
||||
}
|
||||
}
|
||||
|
||||
public int Port
|
||||
{
|
||||
get => _Port;
|
||||
get => _port;
|
||||
set
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
throw new ArgumentException("Port must be zero or greater.");
|
||||
}
|
||||
|
||||
_Port = value;
|
||||
_port = value;
|
||||
SetTcp();
|
||||
}
|
||||
}
|
||||
|
||||
public List<ELogType> TypesToLog { get; set; }
|
||||
|
||||
internal void SetTcp()
|
||||
{
|
||||
try
|
||||
{
|
||||
DisposeTcp();
|
||||
_tcp = new TcpClient(_Hostname, _Port);
|
||||
_tcp = new TcpClient(_hostname, _port);
|
||||
IsConnected = true;
|
||||
}
|
||||
catch
|
||||
@@ -85,80 +74,67 @@ namespace EonaCat.Logger.Servers.Tcp
|
||||
|
||||
internal void DisposeTcp()
|
||||
{
|
||||
if (_tcp != null)
|
||||
{
|
||||
IsConnected = false;
|
||||
IsConnected = false;
|
||||
|
||||
try
|
||||
{
|
||||
_tcp.Close();
|
||||
_tcp.Dispose();
|
||||
_tcp = null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
try
|
||||
{
|
||||
_tcp?.Close();
|
||||
_tcp?.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Silent fail
|
||||
}
|
||||
|
||||
_tcp = null;
|
||||
}
|
||||
|
||||
public async Task WriteAsync(byte[] data)
|
||||
{
|
||||
if (data == null || data.Length == 0) return;
|
||||
await InternalWriteAsync(data);
|
||||
}
|
||||
|
||||
public async Task WriteAsync(string data)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(data)) return;
|
||||
var bytes = Encoding.UTF8.GetBytes(data);
|
||||
await InternalWriteAsync(bytes);
|
||||
}
|
||||
|
||||
private async Task InternalWriteAsync(byte[] data)
|
||||
{
|
||||
await _sendLock.WaitAsync();
|
||||
try
|
||||
{
|
||||
if (!IsConnected || _tcp == null)
|
||||
SetTcp();
|
||||
|
||||
if (!IsConnected || _tcp == null)
|
||||
return;
|
||||
|
||||
var stream = _tcp.GetStream();
|
||||
await stream.WriteAsync(data, 0, data.Length);
|
||||
await stream.FlushAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnException?.Invoke(this, ex);
|
||||
IsConnected = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sendLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeTcp();
|
||||
_sendLock.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public async Task WriteAsync(byte[] data)
|
||||
{
|
||||
if (_tcp == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (data == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsConnected)
|
||||
{
|
||||
SetTcp();
|
||||
}
|
||||
|
||||
if (IsConnected)
|
||||
{
|
||||
using var stream = _tcp.GetStream();
|
||||
await stream.WriteAsync(data, 0, data.Length);
|
||||
await stream.FlushAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task WriteAsync(string data)
|
||||
{
|
||||
if (_tcp == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(data))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsConnected)
|
||||
{
|
||||
SetTcp();
|
||||
}
|
||||
|
||||
if (IsConnected)
|
||||
{
|
||||
var sendData = Encoding.UTF8.GetBytes(data);
|
||||
using var stream = _tcp.GetStream();
|
||||
await stream.WriteAsync(sendData, 0, sendData.Length);
|
||||
await stream.FlushAsync();
|
||||
}
|
||||
}
|
||||
|
||||
~Tcp()
|
||||
{
|
||||
Dispose();
|
||||
|
||||
@@ -1,36 +1,33 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EonaCat.Logger.Servers.Udp
|
||||
{
|
||||
// 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.
|
||||
public class Udp : IDisposable
|
||||
{
|
||||
const int MaxUdpPacketSize = 4096;
|
||||
private string _Hostname = "127.0.0.1";
|
||||
private int _Port = 514;
|
||||
internal UdpClient _udp;
|
||||
private const int MaxUdpPacketSize = 4096;
|
||||
private string _hostname = "127.0.0.1";
|
||||
private int _port = 514;
|
||||
private UdpClient _udp;
|
||||
private readonly SemaphoreSlim _sendLock = new(1, 1);
|
||||
public event EventHandler<Exception> OnException;
|
||||
|
||||
public bool IsConnected { get; private set; }
|
||||
public string Nickname { get; set; }
|
||||
public string IpPort => _Hostname + ":" + _Port;
|
||||
public string IpPort => $"{_hostname}:{_port}";
|
||||
public List<ELogType> TypesToLog { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Udp Server
|
||||
/// </summary>
|
||||
/// <param name="hostname"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="nickname"></param>
|
||||
/// <param name="typesToLog"></param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
public Udp(string hostname, int port, string nickname = null, List<ELogType> typesToLog = null)
|
||||
{
|
||||
_Hostname = hostname ?? throw new ArgumentNullException(nameof(hostname));
|
||||
_Port = port >= 0 ? port : throw new ArgumentException("Port must be zero or greater.");
|
||||
_hostname = hostname ?? throw new ArgumentNullException(nameof(hostname));
|
||||
_port = port >= 0 ? port : throw new ArgumentException("Port must be zero or greater.");
|
||||
TypesToLog = typesToLog;
|
||||
Nickname = nickname ?? IpPort;
|
||||
SetUdp();
|
||||
@@ -38,42 +35,39 @@ namespace EonaCat.Logger.Servers.Udp
|
||||
|
||||
public string Hostname
|
||||
{
|
||||
get => _Hostname;
|
||||
get => _hostname;
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
throw new ArgumentNullException(nameof(Hostname));
|
||||
}
|
||||
|
||||
_Hostname = value;
|
||||
_hostname = value;
|
||||
SetUdp();
|
||||
}
|
||||
}
|
||||
|
||||
public int Port
|
||||
{
|
||||
get => _Port;
|
||||
get => _port;
|
||||
set
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
throw new ArgumentException("Port must be zero or greater.");
|
||||
}
|
||||
|
||||
_Port = value;
|
||||
_port = value;
|
||||
SetUdp();
|
||||
}
|
||||
}
|
||||
|
||||
public List<ELogType> TypesToLog { get; set; }
|
||||
|
||||
internal void SetUdp()
|
||||
{
|
||||
try
|
||||
{
|
||||
DisposeUdp();
|
||||
_udp = new UdpClient(_Hostname, _Port);
|
||||
_udp = new UdpClient(_hostname, _port)
|
||||
{
|
||||
DontFragment = false
|
||||
};
|
||||
IsConnected = true;
|
||||
}
|
||||
catch
|
||||
@@ -84,92 +78,88 @@ namespace EonaCat.Logger.Servers.Udp
|
||||
|
||||
internal void DisposeUdp()
|
||||
{
|
||||
if (_udp != null)
|
||||
IsConnected = false;
|
||||
|
||||
try
|
||||
{
|
||||
_udp?.Close();
|
||||
_udp?.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Silently ignore
|
||||
}
|
||||
|
||||
_udp = null;
|
||||
}
|
||||
|
||||
public async Task WriteAsync(byte[] data, bool dontFragment = false)
|
||||
{
|
||||
if (data == null || data.Length == 0)
|
||||
return;
|
||||
|
||||
await _sendLock.WaitAsync();
|
||||
try
|
||||
{
|
||||
if (!IsConnected)
|
||||
SetUdp();
|
||||
|
||||
if (!IsConnected || _udp == null)
|
||||
return;
|
||||
|
||||
_udp.DontFragment = dontFragment;
|
||||
|
||||
await SendChunksAsync(data);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sendLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task WriteAsync(string data, bool dontFragment = false)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(data))
|
||||
return;
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(data);
|
||||
await WriteAsync(bytes, dontFragment);
|
||||
}
|
||||
|
||||
private async Task SendChunksAsync(byte[] data)
|
||||
{
|
||||
int offset = 0;
|
||||
int length = data.Length;
|
||||
byte[] buffer = ArrayPool<byte>.Shared.Rent(MaxUdpPacketSize);
|
||||
|
||||
try
|
||||
{
|
||||
while (offset < length)
|
||||
{
|
||||
int chunkSize = Math.Min(MaxUdpPacketSize, length - offset);
|
||||
Buffer.BlockCopy(data, offset, buffer, 0, chunkSize);
|
||||
await _udp.SendAsync(buffer, chunkSize);
|
||||
offset += chunkSize;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
IsConnected = false;
|
||||
|
||||
try
|
||||
{
|
||||
_udp?.Close();
|
||||
_udp.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
_udp = null;
|
||||
OnException?.Invoke(this, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeUdp();
|
||||
_sendLock.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public async Task WriteAsync(byte[] data, bool dontFragment = false)
|
||||
{
|
||||
if (_udp == null || data == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsConnected)
|
||||
{
|
||||
SetUdp();
|
||||
}
|
||||
|
||||
if (IsConnected)
|
||||
{
|
||||
_udp.DontFragment = dontFragment;
|
||||
|
||||
int maxChunkSize = MaxUdpPacketSize;
|
||||
int offset = 0;
|
||||
|
||||
while (offset < data.Length)
|
||||
{
|
||||
int chunkSize = Math.Min(maxChunkSize, data.Length - offset);
|
||||
byte[] chunk = new byte[chunkSize];
|
||||
Array.Copy(data, offset, chunk, 0, chunkSize);
|
||||
|
||||
await _udp?.SendAsync(chunk, chunk.Length);
|
||||
offset += chunkSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task WriteAsync(string data, bool dontFragment = false)
|
||||
{
|
||||
if (_udp == null || string.IsNullOrEmpty(data))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsConnected)
|
||||
{
|
||||
SetUdp();
|
||||
}
|
||||
|
||||
if (IsConnected)
|
||||
{
|
||||
var sendData = Encoding.UTF8.GetBytes(data);
|
||||
_udp.DontFragment = dontFragment;
|
||||
|
||||
int maxChunkSize = MaxUdpPacketSize;
|
||||
int offset = 0;
|
||||
|
||||
while (offset < sendData.Length)
|
||||
{
|
||||
int chunkSize = Math.Min(maxChunkSize, sendData.Length - offset);
|
||||
byte[] chunk = new byte[chunkSize];
|
||||
Array.Copy(sendData, offset, chunk, 0, chunkSize);
|
||||
|
||||
await _udp?.SendAsync(chunk, chunk.Length);
|
||||
offset += chunkSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~Udp()
|
||||
{
|
||||
Dispose();
|
||||
|
||||
@@ -5,144 +5,107 @@ using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace EonaCat.Logger.Servers.Zabbix.API;
|
||||
// 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.
|
||||
|
||||
public class ZabbixApi
|
||||
namespace EonaCat.Logger.Servers.Zabbix.API
|
||||
{
|
||||
/// <summary>
|
||||
/// Zabbix Api
|
||||
/// Zabbix API client for authentication and communication with Zabbix server.
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="password"></param>
|
||||
/// <param name="zabbixURL"></param>
|
||||
/// <param name="basicAuth"></param>
|
||||
public ZabbixApi(string user, string password, string zabbixURL, bool basicAuth)
|
||||
public class ZabbixApi
|
||||
{
|
||||
_user = user;
|
||||
_password = password;
|
||||
_zabbixURL = zabbixURL;
|
||||
private readonly string _user;
|
||||
private readonly string _password;
|
||||
private readonly string _zabbixUrl;
|
||||
private readonly string _basicAuth;
|
||||
private string _auth;
|
||||
public event EventHandler<Exception> OnException;
|
||||
|
||||
if (basicAuth)
|
||||
public bool IsLoggedIn { get; private set; }
|
||||
|
||||
public ZabbixApi(string user, string password, string zabbixUrl, bool useBasicAuth = false)
|
||||
{
|
||||
_basicAuth = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(_user + ":" + _password));
|
||||
_user = user ?? throw new ArgumentNullException(nameof(user));
|
||||
_password = password ?? throw new ArgumentNullException(nameof(password));
|
||||
_zabbixUrl = zabbixUrl ?? throw new ArgumentNullException(nameof(zabbixUrl));
|
||||
|
||||
_basicAuth = useBasicAuth
|
||||
? Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes($"{_user}:{_password}"))
|
||||
: null;
|
||||
}
|
||||
|
||||
_auth = null;
|
||||
}
|
||||
|
||||
public ZabbixApi(string user, string password, string zabbixURL) : this(user, password, zabbixURL, false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private readonly string _user;
|
||||
private readonly string _password;
|
||||
private readonly string _zabbixURL;
|
||||
private string _auth;
|
||||
private readonly string _basicAuth = null;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the user is logged in
|
||||
/// </summary>
|
||||
public bool IsLoggedIn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Logs in the user
|
||||
/// </summary>
|
||||
public void Login()
|
||||
{
|
||||
dynamic authentication = new ExpandoObject();
|
||||
authentication.user = _user;
|
||||
authentication.password = _password;
|
||||
|
||||
ZabbixApiResponse zabbixApiResponse = ResponseAsObject("user.login", authentication);
|
||||
|
||||
_auth = zabbixApiResponse.Result;
|
||||
IsLoggedIn = !string.IsNullOrEmpty(_auth);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs out the user
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool Logout()
|
||||
{
|
||||
ZabbixApiResponse zbxResponse = ResponseAsObject("user.logout", new string[] { });
|
||||
var result = zbxResponse.Result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic method to send a request to the Zabbix API
|
||||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
public string ResponseAsJson(string method, object parameters)
|
||||
{
|
||||
ZabbixApiRequest zbxRequest = new ZabbixApiRequest("2.0", method, 1, _auth, parameters);
|
||||
string jsonParameters = JsonHelper.ToJson(zbxRequest);
|
||||
return SendRequest(jsonParameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic method to send a request to the Zabbix API
|
||||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
public ZabbixApiResponse ResponseAsObject(string method, object parameters)
|
||||
{
|
||||
ZabbixApiRequest zbxRequest = new ZabbixApiRequest("2.0", method, 1, _auth, parameters);
|
||||
string jsonParameters = JsonHelper.ToJson(zbxRequest);
|
||||
return CreateResponse(SendRequest(jsonParameters));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a ZabbixApiResponse object from a JSON string
|
||||
/// </summary>
|
||||
/// <param name="json"></param>
|
||||
/// <returns></returns>
|
||||
private ZabbixApiResponse CreateResponse(string json)
|
||||
{
|
||||
ZabbixApiResponse zbxResponse = JsonHelper.ToObject<ZabbixApiResponse>(json);
|
||||
return zbxResponse;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a request to the Zabbix API
|
||||
/// </summary>
|
||||
/// <param name="jsonParams"></param>
|
||||
/// <returns></returns>
|
||||
private string SendRequest(string jsonParams)
|
||||
{
|
||||
WebRequest request = WebRequest.Create(_zabbixURL);
|
||||
|
||||
if (_basicAuth != null)
|
||||
/// <summary>
|
||||
/// Login and retrieve authentication token from Zabbix API.
|
||||
/// </summary>
|
||||
public void Login()
|
||||
{
|
||||
request.Headers.Add("Authorization", "Basic " + _basicAuth);
|
||||
dynamic authParams = new ExpandoObject();
|
||||
authParams.user = _user;
|
||||
authParams.password = _password;
|
||||
|
||||
var response = ResponseAsObject("user.login", authParams);
|
||||
_auth = response?.Result;
|
||||
IsLoggedIn = !string.IsNullOrEmpty(_auth);
|
||||
}
|
||||
|
||||
request.ContentType = "application/json-rpc";
|
||||
request.Method = "POST";
|
||||
string jsonResult;
|
||||
|
||||
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
|
||||
/// <summary>
|
||||
/// Logout from Zabbix API.
|
||||
/// </summary>
|
||||
public bool Logout()
|
||||
{
|
||||
streamWriter.Write(jsonParams);
|
||||
streamWriter.Flush();
|
||||
streamWriter.Close();
|
||||
var response = ResponseAsObject("user.logout", Array.Empty<string>());
|
||||
return response?.Result ?? false;
|
||||
}
|
||||
|
||||
WebResponse response = request.GetResponse();
|
||||
using (var streamReader = new StreamReader(response.GetResponseStream()))
|
||||
/// <summary>
|
||||
/// Send a Zabbix API request and return JSON response as string.
|
||||
/// </summary>
|
||||
public string ResponseAsJson(string method, object parameters)
|
||||
{
|
||||
jsonResult = streamReader.ReadToEnd();
|
||||
var request = CreateRequest(method, parameters);
|
||||
var jsonParams = JsonHelper.ToJson(request);
|
||||
return SendRequest(jsonParams);
|
||||
}
|
||||
|
||||
return jsonResult;
|
||||
/// <summary>
|
||||
/// Send a Zabbix API request and return deserialized response.
|
||||
/// </summary>
|
||||
public ZabbixApiResponse ResponseAsObject(string method, object parameters)
|
||||
{
|
||||
var request = CreateRequest(method, parameters);
|
||||
var jsonParams = JsonHelper.ToJson(request);
|
||||
var responseJson = SendRequest(jsonParams);
|
||||
return JsonHelper.ToObject<ZabbixApiResponse>(responseJson);
|
||||
}
|
||||
|
||||
private ZabbixApiRequest CreateRequest(string method, object parameters) =>
|
||||
new("2.0", method, 1, _auth, parameters);
|
||||
|
||||
private string SendRequest(string jsonParams)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = WebRequest.Create(_zabbixUrl);
|
||||
request.Method = "POST";
|
||||
request.ContentType = "application/json-rpc";
|
||||
|
||||
if (!string.IsNullOrEmpty(_basicAuth))
|
||||
{
|
||||
request.Headers.Add("Authorization", $"Basic {_basicAuth}");
|
||||
}
|
||||
|
||||
using (var writer = new StreamWriter(request.GetRequestStream()))
|
||||
{
|
||||
writer.Write(jsonParams);
|
||||
}
|
||||
|
||||
using var response = request.GetResponse();
|
||||
using var reader = new StreamReader(response.GetResponseStream());
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnException?.Invoke(this, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,10 +7,6 @@ using EonaCat.Logger.Test.Web;
|
||||
using EonaCat.Versioning.Helpers;
|
||||
using EonaCat.Web.RateLimiter;
|
||||
using EonaCat.Web.RateLimiter.Endpoints.Extensions;
|
||||
using EonaCat.Web.Tracer.Extensions;
|
||||
using EonaCat.Web.Tracer.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
int onLogCounter = 0;
|
||||
|
||||
Reference in New Issue
Block a user