AyCode.Core/AyCode.Services/Loggers/AcSignaRClientLogItemWriter.cs

108 lines
4.9 KiB
C#

using AyCode.Core.Enums;
using AyCode.Core.Helpers;
using AyCode.Core.Loggers;
using AyCode.Entities;
using AyCode.Entities.LogItems;
using Microsoft.AspNetCore.SignalR.Client;
namespace AyCode.Services.Loggers
{
public class AcSignaRClientLogItemWriter : AcLogItemWriterBase<AcLogItemClient>, IAcLogWriterClientBase
{
private readonly HubConnection _hubConnection;
private readonly string _fullHubName;
// A log-csatorna a SAJÁT hibáit nem logolhatja önmagán át (rekurzió) → Console.Error.
// Spam-védelem: hiba-állapotonként EGY jelentés; sikeres küldés után a kapu újraélesedik, és
// helyreálláskor kiírjuk, hány log-item veszett el. (Egyszerű mezők — a torz dupla-jelentés ártalmatlan.)
private bool _failureReported;
private long _droppedCount;
public AcSignaRClientLogItemWriter(string fullHubName, AppType appType = AppType.Web, LogLevel logLevel = LogLevel.Detail, string? categoryName = null) : base(appType, logLevel, categoryName)
{
_fullHubName = fullHubName;
_hubConnection = new HubConnectionBuilder()
.WithUrl(fullHubName)
//.WithAutomaticReconnect()
//.AddMessagePackProtocol(options => options.SerializerOptions = MessagePackSerializerOptions.Standard.WithSecurity(MessagePackSecurity.UntrustedData))
.Build();
// A .Forget() logger nélkül elnyelné az induló kapcsolat hibáját → jelentő wrapper.
StartConnectionReportedAsync().Forget();
}
public async Task StartConnection()
{
if (_hubConnection.State == HubConnectionState.Disconnected)
await _hubConnection.StartAsync();
if (_hubConnection.State != HubConnectionState.Connected)
await TaskHelper.WaitToAsync(() => _hubConnection.State == HubConnectionState.Connected, 10000, 10, 25);
}
public async Task StopConnection()
{
await _hubConnection.StopAsync();
await _hubConnection.DisposeAsync();
}
public override void Write(AppType appType, LogLevel logLevel, string? logText, string? callerMemberName, string? categoryName, string? errorType, string? exMessage)
=> WriteLogItem(CreateLogItem(DateTime.UtcNow, appType, Environment.CurrentManagedThreadId, logLevel, logText, callerMemberName, categoryName, errorType, exMessage));
protected override async void WriteLogItemCallback(AcLogItemClient logItem)
{
// Teljes try/catch: async void — egy StartAsync/SendAsync-kivétel különben kezeletlenül szökne el.
try
{
//Ez fontos, mert a signalR nem küldi a DateTime.Kind-ot! A szeró oldalon kap egy utc DateTime-ot, ami a kliens lokális DateTime-ját tartalmazza! - J.
logItem.TimeStampUtc = logItem.TimeStampUtc.ToUniversalTime();
await StartConnection();
if (_hubConnection.State != HubConnectionState.Connected)
{
_droppedCount++;
ReportFailureOnce($"connection not available ({_hubConnection.State}), log item DROPPED", null);
return;
}
//var refreshToken = Setting.UserBasicDetails?.RefreshToken ?? Guid.NewGuid().ToString("N");
await _hubConnection.SendAsync("AddLogItem", /*refreshToken,*/ logItem);
ReportRecoveryIfNeeded();
}
catch (Exception ex)
{
_droppedCount++;
ReportFailureOnce("send failed, log item DROPPED", ex);
}
}
private async Task StartConnectionReportedAsync()
{
try { await StartConnection(); }
catch (Exception ex) { ReportFailureOnce("initial connect failed", ex); }
}
/// <summary>Hiba-állapotonként EGYSZER ír a Console.Error-ra (nem spamel) — a következő sikeres küldés élesíti újra.</summary>
private void ReportFailureOnce(string message, Exception? ex)
{
if (_failureReported) return;
_failureReported = true;
Console.Error.WriteLine($"[{nameof(AcSignaRClientLogItemWriter)}] {message}; url: {_fullHubName}; droppedCount: {_droppedCount}{(ex == null ? string.Empty : $"; ex: {ex.GetType().Name}: {ex.Message}")}");
}
/// <summary>Sikeres küldésnél: ha hiba-állapotból jöttünk vissza, jelentjük a kiesés alatt elveszett logok számát.</summary>
private void ReportRecoveryIfNeeded()
{
if (!_failureReported) return;
_failureReported = false;
Console.Error.WriteLine($"[{nameof(AcSignaRClientLogItemWriter)}] connection recovered; url: {_fullHubName}; DROPPED items while down: {_droppedCount}");
_droppedCount = 0;
}
}
}