149 lines
7.2 KiB
C#
149 lines
7.2 KiB
C#
using AyCode.Core.Enums;
|
|
using AyCode.Core.Helpers;
|
|
using AyCode.Core.Loggers;
|
|
using AyCode.Entities;
|
|
using AyCode.Entities.LogItems;
|
|
using AyCode.Services.SignalRs;
|
|
using Microsoft.AspNetCore.SignalR.Client;
|
|
|
|
namespace AyCode.Services.Loggers
|
|
{
|
|
/// <summary>
|
|
/// Nem-generikus, JSON-protokollos alak — back-compat a meglévő fogyasztóknak
|
|
/// (wire-típus: AcLogItemClient; a szerver-oldali párja az AcLoggerSignalRHub<TLogger> shim).
|
|
/// </summary>
|
|
public class AcSignaRClientLogItemWriter : AcSignaRClientLogItemWriter<AcLogItemClient>
|
|
{
|
|
public AcSignaRClientLogItemWriter(string fullHubName, AppType appType = AppType.Web, LogLevel logLevel = LogLevel.Detail, string? categoryName = null)
|
|
: base(fullHubName, appType, logLevel, categoryName)
|
|
{ }
|
|
}
|
|
|
|
/// <summary>
|
|
/// SignalR log-item feltöltő writer, generikus wire-típussal. A <typeparamref name="TLogItem"/>-et a fogyasztó
|
|
/// adja (tipikusan egy [AcBinarySerializable]-lel jelölt AcLogItemClient-leszármazott — így a csatorna
|
|
/// AcBinary-protokollal mehet; a generált serializer a fogyasztó rétegében él, az AyCode.Entities generator-mentes marad).
|
|
/// Az opcionális acBinaryProtocolOptions-szel a kapcsolat az AcBinary protokollt használja;
|
|
/// enélkül a SignalR default JSON-ja megy (back-compat).
|
|
/// </summary>
|
|
public class AcSignaRClientLogItemWriter<TLogItem> : AcLogItemWriterBase<TLogItem>, IAcLogWriterClientBase
|
|
where TLogItem : class, IAcLogItemClient
|
|
{
|
|
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 bool _connectedReported;
|
|
private long _droppedCount;
|
|
|
|
public AcSignaRClientLogItemWriter(string fullHubName, AppType appType = AppType.Web, LogLevel logLevel = LogLevel.Detail, string? categoryName = null, AcBinaryHubProtocolOptions? acBinaryProtocolOptions = null) : base(appType, logLevel, categoryName)
|
|
{
|
|
_fullHubName = fullHubName;
|
|
|
|
// VISSZACSATOLÁS-VÉDELEM: erre a builderre TILOS app-loggert kötni (AddAcDefaults / ConfigureLogging) —
|
|
// a protokoll saját logja a feltöltőn át újra ide érkezne (log → sorosítás → log → … robbanó hurok).
|
|
// AcBinary-nál ezért a hívó az opciókban EXPLICIT süket loggert adjon (pl. NullLogger.Instance),
|
|
// hogy az AddAcBinaryProtocol ??= DI-fallbackje se tölthesse fel.
|
|
var hubBuilder = new HubConnectionBuilder().WithUrl(fullHubName);
|
|
|
|
if (acBinaryProtocolOptions != null) hubBuilder.AddAcBinaryProtocol(acBinaryProtocolOptions);
|
|
|
|
_hubConnection = hubBuilder
|
|
//.WithAutomaticReconnect()
|
|
.Build();
|
|
|
|
StartConnection().Forget();
|
|
}
|
|
|
|
/// <summary>NEM dob: a kapcsolódási hibát maga jelenti (ReportFailureOnce) — a hívók (ctor fire-and-forget,
|
|
/// WriteLogItemCallback) a State-ből látják az eredményt.</summary>
|
|
public async Task StartConnection()
|
|
{
|
|
try
|
|
{
|
|
if (_hubConnection.State == HubConnectionState.Disconnected)
|
|
await _hubConnection.StartAsync();
|
|
|
|
if (_hubConnection.State != HubConnectionState.Connected)
|
|
await TaskHelper.WaitToAsync(() => _hubConnection.State == HubConnectionState.Connected, 10000, 10, 25);
|
|
|
|
// Felépülés-marker (kapcsolatonként egyszer) — enélkül a „minden csendben OK" és a „soha el sem indult"
|
|
// megkülönböztethetetlen a konzolból.
|
|
if (_hubConnection.State == HubConnectionState.Connected && !_connectedReported)
|
|
{
|
|
_connectedReported = true;
|
|
Console.WriteLine($"[{nameof(AcSignaRClientLogItemWriter)}] connected; url: {_fullHubName}");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ReportFailureOnce("connect failed", ex);
|
|
}
|
|
}
|
|
|
|
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(TLogItem 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);
|
|
}
|
|
}
|
|
|
|
/// <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)
|
|
{
|
|
_connectedReported = false; // a következő sikeres felépülés újra jelentkezhessen
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
}
|