From cb9e045c7f6c9b6449f035f68b40525180b9f758 Mon Sep 17 00:00:00 2001 From: clueless <14300910+theClueless@users.noreply.github.com> Date: Fri, 21 Feb 2020 23:12:58 +0200 Subject: [PATCH] catch plugin init fails - stop Wox from not starting up and crushing and disable and explain the user what is the probalmatic plugin --- Wox.Core/Plugin/PluginManager.cs | 35 +++++++++++++++++++----- Wox.Infrastructure/Logger/Log.cs | 47 +++++++++++++++++++++++++++++--- Wox.Plugin/IPublicAPI.cs | 2 +- Wox.Plugin/PluginMetadata.cs | 3 ++ Wox/PublicAPIInstance.cs | 6 ++-- 5 files changed, 78 insertions(+), 15 deletions(-) diff --git a/Wox.Core/Plugin/PluginManager.cs b/Wox.Core/Plugin/PluginManager.cs index fc9afb7401..27e2d48513 100644 --- a/Wox.Core/Plugin/PluginManager.cs +++ b/Wox.Core/Plugin/PluginManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; @@ -92,21 +93,36 @@ namespace Wox.Core.Plugin Settings.UpdatePluginSettings(_metadatas); AllPlugins = PluginsLoader.Plugins(_metadatas, Settings); } + + /// + /// Call initialize for all plugins + /// + /// return the list of failed to init plugins or null for none public static void InitializePlugins(IPublicAPI api) { API = api; + var failedPlugins = new ConcurrentQueue(); Parallel.ForEach(AllPlugins, pair => { - var milliseconds = Stopwatch.Debug($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>", () => + try { - pair.Plugin.Init(new PluginInitContext + var milliseconds = Stopwatch.Debug($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>", () => { - CurrentPluginMetadata = pair.Metadata, - API = API + pair.Plugin.Init(new PluginInitContext + { + CurrentPluginMetadata = pair.Metadata, + API = API + }); }); - }); - pair.Metadata.InitTime += milliseconds; - Log.Info($"|PluginManager.InitializePlugins|Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>"); + pair.Metadata.InitTime += milliseconds; + Log.Info($"|PluginManager.InitializePlugins|Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>"); + } + catch (Exception e) + { + Log.Exception(nameof(PluginManager), $"Fail to Init plugin: {pair.Metadata.Name}", e); + pair.Metadata.Disabled = true; // TODO: not sure this really disable it later on + failedPlugins.Enqueue(pair); + } }); _contextMenuPlugins = GetPluginsForInterface(); @@ -121,6 +137,11 @@ namespace Wox.Core.Plugin .ForEach(x => NonGlobalPlugins[x] = plugin); } + if (failedPlugins.Any()) + { + var failed = string.Join(",", failedPlugins.Select(x => x.Metadata.Name)); + API.ShowMsg($"Fail to Init Plugins", $"Plugins: {failed} - fail to load and would be disabled, please contact plugin creator for help", "", false); + } } public static void InstallPlugin(string path) diff --git a/Wox.Infrastructure/Logger/Log.cs b/Wox.Infrastructure/Logger/Log.cs index 10e8128c5f..349920076c 100644 --- a/Wox.Infrastructure/Logger/Log.cs +++ b/Wox.Infrastructure/Logger/Log.cs @@ -11,7 +11,7 @@ namespace Wox.Infrastructure.Logger { public const string DirectoryName = "Logs"; - public static string CurrentLogDirectory { get; private set; } + public static string CurrentLogDirectory { get; } static Log() { @@ -53,6 +53,14 @@ namespace Wox.Infrastructure.Logger [MethodImpl(MethodImplOptions.Synchronized)] public static void Exception(string className, string message, System.Exception exception, [CallerMemberName] string methodName = "") + { + var classNameWithMethod = CheckClassAndMessageAndReturnFullClassWithMethod(className, message, methodName); + + ExceptionInternal(classNameWithMethod, message, exception); + } + + private static string CheckClassAndMessageAndReturnFullClassWithMethod(string className, string message, + string methodName) { if (string.IsNullOrWhiteSpace(className)) { @@ -60,16 +68,17 @@ namespace Wox.Infrastructure.Logger } if (string.IsNullOrWhiteSpace(message)) - { // todo: not sure we really need that + { + // todo: not sure we really need that LogFaultyFormat($"Fail to specify a message during logging"); } if (!string.IsNullOrWhiteSpace(methodName)) { - className += "." + methodName; + return className + "." + methodName; } - ExceptionInternal(className, message, exception); + return className; } private static void ExceptionInternal(string classAndMethod, string message, System.Exception e) @@ -140,18 +149,48 @@ namespace Wox.Infrastructure.Logger LogInternal(message, LogLevel.Error); } + public static void Error(string className, string message, [CallerMemberName] string methodName = "") + { + LogInternal(LogLevel.Error, className, message, methodName); + } + + private static void LogInternal(LogLevel level, string className, string message, [CallerMemberName] string methodName = "") + { + var classNameWithMethod = CheckClassAndMessageAndReturnFullClassWithMethod(className, message, methodName); + + var logger = LogManager.GetLogger(classNameWithMethod); + + System.Diagnostics.Debug.WriteLine($"{level.Name}|{message}"); + logger.Log(level, message); + } + + public static void Debug(string className, string message, [CallerMemberName] string methodName = "") + { + LogInternal(LogLevel.Debug, className, message, methodName); + } + /// example: "|prefix|unprefixed" public static void Debug(string message) { LogInternal(message, LogLevel.Debug); } + public static void Info(string className, string message, [CallerMemberName] string methodName = "") + { + LogInternal(LogLevel.Info, className, message, methodName); + } + /// example: "|prefix|unprefixed" public static void Info(string message) { LogInternal(message, LogLevel.Info); } + public static void Warn(string className, string message, [CallerMemberName] string methodName = "") + { + LogInternal(LogLevel.Warn, className, message, methodName); + } + /// example: "|prefix|unprefixed" public static void Warn(string message) { diff --git a/Wox.Plugin/IPublicAPI.cs b/Wox.Plugin/IPublicAPI.cs index eb3f18fa70..8dc838963c 100644 --- a/Wox.Plugin/IPublicAPI.cs +++ b/Wox.Plugin/IPublicAPI.cs @@ -76,7 +76,7 @@ namespace Wox.Plugin /// Message title /// Message subtitle /// Message icon path (relative path to your plugin folder) - void ShowMsg(string title, string subTitle = "", string iconPath = ""); + void ShowMsg(string title, string subTitle = "", string iconPath = "", bool useMainWindowAsOwner = true); /// /// Open setting dialog diff --git a/Wox.Plugin/PluginMetadata.cs b/Wox.Plugin/PluginMetadata.cs index 5b4feb88ef..64b8b032c3 100644 --- a/Wox.Plugin/PluginMetadata.cs +++ b/Wox.Plugin/PluginMetadata.cs @@ -46,6 +46,9 @@ namespace Wox.Plugin [Obsolete("Use IcoPath")] public string FullIcoPath => IcoPath; + /// + /// Init time include both plugin load time and init time + /// [JsonIgnore] public long InitTime { get; set; } [JsonIgnore] diff --git a/Wox/PublicAPIInstance.cs b/Wox/PublicAPIInstance.cs index ab198d6bf1..314fc13b67 100644 --- a/Wox/PublicAPIInstance.cs +++ b/Wox/PublicAPIInstance.cs @@ -91,12 +91,12 @@ namespace Wox _mainVM.MainWindowVisibility = Visibility.Visible; } - public void ShowMsg(string title, string subTitle = "", string iconPath = "") + public void ShowMsg(string title, string subTitle = "", string iconPath = "", bool useMainWindowAsOwner = true) { Application.Current.Dispatcher.Invoke(() => { - var m = new Msg { Owner = Application.Current.MainWindow }; - m.Show(title, subTitle, iconPath); + var msg = useMainWindowAsOwner ? new Msg {Owner = Application.Current.MainWindow} : new Msg(); + msg.Show(title, subTitle, iconPath); }); }