using System; using System.Threading.Tasks; using FinlyticCore.Dtos.Logging; using FinlyticCore.Models.Settings; using Microsoft.Extensions.Logging; namespace FinlyticCore.Services; /// /// Globaler Broadcaster für strukturierte Logs in Echtzeit. /// public static class FinlyticLogBroadcaster { public static Func? OnLogPublished { get; set; } public static void Broadcast(LogMessageDto dto) { if (OnLogPublished != null) { _ = Task.Run(async () => { try { await OnLogPublished(dto); } catch { // Ignore broadcast errors to never disrupt execution } }); } } } /// /// Bietet kanalbasierte, dynamisch steuerbare Logging-Funktionalitäten über den . /// /// Die aufrufende Klasse (für Log-Kategorien). public interface IFinlyticLogger { // --- Debug --- Task LogDebugAsync(SettingKey channelKey, string message, params object[] args); Task LogDebugAsync(SettingKey channelKey, Exception? exception, string message, params object[] args); // --- Info --- Task LogInfoAsync(SettingKey channelKey, string message, params object[] args); Task LogInfoAsync(SettingKey channelKey, Exception? exception, string message, params object[] args); // --- Warning --- Task LogWarningAsync(SettingKey channelKey, string message, params object[] args); Task LogWarningAsync(SettingKey channelKey, Exception? exception, string message, params object[] args); // --- Error --- Task LogErrorAsync(SettingKey channelKey, string message, params object[] args); Task LogErrorAsync(SettingKey channelKey, Exception? exception, string message, params object[] args); // --- Trace & Critical --- Task LogTraceAsync(SettingKey channelKey, string message, params object[] args); Task LogCriticalAsync(SettingKey channelKey, Exception? exception, string message, params object[] args); } /// /// Kanalbasierte Logger-Implementierung, die Einstellungen und Stummschaltungen /// in Echtzeit aus dem bezieht. /// public class FinlyticLogger : IFinlyticLogger { private static readonly string ServiceName = typeof(TContextClass).Assembly.GetName().Name ?? "Finlytic"; private readonly ILogger _logger; private readonly ISettingsService _settingsService; public FinlyticLogger( ILogger logger, ISettingsService settingsService) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _settingsService = settingsService ?? throw new ArgumentNullException(nameof(settingsService)); } private void DispatchBroadcast(SettingKey channelKey, LogLevel level, string message, Exception? exception, params object[] args) { try { string formattedMsg = args != null && args.Length > 0 ? string.Format(message, args) : message; FinlyticLogBroadcaster.Broadcast(new LogMessageDto( Timestamp: DateTime.UtcNow, ServiceName: ServiceName, Channel: channelKey.Name, Level: level.ToString(), Message: formattedMsg, Exception: exception?.ToString() )); } catch { FinlyticLogBroadcaster.Broadcast(new LogMessageDto( Timestamp: DateTime.UtcNow, ServiceName: ServiceName, Channel: channelKey.Name, Level: level.ToString(), Message: message, Exception: exception?.ToString() )); } } #region Debug public async Task LogDebugAsync(SettingKey channelKey, string message, params object[] args) { if (await ShouldLogAsync(channelKey, LogLevel.Debug)) { _logger.LogDebug(message, args); DispatchBroadcast(channelKey, LogLevel.Debug, message, null, args); } } public async Task LogDebugAsync(SettingKey channelKey, Exception? exception, string message, params object[] args) { if (await ShouldLogAsync(channelKey, LogLevel.Debug)) { if (exception != null) _logger.LogDebug(exception, message, args); else _logger.LogDebug(message, args); DispatchBroadcast(channelKey, LogLevel.Debug, message, exception, args); } } #endregion #region Info public async Task LogInfoAsync(SettingKey channelKey, string message, params object[] args) { if (await ShouldLogAsync(channelKey, LogLevel.Information)) { _logger.LogInformation(message, args); DispatchBroadcast(channelKey, LogLevel.Information, message, null, args); } } public async Task LogInfoAsync(SettingKey channelKey, Exception? exception, string message, params object[] args) { if (await ShouldLogAsync(channelKey, LogLevel.Information)) { if (exception != null) _logger.LogInformation(exception, message, args); else _logger.LogInformation(message, args); DispatchBroadcast(channelKey, LogLevel.Information, message, exception, args); } } #endregion #region Warning public async Task LogWarningAsync(SettingKey channelKey, string message, params object[] args) { if (await ShouldLogAsync(channelKey, LogLevel.Warning)) { _logger.LogWarning(message, args); DispatchBroadcast(channelKey, LogLevel.Warning, message, null, args); } } public async Task LogWarningAsync(SettingKey channelKey, Exception? exception, string message, params object[] args) { if (await ShouldLogAsync(channelKey, LogLevel.Warning)) { if (exception != null) _logger.LogWarning(exception, message, args); else _logger.LogWarning(message, args); DispatchBroadcast(channelKey, LogLevel.Warning, message, exception, args); } } #endregion #region Error public async Task LogErrorAsync(SettingKey channelKey, string message, params object[] args) { if (await ShouldLogAsync(channelKey, LogLevel.Error)) { _logger.LogError(message, args); DispatchBroadcast(channelKey, LogLevel.Error, message, null, args); } } public async Task LogErrorAsync(SettingKey channelKey, Exception? exception, string message, params object[] args) { if (await ShouldLogAsync(channelKey, LogLevel.Error)) { if (exception != null) _logger.LogError(exception, message, args); else _logger.LogError(message, args); DispatchBroadcast(channelKey, LogLevel.Error, message, exception, args); } } #endregion #region Trace & Critical public async Task LogTraceAsync(SettingKey channelKey, string message, params object[] args) { if (await ShouldLogAsync(channelKey, LogLevel.Trace)) { _logger.LogTrace(message, args); DispatchBroadcast(channelKey, LogLevel.Trace, message, null, args); } } public async Task LogCriticalAsync(SettingKey channelKey, Exception? exception, string message, params object[] args) { if (await ShouldLogAsync(channelKey, LogLevel.Critical)) { if (exception != null) _logger.LogCritical(exception, message, args); else _logger.LogCritical(message, args); DispatchBroadcast(channelKey, LogLevel.Critical, message, exception, args); } } #endregion private async Task ShouldLogAsync(SettingKey channelKey, LogLevel level) { ArgumentNullException.ThrowIfNull(channelKey); try { return await _settingsService.GetSettingAsync(channelKey); } catch { return channelKey.DefaultValue; } } }