diff --git a/An2WinFileTransfer/An2WinFileTransfer/An2WinFileTransfer.csproj b/An2WinFileTransfer/An2WinFileTransfer/An2WinFileTransfer.csproj index 907a7c1..c7fff87 100644 --- a/An2WinFileTransfer/An2WinFileTransfer/An2WinFileTransfer.csproj +++ b/An2WinFileTransfer/An2WinFileTransfer/An2WinFileTransfer.csproj @@ -12,6 +12,8 @@ 512 true true + + AnyCPU @@ -39,9 +41,40 @@ ..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll + + ..\packages\Serilog.4.3.1-dev-02387\lib\net471\Serilog.dll + + + ..\packages\Serilog.Settings.AppSettings.3.0.0\lib\net471\Serilog.Settings.AppSettings.dll + + + ..\packages\Serilog.Sinks.File.7.0.0\lib\net471\Serilog.Sinks.File.dll + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + ..\packages\System.Diagnostics.DiagnosticSource.8.0.1\lib\net462\System.Diagnostics.DiagnosticSource.dll + + + ..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Threading.Channels.8.0.0\lib\net462\System.Threading.Channels.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + @@ -58,10 +91,12 @@ + + Form @@ -99,4 +134,11 @@ + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + \ No newline at end of file diff --git a/An2WinFileTransfer/An2WinFileTransfer/App.config b/An2WinFileTransfer/An2WinFileTransfer/App.config index a718fc3..1e908d4 100644 --- a/An2WinFileTransfer/An2WinFileTransfer/App.config +++ b/An2WinFileTransfer/An2WinFileTransfer/App.config @@ -1,12 +1,38 @@ - + - - - + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/An2WinFileTransfer/An2WinFileTransfer/Interfaces/ILoggingService.cs b/An2WinFileTransfer/An2WinFileTransfer/Interfaces/ILoggingService.cs new file mode 100644 index 0000000..7c08f7d --- /dev/null +++ b/An2WinFileTransfer/An2WinFileTransfer/Interfaces/ILoggingService.cs @@ -0,0 +1,11 @@ +using System; + +namespace An2WinFileTransfer.Interfaces +{ + public interface ILoggingService + { + void Info(string message, bool includeUI = true); + void Warn(string message, bool includeUI = true); + void Error(string message, Exception ex = null, bool includeUI = true); + } +} diff --git a/An2WinFileTransfer/An2WinFileTransfer/Program.cs b/An2WinFileTransfer/An2WinFileTransfer/Program.cs index a02e46b..549e34e 100644 --- a/An2WinFileTransfer/An2WinFileTransfer/Program.cs +++ b/An2WinFileTransfer/An2WinFileTransfer/Program.cs @@ -1,6 +1,7 @@ using System; using System.Windows.Forms; using An2WinFileTransfer.UI.Forms; +using Serilog; namespace An2WinFileTransfer { @@ -12,9 +13,27 @@ internal static class Program [STAThread] static void Main() { + // Initialize Serilog from App.config + Log.Logger = new LoggerConfiguration() + .ReadFrom.AppSettings() + .CreateLogger(); + Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new FormMain()); + + try + { + Log.Information("Application started."); + Application.Run(new FormMain()); + } + catch (Exception ex) + { + Log.Fatal(ex, "Application terminated unexpectedly!"); + } + finally + { + Log.CloseAndFlush(); + } } } } diff --git a/An2WinFileTransfer/An2WinFileTransfer/Services/BackupService.cs b/An2WinFileTransfer/An2WinFileTransfer/Services/BackupService.cs index 68859d5..007dd04 100644 --- a/An2WinFileTransfer/An2WinFileTransfer/Services/BackupService.cs +++ b/An2WinFileTransfer/An2WinFileTransfer/Services/BackupService.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using An2WinFileTransfer.Enums; +using An2WinFileTransfer.Interfaces; using An2WinFileTransfer.Models; using MediaDevices; using Newtonsoft.Json; @@ -11,29 +12,29 @@ namespace An2WinFileTransfer.Services { public class BackupService { - private readonly Action _logAction; + private readonly ILoggingService _logService; - public BackupService(Action logAction) + public BackupService(ILoggingService log) { - _logAction = logAction ?? (_ => { }); + _logService = log ?? throw new ArgumentNullException(nameof(log)); } public void BackupFromDevice(MediaDevice device, string sourcePath, string targetRoot, IEnumerable fileTypes, bool copyAllFiles) { if (!device.DirectoryExists(sourcePath)) { - _logAction($"Source folder not found: {sourcePath}"); + _logService.Warn($"Source folder not found: {sourcePath}"); return; } var timestampedRootFolder = CreateNewTimeStampedFolder(targetRoot); - _logAction("Scanning previous backups..."); + _logService.Info("Scanning previous backups..."); var previousManifests = LoadPreviousManifests(targetRoot); var existingFiles = BuildExistingFileMap(previousManifests); - _logAction($"Loaded {existingFiles.Count} entries from previous backups."); + _logService.Info($"Loaded {existingFiles.Count} entries from previous backups."); var manifest = new BackupManifest { @@ -42,7 +43,7 @@ public void BackupFromDevice(MediaDevice device, string sourcePath, string targe Files = new List() }; - _logAction("Evaluating files to backup..."); + _logService.Info("Evaluating files to backup..."); var enabledExtensions = new HashSet( fileTypes.Where(ft => ft.IsEnabled && !string.IsNullOrWhiteSpace(ft.Extension)) @@ -68,7 +69,7 @@ public void BackupFromDevice(MediaDevice device, string sourcePath, string targe processedFileCount++; var fileInfo = device.GetFileInfo(file); - _logAction($"Processing file {processedFileCount} of {totalFileCount}. Copied: {copiedFileCount} | Skipped: {skippedFileCount} | Failed: {copyFailedFileCount}"); + _logService.Info($"Processing file {processedFileCount} of {totalFileCount}. Copied: {copiedFileCount} | Skipped: {skippedFileCount} | Failed: {copyFailedFileCount}"); if (fileInfo == null) { @@ -76,6 +77,8 @@ public void BackupFromDevice(MediaDevice device, string sourcePath, string targe continue; } + _logService.Info($"Evaluating file: {file}"); + var relativePath = GetRelativePath(sourcePath, file); var entry = new BackupFileEntry @@ -95,7 +98,7 @@ public void BackupFromDevice(MediaDevice device, string sourcePath, string targe continue; } - var localPath = Path.Combine(timestampedRootFolder, relativePath); + var localPath = Path.Combine(timestampedRootFolder, SanitizePath(relativePath)); Directory.CreateDirectory(Path.GetDirectoryName(localPath)); // Skip if file already backed up in a previous manifest @@ -133,7 +136,7 @@ public void BackupFromDevice(MediaDevice device, string sourcePath, string targe { entry.CopyStatus = ECopyStatus.Failed; copyFailedFileCount++; - _logAction($"Error copying {file}: {ex.Message}"); + _logService.Error($"Error copying {file}: {ex.Message}"); } } @@ -145,14 +148,14 @@ public void BackupFromDevice(MediaDevice device, string sourcePath, string targe var json = JsonConvert.SerializeObject(manifest, Formatting.Indented); File.WriteAllText(manifestPath, json); - _logAction($"Backup manifest saved: {manifestPath}. Backup duration: {manifest.BackupDuration}."); + _logService.Info($"Backup manifest saved: {manifestPath}. Backup duration: {manifest.BackupDuration}."); } catch (Exception ex) { - _logAction($"Failed to save manifest: {ex.Message}"); + _logService.Error($"Failed to save manifest: {ex.Message}"); } - _logAction($"Backup completed: Copied={copiedFileCount}, Skipped={skippedFileCount}, Failed={copyFailedFileCount}, Total={processedFileCount}"); + _logService.Info($"Backup completed: Copied={copiedFileCount}, Skipped={skippedFileCount}, Failed={copyFailedFileCount}, Total={processedFileCount}"); } private string GetRelativePath(string basePath, string fullPath) @@ -215,13 +218,13 @@ private List LoadPreviousManifests(string baseBackupPath) } catch (Exception ex) { - _logAction($"Failed to read manifest {file}: {ex.Message}"); + _logService.Info($"Failed to read manifest {file}: {ex.Message}"); } } } catch (Exception ex) { - _logAction($"Error while scanning for manifests: {ex.Message}"); + _logService.Error($"Error while scanning for manifests: {ex.Message}"); } return manifests; @@ -244,5 +247,31 @@ private Dictionary BuildExistingFileMap(IEnumerable string.Concat(part.Select(ch => invalidChars.Contains(ch) ? '_' : ch))) + ); + + return safePath; + } } } diff --git a/An2WinFileTransfer/An2WinFileTransfer/Services/LoggingService.cs b/An2WinFileTransfer/An2WinFileTransfer/Services/LoggingService.cs new file mode 100644 index 0000000..d280662 --- /dev/null +++ b/An2WinFileTransfer/An2WinFileTransfer/Services/LoggingService.cs @@ -0,0 +1,48 @@ +using System; +using An2WinFileTransfer.Interfaces; +using Serilog; + +namespace An2WinFileTransfer.Services +{ + public class LoggingService : ILoggingService + { + private readonly ILogger _logger; + private readonly Action _uiLogAction; + + public LoggingService(ILogger logger, Action uiLogAction = null) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _uiLogAction = uiLogAction; + } + + public void Info(string message, bool includeUI = true) + { + _logger.Information(message); + + if (includeUI) + { + _uiLogAction?.Invoke(message); + } + } + + public void Warn(string message, bool includeUI = true) + { + _logger.Warning(message); + + if (includeUI) + { + _uiLogAction?.Invoke("⚠️ " + message); + } + } + + public void Error(string message, Exception ex = null, bool includeUI = true) + { + _logger.Error(ex, message); + + if (includeUI) + { + _uiLogAction?.Invoke($"❌ {message}: {ex?.Message}"); + } + } + } +} diff --git a/An2WinFileTransfer/An2WinFileTransfer/UI/Forms/FormMain.cs b/An2WinFileTransfer/An2WinFileTransfer/UI/Forms/FormMain.cs index 963c061..b73f67c 100644 --- a/An2WinFileTransfer/An2WinFileTransfer/UI/Forms/FormMain.cs +++ b/An2WinFileTransfer/An2WinFileTransfer/UI/Forms/FormMain.cs @@ -5,8 +5,10 @@ using System.Windows.Forms; using An2WinFileTransfer.Enums; using An2WinFileTransfer.Extensions; +using An2WinFileTransfer.Interfaces; using An2WinFileTransfer.Models; using An2WinFileTransfer.Services; +using Serilog; namespace An2WinFileTransfer.UI.Forms { @@ -17,6 +19,7 @@ public partial class FormMain : Form private Timer _elapsedTimer = new Timer(); private DateTime backupStartTime; + private ILoggingService _log; private readonly DeviceService _deviceService = new DeviceService(); private BackupService _backupService; @@ -25,6 +28,14 @@ public partial class FormMain : Form public FormMain() { InitializeComponent(); + + // Initialize Serilog from App.config + var serilogLogger = new LoggerConfiguration() + .ReadFrom.AppSettings() + .CreateLogger(); + + _log = new LoggingService(serilogLogger, AppendLogToUI); + this.Load += FormMain_Load; _elapsedTimer.Interval = 1000; // 1 second @@ -35,6 +46,17 @@ public FormMain() }; } + private void AppendLogToUI(string message) + { + if (InvokeRequired) + { + Invoke(new Action(() => AppendLogToUI(message))); + return; + } + + labelProgress.Text = $"{DateTime.Now:HH:mm:ss} {message}{Environment.NewLine}"; + } + private void FormMain_Load(object sender, EventArgs e) { try @@ -228,7 +250,7 @@ private async void buttonStartBackup_Click(object sender, EventArgs e) backupStartTime = DateTime.Now; _elapsedTimer.Start(); - _backupService = new BackupService(Append); + _backupService = new BackupService(_log); await Task.Run(() => { diff --git a/An2WinFileTransfer/An2WinFileTransfer/packages.config b/An2WinFileTransfer/An2WinFileTransfer/packages.config index 399c8c2..d5dd1e2 100644 --- a/An2WinFileTransfer/An2WinFileTransfer/packages.config +++ b/An2WinFileTransfer/An2WinFileTransfer/packages.config @@ -2,4 +2,14 @@ + + + + + + + + + + \ No newline at end of file