From 2351730e4eee714c90c81e824382be52fd431c41 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 15:41:56 +0000 Subject: [PATCH 1/2] Initial plan From b15bafc33b4cee0a4ecfd62027f076e844595f8c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 16:00:36 +0000 Subject: [PATCH 2/2] feat: add full lifecycle GeneralTracer tracing to five core components - GeneralUpdate.Bowl: Add Info/Warn/Error/Fatal/Debug tracing across Bowl, AbstractStrategy, WindowStrategy, and LinuxStrategy for all execution stages (initialization, process startup, crash detection, backup/restore, cleanup) - GeneralUpdate.Extension: Link GeneralTracer.cs from Common into project; add comprehensive tracing to GeneralExtensionHost (constructors, QueryExtensionsAsync, DownloadExtensionAsync, UpdateExtensionAsync, InstallExtensionAsync) and DownloadQueueManager (Enqueue, ProcessQueueAsync, ProcessTaskAsync, CancelTask, Dispose) - GeneralUpdate.Core: Add lifecycle tracing to CompressMiddleware, HashMiddleware, PatchMiddleware; add key-step tracing to GeneralUpdateBootstrap.ExecuteWorkflowAsync and LaunchAsync; add pipeline construction/StartApp tracing to WindowsStrategy and LinuxStrategy - GeneralUpdate.ClientCore: Add lifecycle tracing to CompressMiddleware, HashMiddleware, PatchMiddleware; add pipeline/StartApp tracing to WindowsStrategy and LinuxStrategy; add comprehensive step tracing to SilentUpdateMode (StartAsync, PollLoopAsync, PrepareUpdateIfNeededAsync, OnProcessExit); add connection-state tracing to UpgradeHubService; add key-workflow tracing to GeneralClientBootstrap.ExecuteWorkflowAsync - GeneralUpdate.Drivelution: Add missing using directive and tracing to all static entry points in GeneralDrivelution (QuickUpdateAsync, ValidateAsync, GetDriversFromDirectoryAsync, GetPlatformInfo) Agent-Logs-Url: https://github.com/GeneralLibrary/GeneralUpdate/sessions/fab83fb2-df34-4932-b476-6a6a8299567b Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com> --- src/c#/GeneralUpdate.Bowl/Bowl.cs | 52 ++++++++++--- .../Strategys/AbstractStrategy.cs | 13 +++- .../Strategys/LinuxStrategy.cs | 54 +++++++++++--- .../Strategys/WindowStrategy.cs | 52 +++++++++++-- .../GeneralClientBootstrap.cs | 22 +++++- .../Hubs/UpgradeHubService.cs | 6 ++ .../Pipeline/CompressMiddleware.cs | 18 ++++- .../Pipeline/HashMiddleware.cs | 23 +++++- .../Pipeline/PatchMiddleware.cs | 16 +++- .../SilentUpdateMode.cs | 33 +++++++++ .../Strategys/LinuxStrategy.cs | 9 +++ .../Strategys/WindowsStrategy.cs | 9 +++ .../GeneralUpdateBootstrap.cs | 15 ++++ .../Pipeline/CompressMiddleware.cs | 15 +++- .../Pipeline/HashMiddleware.cs | 23 +++++- .../Pipeline/PatchMiddleware.cs | 16 +++- .../Strategys/LinuxStrategy.cs | 7 ++ .../Strategys/WindowsStrategy.cs | 8 ++ .../GeneralDrivelution.cs | 26 +++++-- .../Core/GeneralExtensionHost.cs | 74 +++++++++++++++++-- .../Download/DownloadQueueManager.cs | 21 ++++++ .../GeneralUpdate.Extension.csproj | 7 ++ 22 files changed, 465 insertions(+), 54 deletions(-) diff --git a/src/c#/GeneralUpdate.Bowl/Bowl.cs b/src/c#/GeneralUpdate.Bowl/Bowl.cs index e84a6240..95875f40 100644 --- a/src/c#/GeneralUpdate.Bowl/Bowl.cs +++ b/src/c#/GeneralUpdate.Bowl/Bowl.cs @@ -5,6 +5,7 @@ using GeneralUpdate.Bowl.Strategys; using GeneralUpdate.Common.Internal.Bootstrap; using GeneralUpdate.Common.Internal.JsonContext; +using GeneralUpdate.Common.Shared; using GeneralUpdate.Common.Shared.Object; namespace GeneralUpdate.Bowl; @@ -20,31 +21,60 @@ private Bowl() { } private static void CreateStrategy() { + GeneralTracer.Info("Bowl.CreateStrategy: detecting current OS platform."); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + GeneralTracer.Info("Bowl.CreateStrategy: Windows platform detected, creating WindowStrategy."); _strategy = new WindowStrategy(); - + } + if (_strategy == null) + { + GeneralTracer.Fatal("Bowl.CreateStrategy: unsupported operating system, no strategy created."); throw new PlatformNotSupportedException("Unsupported operating system"); + } + + GeneralTracer.Info("Bowl.CreateStrategy: strategy created successfully."); } - + public static void Launch(MonitorParameter? monitorParameter = null) { - monitorParameter ??= CreateParameter(); - CreateStrategy(); - _strategy?.SetParameter(monitorParameter); - _strategy?.Launch(); + GeneralTracer.Info("Bowl.Launch: starting surveillance launch."); + try + { + monitorParameter ??= CreateParameter(); + GeneralTracer.Info($"Bowl.Launch: monitor parameter resolved. ProcessNameOrId={monitorParameter.ProcessNameOrId}, TargetPath={monitorParameter.TargetPath}"); + CreateStrategy(); + _strategy?.SetParameter(monitorParameter); + GeneralTracer.Info("Bowl.Launch: strategy parameter set, invoking Launch."); + _strategy?.Launch(); + GeneralTracer.Info("Bowl.Launch: strategy Launch completed."); + } + catch (Exception ex) + { + GeneralTracer.Error("Bowl.Launch: exception occurred during surveillance launch.", ex); + throw; + } } private static MonitorParameter CreateParameter() { + GeneralTracer.Info("Bowl.CreateParameter: reading ProcessInfo from environment variable."); var json = Environments.GetEnvironmentVariable("ProcessInfo"); - if(string.IsNullOrWhiteSpace(json)) - throw new ArgumentNullException("ProcessInfo environment variable not set !"); - + if (string.IsNullOrWhiteSpace(json)) + { + GeneralTracer.Fatal("Bowl.CreateParameter: ProcessInfo environment variable is not set."); + throw new ArgumentNullException("ProcessInfo environment variable not set !"); + } + var processInfo = JsonSerializer.Deserialize(json, ProcessInfoJsonContext.Default.ProcessInfo); - if(processInfo == null) + if (processInfo == null) + { + GeneralTracer.Fatal("Bowl.CreateParameter: failed to deserialize ProcessInfo JSON."); throw new ArgumentNullException("ProcessInfo json deserialize fail!"); - + } + + GeneralTracer.Info($"Bowl.CreateParameter: ProcessInfo deserialized successfully. AppName={processInfo.AppName}, LastVersion={processInfo.LastVersion}"); return new MonitorParameter { ProcessNameOrId = processInfo.AppName, diff --git a/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs b/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs index c6bc12c3..f7663a01 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.IO; using GeneralUpdate.Common.FileBasic; +using GeneralUpdate.Common.Shared; namespace GeneralUpdate.Bowl.Strategys; @@ -14,17 +15,22 @@ internal abstract class AbstractStrategy : IStrategy public virtual void Launch() { + GeneralTracer.Info($"AbstractStrategy.Launch: starting inner application. App={_parameter.InnerApp}, Args={_parameter.InnerArguments}"); Startup(_parameter.InnerApp, _parameter.InnerArguments); + GeneralTracer.Info("AbstractStrategy.Launch: inner application process finished."); } private void Startup(string appName, string arguments) { + GeneralTracer.Info($"AbstractStrategy.Startup: preparing process. FileName={appName}"); if (Directory.Exists(_parameter.FailDirectory)) { + GeneralTracer.Info($"AbstractStrategy.Startup: removing existing fail directory: {_parameter.FailDirectory}"); StorageManager.DeleteDirectory(_parameter.FailDirectory); } Directory.CreateDirectory(_parameter.FailDirectory); - + GeneralTracer.Info($"AbstractStrategy.Startup: fail directory created: {_parameter.FailDirectory}"); + var startInfo = new ProcessStartInfo { FileName = appName, @@ -39,15 +45,20 @@ private void Startup(string appName, string arguments) process.OutputDataReceived += OutputHandler; process.ErrorDataReceived += OutputHandler; process.Start(); + GeneralTracer.Info($"AbstractStrategy.Startup: process started. PID={process.Id}"); process.BeginOutputReadLine(); process.BeginErrorReadLine(); process.WaitForExit(1000 * 10); + GeneralTracer.Info($"AbstractStrategy.Startup: process exited. ExitCode={process.ExitCode}"); } private void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine) { var data = outLine.Data; if (!string.IsNullOrEmpty(data)) + { + GeneralTracer.Debug($"AbstractStrategy.OutputHandler: {data}"); OutputList.Add(data); + } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs b/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs index 7f38e923..77d18b79 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using GeneralUpdate.Bowl.Internal; +using GeneralUpdate.Common.Shared; namespace GeneralUpdate.Bowl.Strategys; @@ -24,15 +25,34 @@ internal class LinuxStrategy : AbstractStrategy public override void Launch() { - Install(); - base.Launch(); + GeneralTracer.Info("LinuxStrategy.Launch: starting Linux surveillance launch."); + try + { + Install(); + GeneralTracer.Info("LinuxStrategy.Launch: procdump installation completed, invoking base launch."); + base.Launch(); + GeneralTracer.Info("LinuxStrategy.Launch: launch lifecycle completed."); + } + catch (Exception ex) + { + GeneralTracer.Error("LinuxStrategy.Launch: exception occurred during Linux surveillance launch.", ex); + throw; + } } private void Install() { + GeneralTracer.Info("LinuxStrategy.Install: determining procdump package and running install script."); string scriptPath = "./install.sh"; string packageFile = GetPacketName(); - + + if (string.IsNullOrEmpty(packageFile)) + { + GeneralTracer.Warn("LinuxStrategy.Install: no matching procdump package found for the current Linux distribution."); + return; + } + + GeneralTracer.Info($"LinuxStrategy.Install: executing install.sh with package={packageFile}."); ProcessStartInfo processStartInfo = new ProcessStartInfo() { FileName = "/bin/bash", @@ -50,25 +70,33 @@ private void Install() string error = process.StandardError.ReadToEnd(); process.WaitForExit(); - Console.WriteLine("Output:"); - Console.WriteLine(output); + if (!string.IsNullOrEmpty(output)) + GeneralTracer.Info($"LinuxStrategy.Install output: {output}"); if (!string.IsNullOrEmpty(error)) + GeneralTracer.Warn($"LinuxStrategy.Install error output: {error}"); + + if (process.ExitCode != 0) { - Console.WriteLine("Error:"); - Console.WriteLine(error); + GeneralTracer.Error($"LinuxStrategy.Install: install script exited with code {process.ExitCode}."); + } + else + { + GeneralTracer.Info("LinuxStrategy.Install: procdump installation succeeded."); } } catch (Exception e) { - Console.WriteLine($"An error occurred: {e.Message}"); + GeneralTracer.Error("LinuxStrategy.Install: exception occurred while running install script.", e); } } private string GetPacketName() { + GeneralTracer.Info("LinuxStrategy.GetPacketName: detecting Linux distribution."); var packageFileName = string.Empty; var system = GetSystem(); + GeneralTracer.Info($"LinuxStrategy.GetPacketName: detected distribution={system.Name}, version={system.Version}."); if (_rocdumpAmd64.Contains(system.Name)) { packageFileName = $"procdump_3.3.0_amd64.deb"; @@ -82,11 +110,17 @@ private string GetPacketName() packageFileName = $"procdump-3.3.0-0.cm2.x86_64.rpm"; } + if (string.IsNullOrEmpty(packageFileName)) + GeneralTracer.Warn($"LinuxStrategy.GetPacketName: no matching package for distribution={system.Name}."); + else + GeneralTracer.Info($"LinuxStrategy.GetPacketName: resolved package={packageFileName}."); + return packageFileName; } private LinuxSystem GetSystem() { + GeneralTracer.Info("LinuxStrategy.GetSystem: reading /etc/os-release."); string osReleaseFile = "/etc/os-release"; if (File.Exists(osReleaseFile)) { @@ -106,9 +140,11 @@ private LinuxSystem GetSystem() } } + GeneralTracer.Info($"LinuxStrategy.GetSystem: distro={distro}, version={version}."); return new LinuxSystem(distro, version); } - + + GeneralTracer.Fatal("LinuxStrategy.GetSystem: /etc/os-release not found, cannot determine Linux distribution."); throw new FileNotFoundException("Cannot determine the Linux distribution. The /etc/os-release file does not exist."); } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Bowl/Strategys/WindowStrategy.cs b/src/c#/GeneralUpdate.Bowl/Strategys/WindowStrategy.cs index 35d4940e..98ff628d 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategys/WindowStrategy.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategys/WindowStrategy.cs @@ -6,6 +6,7 @@ using GeneralUpdate.Bowl.Internal; using GeneralUpdate.Common.FileBasic; using GeneralUpdate.Common.Internal.Bootstrap; +using GeneralUpdate.Common.Shared; namespace GeneralUpdate.Bowl.Strategys; @@ -17,32 +18,48 @@ internal class WindowStrategy : AbstractStrategy public override void Launch() { + GeneralTracer.Info("WindowStrategy.Launch: initializing actions pipeline."); InitializeActions(); _applicationsDirectory = Path.Combine(_parameter.TargetPath, "Applications", "Windows"); _parameter.InnerApp = Path.Combine(_applicationsDirectory, GetAppName()); var dmpFullName = Path.Combine(_parameter.FailDirectory, _parameter.DumpFileName); _parameter.InnerArguments = $"-e -ma {_parameter.ProcessNameOrId} {dmpFullName}"; + GeneralTracer.Info($"WindowStrategy.Launch: launching inner app={_parameter.InnerApp}, dumpFile={dmpFullName}."); //This method is used to launch scripts in applications. base.Launch(); + GeneralTracer.Info("WindowStrategy.Launch: base launch completed, executing final treatment."); ExecuteFinalTreatment(); + GeneralTracer.Info("WindowStrategy.Launch: launch lifecycle completed."); } - private string GetAppName() => RuntimeInformation.OSArchitecture switch + private string GetAppName() { - Architecture.X86 => "procdump.exe", - Architecture.X64 => "procdump64.exe", - _ => "procdump64a.exe" - }; + var name = RuntimeInformation.OSArchitecture switch + { + Architecture.X86 => "procdump.exe", + Architecture.X64 => "procdump64.exe", + _ => "procdump64a.exe" + }; + GeneralTracer.Info($"WindowStrategy.GetAppName: resolved procdump executable={name} for arch={RuntimeInformation.OSArchitecture}."); + return name; + } private void ExecuteFinalTreatment() { var dumpFile = Path.Combine(_parameter.FailDirectory, _parameter.DumpFileName); + GeneralTracer.Info($"WindowStrategy.ExecuteFinalTreatment: checking for dump file at {dumpFile}."); if (File.Exists(dumpFile)) { + GeneralTracer.Info($"WindowStrategy.ExecuteFinalTreatment: dump file found, executing {_actions.Count} remediation action(s)."); foreach (var action in _actions) { action.Invoke(); } + GeneralTracer.Info("WindowStrategy.ExecuteFinalTreatment: all remediation actions completed."); + } + else + { + GeneralTracer.Info("WindowStrategy.ExecuteFinalTreatment: no dump file found, monitored process exited normally."); } } @@ -52,6 +69,7 @@ private void InitializeActions() _actions.Add(Export); _actions.Add(Restore); _actions.Add(SetEnvironment); + GeneralTracer.Debug("WindowStrategy.InitializeActions: registered actions: CreateCrash, Export, Restore, SetEnvironment."); } /// @@ -59,6 +77,7 @@ private void InitializeActions() /// private void CreateCrash() { + GeneralTracer.Info("WindowStrategy.CreateCrash: serializing crash report."); var crash = new Crash { Parameter = _parameter, @@ -66,6 +85,7 @@ private void CreateCrash() }; var failJsonPath = Path.Combine(_parameter.FailDirectory, _parameter.FailFileName); StorageManager.CreateJson(failJsonPath, crash, CrashJsonContext.Default.Crash); + GeneralTracer.Info($"WindowStrategy.CreateCrash: crash report written to {failJsonPath}."); } /// @@ -73,11 +93,16 @@ private void CreateCrash() /// private void Export() { + GeneralTracer.Info("WindowStrategy.Export: exporting OS and system diagnostic information."); var batPath = Path.Combine(_applicationsDirectory, "export.bat"); - if(!File.Exists(batPath)) + if (!File.Exists(batPath)) + { + GeneralTracer.Error($"WindowStrategy.Export: export.bat not found at {batPath}."); throw new FileNotFoundException("export.bat not found!"); - + } + Process.Start(batPath, _parameter.FailDirectory); + GeneralTracer.Info($"WindowStrategy.Export: export.bat started targeting {_parameter.FailDirectory}."); } /// @@ -85,8 +110,17 @@ private void Export() /// private void Restore() { + GeneralTracer.Info($"WindowStrategy.Restore: checking work model. CurrentModel={_parameter.WorkModel}, ExpectedModel={WorkModel}."); if (string.Equals(_parameter.WorkModel, WorkModel)) + { + GeneralTracer.Info($"WindowStrategy.Restore: restoring backup from {_parameter.BackupDirectory} to {_parameter.TargetPath}."); StorageManager.Restore(_parameter.BackupDirectory, _parameter.TargetPath); + GeneralTracer.Info("WindowStrategy.Restore: restore completed successfully."); + } + else + { + GeneralTracer.Info("WindowStrategy.Restore: restore skipped, work model is not Upgrade."); + } } /// @@ -95,7 +129,10 @@ private void Restore() private void SetEnvironment() { if (!string.Equals(_parameter.WorkModel, WorkModel)) + { + GeneralTracer.Info("WindowStrategy.SetEnvironment: skipped, work model is not Upgrade."); return; + } /* * The `UpgradeFail` environment variable is used to mark an exception version number during updates. @@ -103,5 +140,6 @@ private void SetEnvironment() * Once this version number is set, it will not be removed, and updates will not proceed until a version greater than the exception version number is obtained through the HTTP request. */ Environments.SetEnvironmentVariable("UpgradeFail", _parameter.ExtendedField); + GeneralTracer.Warn($"WindowStrategy.SetEnvironment: UpgradeFail environment variable set to version={_parameter.ExtendedField}."); } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs index 66eadbf6..1843613f 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs +++ b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs @@ -137,6 +137,7 @@ private async Task ExecuteWorkflowAsync() Debug.Assert(_configInfo != null); if (GetOption(UpdateOption.EnableSilentUpdate)) { + GeneralTracer.Info("GeneralClientBootstrap.ExecuteWorkflowAsync: silent update mode enabled, delegating to SilentUpdateMode."); await new SilentUpdateMode( _configInfo, GetOption(UpdateOption.Encoding) ?? Encoding.Default, @@ -147,6 +148,7 @@ private async Task ExecuteWorkflowAsync() return; } //Request the upgrade information needed by the client and upgrade end, and determine if an upgrade is necessary. + GeneralTracer.Info($"GeneralClientBootstrap.ExecuteWorkflowAsync: validating client version={_configInfo.ClientVersion} and upgrade version={_configInfo.UpgradeClientVersion}"); var mainResp = await VersionService.Validate(_configInfo.UpdateUrl , _configInfo.ClientVersion , AppType.ClientApp @@ -167,13 +169,18 @@ private async Task ExecuteWorkflowAsync() _configInfo.IsUpgradeUpdate = CheckUpgrade(upgradeResp); _configInfo.IsMainUpdate = CheckUpgrade(mainResp); + GeneralTracer.Info($"GeneralClientBootstrap.ExecuteWorkflowAsync: version validation completed. IsMainUpdate={_configInfo.IsMainUpdate}, IsUpgradeUpdate={_configInfo.IsUpgradeUpdate}"); var updateInfoArgs = new UpdateInfoEventArgs(mainResp); EventManager.Instance.Dispatch(this, updateInfoArgs); //If the main program needs to be forced to update, the skip will not take effect. var isForcibly = CheckForcibly(mainResp.Body) || CheckForcibly(upgradeResp.Body); - if (CanSkip(isForcibly, updateInfoArgs)) return; + if (CanSkip(isForcibly, updateInfoArgs)) + { + GeneralTracer.Info("GeneralClientBootstrap.ExecuteWorkflowAsync: update skipped by precheck callback."); + return; + } //black list initialization. BlackListManager.Instance?.AddBlackFiles(_configInfo.BlackFiles); @@ -194,9 +201,14 @@ private async Task ExecuteWorkflowAsync() if (_configInfo.IsMainUpdate) { _configInfo.LastVersion = mainResp.Body.OrderBy(x => x.ReleaseDate).Last().Version; + GeneralTracer.Info($"GeneralClientBootstrap.ExecuteWorkflowAsync: main update required, LastVersion={_configInfo.LastVersion}"); var failed = CheckFail(_configInfo.LastVersion); - if (failed) return; + if (failed) + { + GeneralTracer.Warn($"GeneralClientBootstrap.ExecuteWorkflowAsync: version {_configInfo.LastVersion} matches or precedes a known failed upgrade, aborting."); + return; + } // Use ConfigurationMapper to create ProcessInfo instead of manual parameter passing // This centralizes the complex parameter mapping logic and reduces errors @@ -213,27 +225,33 @@ private async Task ExecuteWorkflowAsync() if (GetOption(UpdateOption.BackUp) ?? true) { + GeneralTracer.Info($"GeneralClientBootstrap.ExecuteWorkflowAsync: backing up from {_configInfo.InstallPath} to {_configInfo.BackupDirectory}"); StorageManager.Backup(_configInfo.InstallPath , _configInfo.BackupDirectory , BlackListManager.Instance.SkipDirectorys); + GeneralTracer.Info("GeneralClientBootstrap.ExecuteWorkflowAsync: backup completed."); } StrategyFactory(); + GeneralTracer.Info($"GeneralClientBootstrap.ExecuteWorkflowAsync: executing update scenario. IsUpgradeUpdate={_configInfo.IsUpgradeUpdate}, IsMainUpdate={_configInfo.IsMainUpdate}"); switch (_configInfo.IsUpgradeUpdate) { case true when _configInfo.IsMainUpdate: //Both upgrade and main program update. + GeneralTracer.Info("GeneralClientBootstrap.ExecuteWorkflowAsync: both upgrade and main update required - downloading and executing."); await Download(); await _strategy?.ExecuteAsync()!; _strategy?.StartApp(); break; case true when !_configInfo.IsMainUpdate: //Upgrade program update. + GeneralTracer.Info("GeneralClientBootstrap.ExecuteWorkflowAsync: upgrade-only update - downloading and executing."); await Download(); await _strategy?.ExecuteAsync()!; break; case false when _configInfo.IsMainUpdate: //Main program update. + GeneralTracer.Info("GeneralClientBootstrap.ExecuteWorkflowAsync: main update only - starting application (updater will handle it)."); _strategy?.StartApp(); break; } diff --git a/src/c#/GeneralUpdate.ClientCore/Hubs/UpgradeHubService.cs b/src/c#/GeneralUpdate.ClientCore/Hubs/UpgradeHubService.cs index aa8c29c9..35b60e01 100644 --- a/src/c#/GeneralUpdate.ClientCore/Hubs/UpgradeHubService.cs +++ b/src/c#/GeneralUpdate.ClientCore/Hubs/UpgradeHubService.cs @@ -57,7 +57,9 @@ public async Task StartAsync() { try { + GeneralTracer.Info($"UpgradeHubService.StartAsync: connecting to SignalR hub. State={_connection?.State}"); await _connection!.StartAsync(); + GeneralTracer.Info($"UpgradeHubService.StartAsync: SignalR hub connection established. State={_connection?.State}"); } catch (Exception e) { @@ -69,7 +71,9 @@ public async Task StopAsync() { try { + GeneralTracer.Info($"UpgradeHubService.StopAsync: stopping SignalR hub connection. State={_connection?.State}"); await _connection!.StopAsync(); + GeneralTracer.Info("UpgradeHubService.StopAsync: SignalR hub connection stopped."); } catch (Exception e) { @@ -81,7 +85,9 @@ public async Task DisposeAsync() { try { + GeneralTracer.Info("UpgradeHubService.DisposeAsync: disposing SignalR hub connection and releasing resources."); await _connection!.DisposeAsync(); + GeneralTracer.Info("UpgradeHubService.DisposeAsync: SignalR hub connection disposed."); } catch (Exception e) { diff --git a/src/c#/GeneralUpdate.ClientCore/Pipeline/CompressMiddleware.cs b/src/c#/GeneralUpdate.ClientCore/Pipeline/CompressMiddleware.cs index 0658d6e8..e11802b6 100644 --- a/src/c#/GeneralUpdate.ClientCore/Pipeline/CompressMiddleware.cs +++ b/src/c#/GeneralUpdate.ClientCore/Pipeline/CompressMiddleware.cs @@ -1,7 +1,9 @@ -using System.Text; +using System; +using System.Text; using System.Threading.Tasks; using GeneralUpdate.Common.Compress; using GeneralUpdate.Common.Internal.Pipeline; +using GeneralUpdate.Common.Shared; namespace GeneralUpdate.ClientCore.Pipeline; @@ -17,8 +19,18 @@ public Task InvokeAsync(PipelineContext? context) var encoding = context.Get("Encoding"); var appPath = context.Get("SourcePath"); var patchEnabled = context.Get("PatchEnabled"); - - CompressProvider.Decompress(format, sourcePath,patchEnabled == false ? appPath : patchPath, encoding); + var targetPath = patchEnabled == false ? appPath : patchPath; + GeneralTracer.Info($"ClientCore.CompressMiddleware.InvokeAsync: decompressing package. Format={format}, Source={sourcePath}, Target={targetPath}, PatchEnabled={patchEnabled}"); + try + { + CompressProvider.Decompress(format, sourcePath, targetPath, encoding); + GeneralTracer.Info("ClientCore.CompressMiddleware.InvokeAsync: decompression completed successfully."); + } + catch (Exception ex) + { + GeneralTracer.Error("ClientCore.CompressMiddleware.InvokeAsync: decompression failed.", ex); + throw; + } }); } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs b/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs index 64f23bf3..7e437d9f 100644 --- a/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs +++ b/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using GeneralUpdate.Common.HashAlgorithms; using GeneralUpdate.Common.Internal.Pipeline; +using GeneralUpdate.Common.Shared; namespace GeneralUpdate.ClientCore.Pipeline; @@ -12,8 +13,26 @@ public async Task InvokeAsync(PipelineContext context) { var path = context.Get("ZipFilePath"); var hash = context.Get("Hash"); - var isVerify = await VerifyFileHash(path, hash); - if (!isVerify) throw new CryptographicException("Hash verification failed ."); + GeneralTracer.Info($"ClientCore.HashMiddleware.InvokeAsync: verifying hash for file={path}, expectedHash={hash}"); + try + { + var isVerify = await VerifyFileHash(path, hash); + if (!isVerify) + { + GeneralTracer.Error($"ClientCore.HashMiddleware.InvokeAsync: hash verification failed for file={path}."); + throw new CryptographicException("Hash verification failed ."); + } + GeneralTracer.Info("ClientCore.HashMiddleware.InvokeAsync: hash verification passed."); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + GeneralTracer.Error("ClientCore.HashMiddleware.InvokeAsync: unexpected exception during hash verification.", ex); + throw; + } } private Task VerifyFileHash(string path, string hash) diff --git a/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs b/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs index 101232b9..adba119b 100644 --- a/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs +++ b/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs @@ -1,5 +1,7 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using GeneralUpdate.Common.Internal.Pipeline; +using GeneralUpdate.Common.Shared; using GeneralUpdate.Differential; namespace GeneralUpdate.ClientCore.Pipeline; @@ -10,6 +12,16 @@ public async Task InvokeAsync(PipelineContext context) { var sourcePath = context.Get("SourcePath"); var targetPath = context.Get("PatchPath"); - await DifferentialCore.Dirty(sourcePath, targetPath); + GeneralTracer.Info($"ClientCore.PatchMiddleware.InvokeAsync: applying differential patch. SourcePath={sourcePath}, PatchPath={targetPath}"); + try + { + await DifferentialCore.Dirty(sourcePath, targetPath); + GeneralTracer.Info("ClientCore.PatchMiddleware.InvokeAsync: differential patch applied successfully."); + } + catch (Exception ex) + { + GeneralTracer.Error("ClientCore.PatchMiddleware.InvokeAsync: failed to apply differential patch.", ex); + throw; + } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/SilentUpdateMode.cs b/src/c#/GeneralUpdate.ClientCore/SilentUpdateMode.cs index e76a45bb..c0bbc06e 100644 --- a/src/c#/GeneralUpdate.ClientCore/SilentUpdateMode.cs +++ b/src/c#/GeneralUpdate.ClientCore/SilentUpdateMode.cs @@ -47,6 +47,7 @@ public SilentUpdateMode(GlobalConfigInfo configInfo, Encoding encoding, string f public Task StartAsync() { + GeneralTracer.Info($"SilentUpdateMode.StartAsync: initializing silent update mode. PollingInterval={PollingInterval.TotalMinutes}min, BackupEnabled={_backupEnabled}, PatchEnabled={_patchEnabled}"); AppDomain.CurrentDomain.ProcessExit += OnProcessExit; _pollingTask = Task.Run(PollLoopAsync); _pollingTask.ContinueWith(task => @@ -56,15 +57,18 @@ public Task StartAsync() GeneralTracer.Error("The StartAsync method in SilentUpdateMode captured a polling exception.", task.Exception); } }, TaskContinuationOptions.OnlyOnFaulted); + GeneralTracer.Info("SilentUpdateMode.StartAsync: polling loop started, returning to caller."); return Task.CompletedTask; } private async Task PollLoopAsync() { + GeneralTracer.Info("SilentUpdateMode.PollLoopAsync: entering silent update polling loop."); while (Volatile.Read(ref _prepared) == 0) { try { + GeneralTracer.Info("SilentUpdateMode.PollLoopAsync: polling for available updates."); await PrepareUpdateIfNeededAsync(); } catch (Exception exception) @@ -73,14 +77,19 @@ private async Task PollLoopAsync() } if (Volatile.Read(ref _prepared) == 1) + { + GeneralTracer.Info("SilentUpdateMode.PollLoopAsync: update preparation completed, exiting poll loop."); break; + } + GeneralTracer.Info($"SilentUpdateMode.PollLoopAsync: no update prepared this cycle, waiting {PollingInterval.TotalMinutes}min before next poll."); await Task.Delay(PollingInterval); } } private async Task PrepareUpdateIfNeededAsync() { + GeneralTracer.Info($"SilentUpdateMode.PrepareUpdateIfNeededAsync: validating version. UpdateUrl={_configInfo.UpdateUrl}, ClientVersion={_configInfo.ClientVersion}"); var mainResp = await VersionService.Validate(_configInfo.UpdateUrl , _configInfo.ClientVersion , AppType.ClientApp @@ -91,12 +100,20 @@ private async Task PrepareUpdateIfNeededAsync() , _configInfo.Token); if (mainResp?.Code != 200 || mainResp.Body == null || mainResp.Body.Count == 0) + { + GeneralTracer.Info($"SilentUpdateMode.PrepareUpdateIfNeededAsync: no update available. ResponseCode={mainResp?.Code}, BodyCount={mainResp?.Body?.Count ?? 0}"); return; + } var versions = mainResp.Body.OrderBy(x => x.ReleaseDate).ToList(); var latestVersion = versions.Last().Version; + GeneralTracer.Info($"SilentUpdateMode.PrepareUpdateIfNeededAsync: {versions.Count} version(s) available. LatestVersion={latestVersion}"); + if (CheckFail(latestVersion)) + { + GeneralTracer.Warn($"SilentUpdateMode.PrepareUpdateIfNeededAsync: latest version {latestVersion} matches or precedes a known failed upgrade, skipping."); return; + } BlackListManager.Instance?.AddBlackFiles(_configInfo.BlackFiles); BlackListManager.Instance?.AddBlackFileFormats(_configInfo.BlackFormats); @@ -114,7 +131,9 @@ private async Task PrepareUpdateIfNeededAsync() if (_backupEnabled) { + GeneralTracer.Info($"SilentUpdateMode.PrepareUpdateIfNeededAsync: backing up from {_configInfo.InstallPath} to {_configInfo.BackupDirectory}"); StorageManager.Backup(_configInfo.InstallPath, _configInfo.BackupDirectory, BlackListManager.Instance.SkipDirectorys); + GeneralTracer.Info("SilentUpdateMode.PrepareUpdateIfNeededAsync: backup completed."); } var processInfo = ConfigurationMapper.MapToProcessInfo( @@ -125,29 +144,38 @@ private async Task PrepareUpdateIfNeededAsync() BlackListManager.Instance.SkipDirectorys.ToList()); _configInfo.ProcessInfo = JsonSerializer.Serialize(processInfo, ProcessInfoJsonContext.Default.ProcessInfo); + GeneralTracer.Info($"SilentUpdateMode.PrepareUpdateIfNeededAsync: starting download of {versions.Count} package(s)."); var manager = new DownloadManager(_configInfo.TempPath, _configInfo.Format, _configInfo.DownloadTimeOut); foreach (var versionInfo in _configInfo.UpdateVersions) { manager.Add(new DownloadTask(manager, versionInfo)); } await manager.LaunchTasksAsync(); + GeneralTracer.Info("SilentUpdateMode.PrepareUpdateIfNeededAsync: all packages downloaded."); + GeneralTracer.Info("SilentUpdateMode.PrepareUpdateIfNeededAsync: executing update strategy."); var strategy = CreateStrategy(); strategy.Create(_configInfo); await strategy.ExecuteAsync(); + GeneralTracer.Info("SilentUpdateMode.PrepareUpdateIfNeededAsync: update strategy executed, marking as prepared."); Interlocked.Exchange(ref _prepared, 1); } private void OnProcessExit(object? sender, EventArgs e) { + GeneralTracer.Info($"SilentUpdateMode.OnProcessExit: process exit detected. Prepared={Volatile.Read(ref _prepared)}, UpdaterStarted={Volatile.Read(ref _updaterStarted)}"); if (Volatile.Read(ref _prepared) != 1 || Interlocked.Exchange(ref _updaterStarted, 1) == 1) + { + GeneralTracer.Info("SilentUpdateMode.OnProcessExit: silent updater launch skipped (not prepared or already started)."); return; + } try { Environments.SetEnvironmentVariable(ProcessInfoEnvironmentKey, _configInfo.ProcessInfo); var updaterPath = Path.Combine(_configInfo.InstallPath, _configInfo.AppName); + GeneralTracer.Info($"SilentUpdateMode.OnProcessExit: launching silent updater. Path={updaterPath}"); if (File.Exists(updaterPath)) { Process.Start(new ProcessStartInfo @@ -155,6 +183,11 @@ private void OnProcessExit(object? sender, EventArgs e) UseShellExecute = true, FileName = updaterPath }); + GeneralTracer.Info("SilentUpdateMode.OnProcessExit: silent updater launched successfully."); + } + else + { + GeneralTracer.Warn($"SilentUpdateMode.OnProcessExit: updater not found at {updaterPath}, cannot launch."); } } catch (Exception exception) diff --git a/src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs b/src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs index 8caecf2f..df6cd9c4 100644 --- a/src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs +++ b/src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs @@ -19,6 +19,7 @@ public class LinuxStrategy : AbstractStrategy { protected override PipelineContext CreatePipelineContext(VersionInfo version, string patchPath) { + GeneralTracer.Info($"GeneralUpdate.ClientCore.LinuxStrategy.CreatePipelineContext: building context for version={version.Version}, patchPath={patchPath}"); var context = base.CreatePipelineContext(version, patchPath); // Add ClientCore-specific context items (blacklists for Linux) @@ -31,6 +32,7 @@ protected override PipelineContext CreatePipelineContext(VersionInfo version, st protected override PipelineBuilder BuildPipeline(PipelineContext context) { + GeneralTracer.Info($"GeneralUpdate.ClientCore.LinuxStrategy.BuildPipeline: assembling middleware pipeline. PatchEnabled={_configinfo.PatchEnabled}"); return new PipelineBuilder(context) .UseMiddlewareIf(_configinfo.PatchEnabled) .UseMiddleware() @@ -41,6 +43,7 @@ public override void StartApp() { try { + GeneralTracer.Info($"GeneralUpdate.ClientCore.LinuxStrategy.StartApp: setting ProcessInfo environment variable and launching updater app={_configinfo.AppName}"); Environments.SetEnvironmentVariable("ProcessInfo", _configinfo.ProcessInfo); var appPath = Path.Combine(_configinfo.InstallPath, _configinfo.AppName); if (File.Exists(appPath)) @@ -50,6 +53,11 @@ public override void StartApp() UseShellExecute = true, FileName = appPath }); + GeneralTracer.Info($"GeneralUpdate.ClientCore.LinuxStrategy.StartApp: updater process launched successfully. Path={appPath}"); + } + else + { + GeneralTracer.Warn($"GeneralUpdate.ClientCore.LinuxStrategy.StartApp: updater app not found at path={appPath}, skipping launch."); } } catch (Exception e) @@ -59,6 +67,7 @@ public override void StartApp() } finally { + GeneralTracer.Info("GeneralUpdate.ClientCore.LinuxStrategy.StartApp: releasing tracer and terminating client process."); GeneralTracer.Dispose(); Process.GetCurrentProcess().Kill(); } diff --git a/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs index bf25df16..bc607ef0 100644 --- a/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs +++ b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs @@ -19,6 +19,7 @@ public class WindowsStrategy : AbstractStrategy { protected override PipelineContext CreatePipelineContext(VersionInfo version, string patchPath) { + GeneralTracer.Info($"GeneralUpdate.ClientCore.WindowsStrategy.CreatePipelineContext: building context for version={version.Version}, patchPath={patchPath}"); var context = base.CreatePipelineContext(version, patchPath); // Add ClientCore-specific context items (blacklists are not needed for Windows in Core) @@ -29,6 +30,7 @@ protected override PipelineContext CreatePipelineContext(VersionInfo version, st protected override PipelineBuilder BuildPipeline(PipelineContext context) { + GeneralTracer.Info($"GeneralUpdate.ClientCore.WindowsStrategy.BuildPipeline: assembling middleware pipeline. PatchEnabled={_configinfo.PatchEnabled}"); return new PipelineBuilder(context) .UseMiddlewareIf(_configinfo.PatchEnabled) .UseMiddleware() @@ -39,6 +41,7 @@ public override void StartApp() { try { + GeneralTracer.Info($"GeneralUpdate.ClientCore.WindowsStrategy.StartApp: setting ProcessInfo environment variable and launching updater app={_configinfo.AppName}"); Environments.SetEnvironmentVariable("ProcessInfo", _configinfo.ProcessInfo); var appPath = Path.Combine(_configinfo.InstallPath, _configinfo.AppName); if (File.Exists(appPath)) @@ -48,6 +51,11 @@ public override void StartApp() UseShellExecute = true, FileName = appPath }); + GeneralTracer.Info($"GeneralUpdate.ClientCore.WindowsStrategy.StartApp: updater process launched successfully. Path={appPath}"); + } + else + { + GeneralTracer.Warn($"GeneralUpdate.ClientCore.WindowsStrategy.StartApp: updater app not found at path={appPath}, skipping launch."); } } catch (Exception e) @@ -57,6 +65,7 @@ public override void StartApp() } finally { + GeneralTracer.Info("GeneralUpdate.ClientCore.WindowsStrategy.StartApp: releasing tracer and terminating client process."); GeneralTracer.Dispose(); Process.GetCurrentProcess().Kill(); } diff --git a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs index 2edc020f..cf653812 100644 --- a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs +++ b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs @@ -43,13 +43,16 @@ public override async Task LaunchAsync() switch (GetOption(UpdateOption.Mode) ?? UpdateMode.Default) { case UpdateMode.Default: + GeneralTracer.Info("GeneralUpdateBootstrap.LaunchAsync: Default mode - applying runtime options, creating strategy, downloading and executing."); ApplyRuntimeOptions(); _strategy!.Create(_configInfo); await DownloadAsync(); await _strategy.ExecuteAsync(); + GeneralTracer.Info("GeneralUpdateBootstrap.LaunchAsync: Default mode execution completed."); break; case UpdateMode.Scripts: + GeneralTracer.Info("GeneralUpdateBootstrap.LaunchAsync: Scripts mode - executing workflow."); await ExecuteWorkflowAsync(); break; @@ -99,6 +102,7 @@ private async Task ExecuteWorkflowAsync() { try { + GeneralTracer.Info($"GeneralUpdateBootstrap.ExecuteWorkflowAsync: validating version against server. UpdateUrl={_configInfo.UpdateUrl}, ClientVersion={_configInfo.ClientVersion}"); var mainResp = await VersionService.Validate( _configInfo.UpdateUrl, _configInfo.ClientVersion, @@ -110,11 +114,15 @@ private async Task ExecuteWorkflowAsync() _configInfo.Token); _configInfo.IsMainUpdate = CheckUpgrade(mainResp); + GeneralTracer.Info($"GeneralUpdateBootstrap.ExecuteWorkflowAsync: version validation completed. IsMainUpdate={_configInfo.IsMainUpdate}, ResponseCode={mainResp?.Code}"); EventManager.Instance.Dispatch(this, new UpdateInfoEventArgs(mainResp)); if (CanSkip(CheckForcibly(mainResp.Body))) + { + GeneralTracer.Info("GeneralUpdateBootstrap.ExecuteWorkflowAsync: update skipped by custom skip option."); return; + } InitBlackList(); ApplyRuntimeOptions(); @@ -128,23 +136,30 @@ private async Task ExecuteWorkflowAsync() .OrderBy(x => x.ReleaseDate) .ToList(); + GeneralTracer.Info($"GeneralUpdateBootstrap.ExecuteWorkflowAsync: {_configInfo.UpdateVersions.Count} version(s) queued for update."); + if (GetOption(UpdateOption.BackUp) ?? true) { + GeneralTracer.Info($"GeneralUpdateBootstrap.ExecuteWorkflowAsync: backing up from {_configInfo.InstallPath} to {_configInfo.BackupDirectory}"); StorageManager.Backup( _configInfo.InstallPath, _configInfo.BackupDirectory, BlackListManager.Instance.SkipDirectorys); + GeneralTracer.Info("GeneralUpdateBootstrap.ExecuteWorkflowAsync: backup completed."); } _strategy!.Create(_configInfo); if (_configInfo.IsMainUpdate) { + GeneralTracer.Info("GeneralUpdateBootstrap.ExecuteWorkflowAsync: main update required, starting download and execution."); await DownloadAsync(); await _strategy.ExecuteAsync(); + GeneralTracer.Info("GeneralUpdateBootstrap.ExecuteWorkflowAsync: main update execution completed."); } else { + GeneralTracer.Info("GeneralUpdateBootstrap.ExecuteWorkflowAsync: no main update needed, starting application directly."); _strategy.StartApp(); } } diff --git a/src/c#/GeneralUpdate.Core/Pipeline/CompressMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/CompressMiddleware.cs index fd6cb778..e37438b2 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/CompressMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/CompressMiddleware.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using GeneralUpdate.Common.Compress; using GeneralUpdate.Common.Internal.Pipeline; +using GeneralUpdate.Common.Shared; namespace GeneralUpdate.Core.Pipeline; @@ -17,8 +18,18 @@ public Task InvokeAsync(PipelineContext context) var encoding = context.Get("Encoding"); var appPath = context.Get("SourcePath"); var patchEnabled = context.Get("PatchEnabled"); - - CompressProvider.Decompress(format, sourcePath,patchEnabled == false ? appPath : patchPath, encoding); + var targetPath = patchEnabled == false ? appPath : patchPath; + GeneralTracer.Info($"CompressMiddleware.InvokeAsync: decompressing package. Format={format}, Source={sourcePath}, Target={targetPath}, PatchEnabled={patchEnabled}"); + try + { + CompressProvider.Decompress(format, sourcePath, targetPath, encoding); + GeneralTracer.Info("CompressMiddleware.InvokeAsync: decompression completed successfully."); + } + catch (System.Exception ex) + { + GeneralTracer.Error("CompressMiddleware.InvokeAsync: decompression failed.", ex); + throw; + } }); } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs index b0cd0511..aa059dd8 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using GeneralUpdate.Common.HashAlgorithms; using GeneralUpdate.Common.Internal.Pipeline; +using GeneralUpdate.Common.Shared; namespace GeneralUpdate.Core.Pipeline; @@ -12,8 +13,26 @@ public async Task InvokeAsync(PipelineContext context) { var path = context.Get("ZipFilePath"); var hash = context.Get("Hash"); - var isVerify = await VerifyFileHash(path, hash); - if (!isVerify) throw new CryptographicException("Hash verification failed !"); + GeneralTracer.Info($"HashMiddleware.InvokeAsync: verifying hash for file={path}, expectedHash={hash}"); + try + { + var isVerify = await VerifyFileHash(path, hash); + if (!isVerify) + { + GeneralTracer.Error($"HashMiddleware.InvokeAsync: hash verification failed for file={path}."); + throw new CryptographicException("Hash verification failed !"); + } + GeneralTracer.Info("HashMiddleware.InvokeAsync: hash verification passed."); + } + catch (CryptographicException) + { + throw; + } + catch (Exception ex) + { + GeneralTracer.Error("HashMiddleware.InvokeAsync: unexpected exception during hash verification.", ex); + throw; + } } private Task VerifyFileHash(string path, string hash) diff --git a/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs index 2632f714..5cd13fc1 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs @@ -1,5 +1,7 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using GeneralUpdate.Common.Internal.Pipeline; +using GeneralUpdate.Common.Shared; using GeneralUpdate.Differential; namespace GeneralUpdate.Core.Pipeline; @@ -10,6 +12,16 @@ public async Task InvokeAsync(PipelineContext context) { var sourcePath = context.Get("SourcePath"); var targetPath = context.Get("PatchPath"); - await DifferentialCore.Dirty(sourcePath, targetPath); + GeneralTracer.Info($"PatchMiddleware.InvokeAsync: applying differential patch. SourcePath={sourcePath}, PatchPath={targetPath}"); + try + { + await DifferentialCore.Dirty(sourcePath, targetPath); + GeneralTracer.Info("PatchMiddleware.InvokeAsync: differential patch applied successfully."); + } + catch (Exception ex) + { + GeneralTracer.Error("PatchMiddleware.InvokeAsync: failed to apply differential patch.", ex); + throw; + } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Strategys/LinuxStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/LinuxStrategy.cs index d326d8de..9dbf1c12 100644 --- a/src/c#/GeneralUpdate.Core/Strategys/LinuxStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategys/LinuxStrategy.cs @@ -16,12 +16,14 @@ public class LinuxStrategy : AbstractStrategy { protected override PipelineContext CreatePipelineContext(VersionInfo version, string patchPath) { + GeneralTracer.Info($"GeneralUpdate.Core.LinuxStrategy.CreatePipelineContext: building context for version={version.Version}, patchPath={patchPath}, driveEnabled={_configinfo.DriveEnabled}"); var context = base.CreatePipelineContext(version, patchPath); // Driver middleware (Linux-specific) if (_configinfo.DriveEnabled == true) { context.Add("DriverDirectory", _configinfo.DriverDirectory); + GeneralTracer.Info($"GeneralUpdate.Core.LinuxStrategy.CreatePipelineContext: driver update enabled, DriverDirectory={_configinfo.DriverDirectory}"); } return context; @@ -29,6 +31,7 @@ protected override PipelineContext CreatePipelineContext(VersionInfo version, st protected override PipelineBuilder BuildPipeline(PipelineContext context) { + GeneralTracer.Info($"GeneralUpdate.Core.LinuxStrategy.BuildPipeline: assembling middleware pipeline. PatchEnabled={_configinfo.PatchEnabled}, DriveEnabled={_configinfo.DriveEnabled}"); var builder = new PipelineBuilder(context) .UseMiddlewareIf(_configinfo.PatchEnabled) .UseMiddleware() @@ -46,6 +49,7 @@ public override void Execute() protected override void OnExecuteComplete() { + GeneralTracer.Info("GeneralUpdate.Core.LinuxStrategy.OnExecuteComplete: all versions processed, starting application."); StartApp(); } @@ -57,8 +61,10 @@ public override void StartApp() if (string.IsNullOrEmpty(mainAppPath)) throw new Exception($"Can't find the app {mainAppPath}!"); + GeneralTracer.Info($"GeneralUpdate.Core.LinuxStrategy.StartApp: executing startup script then launching main app={mainAppPath}"); ExecuteScript(); Process.Start(mainAppPath); + GeneralTracer.Info("GeneralUpdate.Core.LinuxStrategy.StartApp: main app launched successfully."); } catch (Exception e) { @@ -68,6 +74,7 @@ public override void StartApp() } finally { + GeneralTracer.Info("GeneralUpdate.Core.LinuxStrategy.StartApp: releasing tracer and terminating updater process."); GeneralTracer.Dispose(); Process.GetCurrentProcess().Kill(); } diff --git a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs index ab839f8a..8c3b953e 100644 --- a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs @@ -18,12 +18,14 @@ public class WindowsStrategy : AbstractStrategy { protected override PipelineContext CreatePipelineContext(VersionInfo version, string patchPath) { + GeneralTracer.Info($"GeneralUpdate.Core.WindowsStrategy.CreatePipelineContext: building context for version={version.Version}, patchPath={patchPath}, driveEnabled={_configinfo.DriveEnabled}"); var context = base.CreatePipelineContext(version, patchPath); // Driver middleware (Windows-specific) if (_configinfo.DriveEnabled == true) { context.Add("DriverDirectory", _configinfo.DriverDirectory); + GeneralTracer.Info($"GeneralUpdate.Core.WindowsStrategy.CreatePipelineContext: driver update enabled, DriverDirectory={_configinfo.DriverDirectory}"); } return context; @@ -31,6 +33,7 @@ protected override PipelineContext CreatePipelineContext(VersionInfo version, st protected override PipelineBuilder BuildPipeline(PipelineContext context) { + GeneralTracer.Info($"GeneralUpdate.Core.WindowsStrategy.BuildPipeline: assembling middleware pipeline. PatchEnabled={_configinfo.PatchEnabled}, DriveEnabled={_configinfo.DriveEnabled}"); var builder = new PipelineBuilder(context) .UseMiddlewareIf(_configinfo.PatchEnabled) .UseMiddleware() @@ -43,6 +46,7 @@ protected override PipelineBuilder BuildPipeline(PipelineContext context) protected override void OnExecuteComplete() { + GeneralTracer.Info("GeneralUpdate.Core.WindowsStrategy.OnExecuteComplete: all versions processed, starting application."); StartApp(); } @@ -54,11 +58,14 @@ public override void StartApp() if (string.IsNullOrEmpty(mainAppPath)) throw new Exception($"Can't find the app {mainAppPath}!"); + GeneralTracer.Info($"GeneralUpdate.Core.WindowsStrategy.StartApp: launching main app={mainAppPath}"); Process.Start(mainAppPath); + GeneralTracer.Info("GeneralUpdate.Core.WindowsStrategy.StartApp: main app launched successfully."); var bowlAppPath = CheckPath(_configinfo.InstallPath, _configinfo.Bowl); if (!string.IsNullOrEmpty(bowlAppPath)) { + GeneralTracer.Info($"GeneralUpdate.Core.WindowsStrategy.StartApp: launching Bowl process={bowlAppPath}"); Process.Start(bowlAppPath); } } @@ -69,6 +76,7 @@ public override void StartApp() } finally { + GeneralTracer.Info("GeneralUpdate.Core.WindowsStrategy.StartApp: releasing tracer and terminating updater process."); GeneralTracer.Dispose(); Process.GetCurrentProcess().Kill(); } diff --git a/src/c#/GeneralUpdate.Drivelution/GeneralDrivelution.cs b/src/c#/GeneralUpdate.Drivelution/GeneralDrivelution.cs index 4db4c8e0..e7ae3a5c 100644 --- a/src/c#/GeneralUpdate.Drivelution/GeneralDrivelution.cs +++ b/src/c#/GeneralUpdate.Drivelution/GeneralDrivelution.cs @@ -1,3 +1,4 @@ +using GeneralUpdate.Common.Shared; using GeneralUpdate.Drivelution.Abstractions; using GeneralUpdate.Drivelution.Abstractions.Configuration; using GeneralUpdate.Drivelution.Abstractions.Models; @@ -44,6 +45,7 @@ public static async Task QuickUpdateAsync( DriverInfo driverInfo, CancellationToken cancellationToken = default) { + GeneralTracer.Info($"GeneralDrivelution.QuickUpdateAsync: starting quick driver update. Driver={driverInfo.Name}, Version={driverInfo.Version}"); var updater = Create(); var strategy = new UpdateStrategy { @@ -52,7 +54,9 @@ public static async Task QuickUpdateAsync( RetryIntervalSeconds = 5 }; - return await updater.UpdateAsync(driverInfo, strategy, cancellationToken); + var result = await updater.UpdateAsync(driverInfo, strategy, cancellationToken); + GeneralTracer.Info($"GeneralDrivelution.QuickUpdateAsync: quick driver update completed. Success={result.Success}, Status={result.Status}, DurationMs={result.DurationMs}"); + return result; } /// @@ -67,8 +71,11 @@ public static async Task QuickUpdateAsync( UpdateStrategy strategy, CancellationToken cancellationToken = default) { + GeneralTracer.Info($"GeneralDrivelution.QuickUpdateAsync(strategy): starting driver update with custom strategy. Driver={driverInfo.Name}, Version={driverInfo.Version}, RequireBackup={strategy.RequireBackup}, RetryCount={strategy.RetryCount}"); var updater = Create(); - return await updater.UpdateAsync(driverInfo, strategy, cancellationToken); + var result = await updater.UpdateAsync(driverInfo, strategy, cancellationToken); + GeneralTracer.Info($"GeneralDrivelution.QuickUpdateAsync(strategy): driver update completed. Success={result.Success}, Status={result.Status}, DurationMs={result.DurationMs}"); + return result; } /// @@ -81,8 +88,11 @@ public static async Task ValidateAsync( DriverInfo driverInfo, CancellationToken cancellationToken = default) { + GeneralTracer.Info($"GeneralDrivelution.ValidateAsync: validating driver file. Driver={driverInfo.Name}, FilePath={driverInfo.FilePath}"); var updater = Create(); - return await updater.ValidateAsync(driverInfo, cancellationToken); + var result = await updater.ValidateAsync(driverInfo, cancellationToken); + GeneralTracer.Info($"GeneralDrivelution.ValidateAsync: driver validation completed. Driver={driverInfo.Name}, IsValid={result}"); + return result; } /// @@ -91,7 +101,8 @@ public static async Task ValidateAsync( /// Platform information public static PlatformInfo GetPlatformInfo() { - return new PlatformInfo + GeneralTracer.Info("GeneralDrivelution.GetPlatformInfo: retrieving platform information."); + var info = new PlatformInfo { Platform = Core.DrivelutionFactory.GetCurrentPlatform(), IsSupported = Core.DrivelutionFactory.IsPlatformSupported(), @@ -99,6 +110,8 @@ public static PlatformInfo GetPlatformInfo() Architecture = CompatibilityChecker.GetCurrentArchitecture(), SystemVersion = CompatibilityChecker.GetSystemVersion() }; + GeneralTracer.Info($"GeneralDrivelution.GetPlatformInfo: Platform={info.Platform}, OS={info.OperatingSystem}, Arch={info.Architecture}, Version={info.SystemVersion}, Supported={info.IsSupported}"); + return info; } /// @@ -113,8 +126,11 @@ public static async Task> GetDriversFromDirectoryAsync( string? searchPattern = null, CancellationToken cancellationToken = default) { + GeneralTracer.Info($"GeneralDrivelution.GetDriversFromDirectoryAsync: scanning directory for drivers. Path={directoryPath}, SearchPattern={searchPattern ?? "(default)"}"); var updater = Create(); - return await updater.GetDriversFromDirectoryAsync(directoryPath, searchPattern, cancellationToken); + var drivers = await updater.GetDriversFromDirectoryAsync(directoryPath, searchPattern, cancellationToken); + GeneralTracer.Info($"GeneralDrivelution.GetDriversFromDirectoryAsync: found {drivers?.Count ?? 0} driver(s) in directory={directoryPath}"); + return drivers; } } diff --git a/src/c#/GeneralUpdate.Extension/Core/GeneralExtensionHost.cs b/src/c#/GeneralUpdate.Extension/Core/GeneralExtensionHost.cs index e7ce4d0a..f7975f20 100644 --- a/src/c#/GeneralUpdate.Extension/Core/GeneralExtensionHost.cs +++ b/src/c#/GeneralUpdate.Extension/Core/GeneralExtensionHost.cs @@ -7,6 +7,7 @@ using GeneralUpdate.Extension.Dependencies; using GeneralUpdate.Extension.Communication; using GeneralUpdate.Extension.Common.Models; +using GeneralUpdate.Common.Shared; using Newtonsoft.Json; using System; @@ -82,6 +83,7 @@ public GeneralExtensionHost( // Load installed extensions ExtensionCatalog.LoadInstalledExtensions(); + GeneralTracer.Info($"GeneralExtensionHost: initialized with DI. HostVersion={_hostVersion}, ExtensionsDirectory={_extensionsDirectory}"); } /// @@ -114,17 +116,31 @@ public GeneralExtensionHost(ExtensionHostOptions options) // Load installed extensions ExtensionCatalog.LoadInstalledExtensions(); + GeneralTracer.Info($"GeneralExtensionHost: initialized (legacy). HostVersion={_hostVersion}, ExtensionsDirectory={_extensionsDirectory}, ServerUrl={options.ServerUrl}"); } public async Task>> QueryExtensionsAsync(ExtensionQueryDTO query) { - return await _httpClient.QueryExtensionsAsync(query); + GeneralTracer.Info($"GeneralExtensionHost.QueryExtensionsAsync: querying extensions. ExtensionId={query?.Id}"); + try + { + var result = await _httpClient.QueryExtensionsAsync(query); + GeneralTracer.Info($"GeneralExtensionHost.QueryExtensionsAsync: query completed. Code={result?.Code}, ItemCount={result?.Body?.Items?.Count() ?? 0}"); + return result; + } + catch (Exception ex) + { + GeneralTracer.Error("GeneralExtensionHost.QueryExtensionsAsync: exception occurred during extension query.", ex); + throw; + } } public async Task DownloadExtensionAsync(string extensionId, string savePath) { + GeneralTracer.Info($"GeneralExtensionHost.DownloadExtensionAsync: downloading extension. ExtensionId={extensionId}, SavePath={savePath}"); var progress = new Progress(p => { + GeneralTracer.Debug($"GeneralExtensionHost.DownloadExtensionAsync: progress={p}% for ExtensionId={extensionId}"); ExtensionUpdateStatusChanged?.Invoke(this, new ExtensionUpdateEventArgs { ExtensionId = extensionId, @@ -133,11 +149,25 @@ public async Task DownloadExtensionAsync(string extensionId, string savePa }); }); - return await _httpClient.DownloadExtensionAsync(extensionId, savePath, progress); + try + { + var success = await _httpClient.DownloadExtensionAsync(extensionId, savePath, progress); + if (success) + GeneralTracer.Info($"GeneralExtensionHost.DownloadExtensionAsync: extension downloaded successfully. ExtensionId={extensionId}"); + else + GeneralTracer.Warn($"GeneralExtensionHost.DownloadExtensionAsync: extension download returned false. ExtensionId={extensionId}"); + return success; + } + catch (Exception ex) + { + GeneralTracer.Error($"GeneralExtensionHost.DownloadExtensionAsync: exception during download of ExtensionId={extensionId}.", ex); + throw; + } } public async Task UpdateExtensionAsync(string extensionId) { + GeneralTracer.Info($"GeneralExtensionHost.UpdateExtensionAsync: starting extension update. ExtensionId={extensionId}"); try { // Notify queued @@ -153,30 +183,36 @@ public async Task UpdateExtensionAsync(string extensionId) Id = extensionId }; + GeneralTracer.Info($"GeneralExtensionHost.UpdateExtensionAsync: querying server for extension metadata. ExtensionId={extensionId}"); var response = await QueryExtensionsAsync(query); if (response.Body?.Items == null) { + GeneralTracer.Error($"GeneralExtensionHost.UpdateExtensionAsync: server returned null items for ExtensionId={extensionId}."); throw new InvalidOperationException("Failed to query extension from server"); } var serverExtension = response.Body.Items.FirstOrDefault(e => string.Equals(e.Id, extensionId)); if (serverExtension == null) { + GeneralTracer.Error($"GeneralExtensionHost.UpdateExtensionAsync: extension not found on server. ExtensionId={extensionId}"); throw new InvalidOperationException($"Extension {extensionId} not found on server"); } // Convert DTO to metadata var metadata = ToMetadata(serverExtension); + GeneralTracer.Info($"GeneralExtensionHost.UpdateExtensionAsync: metadata resolved. Name={metadata.Name}, Version={metadata.Version}"); // Check compatibility if (!IsExtensionCompatible(metadata)) { + GeneralTracer.Warn($"GeneralExtensionHost.UpdateExtensionAsync: extension not compatible with host version={_hostVersion}. ExtensionId={extensionId}"); throw new InvalidOperationException($"Extension {extensionId} is not compatible with host version {_hostVersion}"); } // Check platform support if (!_platformMatcher.IsCurrentPlatformSupported(metadata)) { + GeneralTracer.Warn($"GeneralExtensionHost.UpdateExtensionAsync: extension does not support current platform. ExtensionId={extensionId}"); throw new InvalidOperationException($"Extension {extensionId} does not support current platform"); } @@ -184,15 +220,21 @@ public async Task UpdateExtensionAsync(string extensionId) if (!string.IsNullOrWhiteSpace(metadata.Dependencies)) { var dependencies = metadata.Dependencies!.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + GeneralTracer.Info($"GeneralExtensionHost.UpdateExtensionAsync: resolving {dependencies.Length} dependency/ies for ExtensionId={extensionId}"); foreach (var depId in dependencies) { var dep = depId.Trim(); var installedDep = ExtensionCatalog.GetInstalledExtensionById(dep); if (installedDep == null) { + GeneralTracer.Info($"GeneralExtensionHost.UpdateExtensionAsync: dependency not installed, installing dep={dep}"); // Download and install dependency await UpdateExtensionAsync(dep); } + else + { + GeneralTracer.Debug($"GeneralExtensionHost.UpdateExtensionAsync: dependency already installed. dep={dep}"); + } } } @@ -200,22 +242,27 @@ public async Task UpdateExtensionAsync(string extensionId) var fileName = $"{metadata.Name}_{metadata.Version}{metadata.Format}"; var savePath = Path.Combine(_extensionsDirectory, fileName); + GeneralTracer.Info($"GeneralExtensionHost.UpdateExtensionAsync: downloading extension package. SavePath={savePath}"); var downloaded = await DownloadExtensionAsync(extensionId, savePath); if (!downloaded) { + GeneralTracer.Error($"GeneralExtensionHost.UpdateExtensionAsync: download failed for ExtensionId={extensionId}."); throw new InvalidOperationException("Failed to download extension"); } // Install extension + GeneralTracer.Info($"GeneralExtensionHost.UpdateExtensionAsync: installing extension package. Path={savePath}"); var installSuccess = await InstallExtensionAsync(savePath, rollbackOnFailure: true); if (!installSuccess) { + GeneralTracer.Error($"GeneralExtensionHost.UpdateExtensionAsync: installation failed for ExtensionId={extensionId}."); throw new InvalidOperationException("Failed to install extension"); } // Update catalog with metadata metadata.UploadTime = DateTime.UtcNow; ExtensionCatalog.AddOrUpdateInstalledExtension(metadata); + GeneralTracer.Info($"GeneralExtensionHost.UpdateExtensionAsync: extension catalog updated. ExtensionId={extensionId}, Version={metadata.Version}"); // Notify success ExtensionUpdateStatusChanged?.Invoke(this, new ExtensionUpdateEventArgs @@ -226,10 +273,12 @@ public async Task UpdateExtensionAsync(string extensionId) Progress = 100 }); + GeneralTracer.Info($"GeneralExtensionHost.UpdateExtensionAsync: extension update completed successfully. ExtensionId={extensionId}"); return true; } catch (Exception ex) { + GeneralTracer.Error($"GeneralExtensionHost.UpdateExtensionAsync: exception during extension update. ExtensionId={extensionId}", ex); // Notify failure ExtensionUpdateStatusChanged?.Invoke(this, new ExtensionUpdateEventArgs { @@ -246,18 +295,21 @@ public async Task InstallExtensionAsync(string extensionPath, bool rollbac { string? backupPath = null; string? extractedExtensionDir = null; + GeneralTracer.Info($"GeneralExtensionHost.InstallExtensionAsync: starting installation. Path={extensionPath}, RollbackOnFailure={rollbackOnFailure}"); try { // Validate extension file exists if (!File.Exists(extensionPath)) { + GeneralTracer.Error($"GeneralExtensionHost.InstallExtensionAsync: extension file not found. Path={extensionPath}"); throw new FileNotFoundException("Extension file not found", extensionPath); } // Validate it's a zip file if (!extensionPath.EndsWith(".zip", StringComparison.OrdinalIgnoreCase)) { + GeneralTracer.Error($"GeneralExtensionHost.InstallExtensionAsync: extension file is not a .zip. Path={extensionPath}"); throw new InvalidOperationException("Extension file must be a .zip file"); } @@ -275,6 +327,7 @@ public async Task InstallExtensionAsync(string extensionPath, bool rollbac // Determine target installation directory for this extension var targetExtensionDir = Path.Combine(_extensionsDirectory, extensionName); extractedExtensionDir = targetExtensionDir; + GeneralTracer.Info($"GeneralExtensionHost.InstallExtensionAsync: resolved extension name={extensionName}, targetDir={targetExtensionDir}"); // Create backup if extension already exists var existingExtension = ExtensionCatalog.GetInstalledExtensions() @@ -285,18 +338,20 @@ public async Task InstallExtensionAsync(string extensionPath, bool rollbac if (Directory.Exists(targetExtensionDir)) { backupPath = Path.Combine(_backupDirectory, $"{extensionName}_{DateTime.UtcNow:yyyyMMddHHmmss}"); - + GeneralTracer.Info($"GeneralExtensionHost.InstallExtensionAsync: backing up existing installation to {backupPath}"); // Create backup directory structure Directory.CreateDirectory(_backupDirectory); // Copy entire extension directory to backup CopyDirectory(targetExtensionDir, backupPath); + GeneralTracer.Info("GeneralExtensionHost.InstallExtensionAsync: backup created successfully."); } } // Remove existing extension directory if it exists if (Directory.Exists(targetExtensionDir)) { + GeneralTracer.Info($"GeneralExtensionHost.InstallExtensionAsync: removing existing installation at {targetExtensionDir}"); Directory.Delete(targetExtensionDir, true); } @@ -304,21 +359,27 @@ public async Task InstallExtensionAsync(string extensionPath, bool rollbac Directory.CreateDirectory(targetExtensionDir); // Extract the zip file to the installation directory + GeneralTracer.Info($"GeneralExtensionHost.InstallExtensionAsync: extracting package to {targetExtensionDir}"); await Task.Run(() => ZipFile.ExtractToDirectory(extensionPath, targetExtensionDir)); + GeneralTracer.Info("GeneralExtensionHost.InstallExtensionAsync: extraction completed successfully."); // Delete backup on success if (backupPath != null && Directory.Exists(backupPath)) { Directory.Delete(backupPath, true); + GeneralTracer.Debug($"GeneralExtensionHost.InstallExtensionAsync: cleaned up backup at {backupPath}."); } + GeneralTracer.Info($"GeneralExtensionHost.InstallExtensionAsync: extension installed successfully. Name={extensionName}"); return true; } - catch (Exception) + catch (Exception ex) { + GeneralTracer.Error($"GeneralExtensionHost.InstallExtensionAsync: installation failed for path={extensionPath}.", ex); // Rollback on failure if (rollbackOnFailure && backupPath != null && Directory.Exists(backupPath)) { + GeneralTracer.Warn("GeneralExtensionHost.InstallExtensionAsync: attempting rollback from backup."); try { // Remove failed installation @@ -335,10 +396,11 @@ public async Task InstallExtensionAsync(string extensionPath, bool rollbac // Delete backup Directory.Delete(backupPath, true); + GeneralTracer.Info("GeneralExtensionHost.InstallExtensionAsync: rollback completed successfully."); } - catch + catch (Exception rollbackEx) { - // Rollback failed + GeneralTracer.Error("GeneralExtensionHost.InstallExtensionAsync: rollback also failed.", rollbackEx); } } diff --git a/src/c#/GeneralUpdate.Extension/Download/DownloadQueueManager.cs b/src/c#/GeneralUpdate.Extension/Download/DownloadQueueManager.cs index a8aaa3e7..55ad81e8 100644 --- a/src/c#/GeneralUpdate.Extension/Download/DownloadQueueManager.cs +++ b/src/c#/GeneralUpdate.Extension/Download/DownloadQueueManager.cs @@ -7,6 +7,7 @@ using GeneralUpdate.Extension.Dependencies; using GeneralUpdate.Extension.Communication; using GeneralUpdate.Extension.Common.Models; +using GeneralUpdate.Common.Shared; using System; using System.Collections.Generic; @@ -42,11 +43,13 @@ public DownloadQueueManager(int maxConcurrentDownloads = 3) { _maxConcurrentDownloads = maxConcurrentDownloads; _semaphore = new SemaphoreSlim(maxConcurrentDownloads, maxConcurrentDownloads); + GeneralTracer.Info($"DownloadQueueManager: initialized. MaxConcurrentDownloads={maxConcurrentDownloads}"); } /// public void Enqueue(DownloadTask task) { + GeneralTracer.Info($"DownloadQueueManager.Enqueue: queuing extension download. ExtensionId={task.Extension.Id}, Name={task.Extension.Name}"); task.Status = ExtensionUpdateStatus.Queued; _downloadQueue.Enqueue(task); @@ -56,6 +59,7 @@ public void Enqueue(DownloadTask task) if (!_isProcessing) { + GeneralTracer.Info("DownloadQueueManager.Enqueue: starting queue processing."); _ = ProcessQueueAsync(); } } @@ -69,9 +73,15 @@ public void Enqueue(DownloadTask task) /// public void CancelTask(string extensionId) { + GeneralTracer.Info($"DownloadQueueManager.CancelTask: cancelling download. ExtensionId={extensionId}"); if (_activeTasks.TryGetValue(extensionId, out var task)) { task.CancellationTokenSource.Cancel(); + GeneralTracer.Info($"DownloadQueueManager.CancelTask: cancellation requested. ExtensionId={extensionId}"); + } + else + { + GeneralTracer.Warn($"DownloadQueueManager.CancelTask: task not found in active tasks. ExtensionId={extensionId}"); } } @@ -84,6 +94,7 @@ public List GetActiveTasks() private async Task ProcessQueueAsync() { _isProcessing = true; + GeneralTracer.Info("DownloadQueueManager.ProcessQueueAsync: processing download queue."); try { @@ -91,11 +102,13 @@ private async Task ProcessQueueAsync() { if (!_downloadQueue.TryDequeue(out var task)) { + GeneralTracer.Info("DownloadQueueManager.ProcessQueueAsync: queue is empty, stopping processing."); break; } if (task != null) { + GeneralTracer.Debug($"DownloadQueueManager.ProcessQueueAsync: dequeued task for ExtensionId={task.Extension.Id}, waiting for semaphore slot."); await _semaphore.WaitAsync(); _ = ProcessTaskAsync(task); } @@ -109,6 +122,7 @@ private async Task ProcessQueueAsync() private async Task ProcessTaskAsync(DownloadTask task) { + GeneralTracer.Info($"DownloadQueueManager.ProcessTaskAsync: starting download task. ExtensionId={task.Extension.Id}, Name={task.Extension.Name}"); try { task.Status = ExtensionUpdateStatus.Updating; @@ -122,18 +136,21 @@ private async Task ProcessTaskAsync(DownloadTask task) task.Status = ExtensionUpdateStatus.UpdateSuccessful; task.Progress = 100; OnDownloadStatusChanged(task); + GeneralTracer.Info($"DownloadQueueManager.ProcessTaskAsync: download completed. ExtensionId={task.Extension.Id}"); } catch (OperationCanceledException) { task.Status = ExtensionUpdateStatus.UpdateFailed; task.ErrorMessage = "Download cancelled"; OnDownloadStatusChanged(task); + GeneralTracer.Warn($"DownloadQueueManager.ProcessTaskAsync: download cancelled. ExtensionId={task.Extension.Id}"); } catch (Exception ex) { task.Status = ExtensionUpdateStatus.UpdateFailed; task.ErrorMessage = ex.Message; OnDownloadStatusChanged(task); + GeneralTracer.Error($"DownloadQueueManager.ProcessTaskAsync: download failed. ExtensionId={task.Extension.Id}", ex); } finally { @@ -150,6 +167,7 @@ private async Task ProcessTaskAsync(DownloadTask task) if (!t.IsCanceled) { _activeTasks.TryRemove(task.Extension.Id, out var _); + GeneralTracer.Debug($"DownloadQueueManager: cleaned up completed task. ExtensionId={task.Extension.Id}"); } }, TaskScheduler.Default); } @@ -158,6 +176,7 @@ private async Task ProcessTaskAsync(DownloadTask task) private void OnDownloadStatusChanged(DownloadTask task) { + GeneralTracer.Debug($"DownloadQueueManager.OnDownloadStatusChanged: ExtensionId={task.Extension.Id}, Status={task.Status}, Progress={task.Progress}"); DownloadStatusChanged?.Invoke(this, new DownloadTaskEventArgs { Task = task }); } @@ -167,6 +186,7 @@ public void Dispose() if (_disposed) return; + GeneralTracer.Info($"DownloadQueueManager.Dispose: disposing. ActiveTasks={_activeTasks.Count}, QueuedTasks={_downloadQueue.Count}"); _disposed = true; // Cancel all pending cleanup tasks @@ -187,5 +207,6 @@ public void Dispose() _semaphore.Dispose(); _disposalCts.Dispose(); + GeneralTracer.Info("DownloadQueueManager.Dispose: all resources released."); } } diff --git a/src/c#/GeneralUpdate.Extension/GeneralUpdate.Extension.csproj b/src/c#/GeneralUpdate.Extension/GeneralUpdate.Extension.csproj index 2d34e9fa..fa075471 100644 --- a/src/c#/GeneralUpdate.Extension/GeneralUpdate.Extension.csproj +++ b/src/c#/GeneralUpdate.Extension/GeneralUpdate.Extension.csproj @@ -32,6 +32,13 @@ + + + + + + +