Added EonaCat Logger Server

This commit is contained in:
2025-02-22 21:18:42 +01:00
parent a5c00a07e1
commit 6cf1f52e0d
4 changed files with 329 additions and 1 deletions

View 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>

View 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();
}
}
}
}

View File

@@ -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

View File

@@ -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;