using System.Diagnostics; using Microsoft.Extensions.Logging; using System.Globalization; using System.Text; namespace SteUp.Maui.Core.Logger; public class FileLogger : ILogger { private readonly string _path; private readonly string _fileNamePrefix; private readonly string _categoryName; private readonly Lock _lock = new(); private readonly int _retentionDays; private string? _currentFileName; private DateTime _currentFileDate; private DateTime _lastCleanupDate; public FileLogger(string path, string fileNamePrefix, string categoryName, int retentionDays = 60) { _path = path; _fileNamePrefix = fileNamePrefix; _retentionDays = retentionDays; _lastCleanupDate = DateTime.MinValue; _categoryName = categoryName; if (!Directory.Exists(path)) Directory.CreateDirectory(path); UpdateCurrentFileName(); TryCleanOldLogs(); } /// /// Elimina i log più vecchi di giorni. /// Viene eseguita al massimo una volta al giorno. /// private void ClearOldLogs() { try { var cutoff = DateTime.Now.Date.AddDays(-_retentionDays); var logFiles = Directory.GetFiles(_path, $"{_fileNamePrefix}-*.log"); foreach (var file in logFiles) { try { var fileName = Path.GetFileNameWithoutExtension(file); var datePart = fileName[(_fileNamePrefix.Length + 1)..]; if (!DateTime.TryParseExact(datePart, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var fileDate) || fileDate >= cutoff) continue; File.Delete(file); Debug.WriteLine($"[FileLogger] Log eliminato: {file}"); } catch (Exception ex) { Debug.WriteLine($"[FileLogger] Errore durante l'eliminazione del file {file}: {ex.Message}"); } } } catch (Exception ex) { Debug.WriteLine($"[FileLogger] Errore durante la pulizia dei log: {ex.Message}"); } } /// /// Esegue la pulizia dei log solo se non è già stata eseguita oggi. /// private void TryCleanOldLogs() { var today = DateTime.Now.Date; if (_lastCleanupDate == today) return; _lastCleanupDate = today; ClearOldLogs(); } private void UpdateCurrentFileName() { var today = DateTime.Now.Date; if (_currentFileName != null && _currentFileDate == today) return; _currentFileDate = today; _currentFileName = $"{_fileNamePrefix}-{today:yyyy-MM-dd}.log"; } public IDisposable? BeginScope(TState state) => null; public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None; public void Log( LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { if (!IsEnabled(logLevel)) return; try { lock (_lock) { UpdateCurrentFileName(); TryCleanOldLogs(); if (_currentFileName == null) return; var fullPath = Path.Combine(_path, _currentFileName); var logEntry = BuildLogEntry(logLevel, eventId, state, exception, formatter); File.AppendAllText(fullPath, logEntry + Environment.NewLine + Environment.NewLine); Debug.WriteLine($"[FileLogger] {logEntry}"); } } catch (Exception ex) { Debug.WriteLine($"[FileLogger] Errore durante la scrittura del log: {ex}"); } } private string BuildLogEntry( LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { var sb = new StringBuilder(); sb.Append($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}]"); sb.Append($" [{GetLogLevelShort(logLevel)}]"); sb.Append($" [{_categoryName}]"); if (eventId.Id != 0 || !string.IsNullOrEmpty(eventId.Name)) sb.Append($" [{eventId}]"); sb.Append($" {formatter(state, exception)}"); if (exception != null) AppendException(sb, exception); return sb.ToString(); } private static void AppendException(StringBuilder sb, Exception exception, int depth = 0) { while (true) { var indent = depth == 0 ? "" : " Inner "; sb.AppendLine(); sb.Append($"{indent}Exception: {exception.GetType().FullName}: {exception.Message}"); if (!string.IsNullOrWhiteSpace(exception.StackTrace)) { sb.AppendLine(); sb.Append($"{indent}StackTrace: {exception.StackTrace.Trim()}"); } if (exception.InnerException != null) { exception = exception.InnerException; depth += 1; continue; } break; } } private static string GetLogLevelShort(LogLevel level) => level switch { LogLevel.Trace => "TRC", LogLevel.Debug => "DBG", LogLevel.Information => "INF", LogLevel.Warning => "WRN", LogLevel.Error => "ERR", LogLevel.Critical => "CRT", _ => "???" }; }