diff --git a/FreeTubeSyncer/App.axaml.cs b/FreeTubeSyncer/App.axaml.cs index 207aee9..4f96b59 100644 --- a/FreeTubeSyncer/App.axaml.cs +++ b/FreeTubeSyncer/App.axaml.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.IO.Compression; using System.Linq; using System.Text.Json; using System.Threading; @@ -13,6 +14,10 @@ using FreeTubeSyncer.Library; using FreeTubeSyncer.Models; using FreeTubeSyncer.Models.DatabaseModels; using FreeTubeSyncer.REST; +using Serilog; +using Serilog.Sinks.File.GzArchive; +using Serilog.Sinks.FileEx; +using Serilog.Sinks.SystemConsole.Themes; namespace FreeTubeSyncer; @@ -45,6 +50,7 @@ public partial class App : Application public override void OnFrameworkInitializationCompleted() { + SetupLogger(); if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { var path = ""; @@ -59,7 +65,7 @@ public partial class App : Application path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".var", "app", "io.freetubeapp.FreeTube", "config", "FreeTube"); if (!Path.Exists(path)) - Console.WriteLine("Failed to find Path for FreeTube!"); + Log.Error("Failed to find Path for FreeTube!"); } } @@ -112,9 +118,8 @@ public partial class App : Application var path = GetSettingsPath(); if (!File.Exists(path)) { - var dir = Path.GetDirectoryName(path); - if (!Directory.Exists(dir)) - Directory.CreateDirectory(dir); + if (!Directory.Exists(GetAppFolder())) + Directory.CreateDirectory(GetAppFolder()); } File.WriteAllText(path, JsonSerializer.Serialize(_settings)); _settings!.SettingsDirty = false; @@ -129,20 +134,35 @@ public partial class App : Application _settings = new AppSettings(); return; } + var data = File.ReadAllText(path); _settings = JsonSerializer.Deserialize(data); } - - private string GetSettingsPath() => OperatingSystem.IsLinux() - ? Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FreeTubeSyncer", "settings.json") - : Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", "FreeTubeSyncer", "settings.json"); + + private string GetAppFolder() => Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FreeTubeSyncer"); + private string GetSettingsPath() => Path.Join(GetAppFolder(), "settings.json"); + + private void SetupLogger() + { + var path = Path.Join(GetAppFolder(), "logs"); + if (!Directory.Exists(path)) + Directory.CreateDirectory(path); + var log = new LoggerConfiguration() + .WriteTo.Console(theme: AnsiConsoleTheme.Code, + outputTemplate: "[{Timestamp:hh:mm:ss t} {Level:u4}] {Message:lj}{NewLine}{Exception}") + .WriteTo.FileEx(Path.Join(path, "activity.log"), "MM-dd-yy", + rollingInterval: RollingInterval.Day, + outputTemplate: "[{Timestamp:MM/dd/yy - hh:mm:ss t}] [{Level:u4}] {Message:lj}{NewLine}{Exception}", + hooks: new FileArchiveRollingHooks(CompressionLevel.SmallestSize, path)) + .CreateLogger(); + Log.Logger = log; + Log.Information("Log Started."); + } private async Task SyncMonitor() { - // TODO: Replace with Logger - Console.WriteLine("Sync Monitor Starting Up."); - // TODO: Replace with Logger - Console.WriteLine("Starting API Validation Check."); + Log.Information("Sync Monitor Starting Up."); + Log.Information("Starting API Validation Check."); while (_isRunning) { await _semaphoreSlim.WaitAsync(); @@ -154,8 +174,7 @@ public partial class App : Application continue; } - // TODO: Replace with Logger - Console.WriteLine("Fetching initial data from Database."); + Log.Information("Fetching initial data from REST API."); foreach (var syncer in _syncers!) { await syncer.FetchDatabase(); @@ -167,8 +186,7 @@ public partial class App : Application break; } - // TODO: Replace with Logger - Console.WriteLine("Starting Filesystem Sync Monitoring."); + Log.Information("Starting Filesystem Sync Monitoring."); var lastCheck = DateTime.Now; while (_isRunning) { @@ -179,13 +197,11 @@ public partial class App : Application { await _semaphoreSlim.WaitAsync(); var start = DateTime.Now; - // TODO: Replace with Logger - Console.WriteLine("Checking for updates..."); + Log.Information("Checking for updates..."); var updateCheck = await _syncers[0].GetLastUpdated(); if (_lastUpdated < updateCheck) { - // TODO: Replace with Logger - Console.WriteLine($"Update Found, fetching updates..."); + Log.Information("Update Found, fetching updates..."); _lastUpdated = updateCheck; foreach (var syncer in _syncers) await syncer.FetchDatabase(); @@ -193,8 +209,7 @@ public partial class App : Application lastCheck = DateTime.Now; var end = DateTime.Now - start; _semaphoreSlim.Release(); - // TODO: Replace with Logger - Console.WriteLine($"Check Completed. Total Time: {end}"); + Log.Information("Check Completed. Total Time: {EndTime}", end); continue; } @@ -202,8 +217,7 @@ public partial class App : Application var procs = Process.GetProcessesByName("FreeTube"); if (procs.Length > 0) continue; - // TODO: Replace with Logger - Console.WriteLine("FreeTube closed, and we have writes to make..."); + Log.Information("FreeTube instance closed, and we have writes to make..."); await Task.Delay(1500); await _semaphoreSlim.WaitAsync(); @@ -212,71 +226,61 @@ public partial class App : Application syncer.Sync(); var syncEnd = DateTime.Now - syncStart; _semaphoreSlim.Release(); - // TODO: Replace with Logger - Console.WriteLine($"Sync completed in {syncEnd}."); + Log.Information("Sync Completed. Total Time: {EndTime}", syncEnd); } - // TODO: Replace with Logger - Console.WriteLine($"Filesystem Sync Monitor Shutdown."); + Log.Information("Filesystem Sync Monitor shutdown."); } private async void HandleSettingsChanged(object? sender, EventArgs e) { - // TODO: Replace with Logger - Console.WriteLine("Settings have changed. Updating Settings..."); + Log.Information("Settings have changed. Updating Settings..."); await _semaphoreSlim.WaitAsync(); var old = JsonSerializer.Deserialize(_oldSettings!); if (_settings!.RestBaseUrl != old!.RestBaseUrl) { - // TODO: Replace with Logger - Console.WriteLine($"Updating syncers with new URL: {_settings.RestBaseUrl}."); + Log.Information("Updating syncers with new URL: {SettingsRestBaseUrl}.", _settings.RestBaseUrl); foreach (var syncer in _syncers!) syncer.UpdateBaseUrl(_settings.RestBaseUrl); } if (old.CheckInterval != _settings.CheckInterval) { - // TODO: Replace with Logger - Console.WriteLine($"Updating Check Interval to {_settings.CheckInterval}."); + Log.Information("Updating Check Interval to {SettingsCheckInterval}.", _settings.CheckInterval); _checkInterval = TimeSpan.FromSeconds(_settings.CheckInterval); } if (old.SyncHistory != _settings.SyncHistory) { - // TODO: Replace with Logger - Console.WriteLine("History Syncer: " + (_settings.SyncHistory ? "Enabled" : "Disabled")); + Log.Information("History Syncer: {Status}",(_settings.SyncHistory ? "Enabled" : "Disabled")); _historySyncer!.SetEnabled(_settings.SyncHistory); await _historySyncer.FetchDatabase(); } if (old.SyncPlaylist != _settings.SyncPlaylist) { - // TODO: Replace with Logger - Console.WriteLine("Playlist Syncer: " + (_settings.SyncHistory ? "Enabled" : "Disabled")); + Log.Information("Playlist Syncer: {Status}",(_settings.SyncHistory ? "Enabled" : "Disabled")); _playlistSyncer!.SetEnabled(_settings.SyncPlaylist); await _playlistSyncer.FetchDatabase(); } if (old.SyncProfile != _settings.SyncProfile) { - // TODO: Replace with Logger - Console.WriteLine("Profile Syncer: " + (_settings.SyncHistory ? "Enabled" : "Disabled")); + Log.Information("Profile Syncer: {Status}", (_settings.SyncHistory ? "Enabled" : "Disabled")); _profileSyncer!.SetEnabled(_settings.SyncProfile); await _profileSyncer.FetchDatabase(); } if (old.SyncSearchHistory != _settings.SyncSearchHistory) { - // TODO: Replace with Logger - Console.WriteLine("Search History Syncer: " + (_settings.SyncHistory ? "Enabled" : "Disabled")); + Log.Information("Search History Syncer: {Status}", (_settings.SyncHistory ? "Enabled" : "Disabled")); _searchHistorySyncer!.SetEnabled(_settings.SyncSearchHistory); await _searchHistorySyncer.FetchDatabase(); } if (old.SyncSettings != _settings.SyncSettings) { - // TODO: Replace with Logger - Console.WriteLine("Settings Syncer: " + (_settings.SyncHistory ? "Enabled" : "Disabled")); + Log.Information("Settings Syncer: {Status}",(_settings.SyncHistory ? "Enabled" : "Disabled")); _settingSyncer!.SetEnabled(_settings.SyncSettings); await _settingSyncer.FetchDatabase(); } diff --git a/FreeTubeSyncer/FreeTubeSyncer.csproj b/FreeTubeSyncer/FreeTubeSyncer.csproj index a7ed908..44fce9e 100644 --- a/FreeTubeSyncer/FreeTubeSyncer.csproj +++ b/FreeTubeSyncer/FreeTubeSyncer.csproj @@ -1,11 +1,12 @@  WinExe - net8.0 + net9.0 enable true app.manifest true + latestmajor @@ -21,6 +22,10 @@ + + + + diff --git a/FreeTubeSyncer/Library/DBSyncWatcher.cs b/FreeTubeSyncer/Library/DBSyncWatcher.cs index 2abd29f..f7a6bbf 100644 --- a/FreeTubeSyncer/Library/DBSyncWatcher.cs +++ b/FreeTubeSyncer/Library/DBSyncWatcher.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Text.Json; using System.Threading; +using Serilog; namespace FreeTubeSyncer.Library; @@ -44,8 +45,15 @@ public class DBSyncWatcher if (!WatchFiles.Keys.Contains(dbName)) return; - var data = File.ReadAllText(e.FullPath); - foreach (var line in data.Split('\n')) + Log.Information("Database File Changed: {DbName}", dbName); + + var data = new List(); + + using var fh = File.OpenText(e.FullPath); + while (!fh.EndOfStream) + data.Add(fh.ReadLine() ?? string.Empty); + + foreach (var line in data) { if (line == "") continue; var type = WatchFiles[dbName]; @@ -56,11 +64,27 @@ public class DBSyncWatcher private void HandleCreated(object sender, FileSystemEventArgs e) { if (e.ChangeType != WatcherChangeTypes.Created) return; - var dbName = Path.GetFileName(e.FullPath); - if (!WatchFiles.Keys.Contains(dbName)) return; - var data = File.ReadAllText(e.FullPath); - foreach (var line in data.Split('\n')) + + while (Locked) { + Thread.Sleep(100); + } + + var dbName = Path.GetFileName(e.FullPath); + + if (!WatchFiles.Keys.Contains(dbName)) return; + + Log.Information("Database File Created: {DbName}", dbName); + + var data = new List(); + using var fh = File.OpenText(e.FullPath); + + while (!fh.EndOfStream) + data.Add(fh.ReadLine() ?? string.Empty); + + foreach (var line in data) + { + if (line == "") continue; var type = WatchFiles[dbName]; OnDatabaseChange?.Invoke(dbName, line); } @@ -68,6 +92,6 @@ public class DBSyncWatcher private void HandleError(object sender, ErrorEventArgs e) { - Console.WriteLine("Error: {0}\n{1}", e.GetException().Message, e.GetException().StackTrace); + Log.Error("Error: {Message}\n{StackTrace}", e.GetException().Message, e.GetException().StackTrace); } } \ No newline at end of file diff --git a/FreeTubeSyncer/REST/Syncer.cs b/FreeTubeSyncer/REST/Syncer.cs index 71ace55..b92c4ed 100644 --- a/FreeTubeSyncer/REST/Syncer.cs +++ b/FreeTubeSyncer/REST/Syncer.cs @@ -10,6 +10,8 @@ using System.Threading.Tasks; using FreeTubeSyncer.Library; using FreeTubeSyncer.Models.DatabaseModels; using RestSharp; +using Serilog; +using Serilog.Core; namespace FreeTubeSyncer.REST; @@ -68,34 +70,29 @@ public class Syncer : ISyncer where T : class, IDataModel, new() public async Task PingApi() { - // TODO: Replace with Logger - Console.WriteLine($"Pinging API at {_client.BuildUri(new RestRequest("/ping"))}..."); + Log.Information("Pinging API at {BuildUri}...", _client.BuildUri(new RestRequest("/ping"))); try { var res = await _client.GetAsync(new RestRequest("/ping")); if (res == null) { - // TODO: Replace with Logger - Console.WriteLine($"Ping returned null, not the server we are looking for!"); + Log.Information("Ping returned null, not the server we are looking for!"); return false; } if (res.AppVersion == "0.1.3") { - // TODO: Replace with Logger - Console.WriteLine($"Server Online! {res.AppVersion}"); + Log.Information("Server Online! {AppVersion}", res.AppVersion); return true; } } catch (Exception ex) { - // TODO: Replace with Logger - Console.WriteLine($"Network Error: {ex.Message}, API not alive."); + Log.Error("API not alive. Network Error: {Message}", ex.Message); return false; } - - // TODO: Replace with Logger - Console.WriteLine("Responded with something other then 404, API not what we expected."); + + Log.Error("Responded with something other then Ping. This is not the server we are looking for..."); return false; } @@ -128,8 +125,7 @@ public class Syncer : ISyncer where T : class, IDataModel, new() if (_entries.Any(x => x.EqualId(item.Id()))) _entries.RemoveAll(x => x.EqualId(item.Id())); _entries.Add(item); - // TODO: Replace with Logger - Console.WriteLine($"Posting to REST: {item.Id()}"); + Log.Information("Posting to REST: {ItemId}", item.Id()); await _client.PostJsonAsync(_restEndpoint, item); } } @@ -147,15 +143,11 @@ public class Syncer : ISyncer where T : class, IDataModel, new() if (data.Equals(entry)) continue; - // TODO: Replace with Logger - Console.WriteLine($"Updated Entry from REST for {_dbName} - {entry.Id()}"); + Log.Information("Updated Entry from REST for {DbName} - {EntryId}", _dbName, entry.Id()); _entries.RemoveAll(x => x.EqualId(entry.Id())); } else - { - // TODO: Replace with Logger - Console.WriteLine($"New Entry from REST for {_dbName} - {entry.Id()}"); - } + Log.Information("New Entry from REST for {DbName} - {EntryId}", _dbName, entry.Id()); _entries.Add(entry); _isDirty = true; @@ -186,8 +178,8 @@ public class Syncer : ISyncer where T : class, IDataModel, new() } catch (Exception iex) { - // TODO: Replace with Logger - Console.WriteLine($"Failed to parse line: {entryObject}\nMessage: {iex.Message}"); + Log.Error("Failed to parse line: {EntryLine}", entryObject); + Log.Error("Error Message: {Messsage}", iex.Message); entry = null; } } @@ -199,15 +191,11 @@ public class Syncer : ISyncer where T : class, IDataModel, new() { var data = _entries.First(x => x.EqualId(entry.Id())); if (data.Equals(entry)) return; - // TODO: Replace with Logger - Console.WriteLine($"File Entry {entry.Id()} updated for {_dbName}"); + Log.Information("Updated File Entry {EntryId} updated for {DbName}", entry.Id(), _dbName); _entries.RemoveAll(x => x.EqualId(entry.Id())); } else - { - // TODO: Replace with Logger - Console.WriteLine($"New File Entry {entry.Id()} for {_dbName}"); - } + Log.Information("New File Entry {EntryId} for {DbName}", entry.Id(), _dbName); _entries.Add(entry); await _client.PostJsonAsync(_restEndpoint, entry); @@ -219,8 +207,8 @@ public class Syncer : ISyncer where T : class, IDataModel, new() if (!_isDirty) return; _syncing = true; - // TODO: Replace with Logger - Console.WriteLine($"Syncing {_dbPath}..."); + Log.Information("Syncing {DbName}...", _dbPath); + var start = DateTime.Now; var json = new List(); foreach (var entry in _entries) json.Add(entry.JsonData()); @@ -233,8 +221,7 @@ public class Syncer : ISyncer where T : class, IDataModel, new() fh.Close(); } _watcher.Locked = false; - // TODO: Replace with Logger - Console.WriteLine($"Updated {_dbPath}."); + Log.Information("Updated {DbName}, completed in {TimeSpan}", _dbName, DateTime.Now - start); _isDirty = false; _syncing = false; }