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