catch plugin init fails - stop Wox from not starting up and crushing and disable and explain the user what is the probalmatic plugin

This commit is contained in:
clueless
2020-02-21 23:12:58 +02:00
parent 56c2964e96
commit cb9e045c7f
5 changed files with 78 additions and 15 deletions

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -92,21 +93,36 @@ namespace Wox.Core.Plugin
Settings.UpdatePluginSettings(_metadatas); Settings.UpdatePluginSettings(_metadatas);
AllPlugins = PluginsLoader.Plugins(_metadatas, Settings); AllPlugins = PluginsLoader.Plugins(_metadatas, Settings);
} }
/// <summary>
/// Call initialize for all plugins
/// </summary>
/// <returns>return the list of failed to init plugins or null for none</returns>
public static void InitializePlugins(IPublicAPI api) public static void InitializePlugins(IPublicAPI api)
{ {
API = api; API = api;
var failedPlugins = new ConcurrentQueue<PluginPair>();
Parallel.ForEach(AllPlugins, pair => 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, pair.Plugin.Init(new PluginInitContext
API = API {
CurrentPluginMetadata = pair.Metadata,
API = API
});
}); });
}); pair.Metadata.InitTime += milliseconds;
pair.Metadata.InitTime += milliseconds; Log.Info($"|PluginManager.InitializePlugins|Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>");
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<IContextMenu>(); _contextMenuPlugins = GetPluginsForInterface<IContextMenu>();
@@ -121,6 +137,11 @@ namespace Wox.Core.Plugin
.ForEach(x => NonGlobalPlugins[x] = 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) public static void InstallPlugin(string path)

View File

@@ -11,7 +11,7 @@ namespace Wox.Infrastructure.Logger
{ {
public const string DirectoryName = "Logs"; public const string DirectoryName = "Logs";
public static string CurrentLogDirectory { get; private set; } public static string CurrentLogDirectory { get; }
static Log() static Log()
{ {
@@ -53,6 +53,14 @@ namespace Wox.Infrastructure.Logger
[MethodImpl(MethodImplOptions.Synchronized)] [MethodImpl(MethodImplOptions.Synchronized)]
public static void Exception(string className, string message, System.Exception exception, [CallerMemberName] string methodName = "") 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)) if (string.IsNullOrWhiteSpace(className))
{ {
@@ -60,16 +68,17 @@ namespace Wox.Infrastructure.Logger
} }
if (string.IsNullOrWhiteSpace(message)) 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"); LogFaultyFormat($"Fail to specify a message during logging");
} }
if (!string.IsNullOrWhiteSpace(methodName)) 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) private static void ExceptionInternal(string classAndMethod, string message, System.Exception e)
@@ -140,18 +149,48 @@ namespace Wox.Infrastructure.Logger
LogInternal(message, LogLevel.Error); 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);
}
/// <param name="message">example: "|prefix|unprefixed" </param> /// <param name="message">example: "|prefix|unprefixed" </param>
public static void Debug(string message) public static void Debug(string message)
{ {
LogInternal(message, LogLevel.Debug); LogInternal(message, LogLevel.Debug);
} }
public static void Info(string className, string message, [CallerMemberName] string methodName = "")
{
LogInternal(LogLevel.Info, className, message, methodName);
}
/// <param name="message">example: "|prefix|unprefixed" </param> /// <param name="message">example: "|prefix|unprefixed" </param>
public static void Info(string message) public static void Info(string message)
{ {
LogInternal(message, LogLevel.Info); LogInternal(message, LogLevel.Info);
} }
public static void Warn(string className, string message, [CallerMemberName] string methodName = "")
{
LogInternal(LogLevel.Warn, className, message, methodName);
}
/// <param name="message">example: "|prefix|unprefixed" </param> /// <param name="message">example: "|prefix|unprefixed" </param>
public static void Warn(string message) public static void Warn(string message)
{ {

View File

@@ -76,7 +76,7 @@ namespace Wox.Plugin
/// <param name="title">Message title</param> /// <param name="title">Message title</param>
/// <param name="subTitle">Message subtitle</param> /// <param name="subTitle">Message subtitle</param>
/// <param name="iconPath">Message icon path (relative path to your plugin folder)</param> /// <param name="iconPath">Message icon path (relative path to your plugin folder)</param>
void ShowMsg(string title, string subTitle = "", string iconPath = ""); void ShowMsg(string title, string subTitle = "", string iconPath = "", bool useMainWindowAsOwner = true);
/// <summary> /// <summary>
/// Open setting dialog /// Open setting dialog

View File

@@ -46,6 +46,9 @@ namespace Wox.Plugin
[Obsolete("Use IcoPath")] [Obsolete("Use IcoPath")]
public string FullIcoPath => IcoPath; public string FullIcoPath => IcoPath;
/// <summary>
/// Init time include both plugin load time and init time
/// </summary>
[JsonIgnore] [JsonIgnore]
public long InitTime { get; set; } public long InitTime { get; set; }
[JsonIgnore] [JsonIgnore]

View File

@@ -91,12 +91,12 @@ namespace Wox
_mainVM.MainWindowVisibility = Visibility.Visible; _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(() => Application.Current.Dispatcher.Invoke(() =>
{ {
var m = new Msg { Owner = Application.Current.MainWindow }; var msg = useMainWindowAsOwner ? new Msg {Owner = Application.Current.MainWindow} : new Msg();
m.Show(title, subTitle, iconPath); msg.Show(title, subTitle, iconPath);
}); });
} }