Initial version

This commit is contained in:
2026-02-27 21:12:55 +01:00
parent d2bbbd8bc7
commit a73beb6ed5
184 changed files with 90370 additions and 63 deletions

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