Added EonaCat Logger Server
This commit is contained in:
28
EonaCat.Logger.Server/EonaCat.Logger.Server.csproj
Normal file
28
EonaCat.Logger.Server/EonaCat.Logger.Server.csproj
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
|
<Title>EonaCat.Logger.Server</Title>
|
||||||
|
<Company>EonaCat (Jeroen Saey)</Company>
|
||||||
|
<Description>EonaCat.Logger.Server is a server for the logging library</Description>
|
||||||
|
<Copyright>EonaCat (Jeroen Saey)</Copyright>
|
||||||
|
<PackageProjectUrl>https://www.nuget.org/packages/EonaCat.Logger.Server/</PackageProjectUrl>
|
||||||
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
|
<PackageTags>EonaCat;Logger;EonaCatLogger;server;Log;Writer;Jeroen;Saey</PackageTags>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\EonaCat.Logger\icon.png">
|
||||||
|
<Pack>True</Pack>
|
||||||
|
<PackagePath>\</PackagePath>
|
||||||
|
</None>
|
||||||
|
<None Include="..\README.md">
|
||||||
|
<Pack>True</Pack>
|
||||||
|
<PackagePath>\</PackagePath>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
287
EonaCat.Logger.Server/Server.cs
Normal file
287
EonaCat.Logger.Server/Server.cs
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Threading;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace EonaCat.Logger.Server
|
||||||
|
{
|
||||||
|
// 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 Server
|
||||||
|
{
|
||||||
|
private TcpListener _tcpListener;
|
||||||
|
private UdpClient _udpListener;
|
||||||
|
private CancellationTokenSource _cts;
|
||||||
|
private bool _isRunning;
|
||||||
|
private readonly bool _useUdp;
|
||||||
|
private const long MaxLogFileSize = 200 * 1024 * 1024; // 200MB log rollover limit
|
||||||
|
private readonly int _logRetentionDays; // Number of days to retain logs
|
||||||
|
private readonly long _maxLogDirectorySize; // Maximum allowed size of the logs directory
|
||||||
|
private const int UdpBufferSize = 65507; // Maximum UDP packet size (65507 bytes for UDP payload)
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// EonaCat Log Server
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="useUdp">Determine if we need to start a udp server (default: true)</param>
|
||||||
|
/// <param name="logRetentionDays">Max log retention days (default: 30)</param>
|
||||||
|
/// <param name="maxLogDirectorySize">Max log directory size (default: 10GB)</param>
|
||||||
|
public Server(bool useUdp = true, int logRetentionDays = 30, long maxLogDirectorySize = 10L * 1024 * 1024 * 1024) // Default 10GB max directory size
|
||||||
|
{
|
||||||
|
_useUdp = useUdp;
|
||||||
|
_logRetentionDays = logRetentionDays;
|
||||||
|
_maxLogDirectorySize = maxLogDirectorySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual Task ProcessLogAsync(string logData)
|
||||||
|
{
|
||||||
|
string logsRootDirectory = "logs";
|
||||||
|
|
||||||
|
// Create root log directory if it doesn't exist
|
||||||
|
if (!Directory.Exists(logsRootDirectory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(logsRootDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a daily directory for logs
|
||||||
|
string dailyLogsDirectory = Path.Combine(logsRootDirectory, DateTime.Now.ToString("yyyyMMdd"));
|
||||||
|
if (!Directory.Exists(dailyLogsDirectory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(dailyLogsDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base log file name
|
||||||
|
string baseLogFilePath = Path.Combine(dailyLogsDirectory, "EonaCatLogs");
|
||||||
|
string logFilePath = baseLogFilePath + ".log";
|
||||||
|
|
||||||
|
int fileIndex = 1;
|
||||||
|
while (File.Exists(logFilePath) && new FileInfo(logFilePath).Length > MaxLogFileSize)
|
||||||
|
{
|
||||||
|
logFilePath = baseLogFilePath + $"_{fileIndex}.log";
|
||||||
|
fileIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// After processing log, check directory size and clean up if needed
|
||||||
|
CleanUpOldLogs();
|
||||||
|
|
||||||
|
return File.AppendAllTextAsync(logFilePath, logData + Environment.NewLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CleanUpOldLogs()
|
||||||
|
{
|
||||||
|
string logsRootDirectory = "logs";
|
||||||
|
if (!Directory.Exists(logsRootDirectory))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete old directories
|
||||||
|
foreach (var directory in Directory.GetDirectories(logsRootDirectory))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DirectoryInfo dirInfo = new DirectoryInfo(directory);
|
||||||
|
if (dirInfo.CreationTime < DateTime.Now.AddDays(-_logRetentionDays))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Deleting old log directory: {directory}");
|
||||||
|
Directory.Delete(directory, true); // Delete directory and its contents
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error deleting old directory {directory}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure total size of log directory doesn't exceed max limit
|
||||||
|
long totalDirectorySize = GetDirectorySize(logsRootDirectory);
|
||||||
|
if (totalDirectorySize > _maxLogDirectorySize)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Log directory size exceeded limit, cleaning up...");
|
||||||
|
|
||||||
|
// Delete the oldest directories until the size limit is met
|
||||||
|
foreach (var directory in Directory.GetDirectories(logsRootDirectory).OrderBy(d => new DirectoryInfo(d).CreationTime))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DirectoryInfo dirInfo = new DirectoryInfo(directory);
|
||||||
|
long dirSize = GetDirectorySize(directory);
|
||||||
|
totalDirectorySize -= dirSize;
|
||||||
|
|
||||||
|
// Delete the directory if the total size exceeds the limit
|
||||||
|
Directory.Delete(directory, true);
|
||||||
|
Console.WriteLine($"Deleted directory: {directory}");
|
||||||
|
|
||||||
|
// Stop deleting if we are under the size limit
|
||||||
|
if (totalDirectorySize <= _maxLogDirectorySize)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error deleting directory {directory}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long GetDirectorySize(string directory)
|
||||||
|
{
|
||||||
|
long size = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Add size of files in the directory
|
||||||
|
size += Directory.GetFiles(directory).Sum(file => new FileInfo(file).Length);
|
||||||
|
|
||||||
|
// Add size of files in subdirectories
|
||||||
|
foreach (var subdirectory in Directory.GetDirectories(directory))
|
||||||
|
{
|
||||||
|
size += GetDirectorySize(subdirectory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error calculating size for directory {directory}: {ex.Message}");
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Start(IPAddress ipAddress = null, int port = 5555)
|
||||||
|
{
|
||||||
|
if (ipAddress == null)
|
||||||
|
{
|
||||||
|
ipAddress = IPAddress.Any;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cts = new CancellationTokenSource();
|
||||||
|
_isRunning = true;
|
||||||
|
|
||||||
|
if (_useUdp)
|
||||||
|
{
|
||||||
|
_udpListener = new UdpClient(port);
|
||||||
|
Console.WriteLine($"EonaCat UDP Log Server started on port {port}...");
|
||||||
|
await ListenUdpAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_tcpListener = new TcpListener(ipAddress, port);
|
||||||
|
_tcpListener.Start();
|
||||||
|
Console.WriteLine($"EonaCat TCP Log Server started on port {port}...");
|
||||||
|
await ListenTcpAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ListenTcpAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!_cts.Token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
TcpClient client = await _tcpListener.AcceptTcpClientAsync();
|
||||||
|
_ = Task.Run(() => HandleTcpClient(client));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
Console.WriteLine("TCP Server stopping...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ListenUdpAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!_cts.Token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
// Increased buffer size for UDP
|
||||||
|
UdpReceiveResult result = await _udpListener.ReceiveAsync();
|
||||||
|
string logData = Encoding.UTF8.GetString(result.Buffer);
|
||||||
|
|
||||||
|
// If the received data is too large, process it in chunks
|
||||||
|
if (result.Buffer.Length > UdpBufferSize)
|
||||||
|
{
|
||||||
|
// Handle fragmentation and reassembly (this is a basic placeholder logic)
|
||||||
|
Console.WriteLine("Received large UDP data. Handling fragmentation.");
|
||||||
|
await ProcessLargeDataAsync(result.Buffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Received UDP Log: {logData}");
|
||||||
|
await ProcessLogAsync(logData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
Console.WriteLine("UDP Server stopping...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ProcessLargeDataAsync(byte[] data)
|
||||||
|
{
|
||||||
|
// You can implement your own logic here for processing large UDP data, such as fragmentation handling
|
||||||
|
string largeDataString = Encoding.UTF8.GetString(data);
|
||||||
|
await ProcessLogAsync(largeDataString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
if (_isRunning)
|
||||||
|
{
|
||||||
|
_cts.Cancel();
|
||||||
|
|
||||||
|
// Proper cleanup of resources
|
||||||
|
_cts.Dispose();
|
||||||
|
if (_useUdp)
|
||||||
|
{
|
||||||
|
_udpListener?.Close();
|
||||||
|
_udpListener?.Dispose();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_tcpListener?.Stop();
|
||||||
|
_tcpListener?.Server?.Dispose(); // Dispose of the socket (if any)
|
||||||
|
}
|
||||||
|
|
||||||
|
_isRunning = false;
|
||||||
|
Console.WriteLine("EonaCat Log Server stopped.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleTcpClient(TcpClient client)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (NetworkStream stream = client.GetStream())
|
||||||
|
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
|
||||||
|
{
|
||||||
|
char[] buffer = new char[8192]; // 8KB buffer size for large data
|
||||||
|
int bytesRead;
|
||||||
|
StringBuilder logData = new StringBuilder();
|
||||||
|
|
||||||
|
while ((bytesRead = await reader.ReadAsync(buffer, 0, buffer.Length)) > 0)
|
||||||
|
{
|
||||||
|
logData.Append(new string(buffer, 0, bytesRead));
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"Received TCP Log: {logData.ToString()}");
|
||||||
|
await ProcessLogAsync(logData.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Ensure client is properly disposed
|
||||||
|
client.Close();
|
||||||
|
client.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EonaCat.Logger", "EonaCat.L
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EonaCat.Logger.Test.Web", "Testers\EonaCat.Logger.Test.Web\EonaCat.Logger.Test.Web.csproj", "{DBEEF0B0-68AF-4A98-80B9-17FFCDB96E38}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EonaCat.Logger.Test.Web", "Testers\EonaCat.Logger.Test.Web\EonaCat.Logger.Test.Web.csproj", "{DBEEF0B0-68AF-4A98-80B9-17FFCDB96E38}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EonaCat.Logger.Server", "EonaCat.Logger.Server\EonaCat.Logger.Server.csproj", "{5A5B4709-C2D9-4AD6-BF7F-A97B8BC98255}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -41,6 +43,18 @@ Global
|
|||||||
{DBEEF0B0-68AF-4A98-80B9-17FFCDB96E38}.Release|x64.Build.0 = Release|Any CPU
|
{DBEEF0B0-68AF-4A98-80B9-17FFCDB96E38}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{DBEEF0B0-68AF-4A98-80B9-17FFCDB96E38}.Release|x86.ActiveCfg = Release|Any CPU
|
{DBEEF0B0-68AF-4A98-80B9-17FFCDB96E38}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{DBEEF0B0-68AF-4A98-80B9-17FFCDB96E38}.Release|x86.Build.0 = Release|Any CPU
|
{DBEEF0B0-68AF-4A98-80B9-17FFCDB96E38}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{5A5B4709-C2D9-4AD6-BF7F-A97B8BC98255}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{5A5B4709-C2D9-4AD6-BF7F-A97B8BC98255}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{5A5B4709-C2D9-4AD6-BF7F-A97B8BC98255}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{5A5B4709-C2D9-4AD6-BF7F-A97B8BC98255}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{5A5B4709-C2D9-4AD6-BF7F-A97B8BC98255}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{5A5B4709-C2D9-4AD6-BF7F-A97B8BC98255}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{5A5B4709-C2D9-4AD6-BF7F-A97B8BC98255}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{5A5B4709-C2D9-4AD6-BF7F-A97B8BC98255}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{5A5B4709-C2D9-4AD6-BF7F-A97B8BC98255}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{5A5B4709-C2D9-4AD6-BF7F-A97B8BC98255}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{5A5B4709-C2D9-4AD6-BF7F-A97B8BC98255}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{5A5B4709-C2D9-4AD6-BF7F-A97B8BC98255}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using EonaCat.Logger.EonaCatCoreLogger;
|
using EonaCat.Logger.EonaCatCoreLogger;
|
||||||
|
|||||||
Reference in New Issue
Block a user