Added EonaCat.LogStack.Status

Updated EonaCat.LogStack.LogClient to support EonaCat.LogStack.Status
This commit is contained in:
2026-03-12 21:15:33 +01:00
parent 776cc624bd
commit 977374ce02
41 changed files with 3412 additions and 180 deletions

View File

@@ -1,4 +1,6 @@
using EonaCat.LogStack.LogClient.Models;
using EonaCat.Json;
using EonaCat.LogStack.Extensions;
using EonaCat.LogStack.LogClient.Models;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -9,11 +11,30 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
// 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.
namespace EonaCat.LogStack.LogClient
{
public class EonaCatPayLoad
{
public List<EonaCatLogEvent>? Events { get; set; }
}
public class EonaCatLogEvent
{
public string Timestamp { get; set; } = default!;
public string Level { get; set; } = default!;
public string Message { get; set; } = default!;
public string Category { get; set; } = default!;
public ExceptionDto? Exception { get; set; }
public Dictionary<string, object?>? Properties { get; set; }
}
public class ExceptionDto
{
public string Type { get; set; } = default!;
public string Message { get; set; } = default!;
public string? StackTrace { get; set; }
}
public class LogCentralClient : IDisposable
{
private readonly HttpClient _httpClient;
@@ -37,14 +58,16 @@ namespace EonaCat.LogStack.LogClient
public async Task LogAsync(LogEntry entry)
{
entry.ApplicationName = _options.ApplicationName;
entry.ApplicationVersion = _options.ApplicationVersion;
entry.Environment = _options.Environment;
entry.Source = _options.ApplicationName;
entry.Timestamp = DateTime.UtcNow;
entry.Message ??= "";
entry.MachineName ??= Environment.MachineName;
entry.Category ??= entry.Category ?? "Default";
entry.Message ??= entry.Message ?? "";
var properties = new Dictionary<string, object?>();
if (_options.ApplicationName != null) properties.Add("ApplicationName", _options.ApplicationName);
if (_options.ApplicationVersion != null) properties.Add("ApplicationVersion", _options.ApplicationVersion);
if (_options.Environment != null) properties.Add("Environment", _options.Environment);
if (!string.IsNullOrEmpty(entry.TraceId)) properties.Add("TraceId", entry.TraceId);
entry.Properties = JsonHelper.ToJson(properties);
_logQueue.Enqueue(entry);
@@ -54,50 +77,9 @@ namespace EonaCat.LogStack.LogClient
}
}
public async Task LogExceptionAsync(Exception ex, string message = "",
Dictionary<string, object>? properties = null)
{
await LogAsync(new LogEntry
{
Level = (int)LogLevel.Error,
Category = "Exception",
Message = message,
Exception = ex.ToString(),
StackTrace = ex.StackTrace,
Properties = properties
});
}
public async Task LogSecurityEventAsync(string eventType, string message,
Dictionary<string, object>? properties = null)
{
await LogAsync(new LogEntry
{
Level = (int)LogLevel.Security,
Category = "Security",
Message = $"[{eventType}] {message}",
Properties = properties
});
}
public async Task LogAnalyticsAsync(string eventName,
Dictionary<string, object>? properties = null)
{
await LogAsync(new LogEntry
{
Level = (int)LogLevel.Analytics,
Category = "Analytics",
Message = eventName,
Properties = properties
});
}
private async Task FlushAsync()
{
if (_logQueue.IsEmpty)
{
return;
}
if (_logQueue.IsEmpty) return;
await _flushSemaphore.WaitAsync();
try
@@ -110,7 +92,7 @@ namespace EonaCat.LogStack.LogClient
if (batch.Count > 0)
{
await SendBatchAsync(batch);
await SendBatchToEonaCatAsync(batch);
}
}
finally
@@ -119,49 +101,48 @@ namespace EonaCat.LogStack.LogClient
}
}
private async Task SendBatchAsync(List<LogEntry> entries)
private async Task SendBatchToEonaCatAsync(List<LogEntry> batch)
{
try
{
// Map EF entities to DTOs for API
var dtos = entries.Select(e => new LogEntryDto
var eventsArray = batch.Select(e => new
{
Id = e.Id,
Timestamp = e.Timestamp,
ApplicationName = e.ApplicationName,
ApplicationVersion = e.ApplicationVersion,
Environment = e.Environment,
MachineName = e.MachineName,
Level = e.Level,
Category = e.Category,
Message = e.Message,
Exception = e.Exception,
StackTrace = e.StackTrace,
Properties = e.Properties,
UserId = e.UserId,
SessionId = e.SessionId,
RequestId = e.RequestId,
CorrelationId = e.CorrelationId
}).ToList();
timestamp = e.Timestamp.ToString("O"),
level = e.Level,
message = e.Message ?? "", // empty message is fine
exception = string.IsNullOrEmpty(e.Exception) ? null : new
{
type = "Exception",
message = e.Exception,
stackTrace = e.StackTrace
},
properties = string.IsNullOrEmpty(e.Properties)
? new Dictionary<string, object?>() // <-- same type now
: JsonHelper.ToObject<Dictionary<string, object?>>(e.Properties)
}).ToArray();
var response = await _httpClient.PostAsJsonAsync("/api/logs/batch", dtos);
var json = JsonHelper.ToJson(eventsArray);
using var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync("api/logs/eonacat", content);
var responseContent = await response.Content.ReadAsStringAsync();
response.EnsureSuccessStatusCode();
}
catch (Exception ex)
{
if (_options.EnableFallbackLogging)
{
Console.WriteLine($"[LogCentral] Failed to send logs: {ex.Message}");
Console.WriteLine($"[LogCentral] Failed to send logs to EonaCat: {ex.Message}");
}
foreach (var entry in entries)
foreach (var entry in batch)
{
_logQueue.Enqueue(entry);
}
}
}
public async Task FlushAndDisposeAsync()
{
await FlushAsync();
@@ -170,10 +151,7 @@ namespace EonaCat.LogStack.LogClient
public void Dispose()
{
if (_disposed)
{
return;
}
if (_disposed) return;
_flushTimer?.Dispose();
FlushAsync().GetAwaiter().GetResult();
@@ -183,25 +161,4 @@ namespace EonaCat.LogStack.LogClient
GC.SuppressFinalize(this);
}
}
public class LogEntryDto
{
public string Id { get; set; } = Guid.NewGuid().ToString();
public DateTime Timestamp { get; set; }
public string ApplicationName { get; set; } = default!;
public string ApplicationVersion { get; set; } = default!;
public string Environment { get; set; } = default!;
public string MachineName { get; set; } = default!;
public int Level { get; set; }
public string Category { get; set; } = default!;
public string Message { get; set; } = default!;
public string? Exception { get; set; }
public string? StackTrace { get; set; }
public Dictionary<string, object>? Properties { get; set; }
public string? UserId { get; set; }
public string? SessionId { get; set; }
public string? RequestId { get; set; }
public string? CorrelationId { get; set; }
}
}
}