diff --git a/Hollow/App.axaml.cs b/Hollow/App.axaml.cs index de7b8d1..fd85789 100644 --- a/Hollow/App.axaml.cs +++ b/Hollow/App.axaml.cs @@ -33,7 +33,7 @@ public override void Initialize() .WriteTo.File(Path.Combine(AppInfo.LogDir, "log_.txt"), outputTemplate: "{Timestamp:HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}", rollingInterval: RollingInterval.Day, retainedFileCountLimit: null) .CreateLogger(); - Log.Information("Hollow is starting..."); + Log.Information("[App] Hollow is starting"); AvaloniaXamlLoader.Load(this); } @@ -74,6 +74,7 @@ public static void ConfigureServices(Action? customServicesF customServicesFactory?.Invoke(services); _provider = services.BuildServiceProvider(); + Log.Information("[App] Services Configured"); } public static T GetService() where T : notnull => (_provider ?? throw new InvalidOperationException("Services not Configured")).GetRequiredService(); diff --git a/Hollow/Services/ConfigurationService/ConfigurationService.cs b/Hollow/Services/ConfigurationService/ConfigurationService.cs index 9543489..4847d08 100644 --- a/Hollow/Services/ConfigurationService/ConfigurationService.cs +++ b/Hollow/Services/ConfigurationService/ConfigurationService.cs @@ -5,6 +5,7 @@ using Hollow.Abstractions.Models; using Hollow.Abstractions.Models.Configs; using Hollow.Helpers; +using Serilog; namespace Hollow.Services.ConfigurationService; @@ -15,6 +16,8 @@ public ConfigurationService() AppConfig = LoadConfiguration(); CurrentLanguage = AppConfig.Language == "Auto" ? CultureInfo.CurrentCulture.Name : AppConfig.Language; I18NExtension.Culture = AppConfig.Language != "Auto" ? new CultureInfo(AppConfig.Language) : CultureInfo.CurrentCulture; + + Log.Information("[ConfigurationService] Initialized (Current Language: {Language})", CurrentLanguage); } public AppConfig AppConfig { get; set; } @@ -31,6 +34,7 @@ public static AppConfig LoadConfiguration() { File.WriteAllText(AppInfo.ConfigPath, JsonSerializer.Serialize(new AppConfig(), HollowJsonSerializer.Options)); } + Log.Information("[ConfigurationService] Configuration loaded"); return JsonSerializer.Deserialize(File.ReadAllText(AppInfo.ConfigPath))!; } diff --git a/Hollow/Services/GachaService/GachaService.cs b/Hollow/Services/GachaService/GachaService.cs index ee282ce..9067ba1 100644 --- a/Hollow/Services/GachaService/GachaService.cs +++ b/Hollow/Services/GachaService/GachaService.cs @@ -30,10 +30,12 @@ public partial class GachaService(IConfigurationService configurationService, Ht { gachaRecords = new GachaRecords(); await File.WriteAllTextAsync(AppInfo.GachaRecordPath, JsonSerializer.Serialize(gachaRecords, HollowJsonSerializer.Options)); + Log.Information("[GachaService] Gacha record file not found, created new one"); } else { gachaRecords = JsonSerializer.Deserialize(await File.ReadAllTextAsync(AppInfo.GachaRecordPath))!; + Log.Information("[GachaService] Gacha record file loaded"); } var gachaRecordProfileDictionary = gachaRecords.Profiles.ToDictionary(item => item.Uid, item => item); @@ -43,6 +45,8 @@ public partial class GachaService(IConfigurationService configurationService, Ht } GachaRecordProfileDictionary = gachaRecordProfileDictionary; + + Log.Information("[GachaService] Found {0} gacha record profiles", GachaRecordProfileDictionary.Count); return GachaRecordProfileDictionary; } @@ -66,6 +70,7 @@ private static (bool, List) OmitExistedRecords(string time, List> GetGachaRecords(string authKey, IProgress> progress) { + Log.Information("[GachaService] Start fetching gacha records"); var gachaRecords = new GachaRecords(); var targetProfile = new GachaRecordProfile(); var uid = ""; @@ -127,7 +132,11 @@ public async Task> GetGachaRecords(string authKey, IProgr newRecordsCount += pageDataList.Count; targetProfile.List.AddRange(pageDataList); - progress.Report(new Response(true, "progress") { Data = $"{string.Join('^', pageData.Data.List.Select(x => x.Name))}^{uid}^{gachaType}^{nthPage}"}); + + var dataItemsName= pageData.Data.List.Select(x => x.Name).ToArray(); + progress.Report(new Response(true, "progress") { Data = $"{string.Join('^', dataItemsName)}^{uid}^{gachaType}^{nthPage}"}); + Log.Information("[GachaService] Fetched Records ({0} Items | Page {1} | Gacha Type {2} | UID {3})", dataItemsName.Length, nthPage, gachaType, uid); + pageEndId = pageData.Data.List[^1].Id; nthPage++; await Task.Delay(TimeSpan.FromMilliseconds(400)); @@ -145,6 +154,7 @@ public async Task> GetGachaRecords(string authKey, IProgr gachaRecords.Info.ExportTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(); gachaRecords.Info.ExportAppVersion = AppInfo.AppVersion; progress.Report(new Response(true, $"success {fetchRecordsCount} {newRecordsCount}")); + Log.Information("[GachaService] Fetched {0} new records", newRecordsCount); return new Response(true) {Data = gachaRecords}; } @@ -161,24 +171,28 @@ public Response GetAuthKey() var gameDirectory = configurationService.AppConfig.Game.Directory; if (string.IsNullOrWhiteSpace(gameDirectory)) { + Log.Error("[GachaService] Game directory not found"); return new Response(false, Lang.Toast_UnknownGameDirectory_Message); } var webCachesPath = Path.Combine(gameDirectory, "ZenlessZoneZero_Data", "webCaches"); if (!Directory.Exists(webCachesPath)) { + Log.Error("[GachaService] Web caches not found"); return new Response(false, Lang.Toast_WebCachesNotFound_Message); } var webCachesVersionPath = Directory.GetDirectories(webCachesPath).FirstOrDefault(); if (string.IsNullOrWhiteSpace(webCachesVersionPath)) { + Log.Error("[GachaService] Web caches version not found"); return new Response(false, Lang.Toast_WebCachesNotFound_Message); } var dataPath = Path.Combine(webCachesVersionPath, "Cache", "Cache_Data", "data_2"); if (!File.Exists(dataPath)) { + Log.Error("[GachaService] Data file not found"); return new Response(false, Lang.Toast_WebCachesNotFound_Message); } @@ -189,7 +203,7 @@ public Response GetAuthKey() } var targetGachaLogUrl = authKey.Data.Split("&authkey=")[1].Split("&")[0]; - Log.Information("Get authKey: {0}", targetGachaLogUrl); + Log.Information("[GachaService] Get authKey from WebCaches: {0}", targetGachaLogUrl); return new Response (true) {Data = targetGachaLogUrl}; } @@ -197,16 +211,17 @@ public Response GetAuthKeyFromUrl(string url) { if (!GachaLogUrlRegex().IsMatch(url) && !url.StartsWith(_gachaLogClientUrl)) { + Log.Error("[GachaService] Invalid URL: {url}", url); return new Response(false, Lang.Toast_InvalidUrl_Message); } try { var targetGachaLogUrl = url.Split("&authkey=")[1].Split("&")[0]; - Log.Information("Get authKey: {0}", targetGachaLogUrl); + Log.Information("[GachaService] Get authKey from URL: {0}", targetGachaLogUrl); return new Response (true) {Data = targetGachaLogUrl}; } - catch (Exception e) + catch (Exception) { return new Response(false, Lang.Toast_InvalidUrl_Message); } diff --git a/Hollow/Services/GameService/GameService.cs b/Hollow/Services/GameService/GameService.cs index f9875b2..61d4bab 100644 --- a/Hollow/Services/GameService/GameService.cs +++ b/Hollow/Services/GameService/GameService.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using Hollow.Services.ConfigurationService; +using Serilog; namespace Hollow.Services.GameService; @@ -15,32 +16,45 @@ public static bool ValidateGameDirectory(string directoryPath) return false; } var files = Directory.GetFiles(directoryPath); - return files.Any(file => file.EndsWith("config.ini")) && files.Any(file => file.EndsWith("ZenlessZoneZero.exe")); + var validation = files.Any(file => file.EndsWith("config.ini")) && + files.Any(file => file.EndsWith("ZenlessZoneZero.exe")); + if (validation) + { + Log.Information("[GameService] Game directory validated ({path})", directoryPath); + } + else + { + Log.Error("[GameService] Game directory validation failed ({path})", directoryPath); + } + return validation; } public string GetGameVersion() { var files = Directory.GetFiles(configurationService.AppConfig.Game.Directory); var configIni = files.First(file => file.EndsWith("config.ini")); - var lines = File.ReadAllLines(configIni); - return lines.First(line => line.StartsWith("game_version=")).Split("=")[1]; + var version = File.ReadAllLines(configIni).First(line => line.StartsWith("game_version=")).Split("=")[1]; + Log.Information("[GameService] Get game version: {version}", version); + return version; } public bool StartGame() { try { - var gamePath = configurationService.AppConfig.Game.Directory; var gameArguments = configurationService.AppConfig.Game.Arguments; var gameExe = Directory.GetFiles(gamePath).First(file => file.EndsWith("ZenlessZoneZero.exe")); var process = new Process { StartInfo = { Arguments = gameArguments, UseShellExecute = true, FileName = gameExe, CreateNoWindow = true, Verb = "runas" } }; process.Start(); + + Log.Information("[GameService] Game started"); return true; } catch(Win32Exception) { + Log.Error("[GameService] Game start failed"); return false; } } diff --git a/Hollow/Services/MetadataService/MetadataService.cs b/Hollow/Services/MetadataService/MetadataService.cs index 58977d0..141a61d 100644 --- a/Hollow/Services/MetadataService/MetadataService.cs +++ b/Hollow/Services/MetadataService/MetadataService.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Hollow.Abstractions.Models; using Hollow.Abstractions.Models.HttpContrasts; +using Serilog; namespace Hollow.Services.MetadataService; @@ -73,6 +74,7 @@ private async Task DownloadMetadata(string metadataUrl, string metadataPat File.Delete(metadataPath); } await File.WriteAllTextAsync(metadataPath, metadata); + Log.Information("[MetadataService] Metadata downloaded ({key})", metadataUrl); return true; } catch (Exception) diff --git a/Hollow/Services/NavigationService/NavigationService.cs b/Hollow/Services/NavigationService/NavigationService.cs index 615590e..fe78223 100644 --- a/Hollow/Services/NavigationService/NavigationService.cs +++ b/Hollow/Services/NavigationService/NavigationService.cs @@ -1,4 +1,5 @@ using System; +using Serilog; namespace Hollow.Services.NavigationService; diff --git a/Hollow/ViewModels/MainWindowViewModel.cs b/Hollow/ViewModels/MainWindowViewModel.cs index ec79208..5f2a6ad 100644 --- a/Hollow/ViewModels/MainWindowViewModel.cs +++ b/Hollow/ViewModels/MainWindowViewModel.cs @@ -12,6 +12,7 @@ using Hollow.Services.MiHoYoLauncherService; using Hollow.Services.NavigationService; using Hollow.Views.Controls; +using Serilog; namespace Hollow.ViewModels; @@ -52,6 +53,7 @@ private async Task LoadOnlineResources() // Background var allGameBasicInfo = await _miHoYoLauncherService.GetAllGameBasicInfo(); BackgroundUrl = allGameBasicInfo?.Data.GameInfo[0].Backgrounds[0].Image.Url ?? BackgroundUrl; + Log.Information("[MainWindow] Background loaded"); // Metadata var metadataProgress = new Progress>(value => diff --git a/Hollow/ViewModels/Pages/AnnouncementsViewModel.cs b/Hollow/ViewModels/Pages/AnnouncementsViewModel.cs index 2b0b8c9..a9456ac 100644 --- a/Hollow/ViewModels/Pages/AnnouncementsViewModel.cs +++ b/Hollow/ViewModels/Pages/AnnouncementsViewModel.cs @@ -3,6 +3,7 @@ using Hollow.Helpers; using Hollow.Views.Controls.WebView; using Hollow.Views.Pages; +using Serilog; namespace Hollow.ViewModels.Pages; @@ -93,6 +94,7 @@ public void Navigated() public void GameAnnouncementWebView_OnInitialized(object? sender, EventArgs e) { Announcements.AnnouncementWebView.Source = new Uri(AnnouncementUrl); + Log.Information("[Announcements] WebView initialized"); } public void GameAnnouncementWebView_OnNavigationStarting(object? sender, WebViewNavigationStartingEventArgs e) @@ -100,6 +102,8 @@ public void GameAnnouncementWebView_OnNavigationStarting(object? sender, WebView if (e.Request!.ToString().StartsWith("inner:")) { HtmlHelper.OpenUrl(e.Request!.ToString()[6..]); + e.Cancel = true; + Log.Information("[Announcements] Open inner URL: {Url}", e.Request!.ToString()[6..]); } } diff --git a/Hollow/ViewModels/Pages/HomeViewModel.cs b/Hollow/ViewModels/Pages/HomeViewModel.cs index 670181e..4a16fd5 100644 --- a/Hollow/ViewModels/Pages/HomeViewModel.cs +++ b/Hollow/ViewModels/Pages/HomeViewModel.cs @@ -10,6 +10,7 @@ using Hollow.Services.GameService; using Hollow.Services.MiHoYoLauncherService; using Hollow.Services.NavigationService; +using Serilog; namespace Hollow.ViewModels.Pages; @@ -78,6 +79,7 @@ private async Task LoadContents() // Version News VersionNewsImageUrl = allGameBasicInfo?.Data.GameInfo[0].Backgrounds[0].Icon.Url ?? ""; VersionNewsUrl = allGameBasicInfo?.Data.GameInfo[0].Backgrounds[0].Icon.Link ?? ""; + Log.Information("[Home] Version news loaded"); // Banners var banners = gameContent?.Data.Content.Banners; @@ -88,6 +90,7 @@ private async Task LoadContents() using var stream = await _httpClient.GetAsync(banner.Image.Url); Banners.Add(new BannerModel {Link = banner.Image.Link, Image = BitmapOperations.Convert(await stream.Content.ReadAsStreamAsync(), 320)}); } + Log.Information("[Home] Banners loaded"); // Posts var posts = gameContent?.Data.Content.Posts; @@ -109,6 +112,7 @@ private async Task LoadContents() break; } } + Log.Information("[Home] Posts loaded"); } public void Navigated() diff --git a/Hollow/ViewModels/Pages/SettingsViewModel.cs b/Hollow/ViewModels/Pages/SettingsViewModel.cs index ff8deab..5cfc1b6 100644 --- a/Hollow/ViewModels/Pages/SettingsViewModel.cs +++ b/Hollow/ViewModels/Pages/SettingsViewModel.cs @@ -15,6 +15,7 @@ using Hollow.Services.NavigationService; using Hollow.Views.Controls; using Hollow.Views.Pages; +using Serilog; using NotificationType = Hollow.Enums.NotificationType; namespace Hollow.ViewModels.Pages; @@ -54,6 +55,8 @@ public SettingsViewModel(IConfigurationService configurationService, INavigation } var language = _configurationService.AppConfig.Language; Language = language != "Auto" ? GetLanguage.LanguageList.Select(x => x.Key).ToList()[GetLanguage.LanguageList.Select(x => x.Value).ToList().IndexOf(language)] : "Auto"; + + Log.Information("[Settings] Configuration loaded"); } #region Game @@ -70,10 +73,12 @@ private async Task BrowseGameDirectory() GameDirectory = directory; _configurationService.AppConfig.Game.Directory = GameDirectory; _configurationService.Save(); + Log.Information("[Settings] Game directory changed to {GameDirectory}", GameDirectory); } else if (!string.IsNullOrWhiteSpace(directory)) { await HollowHost.ShowToast(Lang.Toast_InvalidGameDirectory_Title, Lang.Toast_InvalidGameDirectory_Message, NotificationType.Error); + Log.Warning("[Settings] Invalid game directory: {GameDirectory}", directory); } } @@ -121,6 +126,7 @@ private void OnChangeLanguage() _configurationService.AppConfig.Language = Language != "Auto" ? GetLanguage.LanguageList[Language] : "Auto"; _configurationService.CurrentLanguage = language != "Auto" ? language : CultureInfo.CurrentCulture.Name; _configurationService.Save(); + Log.Information("[Settings] Language changed to {Language}", Language); } #endregion diff --git a/Hollow/ViewModels/Pages/SignalSearchViewModel.cs b/Hollow/ViewModels/Pages/SignalSearchViewModel.cs index 4d0b84e..3f35e56 100644 --- a/Hollow/ViewModels/Pages/SignalSearchViewModel.cs +++ b/Hollow/ViewModels/Pages/SignalSearchViewModel.cs @@ -22,6 +22,7 @@ using Hollow.Views.Controls; using Hollow.Views.Dialogs; using Hollow.Views.Pages; +using Serilog; namespace Hollow.ViewModels.Pages; @@ -120,6 +121,7 @@ async void UidDeleteConfirmCallback(bool confirmed) var gachaRecords = new GachaRecords { Info = { ExportAppVersion = AppInfo.AppVersion, ExportTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString() }, Profiles = UidList.Where(uid => uid != SelectedUid).Select(uid => _gachaProfiles![uid]).ToList() }; await File.WriteAllTextAsync(AppInfo.GachaRecordPath, JsonSerializer.Serialize(gachaRecords, HollowJsonSerializer.Options)); await LoadGachaRecords(); + Log.Information("[SignalSearch] Profile deleted: {Uid}", SelectedUid); } } @@ -140,6 +142,7 @@ async void SelectedUidListCallback(string[] selectedUidList) await using var stream = await file.OpenWriteAsync(); await using var streamWriter = new StreamWriter(stream); await streamWriter.WriteLineAsync(JsonSerializer.Serialize(gachaRecords, HollowJsonSerializer.Options)); + Log.Information("[SignalSearch] Records exported: {UidList} to {File}", selectedUidList, file.Path); } } @@ -181,7 +184,6 @@ private async Task UpdateByWebCaches() await HollowHost.ShowToast(Lang.SignalSearch_Update_GetRecordsFailed, authKey.Message, NotificationType.Error); return; } - await UpdateRecords(authKey.Data); } diff --git a/Hollow/Views/MainWindow.axaml.cs b/Hollow/Views/MainWindow.axaml.cs index c5cadbb..d6c4816 100644 --- a/Hollow/Views/MainWindow.axaml.cs +++ b/Hollow/Views/MainWindow.axaml.cs @@ -22,6 +22,7 @@ private void CloseButton_OnClick(object? _1, RoutedEventArgs _2) { Log.CloseAndFlush(); Environment.Exit(0); + Log.Information("[App] Hollow is shutting down"); } protected override void OnPointerPressed(PointerPressedEventArgs e)