diff --git a/Flow.Launcher.Core/Configuration/Portable.cs b/Flow.Launcher.Core/Configuration/Portable.cs index 5bca087b879..ceafaff7509 100644 --- a/Flow.Launcher.Core/Configuration/Portable.cs +++ b/Flow.Launcher.Core/Configuration/Portable.cs @@ -47,7 +47,7 @@ public void DisablePortableMode() } catch (Exception e) { - Log.Exception("|Portable.DisablePortableMode|Error occured while disabling portable mode", e); + Log.Exception(nameof(Portable),"Error occured while disabling portable mode", e); } } @@ -71,7 +71,7 @@ public void EnablePortableMode() } catch (Exception e) { - Log.Exception("|Portable.EnablePortableMode|Error occured while enabling portable mode", e); + Log.Exception(nameof(Portable),"Error occured while enabling portable mode", e); } } diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 65f8fc608d6..692a9d42b5a 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -42,7 +42,7 @@ public List LoadContextMenus(Result selectedResult) } catch (Exception e) { - Log.Exception($"|JsonRPCPlugin.LoadContextMenus|Exception on result <{selectedResult}>", e); + Log.Exception(nameof(JsonRPCPlugin), $"Exception on result <{selectedResult}>", e); return null; } } @@ -183,11 +183,11 @@ protected string Execute(ProcessStartInfo startInfo) var error = standardError.ReadToEnd(); if (!string.IsNullOrEmpty(error)) { - Log.Error($"|JsonRPCPlugin.Execute|{error}"); + Log.Error("JsonRPCPlugin", $"{error}"); return string.Empty; } - Log.Error("|JsonRPCPlugin.Execute|Empty standard output and standard error."); + Log.Error("JsonRPCPlugin", "Empty standard output and standard error."); return string.Empty; } @@ -204,9 +204,8 @@ protected string Execute(ProcessStartInfo startInfo) } catch (Exception e) { - Log.Exception( - $"|JsonRPCPlugin.Execute|Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>", - e); + Log.Exception(nameof(JsonRPCPlugin), + $"Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>", e); return string.Empty; } } @@ -218,7 +217,7 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati using var process = Process.Start(startInfo); if (process == null) { - Log.Error("|JsonRPCPlugin.ExecuteAsync|Can't start new process"); + Log.Error("JsonRPCPlugin", "Can't start new process"); return Stream.Null; } @@ -233,11 +232,11 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati if (!string.IsNullOrEmpty(error)) { - Log.Error($"|JsonRPCPlugin.ExecuteAsync|{error}"); + Log.Error("JsonRPCPlugin", $"{error}"); return Stream.Null; } - Log.Error("|JsonRPCPlugin.ExecuteAsync|Empty standard output and standard error."); + Log.Error("JsonRPCPlugin", "Empty standard output and standard error."); return Stream.Null; } @@ -246,7 +245,8 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati catch (Exception e) { Log.Exception( - $"|JsonRPCPlugin.ExecuteAsync|Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>", + "JsonRPCPlugin", + $"Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>", e); return Stream.Null; } @@ -261,7 +261,7 @@ public async Task> QueryAsync(Query query, CancellationToken token) } catch (Exception e) { - Log.Exception($"|JsonRPCPlugin.Query|Exception when query <{query}>", e); + Log.Exception(nameof(JsonRPCPlugin),$"Exception when query <{query}>", e); return null; } } diff --git a/Flow.Launcher.Core/Plugin/PluginConfig.cs b/Flow.Launcher.Core/Plugin/PluginConfig.cs index ea2119e60bc..5ba680fe312 100644 --- a/Flow.Launcher.Core/Plugin/PluginConfig.cs +++ b/Flow.Launcher.Core/Plugin/PluginConfig.cs @@ -33,7 +33,7 @@ public static List Parse(string[] pluginDirectories) } catch (Exception e) { - Log.Exception($"|PluginConfig.ParsePLuginConfigs|Can't delete <{directory}>", e); + Log.Exception(nameof(PluginConfig),$"Can't delete <{directory}>", e); } } else @@ -54,7 +54,7 @@ private static PluginMetadata GetPluginMetadata(string pluginDirectory) string configPath = Path.Combine(pluginDirectory, Constant.PluginMetadataFileName); if (!File.Exists(configPath)) { - Log.Error($"|PluginConfig.GetPluginMetadata|Didn't find config file <{configPath}>"); + Log.Error(nameof(PluginConfig),$"Didn't find config file <{configPath}>"); return null; } @@ -70,19 +70,19 @@ private static PluginMetadata GetPluginMetadata(string pluginDirectory) } catch (Exception e) { - Log.Exception($"|PluginConfig.GetPluginMetadata|invalid json for config <{configPath}>", e); + Log.Exception(nameof(PluginConfig),$"invalid json for config <{configPath}>", e); return null; } if (!AllowedLanguage.IsAllowed(metadata.Language)) { - Log.Error($"|PluginConfig.GetPluginMetadata|Invalid language <{metadata.Language}> for config <{configPath}>"); + Log.Error(nameof(PluginConfig),$"Invalid language <{metadata.Language}> for config <{configPath}>"); return null; } if (!File.Exists(metadata.ExecuteFilePath)) { - Log.Error($"|PluginConfig.GetPluginMetadata|execute file path didn't exist <{metadata.ExecuteFilePath}> for conifg <{configPath}"); + Log.Error(nameof(PluginConfig),$"execute file path didn't exist <{metadata.ExecuteFilePath}> for conifg <{configPath}"); return null; } diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 59f34de742e..c78086c5232 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -22,7 +22,7 @@ public static class PluginManager public static List AllPlugins { get; private set; } public static readonly HashSet GlobalPlugins = new(); - public static readonly Dictionary NonGlobalPlugins = new (); + public static readonly Dictionary NonGlobalPlugins = new(); public static IPublicAPI API { private set; get; } @@ -33,7 +33,7 @@ public static class PluginManager /// /// Directories that will hold Flow Launcher plugin directory /// - private static readonly string[] Directories = { Constant.PreinstalledDirectory, DataLocation.PluginsDirectory }; + private static readonly string[] Directories = {Constant.PreinstalledDirectory, DataLocation.PluginsDirectory}; private static void DeletePythonBinding() { @@ -99,12 +99,13 @@ public static async Task InitializePlugins(IPublicAPI api) { try { - var milliseconds = await Stopwatch.DebugAsync($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>", - () => pair.Plugin.InitAsync(new PluginInitContext(pair.Metadata, API))); + var milliseconds = await Stopwatch.DebugAsync(nameof(PluginManager), + $"Init method time cost for <{pair.Metadata.Name}>", + () => pair.Plugin.InitAsync(new PluginInitContext(pair.Metadata, API))); pair.Metadata.InitTime += milliseconds; - Log.Info( - $"|PluginManager.InitializePlugins|Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>"); + Log.Info(nameof(PluginManager), + $"Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>"); } catch (Exception e) { @@ -149,7 +150,7 @@ public static ICollection ValidPluginsForQuery(Query query) if (NonGlobalPlugins.ContainsKey(query.ActionKeyword)) { var plugin = NonGlobalPlugins[query.ActionKeyword]; - return new List { plugin }; + return new List {plugin}; } else { @@ -157,7 +158,8 @@ public static ICollection ValidPluginsForQuery(Query query) } } - public static async Task> QueryForPlugin(PluginPair pair, Query query, CancellationToken token) + public static async Task> QueryForPluginAsync(PluginPair pair, Query query, + CancellationToken token) { var results = new List(); try @@ -166,12 +168,12 @@ public static async Task> QueryForPlugin(PluginPair pair, Query que long milliseconds = -1L; - milliseconds = await Stopwatch.DebugAsync($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}", + milliseconds = await Stopwatch.DebugAsync(nameof(PluginManager), $"Cost for {metadata.Name}", async () => results = await pair.Plugin.QueryAsync(query, token).ConfigureAwait(false)); token.ThrowIfCancellationRequested(); if (results == null) - return results; + return null; UpdatePluginMetadata(results, metadata, query); metadata.QueryCount += 1; @@ -184,10 +186,6 @@ public static async Task> QueryForPlugin(PluginPair pair, Query que // null will be fine since the results will only be added into queue if the token hasn't been cancelled return null; } - catch (Exception e) - { - Log.Exception($"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", e); - } return results; } @@ -228,7 +226,7 @@ public static List GetContextMenusForPlugin(Result result) var pluginPair = _contextMenuPlugins.FirstOrDefault(o => o.Metadata.ID == result.PluginID); if (pluginPair != null) { - var plugin = (IContextMenu)pluginPair.Plugin; + var plugin = (IContextMenu) pluginPair.Plugin; try { @@ -242,9 +240,8 @@ public static List GetContextMenusForPlugin(Result result) } catch (Exception e) { - Log.Exception( - $"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{pluginPair.Metadata.Name}>", - e); + Log.Exception(nameof(PluginManager), + $"Can't load context menus for plugin <{pluginPair.Metadata.Name}>", e); } } @@ -309,4 +306,4 @@ public static void ReplaceActionKeyword(string id, string oldActionKeyword, stri } } } -} +} \ No newline at end of file diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 1b78c68aee8..31059a3b811 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -38,8 +38,8 @@ public static IEnumerable DotNetPlugins(List source) foreach (var metadata in metadatas) { - var milliseconds = Stopwatch.Debug( - $"|PluginsLoader.DotNetPlugins|Constructor init cost for {metadata.Name}", () => + var milliseconds = Stopwatch.Debug(nameof(PluginsLoader), + $"Constructor init cost for {metadata.Name}", () => { #if DEBUG var assemblyLoader = new PluginAssemblyLoader(metadata.ExecuteFilePath); @@ -64,19 +64,19 @@ public static IEnumerable DotNetPlugins(List source) } catch (Exception e) when (assembly == null) { - Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for the plugin: {metadata.Name}", e); + Log.Exception(nameof(PluginsLoader), $"Couldn't load assembly for the plugin: {metadata.Name}", e); } catch (InvalidOperationException e) { - Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e); + Log.Exception(nameof(PluginsLoader), $"Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e); } catch (ReflectionTypeLoadException e) { - Log.Exception($"|PluginsLoader.DotNetPlugins|The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e); + Log.Exception(nameof(PluginsLoader), $"The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e); } catch (Exception e) { - Log.Exception($"|PluginsLoader.DotNetPlugins|The following plugin has errored and can not be loaded: <{metadata.Name}>", e); + Log.Exception(nameof(PluginsLoader), $"The following plugin has errored and can not be loaded: <{metadata.Name}>", e); } #endif if (plugin == null) diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs index 64b949cbbd2..4aef0997798 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -51,7 +51,8 @@ private void AddPluginLanguageDirectories() } else { - Log.Error($"|Internationalization.AddPluginLanguageDirectories|Can't find plugin path <{location}> for <{plugin.Metadata.Name}>"); + Log.Error("Internationalization", + $"Can't find plugin path <{location}> for <{plugin.Metadata.Name}>"); } } } @@ -72,10 +73,11 @@ public void ChangeLanguage(string languageCode) private Language GetLanguageByLanguageCode(string languageCode) { var lowercase = languageCode.ToLower(); - var language = AvailableLanguages.GetAvailableLanguages().FirstOrDefault(o => o.LanguageCode.ToLower() == lowercase); + var language = AvailableLanguages.GetAvailableLanguages() + .FirstOrDefault(o => o.LanguageCode.ToLower() == lowercase); if (language == null) { - Log.Error($"|Internationalization.GetLanguageByLanguageCode|Language code can't be found <{languageCode}>"); + Log.Error("Internationalization", $"Language code can't be found <{languageCode}>"); return AvailableLanguages.English; } else @@ -94,9 +96,9 @@ public void ChangeLanguage(Language language) { LoadLanguage(language); } + UpdatePluginMetadataTranslations(); Settings.Language = language.LanguageCode; - } public bool PromptShouldUsePinyin(string languageCodeToSet) @@ -109,7 +111,8 @@ public bool PromptShouldUsePinyin(string languageCodeToSet) if (languageToSet != AvailableLanguages.Chinese && languageToSet != AvailableLanguages.Chinese_TW) return false; - if (MessageBox.Show("Do you want to turn on search with Pinyin?", string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No) + if (MessageBox.Show("Do you want to turn on search with Pinyin?", string.Empty, MessageBoxButton.YesNo) == + MessageBoxResult.No) return false; return true; @@ -161,7 +164,7 @@ public string GetTranslation(string key) } else { - Log.Error($"|Internationalization.GetTranslation|No Translation for key {key}"); + Log.Error("Internationalization", $"No Translation for key {key}"); return $"No Translation for key {key}"; } } @@ -179,7 +182,7 @@ private void UpdatePluginMetadataTranslations() } catch (Exception e) { - Log.Exception($"|Internationalization.UpdatePluginMetadataTranslations|Failed for <{p.Metadata.Name}>", e); + Log.Exception(nameof(Internationalization), $"Failed for <{p.Metadata.Name}>", e); } } } @@ -195,7 +198,7 @@ public string LanguageFile(string folder, string language) } else { - Log.Error($"|Internationalization.LanguageFile|Language path can't be found <{path}>"); + Log.Error(nameof(Internationalization), $"Language path can't be found <{path}>"); string english = Path.Combine(folder, DefaultFile); if (File.Exists(english)) { @@ -203,7 +206,8 @@ public string LanguageFile(string folder, string language) } else { - Log.Error($"|Internationalization.LanguageFile|Default English Language path can't be found <{path}>"); + Log.Error(nameof(Internationalization), + $"Default English Language path can't be found <{path}>"); return string.Empty; } } diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index fe64e0c3e17..2a64227fd98 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -63,7 +63,7 @@ private void MakesureThemeDirectoriesExist() } catch (Exception e) { - Log.Exception($"|Theme.MakesureThemeDirectoriesExist|Exception when create directory <{dir}>", e); + Log.Exception(nameof(Theme),$"Exception when create directory <{dir}>", e); } } } @@ -93,7 +93,7 @@ public bool ChangeTheme(string theme) } catch (DirectoryNotFoundException e) { - Log.Error($"|Theme.ChangeTheme|Theme <{theme}> path can't be found"); + Log.Error(nameof(Theme),$"Theme <{theme}> path can't be found"); if (theme != defaultTheme) { MessageBox.Show(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_path_not_exists"), theme)); @@ -103,7 +103,7 @@ public bool ChangeTheme(string theme) } catch (XamlParseException e) { - Log.Error($"|Theme.ChangeTheme|Theme <{theme}> fail to parse"); + Log.Error(nameof(Theme),$"Theme <{theme}> fail to parse"); if (theme != defaultTheme) { MessageBox.Show(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_parse_error"), theme)); diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 76713ce2ab1..225de35132e 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -47,7 +47,7 @@ public async Task UpdateApp(IPublicAPI api, bool silentUpdate = true) var newReleaseVersion = Version.Parse(newUpdateInfo.FutureReleaseEntry.Version.ToString()); var currentVersion = Version.Parse(Constant.Version); - Log.Info($"|Updater.UpdateApp|Future Release <{newUpdateInfo.FutureReleaseEntry.Formatted()}>"); + Log.Info(nameof(Updater),$"Future Release <{newUpdateInfo.FutureReleaseEntry.Formatted()}>"); if (newReleaseVersion <= currentVersion) { @@ -80,7 +80,7 @@ public async Task UpdateApp(IPublicAPI api, bool silentUpdate = true) var newVersionTips = NewVersinoTips(newReleaseVersion.ToString()); - Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}"); + Log.Info(nameof(Updater),$"Update success:{newVersionTips}"); if (MessageBox.Show(newVersionTips, api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes) { @@ -89,7 +89,7 @@ public async Task UpdateApp(IPublicAPI api, bool silentUpdate = true) } catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException || e.InnerException is TimeoutException) { - Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e); + Log.Exception(nameof(Updater),"Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e); api.ShowMsg(api.GetTranslation("update_flowlauncher_fail"), api.GetTranslation("update_flowlauncher_check_connection")); return; diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index 2f919b5c9e9..aea43506ef5 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -49,8 +49,11 @@ - - + + + + + diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index b45b6adcde1..74c7283a356 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -116,7 +116,7 @@ public static async Task DownloadAsync([NotNull] string url, [NotNull] string fi /// The Http result as string. Null if cancellation requested public static Task GetAsync([NotNull] string url, CancellationToken token = default) { - Log.Debug($"|Http.Get|Url <{url}>"); + Log.Debug("Http",$"Url <{url}>"); return GetAsync(new Uri(url.Replace("#", "%23")), token); } @@ -128,7 +128,7 @@ public static Task GetAsync([NotNull] string url, CancellationToken toke /// The Http result as string. Null if cancellation requested public static async Task GetAsync([NotNull] Uri url, CancellationToken token = default) { - Log.Debug($"|Http.Get|Url <{url}>"); + Log.Debug("Http",$"Url <{url}>"); using var response = await client.GetAsync(url, token); var content = await response.Content.ReadAsStringAsync(); if (response.StatusCode == HttpStatusCode.OK) @@ -149,7 +149,7 @@ public static async Task GetAsync([NotNull] Uri url, CancellationToken t /// public static async Task GetStreamAsync([NotNull] string url, CancellationToken token = default) { - Log.Debug($"|Http.Get|Url <{url}>"); + Log.Debug("Http",$"Url <{url}>"); var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token); return await response.Content.ReadAsStreamAsync(); } diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs index ac333d567b8..b4d67d1eb9c 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs @@ -48,14 +48,14 @@ public static void Initialize() Task.Run(() => { - Stopwatch.Normal("|ImageLoader.Initialize|Preload images cost", () => + Stopwatch.Normal(nameof(ImageLoader),"Preload images cost", () => { ImageCache.Data.AsParallel().ForAll(x => { Load(x.Key); }); }); - Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.CacheSize()}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}"); + Log.Info(nameof(ImageLoader),$"Number of preload images is <{ImageCache.CacheSize()}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}"); }); } @@ -137,8 +137,8 @@ private static ImageResult LoadInternal(string path, bool loadFullImage = false) } catch (System.Exception e2) { - Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path} on first try", e); - Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path} on second try", e2); + Log.Exception(nameof(ImageLoader),$"Failed to get thumbnail for {path} on first try", e); + Log.Exception(nameof(ImageLoader),$"Failed to get thumbnail for {path} on second try", e2); ImageSource image = ImageCache[Constant.MissingImgIcon]; ImageCache[path] = image; diff --git a/Flow.Launcher.Infrastructure/Logger/Log.cs b/Flow.Launcher.Infrastructure/Logger/Log.cs index 94132b27f18..e97d82907b4 100644 --- a/Flow.Launcher.Infrastructure/Logger/Log.cs +++ b/Flow.Launcher.Infrastructure/Logger/Log.cs @@ -5,6 +5,11 @@ using NLog.Config; using NLog.Targets; using Flow.Launcher.Infrastructure.UserSettings; +using JetBrains.Annotations; +using NLog.Fluent; +using NLog.Targets.Wrappers; +using System.Runtime.ExceptionServices; +using System.Text; namespace Flow.Launcher.Infrastructure.Logger { @@ -23,15 +28,46 @@ static Log() } var configuration = new LoggingConfiguration(); - var target = new FileTarget(); - configuration.AddTarget("file", target); - target.FileName = CurrentLogDirectory.Replace(@"\", "/") + "/${shortdate}.txt"; + + const string layout = + @"${date:format=HH\:mm\:ss.ffffK} - " + + @"${level:uppercase=true:padding=-5} - ${logger} - ${message:l}" + + @"${onexception:${newline}" + + @"EXCEPTION OCCURS\: ${exception:format=tostring}${newline}}"; + + var fileTarget = new FileTarget + { + Name = "file", + FileName = CurrentLogDirectory.Replace(@"\", "/") + "/${shortdate}.txt", + Layout = layout + }; + + var fileTargetASyncWrapper = new AsyncTargetWrapper(fileTarget) + { + Name = "asyncFile" + }; + + var debugTarget = new OutputDebugStringTarget + { + Name = "debug", + Layout = layout, + OptimizeBufferReuse = false + }; + + configuration.AddTarget(fileTargetASyncWrapper); + configuration.AddTarget(debugTarget); + #if DEBUG - var rule = new LoggingRule("*", LogLevel.Debug, target); + var fileRule = new LoggingRule("*", LogLevel.Debug, fileTargetASyncWrapper); + var debugRule = new LoggingRule("*", LogLevel.Debug, debugTarget); + configuration.LoggingRules.Add(debugRule); #else - var rule = new LoggingRule("*", LogLevel.Info, target); + var fileRule = new LoggingRule("*", LogLevel.Info, fileTargetASyncWrapper); #endif - configuration.LoggingRules.Add(rule); + configuration.LoggingRules.Add(fileRule); + + NLog.Common.InternalLogger.LogToTrace = true; + LogManager.Configuration = configuration; } @@ -39,167 +75,59 @@ private static void LogFaultyFormat(string message) { var logger = LogManager.GetLogger("FaultyLogger"); message = $"Wrong logger message format <{message}>"; - System.Diagnostics.Debug.WriteLine($"FATAL|{message}"); logger.Fatal(message); } private static bool FormatValid(string message) { var parts = message.Split('|'); - var valid = parts.Length == 3 && !string.IsNullOrWhiteSpace(parts[1]) && !string.IsNullOrWhiteSpace(parts[2]); + var valid = parts.Length == 3 && !string.IsNullOrWhiteSpace(parts[1]) && + !string.IsNullOrWhiteSpace(parts[2]); return valid; } - - [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 = "") { + exception = exception.Demystify(); #if DEBUG - throw exception; + ExceptionDispatchInfo.Capture(exception).Throw(); #else - var classNameWithMethod = CheckClassAndMessageAndReturnFullClassWithMethod(className, message, methodName); - ExceptionInternal(classNameWithMethod, message, exception); + LogInternal(LogLevel.Error, className, message, methodName, exception); #endif } - private static string CheckClassAndMessageAndReturnFullClassWithMethod(string className, string message, - string methodName) - { - if (string.IsNullOrWhiteSpace(className)) - { - LogFaultyFormat($"Fail to specify a class name during logging of message: {message ?? "no message entered"}"); - } - - if (string.IsNullOrWhiteSpace(message)) - { - // todo: not sure we really need that - LogFaultyFormat($"Fail to specify a message during logging"); - } - - if (!string.IsNullOrWhiteSpace(methodName)) - { - return className + "." + methodName; - } - - return className; - } - - private static void ExceptionInternal(string classAndMethod, string message, System.Exception e) - { - var logger = LogManager.GetLogger(classAndMethod); - - System.Diagnostics.Debug.WriteLine($"ERROR|{message}"); - - logger.Error("-------------------------- Begin exception --------------------------"); - logger.Error(message); - - do - { - logger.Error($"Exception full name:\n <{e.GetType().FullName}>"); - logger.Error($"Exception message:\n <{e.Message}>"); - logger.Error($"Exception stack trace:\n <{e.StackTrace}>"); - logger.Error($"Exception source:\n <{e.Source}>"); - logger.Error($"Exception target site:\n <{e.TargetSite}>"); - logger.Error($"Exception HResult:\n <{e.HResult}>"); - e = e.InnerException; - } while (e != null); - - logger.Error("-------------------------- End exception --------------------------"); - } - - private static void LogInternal(string message, LogLevel level) - { - if (FormatValid(message)) - { - var parts = message.Split('|'); - var prefix = parts[1]; - var unprefixed = parts[2]; - var logger = LogManager.GetLogger(prefix); - - System.Diagnostics.Debug.WriteLine($"{level.Name}|{message}"); - logger.Log(level, unprefixed); - } - else - { - LogFaultyFormat(message); - } - } - - /// example: "|prefix|unprefixed" - [MethodImpl(MethodImplOptions.Synchronized)] - public static void Exception(string message, System.Exception e) - { -#if DEBUG - throw e; -#else - if (FormatValid(message)) - { - var parts = message.Split('|'); - var prefix = parts[1]; - var unprefixed = parts[2]; - ExceptionInternal(prefix, unprefixed, e); - } - else - { - LogFaultyFormat(message); - } -#endif - } - - /// example: "|prefix|unprefixed" - public static void Error(string message) - { - 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 = "") + private static void LogInternal(LogLevel level, string className, string message, string methodName = "", + System.Exception e = null) { - var classNameWithMethod = CheckClassAndMessageAndReturnFullClassWithMethod(className, message, methodName); - - var logger = LogManager.GetLogger(classNameWithMethod); + var logger = LogManager.GetLogger(className); - System.Diagnostics.Debug.WriteLine($"{level.Name}|{message}"); - logger.Log(level, message); + logger.Log(level, e, "{MethodName:l}|{Message}", methodName, message); } + [Conditional("DEBUG")] 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) + public static void Warn(string className, string message, [CallerMemberName] string methodName = "", System.Exception e = null) { - LogInternal(message, LogLevel.Warn); + LogInternal(LogLevel.Warn, className, message, methodName, e); } } } \ No newline at end of file diff --git a/Flow.Launcher.Infrastructure/Stopwatch.cs b/Flow.Launcher.Infrastructure/Stopwatch.cs index dd6edaff93b..9ddee251cc6 100644 --- a/Flow.Launcher.Infrastructure/Stopwatch.cs +++ b/Flow.Launcher.Infrastructure/Stopwatch.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using Flow.Launcher.Infrastructure.Logger; @@ -7,12 +8,13 @@ namespace Flow.Launcher.Infrastructure { public static class Stopwatch { - private static readonly Dictionary Count = new Dictionary(); - private static readonly object Locker = new object(); + private static readonly Dictionary Count = new(); + private static readonly object Locker = new(); + /// /// This stopwatch will appear only in Debug mode /// - public static long Debug(string message, Action action) + public static long Debug(string className, string message, Action action, [CallerMemberName]string methodName = "") { var stopWatch = new System.Diagnostics.Stopwatch(); stopWatch.Start(); @@ -20,14 +22,14 @@ public static long Debug(string message, Action action) stopWatch.Stop(); var milliseconds = stopWatch.ElapsedMilliseconds; string info = $"{message} <{milliseconds}ms>"; - Log.Debug(info); + Log.Debug(className, info, methodName); return milliseconds; } - + /// /// This stopwatch will appear only in Debug mode /// - public static async Task DebugAsync(string message, Func action) + public static async Task DebugAsync(string className,string message, Func action,[CallerMemberName]string methodName = "") { var stopWatch = new System.Diagnostics.Stopwatch(); stopWatch.Start(); @@ -35,11 +37,11 @@ public static async Task DebugAsync(string message, Func action) stopWatch.Stop(); var milliseconds = stopWatch.ElapsedMilliseconds; string info = $"{message} <{milliseconds}ms>"; - Log.Debug(info); + Log.Debug(className, info, methodName); return milliseconds; } - - public static long Normal(string message, Action action) + + public static long Normal(string className, string message, Action action, [CallerMemberName]string methodName = "") { var stopWatch = new System.Diagnostics.Stopwatch(); stopWatch.Start(); @@ -47,11 +49,11 @@ public static long Normal(string message, Action action) stopWatch.Stop(); var milliseconds = stopWatch.ElapsedMilliseconds; string info = $"{message} <{milliseconds}ms>"; - Log.Info(info); + Log.Info(className, info, methodName); return milliseconds; } - - public static async Task NormalAsync(string message, Func action) + + public static async Task NormalAsync(string className,string message, Func action,[CallerMemberName]string methodName = "") { var stopWatch = new System.Diagnostics.Stopwatch(); stopWatch.Start(); @@ -59,11 +61,10 @@ public static async Task NormalAsync(string message, Func action) stopWatch.Stop(); var milliseconds = stopWatch.ElapsedMilliseconds; string info = $"{message} <{milliseconds}ms>"; - Log.Info(info); + Log.Info(className, info, methodName); return milliseconds; } - - + public static void StartCount(string name, Action action) { @@ -90,8 +91,8 @@ public static void EndCount() foreach (var key in Count.Keys) { string info = $"{key} already cost {Count[key]}ms"; - Log.Debug(info); + Log.Debug("", info, ""); } } } -} +} \ No newline at end of file diff --git a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs index 5205543b1d3..206c110a928 100644 --- a/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs @@ -33,7 +33,7 @@ public T TryLoad(T defaultData) { if (new FileInfo(FilePath).Length == 0) { - Log.Error($"|BinaryStorage.TryLoad|Zero length cache file <{FilePath}>"); + Log.Error(nameof(BinaryStorage),$"Zero length cache file <{FilePath}>"); Save(defaultData); return defaultData; } @@ -46,7 +46,7 @@ public T TryLoad(T defaultData) } else { - Log.Info("|BinaryStorage.TryLoad|Cache file not exist, load default data"); + Log.Info(nameof(BinaryStorage),$"Zero length cache file <{FilePath}>"); Save(defaultData); return defaultData; } @@ -68,7 +68,7 @@ private T Deserialize(FileStream stream, T defaultData) } catch (System.Exception e) { - Log.Exception($"|BinaryStorage.Deserialize|Deserialize error for file <{FilePath}>", e); + Log.Exception(nameof(BinaryStorage),$"Deserialize error for file <{FilePath}>", e); return defaultData; } finally @@ -108,7 +108,7 @@ public void Save(T data) } catch (SerializationException e) { - Log.Exception($"|BinaryStorage.Save|serialize error for file <{FilePath}>", e); + Log.Exception(nameof(BinaryStorage),$"serialize error for file <{FilePath}>", e); } } } diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs index 0083ccb87b0..cb03a7592b8 100644 --- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs @@ -12,6 +12,7 @@ namespace Flow.Launcher.Infrastructure.Storage public class JsonStorage where T : new() { protected T _data; + // need a new directory name public const string DirectoryName = "Settings"; public const string FileSuffix = ".json"; @@ -37,6 +38,7 @@ public T Load() { LoadDefault(); } + return _data.NonNull(); } @@ -49,7 +51,7 @@ private void Deserialize(string serialized) catch (JsonException e) { LoadDefault(); - Log.Exception($"|JsonStorage.Deserialize|Deserialize error for json <{FilePath}>", e); + Log.Exception(nameof(JsonStorage), $"Deserialize error for json <{FilePath}>", e); } if (_data == null) diff --git a/Flow.Launcher.Plugin/IPublicAPI.cs b/Flow.Launcher.Plugin/IPublicAPI.cs index d9cdf5581d0..214d6c11636 100644 --- a/Flow.Launcher.Plugin/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/IPublicAPI.cs @@ -161,7 +161,7 @@ public interface IPublicAPI /// /// Log warning message /// - void LogWarn(string className, string message, [CallerMemberName] string methodName = ""); + void LogWarn(string className, string message, Exception e = null, [CallerMemberName] string methodName = ""); /// /// Log an Exception. Will throw if in debug mode so developer will be aware, diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 7b808dd5499..42ec9ee7fbd 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -47,12 +47,12 @@ public static void Main() private async void OnStartupAsync(object sender, StartupEventArgs e) { - await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => + await Stopwatch.NormalAsync(nameof(App),"Startup cost", async () => { _portable.PreStartCleanUpAfterPortabilityUpdate(); - Log.Info("|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------"); - Log.Info($"|App.OnStartup|Runtime info:{ErrorReporting.RuntimeInfo()}"); + Log.Info(nameof(App), "Begin Flow Launcher startup ----------------------------------------------------"); + Log.Info(nameof(App), $"Runtime info:{ErrorReporting.RuntimeInfo()}"); RegisterAppDomainExceptions(); RegisterDispatcherUnhandledException(); @@ -72,11 +72,11 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => Http.API = API; Http.Proxy = _settings.Proxy; - + await PluginManager.InitializePlugins(API); var window = new MainWindow(_settings, _mainVM); - Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}"); + Log.Info(nameof(App), $"Dependencies Info:{ErrorReporting.DependenciesInfo()}"); Current.MainWindow = window; Current.MainWindow.Title = Constant.FlowLauncher; @@ -97,7 +97,7 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => AutoUpdates(); _mainVM.MainWindowVisibility = _settings.HideOnStartup ? Visibility.Hidden : Visibility.Visible; - Log.Info("|App.OnStartup|End Flow Launcher startup ---------------------------------------------------- "); + Log.Info(nameof(App), "End Flow Launcher startup ---------------------------------------------------- "); }); } @@ -122,10 +122,7 @@ private void AutoUpdates() { // check udpate every 5 hours var timer = new Timer(1000 * 60 * 60 * 5); - timer.Elapsed += async (s, e) => - { - await _updater.UpdateApp(API); - }; + timer.Elapsed += async (s, e) => { await _updater.UpdateApp(API); }; timer.Start(); // check updates on startup diff --git a/Flow.Launcher/Helper/ErrorReporting.cs b/Flow.Launcher/Helper/ErrorReporting.cs index 94e2ed2bc65..f3f590167b1 100644 --- a/Flow.Launcher/Helper/ErrorReporting.cs +++ b/Flow.Launcher/Helper/ErrorReporting.cs @@ -3,6 +3,8 @@ using NLog; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Exception; +using NLog.Fluent; +using Log = Flow.Launcher.Infrastructure.Logger.Log; namespace Flow.Launcher.Helper { @@ -45,4 +47,4 @@ public static string DependenciesInfo() return info; } } -} +} \ No newline at end of file diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index f3403696e05..30cdfac8c87 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -139,8 +139,8 @@ public void LogDebug(string className, string message, [CallerMemberName] string public void LogInfo(string className, string message, [CallerMemberName] string methodName = "") => Log.Info(className, message, methodName); - public void LogWarn(string className, string message, [CallerMemberName] string methodName = "") => - Log.Warn(className, message, methodName); + public void LogWarn(string className, string message, Exception e = null, [CallerMemberName] string methodName = "") => + Log.Warn(className, message, methodName, e); public void LogException(string className, string message, Exception e, [CallerMemberName] string methodName = "") => Log.Exception(className, message, e, methodName); diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 4f6d3be84d3..d132829480a 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -19,8 +19,10 @@ using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.Storage; using Flow.Launcher.Infrastructure.Logger; +using Microsoft.VisualStudio.Threading; using System.Threading.Channels; using ISavable = Flow.Launcher.Plugin.ISavable; +using System.Windows.Threading; namespace Flow.Launcher.ViewModel { @@ -88,7 +90,7 @@ private void RegisterViewUpdate() var resultUpdateChannel = Channel.CreateUnbounded(); _resultsUpdateChannelWriter = resultUpdateChannel.Writer; _resultsViewUpdateTask = - Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted); + Task.Run(updateAction).ContinueWith(ContinueAction, TaskContinuationOptions.OnlyOnFaulted); async Task updateAction() { @@ -110,16 +112,19 @@ async Task updateAction() } Log.Error("MainViewModel", "Unexpected ResultViewUpdate ends"); - }; + } - void continueAction(Task t) + ; + + void ContinueAction(Task t) { #if DEBUG throw t.Exception; #else - Log.Error($"Error happen in task dealing with viewupdate for results. {t.Exception}"); - _resultsViewUpdateTask = - Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted); + Log.Error(nameof(MainViewModel), + $"Error happen in task dealing with viewupdate for results. {t.Exception}"); + _resultsViewUpdateTask = + Task.Run(updateAction).ContinueWith(ContinueAction, TaskContinuationOptions.OnlyOnFaulted); #endif } } @@ -128,16 +133,19 @@ private void RegisterResultsUpdatedEvent() { foreach (var pair in PluginManager.GetPluginsForInterface()) { - var plugin = (IResultUpdated)pair.Plugin; + var plugin = (IResultUpdated) pair.Plugin; plugin.ResultsUpdated += (s, e) => { if (e.Query.RawQuery == QueryText) // TODO: allow cancellation { PluginManager.UpdatePluginMetadata(e.Results, pair.Metadata, e.Query); - if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, _updateToken))) + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, + e.Query, _updateToken))) { Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); - }; + } + + ; } }; } @@ -234,21 +242,24 @@ private void InitializeKeyCommands() ReloadPluginDataCommand = new RelayCommand(_ => { - var msg = new Msg { Owner = Application.Current.MainWindow }; + var msg = new Msg + { + Owner = Application.Current.MainWindow + }; MainWindowVisibility = Visibility.Collapsed; PluginManager - .ReloadData() - .ContinueWith(_ => - Application.Current.Dispatcher.Invoke(() => - { - msg.Show( - InternationalizationManager.Instance.GetTranslation("success"), - InternationalizationManager.Instance.GetTranslation("completedSuccessfully"), - ""); - })) - .ConfigureAwait(false); + .ReloadData() + .ContinueWith(_ => + Application.Current.Dispatcher.Invoke(() => + { + msg.Show( + InternationalizationManager.Instance.GetTranslation("success"), + InternationalizationManager.Instance.GetTranslation("completedSuccessfully"), + ""); + })) + .ConfigureAwait(false); }); } @@ -392,7 +403,6 @@ private void QueryContextMenu() r.Score = match.Score; return true; - }).ToList(); ContextMenu.AddResults(filtered, id); } @@ -419,7 +429,10 @@ private void QueryHistory() Title = string.Format(title, h.Query), SubTitle = string.Format(time, h.ExecutedDateTime), IcoPath = "Images\\history.png", - OriginQuery = new Query { RawQuery = h.Query }, + OriginQuery = new Query + { + RawQuery = h.Query + }, Action = _ => { SelectedResults = Results; @@ -445,7 +458,9 @@ private void QueryHistory() } } - private void QueryResults() + private readonly IReadOnlyList _emptyResult = new List(); + + private async void QueryResults() { _updateSource?.Cancel(); @@ -466,6 +481,12 @@ private void QueryResults() ProgressBarVisibility = Visibility.Hidden; _isQueryRunning = true; + // Switch to ThreadPool thread + await TaskScheduler.Default; + + if (currentCancellationToken.IsCancellationRequested) + return; + var query = QueryBuilder.Build(QueryText.Trim(), PluginManager.NonGlobalPlugins); // handle the exclusiveness of plugin using action keyword @@ -475,74 +496,75 @@ private void QueryResults() var plugins = PluginManager.ValidPluginsForQuery(query); - Task.Run(async () => + if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign) + { + // Wait 45 millisecond for query change in global query + // if query changes, return so that it won't be calculated + await Task.Delay(45, currentCancellationToken); + if (currentCancellationToken.IsCancellationRequested) + return; + } + + _ = Task.Delay(200, currentCancellationToken).ContinueWith(_ => + { + // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet + if (!currentCancellationToken.IsCancellationRequested && _isQueryRunning) { - if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign) - { - // Wait 45 millisecond for query change in global query - // if query changes, return so that it won't be calculated - await Task.Delay(45, currentCancellationToken); - if (currentCancellationToken.IsCancellationRequested) - return; - } + ProgressBarVisibility = Visibility.Visible; + } + }, currentCancellationToken, TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default); - _ = Task.Delay(200, currentCancellationToken).ContinueWith(_ => - { - // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet - if (!currentCancellationToken.IsCancellationRequested && _isQueryRunning) - { - ProgressBarVisibility = Visibility.Visible; - } - }, currentCancellationToken); + // plugins is ICollection, meaning LINQ will get the Count and preallocate Array - // plugins is ICollection, meaning LINQ will get the Count and preallocate Array + var tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch + { + false => QueryTask(plugin), + true => Task.CompletedTask + }).ToArray(); - Task[] tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch - { - false => QueryTask(plugin), - true => Task.CompletedTask - }).ToArray(); - try - { - // Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first - await Task.WhenAll(tasks); - } - catch (OperationCanceledException) - { - // nothing to do here - } + try + { + // Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first + await Task.WhenAll(tasks); + } + catch (OperationCanceledException) + { + // nothing to do here + } - if (currentCancellationToken.IsCancellationRequested) - return; + if (currentCancellationToken.IsCancellationRequested) + return; - // this should happen once after all queries are done so progress bar should continue - // until the end of all querying - _isQueryRunning = false; - if (!currentCancellationToken.IsCancellationRequested) - { - // update to hidden if this is still the current query - ProgressBarVisibility = Visibility.Hidden; - } + // this should happen once after all queries are done so progress bar should continue + // until the end of all querying + _isQueryRunning = false; + if (!currentCancellationToken.IsCancellationRequested) + { + // update to hidden if this is still the current query + ProgressBarVisibility = Visibility.Hidden; + } - // Local function - async Task QueryTask(PluginPair plugin) - { - // Since it is wrapped within a Task.Run, the synchronous context is null - // Task.Yield will force it to run in ThreadPool - await Task.Yield(); + // Local function + async Task QueryTask(PluginPair plugin) + { + // Since it is wrapped within a ThreadPool Thread, the synchronous context is null + // Task.Yield will force it to run in ThreadPool + await Task.Yield(); - var results = await PluginManager.QueryForPlugin(plugin, query, currentCancellationToken); - if (currentCancellationToken.IsCancellationRequested || results == null) return; + IReadOnlyList results = + await PluginManager.QueryForPluginAsync(plugin, query, currentCancellationToken); - if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, currentCancellationToken))) - { - Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); - }; - } - }, currentCancellationToken) - .ContinueWith(t => Log.Exception("|MainViewModel|Plugins Query Exceptions", t.Exception), - TaskContinuationOptions.OnlyOnFaulted); + currentCancellationToken.ThrowIfCancellationRequested(); + + results ??= _emptyResult; + + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, + currentCancellationToken))) + { + Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); + } + } } diff --git a/Flow.Launcher/ViewModel/ResultViewModel.cs b/Flow.Launcher/ViewModel/ResultViewModel.cs index c91bbb1074f..78193180dfd 100644 --- a/Flow.Launcher/ViewModel/ResultViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultViewModel.cs @@ -98,7 +98,7 @@ private async ValueTask SetImage() } catch (Exception e) { - Log.Exception($"|ResultViewModel.Image|IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e); + Log.Exception(nameof(ResultViewModel),$"IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e); return ImageLoader.DefaultImage; } } diff --git a/Flow.Launcher/ViewModel/ResultsForUpdate.cs b/Flow.Launcher/ViewModel/ResultsForUpdate.cs index 87d526fd6d8..94c6a923aa5 100644 --- a/Flow.Launcher/ViewModel/ResultsForUpdate.cs +++ b/Flow.Launcher/ViewModel/ResultsForUpdate.cs @@ -8,7 +8,7 @@ namespace Flow.Launcher.ViewModel { public struct ResultsForUpdate { - public List Results { get; } + public IReadOnlyList Results { get; } public PluginMetadata Metadata { get; } public string ID { get; } @@ -16,7 +16,7 @@ public struct ResultsForUpdate public Query Query { get; } public CancellationToken Token { get; } - public ResultsForUpdate(List results, PluginMetadata metadata, Query query, CancellationToken token) + public ResultsForUpdate(IReadOnlyList results, PluginMetadata metadata, Query query, CancellationToken token) { Results = results; Metadata = metadata; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs index c6bf7bcacaf..08eb64ded1b 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs @@ -9,6 +9,7 @@ using Flow.Launcher.Plugin.Explorer.Search; using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks; using System.Linq; +using System.Runtime.CompilerServices; using MessageBox = System.Windows.Forms.MessageBox; using MessageBoxIcon = System.Windows.Forms.MessageBoxIcon; using MessageBoxButton = System.Windows.Forms.MessageBoxButtons; @@ -48,9 +49,12 @@ public List LoadContextMenus(Result selectedResult) contextMenus.Add(CreateOpenWindowsIndexingOptions()); if (record.ShowIndexState) - contextMenus.Add(new Result {Title = "From index search: " + (record.WindowsIndexed ? "Yes" : "No"), - SubTitle = "Location: " + record.FullPath, - Score = 501, IcoPath = Constants.IndexImagePath}); + contextMenus.Add(new Result + { + Title = "From index search: " + (record.WindowsIndexed ? "Yes" : "No"), + SubTitle = "Location: " + record.FullPath, + Score = 501, IcoPath = Constants.IndexImagePath + }); var icoPath = (record.Type == ResultType.File) ? Constants.FileImagePath : Constants.FolderImagePath; var fileOrFolder = (record.Type == ResultType.File) ? "file" : "folder"; @@ -60,16 +64,17 @@ public List LoadContextMenus(Result selectedResult) contextMenus.Add(new Result { Title = Context.API.GetTranslation("plugin_explorer_add_to_quickaccess_title"), - SubTitle = string.Format(Context.API.GetTranslation("plugin_explorer_add_to_quickaccess_subtitle"), fileOrFolder), + SubTitle = string.Format( + Context.API.GetTranslation("plugin_explorer_add_to_quickaccess_subtitle"), fileOrFolder), Action = (context) => { - Settings.QuickAccessLinks.Add(new AccessLink { Path = record.FullPath, Type = record.Type }); + Settings.QuickAccessLinks.Add(new AccessLink {Path = record.FullPath, Type = record.Type}); Context.API.ShowMsg(Context.API.GetTranslation("plugin_explorer_addfilefoldersuccess"), - string.Format( - Context.API.GetTranslation("plugin_explorer_addfilefoldersuccess_detail"), - fileOrFolder), - Constants.ExplorerIconImageFullPath); + string.Format( + Context.API.GetTranslation("plugin_explorer_addfilefoldersuccess_detail"), + fileOrFolder), + Constants.ExplorerIconImageFullPath); ViewModel.Save(); @@ -85,16 +90,19 @@ public List LoadContextMenus(Result selectedResult) contextMenus.Add(new Result { Title = Context.API.GetTranslation("plugin_explorer_remove_from_quickaccess_title"), - SubTitle = string.Format(Context.API.GetTranslation("plugin_explorer_remove_from_quickaccess_subtitle"), fileOrFolder), + SubTitle = string.Format( + Context.API.GetTranslation("plugin_explorer_remove_from_quickaccess_subtitle"), + fileOrFolder), Action = (context) => { - Settings.QuickAccessLinks.Remove(Settings.QuickAccessLinks.FirstOrDefault(x => x.Path == record.FullPath)); + Settings.QuickAccessLinks.Remove( + Settings.QuickAccessLinks.FirstOrDefault(x => x.Path == record.FullPath)); Context.API.ShowMsg(Context.API.GetTranslation("plugin_explorer_removefilefoldersuccess"), - string.Format( - Context.API.GetTranslation("plugin_explorer_removefilefoldersuccess_detail"), - fileOrFolder), - Constants.ExplorerIconImageFullPath); + string.Format( + Context.API.GetTranslation("plugin_explorer_removefilefoldersuccess_detail"), + fileOrFolder), + Constants.ExplorerIconImageFullPath); ViewModel.Save(); @@ -105,7 +113,7 @@ public List LoadContextMenus(Result selectedResult) IcoPath = Constants.RemoveQuickAccessImagePath }); } - + contextMenus.Add(new Result { Title = Context.API.GetTranslation("plugin_explorer_copypath"), @@ -136,7 +144,8 @@ public List LoadContextMenus(Result selectedResult) { try { - Clipboard.SetFileDropList(new System.Collections.Specialized.StringCollection { record.FullPath }); + Clipboard.SetFileDropList(new System.Collections.Specialized.StringCollection + {record.FullPath}); return true; } catch (Exception e) @@ -146,7 +155,6 @@ public List LoadContextMenus(Result selectedResult) Context.API.ShowMsg(message); return false; } - }, IcoPath = icoPath }); @@ -155,16 +163,19 @@ public List LoadContextMenus(Result selectedResult) contextMenus.Add(new Result { Title = Context.API.GetTranslation("plugin_explorer_deletefilefolder") + $" {fileOrFolder}", - SubTitle = Context.API.GetTranslation("plugin_explorer_deletefilefolder_subtitle") + $" {fileOrFolder}", + SubTitle = Context.API.GetTranslation("plugin_explorer_deletefilefolder_subtitle") + + $" {fileOrFolder}", Action = (context) => { try { if (MessageBox.Show( - string.Format(Context.API.GetTranslation("plugin_explorer_deletefilefolderconfirm"),fileOrFolder), - string.Empty, - MessageBoxButton.YesNo, - MessageBoxIcon.Warning) + string.Format( + Context.API.GetTranslation("plugin_explorer_deletefilefolderconfirm"), + fileOrFolder), + string.Empty, + MessageBoxButton.YesNo, + MessageBoxIcon.Warning) == DialogResult.No) return false; @@ -175,9 +186,12 @@ public List LoadContextMenus(Result selectedResult) Task.Run(() => { - Context.API.ShowMsg(Context.API.GetTranslation("plugin_explorer_deletefilefoldersuccess"), - string.Format(Context.API.GetTranslation("plugin_explorer_deletefilefoldersuccess_detail"), fileOrFolder), - Constants.ExplorerIconImageFullPath); + Context.API.ShowMsg( + Context.API.GetTranslation("plugin_explorer_deletefilefoldersuccess"), + string.Format( + Context.API.GetTranslation( + "plugin_explorer_deletefilefoldersuccess_detail"), fileOrFolder), + Constants.ExplorerIconImageFullPath); }); } catch (Exception e) @@ -250,8 +264,8 @@ private Result CreateOpenWithEditorResult(SearchResult record) { string editorPath = "Notepad.exe"; // TODO add the ability to create a custom editor - var name = Context.API.GetTranslation("plugin_explorer_openwitheditor") - + " " + Path.GetFileNameWithoutExtension(editorPath); + var name = Context.API.GetTranslation("plugin_explorer_openwitheditor") + + " " + Path.GetFileNameWithoutExtension(editorPath); return new Result { @@ -283,14 +297,14 @@ private Result CreateAddToIndexSearchExclusionListResult(SearchResult record) SubTitle = Context.API.GetTranslation("plugin_explorer_path") + " " + record.FullPath, Action = _ => { - if(!Settings.IndexSearchExcludedSubdirectoryPaths.Any(x => x.Path == record.FullPath)) - Settings.IndexSearchExcludedSubdirectoryPaths.Add(new AccessLink { Path = record.FullPath }); + if (!Settings.IndexSearchExcludedSubdirectoryPaths.Any(x => x.Path == record.FullPath)) + Settings.IndexSearchExcludedSubdirectoryPaths.Add(new AccessLink {Path = record.FullPath}); Task.Run(() => { - Context.API.ShowMsg(Context.API.GetTranslation("plugin_explorer_excludedfromindexsearch_msg"), - Context.API.GetTranslation("plugin_explorer_path") + - " " + record.FullPath, Constants.ExplorerIconImageFullPath); + Context.API.ShowMsg(Context.API.GetTranslation("plugin_explorer_excludedfromindexsearch_msg"), + Context.API.GetTranslation("plugin_explorer_path") + + " " + record.FullPath, Constants.ExplorerIconImageFullPath); // so the new path can be persisted to storage and not wait till next ViewModel save. Context.API.SaveAppAllSettings(); @@ -313,11 +327,11 @@ private Result CreateOpenWindowsIndexingOptions() try { var psi = new ProcessStartInfo - { - FileName = "control.exe", - UseShellExecute = true, - Arguments = "srchadmin.dll" - }; + { + FileName = "control.exe", + UseShellExecute = true, + Arguments = "srchadmin.dll" + }; Process.Start(psi); return true; @@ -334,9 +348,9 @@ private Result CreateOpenWindowsIndexingOptions() }; } - public void LogException(string message, Exception e) + public void LogException(string message, Exception e, [CallerMemberName] string methodName = "") { - Log.Exception($"|Flow.Launcher.Plugin.Folder.ContextMenu|{message}", e); + Log.Exception("Flow.Launcher.Plugin.Explorer", message, e, methodName); } private bool CanRunAsDifferentUser(string path) @@ -350,8 +364,7 @@ private bool CanRunAsDifferentUser(string path) default: return false; - } } } -} +} \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs index 14833bae9b1..14c90d57f42 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/DirectoryInfoSearch.cs @@ -21,7 +21,8 @@ internal static List TopLevelDirectorySearch(Query query, string search, RecurseSubdirectories = true }, query, search, criteria, token); - return DirectorySearch(new EnumerationOptions(), query, search, criteria, token); // null will be passed as default + return DirectorySearch(new EnumerationOptions(), query, search, criteria, + token); // null will be passed as default } public static string ConstructSearchCriteria(string search) @@ -57,7 +58,8 @@ private static List DirectorySearch(EnumerationOptions enumerationOption { var directoryInfo = new System.IO.DirectoryInfo(path); - foreach (var fileSystemInfo in directoryInfo.EnumerateFileSystemInfos(searchCriteria, enumerationOption)) + foreach (var fileSystemInfo in directoryInfo.EnumerateFileSystemInfos(searchCriteria, enumerationOption) + ) { if (fileSystemInfo is System.IO.DirectoryInfo) { @@ -74,17 +76,10 @@ private static List DirectorySearch(EnumerationOptions enumerationOption } catch (Exception e) { - if (!(e is ArgumentException)) - throw e; - + Log.Exception("Flow.Plugin.Explorer.", nameof(DirectoryInfoSearch), e); results.Add(new Result {Title = e.Message, Score = 501}); return results; - -#if DEBUG // Please investigate and handle error from DirectoryInfo search -#else - Log.Exception($"|Flow.Launcher.Plugin.Explorer.DirectoryInfoSearch|Error from performing DirectoryInfoSearch", e); -#endif } // Initial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection. diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs index 0748b0cfef6..3aa520f55e7 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/IndexSearch.cs @@ -13,24 +13,24 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex { internal static class IndexSearch { - // Reserved keywords in oleDB private const string reservedStringPattern = @"^[`\@\#\^,\&\/\\\$\%_]+$"; - internal async static Task> ExecuteWindowsIndexSearchAsync(string indexQueryString, string connectionString, Query query, CancellationToken token) + internal static async Task> ExecuteWindowsIndexSearchAsync(string indexQueryString, + string connectionString, Query query, CancellationToken token) { var results = new List(); var fileResults = new List(); try { - using var conn = new OleDbConnection(connectionString); + await using var conn = new OleDbConnection(connectionString); await conn.OpenAsync(token); token.ThrowIfCancellationRequested(); - using var command = new OleDbCommand(indexQueryString, conn); + await using var command = new OleDbCommand(indexQueryString, conn); // Results return as an OleDbDataReader. - using var dataReaderResults = await command.ExecuteReaderAsync(token) as OleDbDataReader; + await using var dataReaderResults = await command.ExecuteReaderAsync(token) as OleDbDataReader; token.ThrowIfCancellationRequested(); if (dataReaderResults.HasRows) @@ -38,22 +38,23 @@ internal async static Task> ExecuteWindowsIndexSearchAsync(string i while (await dataReaderResults.ReadAsync(token)) { token.ThrowIfCancellationRequested(); - if (dataReaderResults.GetValue(0) != DBNull.Value && dataReaderResults.GetValue(1) != DBNull.Value) + if (dataReaderResults.GetValue(0) != DBNull.Value && + dataReaderResults.GetValue(1) != DBNull.Value) { // # is URI syntax for the fragment component, need to be encoded so LocalPath returns complete path var encodedFragmentPath = dataReaderResults - .GetString(1) - .Replace("#", "%23", StringComparison.OrdinalIgnoreCase); + .GetString(1) + .Replace("#", "%23", StringComparison.OrdinalIgnoreCase); var path = new Uri(encodedFragmentPath).LocalPath; if (dataReaderResults.GetString(2) == "Directory") { results.Add(ResultManager.CreateFolderResult( - dataReaderResults.GetString(0), - path, - path, - query, 0, true, true)); + dataReaderResults.GetString(0), + path, + path, + query, 0, true, true)); } else { @@ -63,6 +64,11 @@ internal async static Task> ExecuteWindowsIndexSearchAsync(string i } } } + catch (OperationCanceledException) + { + // return empty result when cancelled + return results; + } catch (InvalidOperationException e) { // Internal error from ExecuteReader(): Connection closed. @@ -76,14 +82,14 @@ internal async static Task> ExecuteWindowsIndexSearchAsync(string i results.AddRange(fileResults); // Intial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection. - return results; + return results; } internal async static Task> WindowsIndexSearchAsync(string searchString, string connectionString, - Func constructQuery, - List exclusionList, - Query query, - CancellationToken token) + Func constructQuery, + List exclusionList, + Query query, + CancellationToken token) { var regexMatch = Regex.Match(searchString, reservedStringPattern); @@ -92,12 +98,14 @@ internal async static Task> WindowsIndexSearchAsync(string searchSt var constructedQuery = constructQuery(searchString); return RemoveResultsInExclusionList( - await ExecuteWindowsIndexSearchAsync(constructedQuery, connectionString, query, token).ConfigureAwait(false), - exclusionList, - token); + await ExecuteWindowsIndexSearchAsync(constructedQuery, connectionString, query, token) + .ConfigureAwait(false), + exclusionList, + token); } - private static List RemoveResultsInExclusionList(List results, List exclusionList, CancellationToken token) + private static List RemoveResultsInExclusionList(List results, List exclusionList, + CancellationToken token) { var indexExclusionListCount = exclusionList.Count; @@ -142,8 +150,8 @@ private static void LogException(string message, Exception e) #if DEBUG // Please investigate and handle error from index search throw e; #else - Log.Exception($"|Flow.Launcher.Plugin.Explorer.IndexSearch|{message}", e); -#endif + Log.Exception("Flow.Launcher.Plugin.Explorer." + nameof(IndexSearch), message, e); +#endif } } -} +} \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs index 145aadc986a..07af3a1aa98 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Models/PluginsManifest.cs @@ -15,14 +15,17 @@ internal async Task DownloadManifest() { try { - await using var jsonStream = await Http.GetStreamAsync("https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/main/plugins.json") - .ConfigureAwait(false); + await using var jsonStream = await Http + .GetStreamAsync( + "https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/main/plugins.json") + .ConfigureAwait(false); UserPlugins = await JsonSerializer.DeserializeAsync>(jsonStream).ConfigureAwait(false); } catch (Exception e) { - Log.Exception("|PluginManagement.GetManifest|Encountered error trying to download plugins manifest", e); + Log.Exception("Flow.Plugin.PluginsManager." + nameof(PluginsManifest), + "Encountered error trying to download plugins manifest", e); UserPlugins = new List(); } diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs index 16a8687e6be..00dfb210698 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs @@ -79,7 +79,7 @@ public void TryKill(Process p) } catch (Exception e) { - Log.Exception($"|ProcessKiller.CreateResultsFromProcesses|Failed to kill process {p.ProcessName}", e); + Log.Exception("Flow.Launcher.Plugin.ProcessKiller",$"Failed to kill process {p.ProcessName}", e); } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Logger/ProgramExceptionLogger.cs b/Plugins/Flow.Launcher.Plugin.Program/Logger/ProgramExceptionLogger.cs new file mode 100644 index 00000000000..4f794f24812 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Program/Logger/ProgramExceptionLogger.cs @@ -0,0 +1,69 @@ +using NLog; +using NLog.Config; +using NLog.Targets; +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Security; +using Flow.Launcher.Infrastructure; +using Flow.Launcher.Infrastructure.UserSettings; +using System.Text; + +namespace Flow.Launcher.Plugin.Program.Logger +{ + /// + /// The Program plugin has seen many issues recorded in the Flow Launcher repo related to various loading of Windows programs. + /// This is a dedicated logger for this Program plugin with the aim to output a more friendlier message and clearer + /// log that will allow debugging to be quicker and easier. + /// + internal static class ProgramExceptionLogger + { + internal static IPublicAPI Api { private get; set; } + + /// + /// Logs an exception + /// + internal static void LogException(string classname, string loadingProgramPath, + string interpretationMessage, Exception e, [CallerMemberName] string callingMethodName = "") + { + var possibleResolution = "Possible Resolution: Not yet known"; + + bool known = false; + + StringBuilder messageBuilder = new StringBuilder(); + + messageBuilder.AppendLine(interpretationMessage); + + if (IsKnownWinProgramError(e, callingMethodName) || IsKnownUWPProgramError(e, callingMethodName)) + { + possibleResolution = "Possible Resolution: Can be ignored and Flow Launcher should still continue, however the program may not be loaded"; + known = true; + } + + messageBuilder.AppendLine(possibleResolution); + messageBuilder.Append($"Program Path: {loadingProgramPath}"); + + if (known) + Api.LogWarn($"Flow.Plugin.Program.{classname}", messageBuilder.ToString(), e, callingMethodName); + else + Api.LogException($"Flow.Plugin.Program.{classname}", messageBuilder.ToString(), e, callingMethodName); + + } + + private static bool IsKnownWinProgramError(Exception e, string callingMethodName) => e switch + { + { TargetSite: { Name: "GetDescription" } } when callingMethodName is "LnkProgram" => true, + SecurityException or UnauthorizedAccessException or DirectoryNotFoundException => true, + _ => false, + }; + + private static bool IsKnownUWPProgramError(Exception e, string callingMethodName) => e.HResult switch + { + -2147024774 or -2147009769 => callingMethodName == "ResourceFromPri", + -2147024894 => callingMethodName is "LogoPathFromUri" or "ImageFromPath", + -2147024864 => callingMethodName == "InitializeAppInfo", + _ => callingMethodName is "XmlNamespaces" or "InitPackageVersion" + }; + } +} \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Program/Logger/ProgramLogger.cs b/Plugins/Flow.Launcher.Plugin.Program/Logger/ProgramLogger.cs deleted file mode 100644 index 06264c06c6d..00000000000 --- a/Plugins/Flow.Launcher.Plugin.Program/Logger/ProgramLogger.cs +++ /dev/null @@ -1,140 +0,0 @@ -using NLog; -using NLog.Config; -using NLog.Targets; -using System; -using System.Diagnostics; -using System.IO; -using System.Runtime.CompilerServices; -using System.Security; -using Flow.Launcher.Infrastructure; -using Flow.Launcher.Infrastructure.UserSettings; - -namespace Flow.Launcher.Plugin.Program.Logger -{ - /// - /// The Program plugin has seen many issues recorded in the Flow Launcher repo related to various loading of Windows programs. - /// This is a dedicated logger for this Program plugin with the aim to output a more friendlier message and clearer - /// log that will allow debugging to be quicker and easier. - /// - internal static class ProgramLogger - { - public const string DirectoryName = "Logs"; - - static ProgramLogger() - { - var path = Path.Combine(DataLocation.DataDirectory(), DirectoryName, Constant.Version); - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path); - } - - var configuration = new LoggingConfiguration(); - var target = new FileTarget(); - configuration.AddTarget("file", target); - target.FileName = path.Replace(@"\", "/") + "/${shortdate}.txt"; -#if DEBUG - var rule = new LoggingRule("*", LogLevel.Debug, target); -#else - var rule = new LoggingRule("*", LogLevel.Error, target); -#endif - configuration.LoggingRules.Add(rule); - LogManager.Configuration = configuration; - } - - /// - /// Logs an exception - /// - [MethodImpl(MethodImplOptions.Synchronized)] - internal static void LogException(string classname, string callingMethodName, string loadingProgramPath, - string interpretationMessage, Exception e) - { - Debug.WriteLine($"ERROR{classname}|{callingMethodName}|{loadingProgramPath}|{interpretationMessage}"); - - var logger = LogManager.GetLogger(""); - - var innerExceptionNumber = 1; - - var possibleResolution = "Not yet known"; - var errorStatus = "UNKNOWN"; - - logger.Error("------------- BEGIN Flow.Launcher.Plugin.Program exception -------------"); - - do - { - if (IsKnownWinProgramError(e, callingMethodName) || IsKnownUWPProgramError(e, callingMethodName)) - { - possibleResolution = "Can be ignored and Flow Launcher should still continue, however the program may not be loaded"; - errorStatus = "KNOWN"; - } - - var calledMethod = e.TargetSite != null ? e.TargetSite.ToString() : e.StackTrace; - - calledMethod = string.IsNullOrEmpty(calledMethod) ? "Not available" : calledMethod; - - logger.Error($"\nException full name: {e.GetType().FullName}" - + $"\nError status: {errorStatus}" - + $"\nClass name: {classname}" - + $"\nCalling method: {callingMethodName}" - + $"\nProgram path: {loadingProgramPath}" - + $"\nInnerException number: {innerExceptionNumber}" - + $"\nException message: {e.Message}" - + $"\nException error type: HResult {e.HResult}" - + $"\nException thrown in called method: {calledMethod}" - + $"\nPossible interpretation of the error: {interpretationMessage}" - + $"\nPossible resolution: {possibleResolution}"); - - innerExceptionNumber++; - e = e.InnerException; - } while (e != null); - - logger.Error("------------- END Flow.Launcher.Plugin.Program exception -------------"); - } - - /// - /// Please follow exception format: |class name|calling method name|loading program path|user friendly message that explains the error - /// => Example: |Win32|LnkProgram|c:\..\chrome.exe|Permission denied on directory, but Flow Launcher should continue - /// - [MethodImpl(MethodImplOptions.Synchronized)] - internal static void LogException(string message, Exception e) - { - //Index 0 is always empty. - var parts = message.Split('|', StringSplitOptions.RemoveEmptyEntries); - if (parts.Length < 4) - { - var logger = LogManager.GetLogger(""); - logger.Error(e, $"fail to log exception in program logger, parts length is too small: {parts.Length}, message: {message}"); - } - - var classname = parts[0]; - var callingMethodName = parts[1]; - var loadingProgramPath = parts[2]; - var interpretationMessage = parts[3]; - - LogException(classname, callingMethodName, loadingProgramPath, interpretationMessage, e); - } - - private static bool IsKnownWinProgramError(Exception e, string callingMethodName) - { - if (e.TargetSite?.Name == "GetDescription" && callingMethodName == "LnkProgram") - return true; - - if (e is SecurityException || e is UnauthorizedAccessException || e is DirectoryNotFoundException) - return true; - - return false; - } - - private static bool IsKnownUWPProgramError(Exception e, string callingMethodName) - { - if (((e.HResult == -2147024774 || e.HResult == -2147009769) && callingMethodName == "ResourceFromPri") - || (e.HResult == -2147024894 && (callingMethodName == "LogoPathFromUri" || callingMethodName == "ImageFromPath")) - || (e.HResult == -2147024864 && callingMethodName == "InitializeAppInfo")) - return true; - - if (callingMethodName == "XmlNamespaces") - return true; - - return false; - } - } -} \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 5175970fd41..d12410f8f49 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -7,6 +7,7 @@ using System.Windows.Controls; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.Storage; +using Flow.Launcher.Plugin.Program.Logger; using Flow.Launcher.Plugin.Program.Programs; using Flow.Launcher.Plugin.Program.Views; using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; @@ -71,17 +72,19 @@ public async Task InitAsync(PluginInitContext context) _settings = context.API.LoadSettingJsonStorage(); + ProgramExceptionLogger.Api = context.API; + await Task.Yield(); - Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Preload programs cost", () => + Stopwatch.Normal("Flow.Plugin.Program.Main","Preload programs cost", () => { _win32Storage = new BinaryStorage("Win32"); _win32s = _win32Storage.TryLoad(new Win32[] { }); _uwpStorage = new BinaryStorage("UWP"); _uwps = _uwpStorage.TryLoad(new UWP.Application[] { }); }); - Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Length}>"); - Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwps.Length}>"); + Log.Info("Flow.Plugin.Program.Main",$"Number of preload win32 programs <{_win32s.Length}>"); + Log.Info("Flow.Plugin.Program.Main",$"Number of preload uwps <{_uwps.Length}>"); bool indexedWinApps = false; @@ -91,7 +94,7 @@ public async Task InitAsync(PluginInitContext context) { if (IsStartupIndexProgramsRequired || !_win32s.Any()) { - Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Win32Program index cost", IndexWin32Programs); + Stopwatch.Normal("Flow.Plugin.Program","Win32Program index cost", IndexWin32Programs); indexedWinApps = true; } }); @@ -100,7 +103,7 @@ public async Task InitAsync(PluginInitContext context) { if (IsStartupIndexProgramsRequired || !_uwps.Any()) { - Stopwatch.Normal("|Flow.Launcher.Plugin.Program.Main|Win32Program index cost", IndexUwpPrograms); + Stopwatch.Normal("Flow.Plugin.Program","Win32Program index cost", IndexUwpPrograms); indexedUWPApps = true; } }); diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index e53fb7a5264..e167a4c466e 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -77,8 +77,7 @@ private void InitializeAppInfo() else { var e = Marshal.GetExceptionForHR((int)hResult); - ProgramLogger.LogException($"|UWP|InitializeAppInfo|{path}" + - "|Error caused while trying to get the details of the UWP program", e); + ProgramExceptionLogger.LogException(nameof(UWP), path, "Error caused while trying to get the details of the UWP program", e); Apps = new List().ToArray(); } @@ -109,8 +108,7 @@ private string[] XmlNamespaces(string path) } else { - ProgramLogger.LogException($"|UWP|XmlNamespaces|{path}" + - $"|Error occured while trying to get the XML from {path}", new ArgumentNullException()); + ProgramExceptionLogger.LogException(nameof(UWP), path, $"Error occured while trying to get the XML from {path}", new ArgumentNullException()); return new string[] { }; } @@ -134,9 +132,7 @@ private void InitPackageVersion(string[] namespaces) } } - ProgramLogger.LogException($"|UWP|XmlNamespaces|{Location}" + - "|Trying to get the package version of the UWP program, but a unknown UWP appmanifest version " - + $"{FullName} from location {Location} is returned.", new FormatException()); + ProgramExceptionLogger.LogException(nameof(UWP), Location, $"Trying to get the package version of the UWP program, but a unknown UWP appmanifest version {FullName} from location {Location} is returned.", new FormatException()); Version = PackageVersion.Unknown; } @@ -157,8 +153,7 @@ public static Application[] All() #if !DEBUG catch (Exception e) { - ProgramLogger.LogException($"|UWP|All|{p.InstalledLocation}|An unexpected error occured and " - + $"unable to convert Package to UWP for {p.Id.FullName}", e); + ProgramExceptionLogger.LogException(nameof(UWP), p.InstalledLocation.ToString(), "|An unexpected error occured and unable to convert Package to UWP for {p.Id.FullName}", e); return new Application[] { }; } #endif @@ -215,8 +210,7 @@ private static IEnumerable CurrentUserPackages() } catch (Exception e) { - ProgramLogger.LogException("UWP", "CurrentUserPackages", $"id", "An unexpected error occured and " - + $"unable to verify if package is valid", e); + ProgramExceptionLogger.LogException("UWP", id, "An unexpected error occured and unable to verify if package is valid", e); return false; } @@ -425,15 +419,14 @@ internal string ResourceFromPri(string packageFullName, string packageName, stri } else { - ProgramLogger.LogException($"|UWP|ResourceFromPri|{Package.Location}|Can't load null or empty result " - + $"pri {source} in uwp location {Package.Location}", new NullReferenceException()); + ProgramExceptionLogger.LogException(nameof(UWP), Package.Location, $"Can't load null or empty result pri {source} in uwp location {Package.Location}", new NullReferenceException()); return string.Empty; } } else { var e = Marshal.GetExceptionForHR((int)hResult); - ProgramLogger.LogException($"|UWP|ResourceFromPri|{Package.Location}|Load pri failed {source} with HResult {hResult} and location {Package.Location}", e); + ProgramExceptionLogger.LogException(nameof(UWP), Package.Location, $"Load pri failed {source} with HResult {hResult} and location {Package.Location}", e); return string.Empty; } } @@ -530,15 +523,13 @@ internal string LogoPathFromUri(string uri) } else { - ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Package.Location}" + - $"|{UserModelId} can't find logo uri for {uri} in package location: {Package.Location}", new FileNotFoundException()); + ProgramExceptionLogger.LogException(nameof(UWP), Package.Location, $"{UserModelId} can't find logo uri for {uri} in package location: {Package.Location}", new FileNotFoundException()); return string.Empty; } } else { - ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Package.Location}" + - $"|Unable to find extension from {uri} for {UserModelId} " + + ProgramExceptionLogger.LogException(nameof(UWP), Package.Location, $"Unable to find extension from {uri} for {UserModelId} " + $"in package location {Package.Location}", new FileNotFoundException()); return string.Empty; } @@ -565,9 +556,7 @@ private BitmapImage ImageFromPath(string path) } else { - ProgramLogger.LogException($"|UWP|ImageFromPath|{path}" + - $"|Unable to get logo for {UserModelId} from {path} and" + - $" located in {Package.Location}", new FileNotFoundException()); + ProgramExceptionLogger.LogException(nameof(UWP), path, $"Unable to get logo for {UserModelId} from {path} and located in {Package.Location}", new FileNotFoundException()); return new BitmapImage(new Uri(Constant.MissingImgIcon)); } } @@ -614,9 +603,7 @@ private ImageSource PlatedImage(BitmapImage image) } else { - ProgramLogger.LogException($"|UWP|PlatedImage|{Package.Location}" + - $"|Unable to convert background string {BackgroundColor} " + - $"to color for {Package.Location}", new InvalidOperationException()); + ProgramExceptionLogger.LogException(nameof(UWP), Package.Location, $"Unable to convert background string {BackgroundColor} to color for {Package.Location}", new InvalidOperationException()); return new BitmapImage(new Uri(Constant.MissingImgIcon)); } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 931cfacc16d..2be68090991 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -215,8 +215,7 @@ private static Win32 Win32Program(string path) } catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) { - ProgramLogger.LogException($"|Win32|Win32Program|{path}" + - $"|Permission denied when trying to load the program from {path}", e); + ProgramExceptionLogger.LogException(nameof(Win32), path, $"|Permission denied when trying to load the program from {path}", e); return new Win32() { Valid = false, Enabled = false }; } @@ -262,9 +261,7 @@ private static Win32 LnkProgram(string path) catch (COMException e) { // C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\MiracastView.lnk always cause exception - ProgramLogger.LogException($"|Win32|LnkProgram|{path}" + - "|Error caused likely due to trying to get the description of the program", - e); + ProgramExceptionLogger.LogException(nameof(Win32), path, "|Error caused likely due to trying to get the description of the program", e); program.Valid = false; return program; @@ -272,8 +269,7 @@ private static Win32 LnkProgram(string path) #if !DEBUG //Only do a catch all in production. This is so make developer aware of any unhandled exception and add the exception handling in. catch (Exception e) { - ProgramLogger.LogException($"|Win32|LnkProgram|{path}" + - "|An unexpected error occurred in the calling method LnkProgram", e); + ProgramExceptionLogger.LogException(nameof(Win32), path, "An unexpected error occurred in the calling method LnkProgram", e); program.Valid = false; return program; @@ -293,8 +289,7 @@ private static Win32 ExeProgram(string path) } catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) { - ProgramLogger.LogException($"|Win32|ExeProgram|{path}" + - $"|Permission denied when trying to load the program from {path}", e); + ProgramExceptionLogger.LogException(nameof(Win32), path, $"|Permission denied when trying to load the program from {path}", e); return new Win32() { Valid = false, Enabled = false }; } @@ -420,8 +415,7 @@ private static string GetProgramPathFromRegistrySubKeys(RegistryKey root, string } catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) { - ProgramLogger.LogException($"|Win32|GetProgramPathFromRegistrySubKeys|{path}" + - $"|Permission denied when trying to load the program from {path}", e); + ProgramExceptionLogger.LogException(nameof(Win32), path, $"|Permission denied when trying to load the program from {path}", e); return string.Empty; } @@ -528,7 +522,7 @@ public static Win32[] All(Settings settings) #if !DEBUG //Only do a catch all in production. catch (Exception e) { - ProgramLogger.LogException("|Win32|All|Not available|An unexpected error occurred", e); + ProgramExceptionLogger.LogException(nameof(Win32), "Not available", "An unexpected error occurred", e); return Array.Empty(); } diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs index 829398a4b8c..e38bdecc50a 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs @@ -61,11 +61,11 @@ public List Query(Query query) if (basedir != null) { - var autocomplete = Directory.GetFileSystemEntries(basedir). - Select(o => dir + Path.GetFileName(o)). - Where(o => o.StartsWith(cmd, StringComparison.OrdinalIgnoreCase) && - !results.Any(p => o.Equals(p.Title, StringComparison.OrdinalIgnoreCase)) && - !results.Any(p => o.Equals(p.Title, StringComparison.OrdinalIgnoreCase))).ToList(); + var autocomplete = Directory.GetFileSystemEntries(basedir) + .Select(o => dir + Path.GetFileName(o)).Where(o => + o.StartsWith(cmd, StringComparison.OrdinalIgnoreCase) && + !results.Any(p => o.Equals(p.Title, StringComparison.OrdinalIgnoreCase)) && + !results.Any(p => o.Equals(p.Title, StringComparison.OrdinalIgnoreCase))).ToList(); autocomplete.Sort(); results.AddRange(autocomplete.ConvertAll(m => new Result { @@ -81,8 +81,9 @@ public List Query(Query query) } catch (Exception e) { - Log.Exception($"|Flow.Launcher.Plugin.Shell.Main.Query|Exception when query for <{query}>", e); + Log.Exception("Flow.Launcher.Plugin.Shell.Main", $"Exception when query for <{query}>", e); } + return results; } } @@ -95,14 +96,18 @@ private List GetHistoryCmds(string cmd, Result result) { if (m.Key == cmd) { - result.SubTitle = string.Format(context.API.GetTranslation("flowlauncher_plugin_cmd_cmd_has_been_executed_times"), m.Value); + result.SubTitle = + string.Format( + context.API.GetTranslation("flowlauncher_plugin_cmd_cmd_has_been_executed_times"), + m.Value); return null; } var ret = new Result { Title = m.Key, - SubTitle = string.Format(context.API.GetTranslation("flowlauncher_plugin_cmd_cmd_has_been_executed_times"), m.Value), + SubTitle = string.Format( + context.API.GetTranslation("flowlauncher_plugin_cmd_cmd_has_been_executed_times"), m.Value), IcoPath = Image, Action = c => { @@ -143,7 +148,8 @@ private List ResultsFromlHistory() .Select(m => new Result { Title = m.Key, - SubTitle = string.Format(context.API.GetTranslation("flowlauncher_plugin_cmd_cmd_has_been_executed_times"), m.Value), + SubTitle = string.Format( + context.API.GetTranslation("flowlauncher_plugin_cmd_cmd_has_been_executed_times"), m.Value), IcoPath = Image, Action = c => { @@ -184,18 +190,20 @@ private ProcessStartInfo PrepareProcessStartInfo(string command, bool runAsAdmin arguments = $"\"{command} ; Read-Host -Prompt \\\"Press Enter to continue\\\"\""; } - info = ShellCommand.SetProcessStartInfo("powershell.exe", workingDirectory, arguments, runAsAdministratorArg); + info = ShellCommand.SetProcessStartInfo("powershell.exe", workingDirectory, arguments, + runAsAdministratorArg); } else if (_settings.Shell == Shell.RunCommand) { - var parts = command.Split(new[] { ' ' }, 2); + var parts = command.Split(new[] {' '}, 2); if (parts.Length == 2) { var filename = parts[0]; if (ExistInPath(filename)) { var arguments = parts[1]; - info = ShellCommand.SetProcessStartInfo(filename, workingDirectory, arguments, runAsAdministratorArg); + info = ShellCommand.SetProcessStartInfo(filename, workingDirectory, arguments, + runAsAdministratorArg); } else { @@ -259,6 +267,7 @@ private bool ExistInPath(string filename) return true; } } + return false; } else @@ -279,19 +288,21 @@ bool API_GlobalKeyboardEvent(int keyevent, int vkcode, SpecialKeyState state) { if (_settings.ReplaceWinR) { - if (keyevent == (int)KeyEvent.WM_KEYDOWN && vkcode == (int)Keys.R && state.WinPressed) + if (keyevent == (int) KeyEvent.WM_KEYDOWN && vkcode == (int) Keys.R && state.WinPressed) { _winRStroked = true; OnWinRPressed(); return false; } - if (keyevent == (int)KeyEvent.WM_KEYUP && _winRStroked && vkcode == (int)Keys.LWin) + + if (keyevent == (int) KeyEvent.WM_KEYUP && _winRStroked && vkcode == (int) Keys.LWin) { _winRStroked = false; _keyboardSimulator.ModifiedKeyStroke(VirtualKeyCode.LWIN, VirtualKeyCode.CONTROL); return false; } } + return true; } @@ -325,7 +336,8 @@ public List LoadContextMenus(Result selectedResult) Title = context.API.GetTranslation("flowlauncher_plugin_cmd_run_as_different_user"), Action = c => { - Task.Run(() =>Execute(ShellCommand.RunAsDifferentUser, PrepareProcessStartInfo(selectedResult.Title))); + Task.Run(() => Execute(ShellCommand.RunAsDifferentUser, + PrepareProcessStartInfo(selectedResult.Title))); return true; }, IcoPath = "Images/user.png" @@ -355,4 +367,4 @@ public List LoadContextMenus(Result selectedResult) return resultlist; } } -} +} \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs index ccb5b20d730..8349f03257d 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Baidu.cs @@ -27,7 +27,7 @@ public override async Task> Suggestions(string query, CancellationT } catch (Exception e) when (e is HttpRequestException || e.InnerException is TimeoutException) { - Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e); + Log.Exception("Flow.Plugin.WebSearch." + nameof(Baidu).Length,"Can't get suggestion from baidu", e); return null; } @@ -42,7 +42,7 @@ public override async Task> Suggestions(string query, CancellationT } catch (JsonException e) { - Log.Exception("|Baidu.Suggestions|can't parse suggestions", e); + Log.Exception("Flow.Plugin.WebSearch." + nameof(Baidu),"can't parse suggestions", e); return new List(); } diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs index 38c5fb4a0f8..adaa9fccd30 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Bing.cs @@ -42,12 +42,12 @@ public override async Task> Suggestions(string query, CancellationT } catch (Exception e) when (e is HttpRequestException || e.InnerException is TimeoutException) { - Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e); + Log.Exception("Flow.Plugin.WebSearch." + nameof(Bing),"Can't get suggestion from baidu", e); return null; } catch (JsonException e) { - Log.Exception("|Bing.Suggestions|can't parse suggestions", e); + Log.Exception("Flow.Plugin.WebSearch." + nameof(Bing),"can't parse suggestions", e); return new List(); } } diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs index c5f43d081be..633d9ffc9f7 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/Google.cs @@ -20,7 +20,8 @@ public override async Task> Suggestions(string query, CancellationT { const string api = "https://www.google.com/complete/search?output=chrome&q="; - using var resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); + using var resultStream = + await Http.GetStreamAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); using var json = await JsonDocument.ParseAsync(resultStream, cancellationToken: token); @@ -30,16 +31,15 @@ public override async Task> Suggestions(string query, CancellationT var results = json.RootElement.EnumerateArray().ElementAt(1); return results.EnumerateArray().Select(o => o.GetString()).ToList(); - } catch (Exception e) when (e is HttpRequestException || e.InnerException is TimeoutException) { - Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e); + Log.Exception("Flow.Plugin.WebSearch." + nameof(Google), "Can't get suggestion from baidu", e); return null; } catch (JsonException e) { - Log.Exception("|Google.Suggestions|can't parse suggestions", e); + Log.Exception("Flow.Plugin.WebSearch." + nameof(Google),"can't parse suggestions", e); return new List(); } }