287 lines
10 KiB
C#
287 lines
10 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|
|
} |