diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 0ee268f5c..4c85fb061 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -23,6 +23,8 @@ namespace Flow.Launcher.Core.Plugin /// public static class PluginManager { + private static readonly string ClassName = nameof(PluginManager); + private static IEnumerable _contextMenuPlugins; public static List AllPlugins { get; private set; } @@ -194,7 +196,7 @@ namespace Flow.Launcher.Core.Plugin { try { - var milliseconds = await Stopwatch.DebugAsync($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>", + var milliseconds = await API.StopwatchLogDebugAsync(ClassName, $"Init method time cost for <{pair.Metadata.Name}>", () => pair.Plugin.InitAsync(new PluginInitContext(pair.Metadata, API))); pair.Metadata.InitTime += milliseconds; @@ -266,7 +268,7 @@ namespace Flow.Launcher.Core.Plugin try { - var milliseconds = await Stopwatch.DebugAsync($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}", + var milliseconds = await API.StopwatchLogDebugAsync(ClassName, $"Cost for {metadata.Name}", async () => results = await pair.Plugin.QueryAsync(query, token).ConfigureAwait(false)); token.ThrowIfCancellationRequested(); diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 495a4c1ab..1010d9f08 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -11,12 +11,17 @@ using Flow.Launcher.Infrastructure.Logger; #pragma warning restore IDE0005 using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; -using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; namespace Flow.Launcher.Core.Plugin { public static class PluginsLoader { + private static readonly string ClassName = nameof(PluginsLoader); + + // We should not initialize API in static constructor because it will create another API instance + private static IPublicAPI api = null; + private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); + public static List Plugins(List metadatas, PluginsSettings settings) { var dotnetPlugins = DotNetPlugins(metadatas); @@ -59,8 +64,7 @@ namespace Flow.Launcher.Core.Plugin foreach (var metadata in metadatas) { - var milliseconds = Stopwatch.Debug( - $"|PluginsLoader.DotNetPlugins|Constructor init cost for {metadata.Name}", () => + var milliseconds = API.StopwatchLogDebug(ClassName, $"Constructor init cost for {metadata.Name}", () => { Assembly assembly = null; IAsyncPlugin plugin = null; diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs index 0433036b7..be3609956 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs @@ -7,8 +7,9 @@ using System.Threading; using System.Threading.Tasks; using System.Windows.Media; using System.Windows.Media.Imaging; -using Flow.Launcher.Infrastructure.Logger; +using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.Infrastructure.Storage; +using Flow.Launcher.Plugin; using SharpVectors.Converters; using SharpVectors.Renderers.Wpf; @@ -16,6 +17,12 @@ namespace Flow.Launcher.Infrastructure.Image { public static class ImageLoader { + // We should not initialize API in static constructor because it will create another API instance + private static IPublicAPI api = null; + private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService(); + + private static readonly string ClassName = nameof(ImageLoader); + private static readonly ImageCache ImageCache = new(); private static SemaphoreSlim storageLock { get; } = new SemaphoreSlim(1, 1); private static BinaryStorage> _storage; @@ -51,15 +58,14 @@ namespace Flow.Launcher.Infrastructure.Image _ = Task.Run(async () => { - await Stopwatch.NormalAsync("|ImageLoader.Initialize|Preload images cost", async () => + await API.StopwatchLogInfoAsync(ClassName, "Preload images cost", async () => { foreach (var (path, isFullImage) in usage) { await LoadAsync(path, isFullImage); } }); - Log.Info( - $"|ImageLoader.Initialize|Number of preload images is <{ImageCache.CacheSize()}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}"); + API.LogInfo(ClassName, $"Number of preload images is <{ImageCache.CacheSize()}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}"); }); } @@ -75,7 +81,7 @@ namespace Flow.Launcher.Infrastructure.Image } catch (System.Exception e) { - Log.Exception($"|ImageLoader.SaveAsync|Failed to save image cache to file", e); + API.LogException(ClassName, "Failed to save image cache to file", e); } finally { @@ -170,8 +176,8 @@ namespace Flow.Launcher.Infrastructure.Image } 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); + API.LogException(ClassName, $"|ImageLoader.Load|Failed to get thumbnail for {path} on first try", e); + API.LogException(ClassName, $"|ImageLoader.Load|Failed to get thumbnail for {path} on second try", e2); ImageSource image = ImageCache[Constant.MissingImgIcon, false]; ImageCache[path, false] = image; diff --git a/Flow.Launcher.Infrastructure/Stopwatch.cs b/Flow.Launcher.Infrastructure/Stopwatch.cs index dd6edaff9..784d323fe 100644 --- a/Flow.Launcher.Infrastructure/Stopwatch.cs +++ b/Flow.Launcher.Infrastructure/Stopwatch.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Threading.Tasks; using Flow.Launcher.Infrastructure.Logger; @@ -7,8 +6,6 @@ namespace Flow.Launcher.Infrastructure { public static class Stopwatch { - private static readonly Dictionary Count = new Dictionary(); - private static readonly object Locker = new object(); /// /// This stopwatch will appear only in Debug mode /// @@ -62,36 +59,5 @@ namespace Flow.Launcher.Infrastructure Log.Info(info); return milliseconds; } - - - - public static void StartCount(string name, Action action) - { - var stopWatch = new System.Diagnostics.Stopwatch(); - stopWatch.Start(); - action(); - stopWatch.Stop(); - var milliseconds = stopWatch.ElapsedMilliseconds; - lock (Locker) - { - if (Count.ContainsKey(name)) - { - Count[name] += milliseconds; - } - else - { - Count[name] = 0; - } - } - } - - public static void EndCount() - { - foreach (var key in Count.Keys) - { - string info = $"{key} already cost {Count[key]}ms"; - Log.Debug(info); - } - } } } diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index f37496fd1..a73ead814 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -510,5 +510,31 @@ namespace Flow.Launcher.Plugin /// /// public Task UninstallPluginAsync(PluginMetadata pluginMetadata, bool removePluginSettings = false); + + /// + /// Log debug message of the time taken to execute a method + /// Message will only be logged in Debug mode + /// + /// The time taken to execute the method in milliseconds + public long StopwatchLogDebug(string className, string message, Action action, [CallerMemberName] string methodName = ""); + + /// + /// Log debug message of the time taken to execute a method asynchronously + /// Message will only be logged in Debug mode + /// + /// The time taken to execute the method in milliseconds + public Task StopwatchLogDebugAsync(string className, string message, Func action, [CallerMemberName] string methodName = ""); + + /// + /// Log info message of the time taken to execute a method + /// + /// The time taken to execute the method in milliseconds + public long StopwatchLogInfo(string className, string message, Action action, [CallerMemberName] string methodName = ""); + + /// + /// Log info message of the time taken to execute a method asynchronously + /// + /// The time taken to execute the method in milliseconds + public Task StopwatchLogInfoAsync(string className, string message, Func action, [CallerMemberName] string methodName = ""); } } diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 81938612c..90fefe0a6 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -21,7 +21,6 @@ using Flow.Launcher.Plugin; using Flow.Launcher.ViewModel; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; namespace Flow.Launcher { @@ -35,6 +34,8 @@ namespace Flow.Launcher #region Private Fields + private static readonly string ClassName = nameof(App); + private static bool _disposed; private MainWindow _mainWindow; private readonly MainViewModel _mainVM; @@ -136,7 +137,7 @@ namespace Flow.Launcher private async void OnStartup(object sender, StartupEventArgs e) { - await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => + await API.StopwatchLogInfoAsync(ClassName, "Startup cost", async () => { // Because new message box api uses MessageBoxEx window, // if it is created and closed before main window is created, it will cause the application to exit. @@ -313,7 +314,7 @@ namespace Flow.Launcher _disposed = true; } - Stopwatch.Normal("|App.Dispose|Dispose cost", () => + API.StopwatchLogInfo(ClassName, "Dispose cost", () => { Log.Info("|App.Dispose|Begin Flow Launcher dispose ----------------------------------------------------"); diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index b67ef6ab6..95ef6c9f3 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -31,6 +31,7 @@ using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.ViewModel; using JetBrains.Annotations; using Squirrel; +using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; namespace Flow.Launcher { @@ -431,6 +432,18 @@ namespace Flow.Launcher public Task UninstallPluginAsync(PluginMetadata pluginMetadata, bool removePluginSettings = false) => PluginManager.UninstallPluginAsync(pluginMetadata, removePluginSettings); + public long StopwatchLogDebug(string className, string message, Action action, [CallerMemberName] string methodName = "") => + Stopwatch.Debug($"|{className}.{methodName}|{message}", action); + + public Task StopwatchLogDebugAsync(string className, string message, Func action, [CallerMemberName] string methodName = "") => + Stopwatch.DebugAsync($"|{className}.{methodName}|{message}", action); + + public long StopwatchLogInfo(string className, string message, Action action, [CallerMemberName] string methodName = "") => + Stopwatch.Normal($"|{className}.{methodName}|{message}", action); + + public Task StopwatchLogInfoAsync(string className, string message, Func action, [CallerMemberName] string methodName = "") => + Stopwatch.NormalAsync($"|{className}.{methodName}|{message}", action); + #endregion #region Private Methods diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs index d2b85e687..407300924 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs @@ -524,7 +524,7 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels } } - public int MaxResultLowerLimit => 100; + public int MaxResultLowerLimit => 1; public int MaxResultUpperLimit => 100000; public int MaxResult diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index 54e59d1be..dca07d085 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -13,7 +13,6 @@ using Flow.Launcher.Plugin.Program.Views.Models; using Flow.Launcher.Plugin.SharedCommands; using Microsoft.Extensions.Caching.Memory; using Path = System.IO.Path; -using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch; namespace Flow.Launcher.Plugin.Program { @@ -33,6 +32,8 @@ namespace Flow.Launcher.Plugin.Program internal static PluginInitContext Context { get; private set; } + private static readonly string ClassName = nameof(Main); + private static readonly List emptyResults = new(); private static readonly MemoryCacheOptions cacheOptions = new() { SizeLimit = 1560 }; @@ -189,7 +190,7 @@ namespace Flow.Launcher.Plugin.Program var _win32sCount = 0; var _uwpsCount = 0; - await Stopwatch.NormalAsync("|Flow.Launcher.Plugin.Program.Main|Preload programs cost", async () => + await Context.API.StopwatchLogInfoAsync(ClassName, "Preload programs cost", async () => { var pluginCacheDirectory = Context.CurrentPluginMetadata.PluginCacheDirectoryPath; FilesFolders.ValidateDirectory(pluginCacheDirectory); @@ -256,6 +257,7 @@ namespace Flow.Launcher.Plugin.Program }); Context.API.LogInfo(ClassName, $"Number of preload win32 programs <{_win32sCount}>"); Context.API.LogInfo(ClassName, $"Number of preload uwps <{_uwpsCount}>"); + var cacheEmpty = _win32sCount == 0 || _uwpsCount == 0; if (cacheEmpty || _settings.LastIndexTime.AddHours(30) < DateTime.Now) @@ -332,12 +334,12 @@ namespace Flow.Launcher.Plugin.Program { var win32Task = Task.Run(async () => { - await Stopwatch.NormalAsync("|Flow.Launcher.Plugin.Program.Main|Win32Program index cost", IndexWin32ProgramsAsync); + await Context.API.StopwatchLogInfoAsync(ClassName, "Win32Program index cost", IndexWin32ProgramsAsync); }); var uwpTask = Task.Run(async () => { - await Stopwatch.NormalAsync("|Flow.Launcher.Plugin.Program.Main|UWPProgram index cost", IndexUwpProgramsAsync); + await Context.API.StopwatchLogInfoAsync(ClassName, "UWPProgram index cost", IndexUwpProgramsAsync); }); await Task.WhenAll(win32Task, uwpTask).ConfigureAwait(false);