diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index aecbfeec..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -name: Bug Report -about: Report a problem with Performance Monitor -title: '[BUG] ' -labels: bug -assignees: '' ---- - -## Environment - -- **SQL Server Version**: (e.g., SQL Server 2019 CU25) -- **Windows Version**: (e.g., Windows Server 2022) -- **Component**: (Dashboard / Lite / Installer / SQL Scripts) -- **Performance Monitor Version**: (e.g., 1.0.0) - -## Describe the Bug - -A clear description of what the bug is. - -## Steps to Reproduce - -1. -2. -3. - -## Expected Behavior - -What you expected to happen. - -## Actual Behavior - -What actually happened. - -## Error Messages - -``` -Paste any error messages here -``` - -## Screenshots - -If applicable, add screenshots to help explain the problem. - -## Additional Context - -- Did this work before? If so, what changed? -- Are there any relevant SQL Server configuration settings? -- Is SQL Server Agent running? (Full Edition only) -- Are you monitoring Azure SQL DB, Azure MI, or AWS RDS? - -**IMPORTANT: If you're going to contribute code, please read the [contributing guide](https://github.com/erikdarlingdata/PerformanceMonitor/blob/main/CONTRIBUTING.md) first.** diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..2dd0a89b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,111 @@ +name: Bug Report +description: Report a problem with Performance Monitor +title: "[BUG] " +labels: ["bug"] + +body: + - type: dropdown + id: component + attributes: + label: Component + description: Which part of Performance Monitor is affected? + options: + - Full Dashboard + - Lite + - Installer (CLI) + - Installer (GUI) + - SQL collection scripts + validations: + required: true + + - type: input + id: version + attributes: + label: Performance Monitor Version + description: Check the About dialog or the release you downloaded. + placeholder: "e.g., 1.2.0" + validations: + required: true + + - type: input + id: sql-version + attributes: + label: SQL Server Version + description: "Run SELECT @@VERSION on the target server." + placeholder: "e.g., SQL Server 2019 CU25" + validations: + required: true + + - type: input + id: windows-version + attributes: + label: Windows Version + description: The OS where the Dashboard or Lite app is running. + placeholder: "e.g., Windows 11 23H2, Windows Server 2022" + validations: + required: true + + - type: textarea + id: description + attributes: + label: Describe the Bug + description: A clear description of what the bug is. + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to Reproduce + description: How can we reproduce this? + placeholder: | + 1. Open the Dashboard + 2. Click on... + 3. See error + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected Behavior + description: What you expected to happen. + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual Behavior + description: What actually happened. + validations: + required: true + + - type: textarea + id: errors + attributes: + label: Error Messages / Log Output + description: Paste any error messages or relevant log entries. Dashboard logs are in %LOCALAPPDATA%\PerformanceMonitor\Logs\. + render: text + validations: + required: false + + - type: textarea + id: screenshots + attributes: + label: Screenshots + description: If applicable, add screenshots to help explain the problem. + validations: + required: false + + - type: textarea + id: context + attributes: + label: Additional Context + description: | + Anything else that might help: + - Did this work before? If so, what changed? + - Is SQL Server Agent running? (Full Edition only) + - Are you monitoring Azure SQL DB, Azure MI, or AWS RDS? + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index f4437a2f..1e486216 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,4 @@ -blank_issues_enabled: true +blank_issues_enabled: false contact_links: - name: Questions & Discussion url: https://github.com/erikdarlingdata/PerformanceMonitor/discussions diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index cc3aa157..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -name: Feature Request -about: Suggest a new feature or enhancement -title: '[FEATURE] ' -labels: enhancement -assignees: '' ---- - -## Problem Statement - -Describe the problem you're trying to solve or the limitation you're facing. - -## Proposed Solution - -Describe your proposed feature or enhancement. - -## Use Case - -How would you use this feature? Provide a specific example. - -## Alternatives Considered - -Have you considered any alternative solutions or workarounds? - -## Additional Context - -- Is this related to a specific SQL Server version? -- Would this require schema changes? -- How frequently would you use this feature? - -## Scope - -Please indicate which component(s) this affects: - -- [ ] Full Dashboard -- [ ] Lite -- [ ] SQL collection scripts -- [ ] Installer -- [ ] Documentation - -**IMPORTANT: If you're going to contribute code, please read the [contributing guide](https://github.com/erikdarlingdata/PerformanceMonitor/blob/main/CONTRIBUTING.md) first.** diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..10c4de58 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,62 @@ +name: Feature Request +description: Suggest a new feature or enhancement +title: "[FEATURE] " +labels: ["enhancement"] + +body: + - type: checkboxes + id: component + attributes: + label: Which component(s) does this affect? + options: + - label: Full Dashboard + - label: Lite + - label: SQL collection scripts + - label: Installer + - label: Documentation + validations: + required: true + + - type: textarea + id: problem + attributes: + label: Problem Statement + description: Describe the problem you're trying to solve or the limitation you're facing. + validations: + required: true + + - type: textarea + id: solution + attributes: + label: Proposed Solution + description: Describe your proposed feature or enhancement. + validations: + required: true + + - type: textarea + id: use-case + attributes: + label: Use Case + description: How would you use this feature? Provide a specific example. + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives Considered + description: Have you considered any alternative solutions or workarounds? + validations: + required: false + + - type: textarea + id: context + attributes: + label: Additional Context + description: | + Anything else: + - Is this related to a specific SQL Server version? + - Would this require schema changes? + - How frequently would you use this feature? + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index bbe5554a..00000000 --- a/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: Question -about: I have a question about Performance Monitor -title: '[QUESTION] ' -labels: question -assignees: '' ---- - -## Which component is your question about? - -- [ ] Full Dashboard -- [ ] Lite -- [ ] SQL collection scripts -- [ ] Installer -- [ ] General / setup - -## Version - -All components have a version number. Make sure you're on a recent version. - -**Performance Monitor Version**: (e.g., 1.0.0) - -## Is your question about how it works, or the results? - -Questions about *how the tool works* can be answered here. - -If you have questions about the results, performance tuning, or SQL Server in general, you'll want to check: -* https://www.erikdarling.com/ -* https://dba.stackexchange.com/ - -## What's your question? - -A clear description of what you'd like to know. - -## Additional Context - -- SQL Server version and edition -- Are you using Azure SQL DB, Azure MI, or AWS RDS? diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml new file mode 100644 index 00000000..dec7b93d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.yml @@ -0,0 +1,59 @@ +name: Question +description: I have a question about Performance Monitor +title: "[QUESTION] " +labels: ["question"] + +body: + - type: dropdown + id: component + attributes: + label: Which component is your question about? + options: + - Full Dashboard + - Lite + - SQL collection scripts + - Installer + - General / setup + validations: + required: true + + - type: input + id: version + attributes: + label: Performance Monitor Version + description: Check the About dialog or the release you downloaded. + placeholder: "e.g., 1.2.0" + validations: + required: true + + - type: dropdown + id: question-type + attributes: + label: Is your question about how it works, or the results? + description: Questions about results or SQL Server tuning are better suited for https://www.erikdarling.com/ or https://dba.stackexchange.com/ + options: + - How the tool works + - Setup / installation + - Results / data interpretation + - Contributing / development + validations: + required: true + + - type: textarea + id: question + attributes: + label: What's your question? + description: A clear description of what you'd like to know. + validations: + required: true + + - type: textarea + id: context + attributes: + label: Additional Context + description: | + Anything that might help: + - SQL Server version and edition + - Are you using Azure SQL DB, Azure MI, or AWS RDS? + validations: + required: false diff --git a/Dashboard/ColumnFilterPopup.xaml.cs b/Dashboard/ColumnFilterPopup.xaml.cs index e4d27978..c71d3c3f 100644 --- a/Dashboard/ColumnFilterPopup.xaml.cs +++ b/Dashboard/ColumnFilterPopup.xaml.cs @@ -32,7 +32,7 @@ private void PopulateOperatorComboBox() { OperatorComboBox.Items.Clear(); - foreach (FilterOperator op in Enum.GetValues(typeof(FilterOperator))) + foreach (FilterOperator op in Enum.GetValues()) { OperatorComboBox.Items.Add(new ComboBoxItem { diff --git a/Dashboard/Controls/CriticalIssuesContent.xaml b/Dashboard/Controls/CriticalIssuesContent.xaml index 545f9cf4..17ce8acb 100644 --- a/Dashboard/Controls/CriticalIssuesContent.xaml +++ b/Dashboard/Controls/CriticalIssuesContent.xaml @@ -3,6 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:controls="clr-namespace:PerformanceMonitorDashboard.Controls" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> @@ -107,6 +108,7 @@ - + + diff --git a/Dashboard/Controls/CriticalIssuesContent.xaml.cs b/Dashboard/Controls/CriticalIssuesContent.xaml.cs index 93ded66c..cecc99c2 100644 --- a/Dashboard/Controls/CriticalIssuesContent.xaml.cs +++ b/Dashboard/Controls/CriticalIssuesContent.xaml.cs @@ -93,6 +93,13 @@ private async System.Threading.Tasks.Task LoadCriticalIssuesAsync() try { + // Only show loading overlay on initial load (no existing data) + if (CriticalIssuesDataGrid.ItemsSource == null) + { + CriticalIssuesLoading.IsLoading = true; + CriticalIssuesNoDataMessage.Visibility = Visibility.Collapsed; + } + var data = await _databaseService.GetCriticalIssuesAsync( _criticalIssuesHoursBack, _criticalIssuesFromDate, @@ -111,6 +118,10 @@ private async System.Threading.Tasks.Task LoadCriticalIssuesAsync() { Logger.Error($"Error loading critical issues: {ex.Message}"); } + finally + { + CriticalIssuesLoading.IsLoading = false; + } } #region Filter Popup Handlers diff --git a/Dashboard/Controls/DailySummaryContent.xaml b/Dashboard/Controls/DailySummaryContent.xaml index e6d2ab0c..652a8b8f 100644 --- a/Dashboard/Controls/DailySummaryContent.xaml +++ b/Dashboard/Controls/DailySummaryContent.xaml @@ -3,6 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:controls="clr-namespace:PerformanceMonitorDashboard.Controls" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> @@ -144,7 +145,9 @@ - + + diff --git a/Dashboard/Controls/DailySummaryContent.xaml.cs b/Dashboard/Controls/DailySummaryContent.xaml.cs index 802c1a47..e7d77b5b 100644 --- a/Dashboard/Controls/DailySummaryContent.xaml.cs +++ b/Dashboard/Controls/DailySummaryContent.xaml.cs @@ -79,6 +79,13 @@ private async System.Threading.Tasks.Task LoadDailySummaryAsync() try { + // Only show loading overlay on initial load (no existing data) + if (DailySummaryDataGrid.ItemsSource == null) + { + DailySummaryLoading.IsLoading = true; + DailySummaryNoDataMessage.Visibility = Visibility.Collapsed; + } + var data = await _databaseService.GetDailySummaryAsync(_dailySummaryDate); // Store unfiltered data and reset filters when new data is loaded @@ -100,6 +107,10 @@ private async System.Threading.Tasks.Task LoadDailySummaryAsync() { Logger.Error($"Error loading daily summary: {ex.Message}"); } + finally + { + DailySummaryLoading.IsLoading = false; + } } #region Event Handlers diff --git a/Dashboard/Controls/LoadingOverlay.xaml b/Dashboard/Controls/LoadingOverlay.xaml new file mode 100644 index 00000000..4fbbeaa6 --- /dev/null +++ b/Dashboard/Controls/LoadingOverlay.xaml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Dashboard/Controls/LoadingOverlay.xaml.cs b/Dashboard/Controls/LoadingOverlay.xaml.cs new file mode 100644 index 00000000..b9b6810f --- /dev/null +++ b/Dashboard/Controls/LoadingOverlay.xaml.cs @@ -0,0 +1,41 @@ +using System.Windows; +using System.Windows.Controls; +using PerformanceMonitorDashboard.Helpers; + +namespace PerformanceMonitorDashboard.Controls +{ + public partial class LoadingOverlay : UserControl + { + public static readonly DependencyProperty IsLoadingProperty = + DependencyProperty.Register( + nameof(IsLoading), + typeof(bool), + typeof(LoadingOverlay), + new PropertyMetadata(false, OnIsLoadingChanged)); + + public bool IsLoading + { + get => (bool)GetValue(IsLoadingProperty); + set => SetValue(IsLoadingProperty, value); + } + + public LoadingOverlay() + { + InitializeComponent(); + } + + private static void OnIsLoadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is LoadingOverlay overlay) + { + var isLoading = (bool)e.NewValue; + overlay.Visibility = isLoading ? Visibility.Visible : Visibility.Collapsed; + + if (isLoading) + { + overlay.MessageText.Text = LoadingMessages.GetRandom(); + } + } + } + } +} diff --git a/Dashboard/Controls/MemoryContent.xaml b/Dashboard/Controls/MemoryContent.xaml index 298686c1..bae6cf3b 100644 --- a/Dashboard/Controls/MemoryContent.xaml +++ b/Dashboard/Controls/MemoryContent.xaml @@ -4,6 +4,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:ScottPlot="clr-namespace:ScottPlot.WPF;assembly=ScottPlot.WPF" + xmlns:controls="clr-namespace:PerformanceMonitorDashboard.Controls" mc:Ignorable="d" d:DesignHeight="600" d:DesignWidth="1200"> @@ -90,7 +91,9 @@ - + + @@ -116,7 +119,9 @@ - + + diff --git a/Dashboard/Controls/MemoryContent.xaml.cs b/Dashboard/Controls/MemoryContent.xaml.cs index b9c7c9e6..2ce4ee10 100644 --- a/Dashboard/Controls/MemoryContent.xaml.cs +++ b/Dashboard/Controls/MemoryContent.xaml.cs @@ -268,7 +268,6 @@ private void LoadMemoryStatsOverviewChart(List memoryData, int MemoryStatsOverviewChart.Plot.Axes.DateTimeTicksBottom(); MemoryStatsOverviewChart.Plot.Axes.SetLimitsX(xMin, xMax); MemoryStatsOverviewChart.Plot.YLabel("MB"); - MemoryStatsOverviewChart.Plot.HideGrid(); // Fixed negative space for legend MemoryStatsOverviewChart.Plot.Axes.AutoScaleY(); var memOverviewLimits = MemoryStatsOverviewChart.Plot.Axes.GetLimits(); @@ -383,6 +382,13 @@ private async System.Threading.Tasks.Task RefreshMemoryGrantsAsync() try { + // Only show loading overlay on initial load (no existing chart data) + if (!MemoryGrantsChart.Plot.GetPlottables().Any()) + { + MemoryGrantsLoading.IsLoading = true; + MemoryGrantsNoDataMessage.Visibility = Visibility.Collapsed; + } + var data = await _databaseService.GetMemoryGrantStatsAsync(_memoryGrantsHoursBack, _memoryGrantsFromDate, _memoryGrantsToDate); var dataList = data.ToList(); MemoryGrantsNoDataMessage.Visibility = dataList.Count == 0 ? Visibility.Visible : Visibility.Collapsed; @@ -392,6 +398,10 @@ private async System.Threading.Tasks.Task RefreshMemoryGrantsAsync() { Logger.Error($"Error loading memory grants: {ex.Message}"); } + finally + { + MemoryGrantsLoading.IsLoading = false; + } } private void LoadMemoryGrantsChart(IEnumerable data, int hoursBack, DateTime? fromDate, DateTime? toDate) @@ -465,7 +475,6 @@ private void LoadMemoryGrantsChart(IEnumerable data, int h MemoryGrantsChart.Plot.Axes.DateTimeTicksBottom(); MemoryGrantsChart.Plot.Axes.SetLimitsX(xMin, xMax); MemoryGrantsChart.Plot.YLabel("MB"); - MemoryGrantsChart.Plot.HideGrid(); // Fixed negative space for legend MemoryGrantsChart.Plot.Axes.AutoScaleY(); var grantsLimits = MemoryGrantsChart.Plot.Axes.GetLimits(); @@ -485,6 +494,13 @@ private async System.Threading.Tasks.Task RefreshMemoryClerksAsync() try { + // Only show loading overlay on initial load (no existing chart data) + if (!MemoryClerksChart.Plot.GetPlottables().Any()) + { + MemoryClerksLoading.IsLoading = true; + MemoryClerksNoDataMessage.Visibility = Visibility.Collapsed; + } + var data = await _databaseService.GetMemoryClerksTopNAsync(5, _memoryClerksHoursBack, _memoryClerksFromDate, _memoryClerksToDate); var dataList = data.ToList(); MemoryClerksNoDataMessage.Visibility = dataList.Count == 0 ? Visibility.Visible : Visibility.Collapsed; @@ -494,6 +510,10 @@ private async System.Threading.Tasks.Task RefreshMemoryClerksAsync() { Logger.Error($"Error loading memory clerks: {ex.Message}"); } + finally + { + MemoryClerksLoading.IsLoading = false; + } } private void LoadMemoryClerksChart(List data, int hoursBack, DateTime? fromDate, DateTime? toDate) @@ -565,7 +585,6 @@ private void LoadMemoryClerksChart(List data, int hoursBack, D MemoryClerksChart.Plot.Axes.DateTimeTicksBottom(); MemoryClerksChart.Plot.Axes.SetLimitsX(xMin, xMax); MemoryClerksChart.Plot.YLabel("MB"); - MemoryClerksChart.Plot.HideGrid(); // Fixed negative space for legend MemoryClerksChart.Plot.Axes.AutoScaleY(); var clerksLimits = MemoryClerksChart.Plot.Axes.GetLimits(); @@ -715,7 +734,6 @@ private void LoadPlanCacheChart(IEnumerable data, int hoursB PlanCacheChart.Plot.Axes.DateTimeTicksBottom(); PlanCacheChart.Plot.Axes.SetLimitsX(xMin, xMax); PlanCacheChart.Plot.YLabel("MB"); - PlanCacheChart.Plot.HideGrid(); // Fixed negative space for legend PlanCacheChart.Plot.Axes.AutoScaleY(); var planCacheLimits = PlanCacheChart.Plot.Axes.GetLimits(); @@ -835,7 +853,6 @@ private void LoadMemoryPressureEventsChart(IEnumerable MemoryPressureEventsChart.Plot.Axes.DateTimeTicksBottom(); MemoryPressureEventsChart.Plot.Axes.SetLimitsX(xMin, xMax); MemoryPressureEventsChart.Plot.YLabel("Event Count"); - MemoryPressureEventsChart.Plot.HideGrid(); // Fixed negative space for legend MemoryPressureEventsChart.Plot.Axes.AutoScaleY(); var pressureLimits = MemoryPressureEventsChart.Plot.Axes.GetLimits(); diff --git a/Dashboard/Controls/QueryPerformanceContent.xaml b/Dashboard/Controls/QueryPerformanceContent.xaml index 12bf9709..ca8c9dd3 100644 --- a/Dashboard/Controls/QueryPerformanceContent.xaml +++ b/Dashboard/Controls/QueryPerformanceContent.xaml @@ -314,7 +314,9 @@ - + + @@ -585,7 +587,9 @@ - + + diff --git a/Dashboard/Controls/QueryPerformanceContent.xaml.cs b/Dashboard/Controls/QueryPerformanceContent.xaml.cs index e903040d..7cefe544 100644 --- a/Dashboard/Controls/QueryPerformanceContent.xaml.cs +++ b/Dashboard/Controls/QueryPerformanceContent.xaml.cs @@ -217,6 +217,13 @@ public async Task RefreshAllDataAsync() if (_databaseService == null) return; + // Only show loading overlay on initial load (no existing data) + if (QueryStatsDataGrid.ItemsSource == null) + { + QueryStatsLoading.IsLoading = true; + QueryStatsNoDataMessage.Visibility = Visibility.Collapsed; + } + // Fetch grid data (summary views aggregated per query/procedure) var queryStatsTask = _databaseService.GetQueryStatsAsync(_queryStatsHoursBack, _queryStatsFromDate, _queryStatsToDate); var procStatsTask = _databaseService.GetProcedureStatsAsync(_procStatsHoursBack, _procStatsFromDate, _procStatsToDate); @@ -263,6 +270,10 @@ await Task.WhenAll( { Logger.Error($"Error refreshing QueryPerformance data: {ex.Message}", ex); } + finally + { + QueryStatsLoading.IsLoading = false; + } } private void SetStatus(string message) @@ -367,7 +378,14 @@ private async Task RefreshActiveQueriesAsync() try { + // Only show loading overlay on initial load (no existing data) + if (ActiveQueriesDataGrid.ItemsSource == null) + { + ActiveQueriesLoading.IsLoading = true; + ActiveQueriesNoDataMessage.Visibility = Visibility.Collapsed; + } SetStatus("Loading active queries..."); + var data = await _databaseService.GetQuerySnapshotsAsync(_activeQueriesHoursBack, _activeQueriesFromDate, _activeQueriesToDate); ActiveQueriesDataGrid.ItemsSource = data; ActiveQueriesNoDataMessage.Visibility = data.Count == 0 ? Visibility.Visible : Visibility.Collapsed; @@ -378,6 +396,10 @@ private async Task RefreshActiveQueriesAsync() Logger.Error($"Error loading active queries: {ex.Message}"); SetStatus("Error loading active queries"); } + finally + { + ActiveQueriesLoading.IsLoading = false; + } } private void ActiveQueriesFilter_Click(object sender, RoutedEventArgs e) @@ -949,7 +971,6 @@ private void LoadDurationChart(WpfPlot chart, IEnumerable tre chart.Plot.Axes.DateTimeTicksBottom(); chart.Plot.Axes.SetLimitsX(xMin, xMax); chart.Plot.YLabel("Duration (ms/sec)"); - chart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(chart); chart.Refresh(); } @@ -1005,7 +1026,6 @@ private void LoadExecChart(IEnumerable execTrends, int hours QueryPerfTrendsExecChart.Plot.Axes.DateTimeTicksBottom(); QueryPerfTrendsExecChart.Plot.Axes.SetLimitsX(xMin, xMax); QueryPerfTrendsExecChart.Plot.YLabel("Executions/sec"); - QueryPerfTrendsExecChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(QueryPerfTrendsExecChart); QueryPerfTrendsExecChart.Refresh(); } diff --git a/Dashboard/Controls/ResourceMetricsContent.xaml.cs b/Dashboard/Controls/ResourceMetricsContent.xaml.cs index 08e9bcde..119fcdc1 100644 --- a/Dashboard/Controls/ResourceMetricsContent.xaml.cs +++ b/Dashboard/Controls/ResourceMetricsContent.xaml.cs @@ -358,7 +358,6 @@ private void LoadLatchStatsChart(IEnumerable data, int hoursBack LatchStatsChart.Plot.Axes.SetLimitsX(xMin, xMax); TabHelpers.SetChartYLimitsWithLegendPadding(LatchStatsChart); LatchStatsChart.Plot.YLabel("Wait Time (ms/sec)"); - LatchStatsChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(LatchStatsChart); LatchStatsChart.Refresh(); } @@ -450,7 +449,6 @@ private void LoadSpinlockStatsChart(IEnumerable data, int hou SpinlockStatsChart.Plot.Axes.SetLimitsX(xMin, xMax); TabHelpers.SetChartYLimitsWithLegendPadding(SpinlockStatsChart); SpinlockStatsChart.Plot.YLabel("Collisions/sec"); - SpinlockStatsChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(SpinlockStatsChart); SpinlockStatsChart.Refresh(); } @@ -559,7 +557,6 @@ private void LoadCombinedTempDbLatencyChart(List da TempDbLatencyChart.Plot.Axes.SetLimitsX(xMin, xMax); TabHelpers.SetChartYLimitsWithLegendPadding(TempDbLatencyChart); TempDbLatencyChart.Plot.YLabel("Latency (ms)"); - TempDbLatencyChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(TempDbLatencyChart); TempDbLatencyChart.Refresh(); } @@ -665,7 +662,6 @@ private void LoadTempdbStatsChart(IEnumerable data, int hoursBa TempdbStatsChart.Plot.Axes.SetLimitsX(xMin, xMax); TempdbStatsChart.Plot.Axes.AutoScaleY(); TempdbStatsChart.Plot.YLabel("MB"); - TempdbStatsChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(TempdbStatsChart); TempdbStatsChart.Refresh(); } @@ -837,7 +833,6 @@ private void LoadSessionStatsChart(IEnumerable data, int hours SessionStatsChart.Plot.Axes.SetLimitsX(xMin, xMax); TabHelpers.SetChartYLimitsWithLegendPadding(SessionStatsChart); SessionStatsChart.Plot.YLabel("Session Count"); - SessionStatsChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(SessionStatsChart); SessionStatsChart.Refresh(); } @@ -951,7 +946,6 @@ private void LoadFileIoChart(ScottPlot.WPF.WpfPlot chart, List cpuData, int hou ServerUtilTrendsCpuChart.Plot.Axes.SetLimitsX(xMin, xMax); TabHelpers.SetChartYLimitsWithLegendPadding(ServerUtilTrendsCpuChart); ServerUtilTrendsCpuChart.Plot.YLabel("CPU %"); - ServerUtilTrendsCpuChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(ServerUtilTrendsCpuChart); ServerUtilTrendsCpuChart.Refresh(); } @@ -1105,7 +1098,6 @@ private void LoadServerTrendsTempdbChart(IEnumerable tempdbData ServerUtilTrendsTempdbChart.Plot.Axes.SetLimitsX(xMin, xMax); TabHelpers.SetChartYLimitsWithLegendPadding(ServerUtilTrendsTempdbChart); ServerUtilTrendsTempdbChart.Plot.YLabel("MB"); - ServerUtilTrendsTempdbChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(ServerUtilTrendsTempdbChart); ServerUtilTrendsTempdbChart.Refresh(); } @@ -1211,7 +1203,6 @@ private void LoadServerTrendsMemoryChart(IEnumerable memoryData ServerUtilTrendsMemoryChart.Plot.Axes.SetLimitsX(xMin, xMax); TabHelpers.SetChartYLimitsWithLegendPadding(ServerUtilTrendsMemoryChart); ServerUtilTrendsMemoryChart.Plot.YLabel("MB"); - ServerUtilTrendsMemoryChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(ServerUtilTrendsMemoryChart); ServerUtilTrendsMemoryChart.Refresh(); } @@ -1287,7 +1278,6 @@ private void LoadServerTrendsPerfmonChart(IEnumerable perfmonD ServerUtilTrendsPerfmonChart.Plot.Axes.SetLimitsX(xMin, xMax); TabHelpers.SetChartYLimitsWithLegendPadding(ServerUtilTrendsPerfmonChart); ServerUtilTrendsPerfmonChart.Plot.YLabel("Per Second"); - ServerUtilTrendsPerfmonChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(ServerUtilTrendsPerfmonChart); ServerUtilTrendsPerfmonChart.Refresh(); } @@ -1791,7 +1781,6 @@ private void LoadPerfmonCountersChart(List? data, int hoursBac PerfmonCountersChart.Plot.Axes.SetLimitsX(xMin, xMax); TabHelpers.SetChartYLimitsWithLegendPadding(PerfmonCountersChart); PerfmonCountersChart.Plot.YLabel("Value/sec"); - PerfmonCountersChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(PerfmonCountersChart); PerfmonCountersChart.Refresh(); } @@ -2167,7 +2156,6 @@ private void LoadWaitStatsDetailChart(List? data, int hoursB WaitStatsDetailChart.Plot.Axes.SetLimitsX(xMin, xMax); TabHelpers.SetChartYLimitsWithLegendPadding(WaitStatsDetailChart); WaitStatsDetailChart.Plot.YLabel(useAvgPerWait ? "Avg Wait Time (ms/wait)" : "Wait Time (ms/sec)"); - WaitStatsDetailChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(WaitStatsDetailChart); WaitStatsDetailChart.Refresh(); } diff --git a/Dashboard/Controls/SystemEventsContent.xaml.cs b/Dashboard/Controls/SystemEventsContent.xaml.cs index 5773c094..e17a5ee6 100644 --- a/Dashboard/Controls/SystemEventsContent.xaml.cs +++ b/Dashboard/Controls/SystemEventsContent.xaml.cs @@ -474,7 +474,6 @@ private void LoadCorruptionEventsCharts(List data, BadPagesChart.Plot.Axes.DateTimeTicksBottom(); BadPagesChart.Plot.Axes.SetLimitsX(xMin, xMax); BadPagesChart.Plot.YLabel("Count"); - BadPagesChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(BadPagesChart); BadPagesChart.Refresh(); @@ -504,7 +503,6 @@ private void LoadCorruptionEventsCharts(List data, DumpRequestsChart.Plot.Axes.DateTimeTicksBottom(); DumpRequestsChart.Plot.Axes.SetLimitsX(xMin, xMax); DumpRequestsChart.Plot.YLabel("Count"); - DumpRequestsChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(DumpRequestsChart); DumpRequestsChart.Refresh(); @@ -534,7 +532,6 @@ private void LoadCorruptionEventsCharts(List data, AccessViolationsChart.Plot.Axes.DateTimeTicksBottom(); AccessViolationsChart.Plot.Axes.SetLimitsX(xMin, xMax); AccessViolationsChart.Plot.YLabel("Count"); - AccessViolationsChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(AccessViolationsChart); AccessViolationsChart.Refresh(); @@ -564,7 +561,6 @@ private void LoadCorruptionEventsCharts(List data, WriteAccessViolationsChart.Plot.Axes.DateTimeTicksBottom(); WriteAccessViolationsChart.Plot.Axes.SetLimitsX(xMin, xMax); WriteAccessViolationsChart.Plot.YLabel("Count"); - WriteAccessViolationsChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(WriteAccessViolationsChart); WriteAccessViolationsChart.Refresh(); } @@ -606,7 +602,6 @@ private void LoadContentionEventsCharts(List data, NonYieldingTasksChart.Plot.Axes.DateTimeTicksBottom(); NonYieldingTasksChart.Plot.Axes.SetLimitsX(xMin, xMax); NonYieldingTasksChart.Plot.YLabel("Count"); - NonYieldingTasksChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(NonYieldingTasksChart); NonYieldingTasksChart.Refresh(); @@ -636,7 +631,6 @@ private void LoadContentionEventsCharts(List data, LatchWarningsChart.Plot.Axes.DateTimeTicksBottom(); LatchWarningsChart.Plot.Axes.SetLimitsX(xMin, xMax); LatchWarningsChart.Plot.YLabel("Count"); - LatchWarningsChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(LatchWarningsChart); LatchWarningsChart.Refresh(); @@ -700,7 +694,6 @@ private void LoadContentionEventsCharts(List data, SickSpinlocksChart.Plot.Axes.DateTimeTicksBottom(); SickSpinlocksChart.Plot.Axes.SetLimitsX(xMin, xMax); SickSpinlocksChart.Plot.YLabel("Backoffs"); - SickSpinlocksChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(SickSpinlocksChart); SickSpinlocksChart.Refresh(); @@ -752,7 +745,6 @@ private void LoadContentionEventsCharts(List data, CpuComparisonChart.Plot.Axes.SetLimitsX(xMin, xMax); CpuComparisonChart.Plot.Axes.SetLimitsY(0, 100); // Fixed Y-axis for CPU percentage CpuComparisonChart.Plot.YLabel("CPU %"); - CpuComparisonChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(CpuComparisonChart); CpuComparisonChart.Refresh(); } @@ -853,7 +845,6 @@ private void LoadSevereErrorsChart(IEnumerable data SevereErrorsChart.Plot.Axes.DateTimeTicksBottom(); SevereErrorsChart.Plot.Axes.SetLimitsX(xMin, xMax); SevereErrorsChart.Plot.YLabel("Event Count"); - SevereErrorsChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(SevereErrorsChart); SevereErrorsChart.Refresh(); } @@ -1013,7 +1004,6 @@ private void LoadIOIssuesChart(IEnumerable data, int ho IOIssuesChart.Plot.Axes.DateTimeTicksBottom(); IOIssuesChart.Plot.Axes.SetLimitsX(xMin, xMax); IOIssuesChart.Plot.YLabel("Count"); - IOIssuesChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(IOIssuesChart); IOIssuesChart.Refresh(); } @@ -1108,7 +1098,6 @@ private void LoadLongestPendingIOChart(IEnumerable data LongestPendingIOChart.Plot.Axes.DateTimeTicksBottom(); LongestPendingIOChart.Plot.Axes.SetLimitsX(xMin, xMax); LongestPendingIOChart.Plot.YLabel("Duration (ms)"); - LongestPendingIOChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(LongestPendingIOChart); LongestPendingIOChart.Refresh(); } @@ -1208,7 +1197,6 @@ long ParseNonYield(string? value) SchedulerIssuesChart.Plot.Axes.DateTimeTicksBottom(); SchedulerIssuesChart.Plot.Axes.SetLimitsX(xMin, xMax); SchedulerIssuesChart.Plot.YLabel("Total Non-Yield Time (ms)"); - SchedulerIssuesChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(SchedulerIssuesChart); SchedulerIssuesChart.Refresh(); } @@ -1359,7 +1347,6 @@ private void LoadMemoryConditionsChart(IEnumerable data, int h CPUTasksChart.Plot.Axes.DateTimeTicksBottom(); CPUTasksChart.Plot.Axes.SetLimitsX(xMin, xMax); CPUTasksChart.Plot.YLabel("Workers"); - CPUTasksChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(CPUTasksChart); CPUTasksChart.Refresh(); } @@ -1739,7 +1725,6 @@ private void LoadMemoryBrokerChart(IEnumerable dat { chart.Plot.Axes.DateTimeTicksBottom(); chart.Plot.Axes.SetLimitsX(xMin, xMax); - chart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(chart); chart.Refresh(); } @@ -1894,7 +1879,6 @@ private void LoadMemoryNodeOOMChart(IEnumerable d MemoryNodeOOMChart.Plot.Axes.DateTimeTicksBottom(); MemoryNodeOOMChart.Plot.Axes.SetLimitsX(xMin, xMax); MemoryNodeOOMChart.Plot.YLabel("Event Count"); - MemoryNodeOOMChart.Plot.HideGrid(); TabHelpers.LockChartVerticalAxis(MemoryNodeOOMChart); MemoryNodeOOMChart.Refresh(); } @@ -1944,7 +1928,6 @@ private void LoadMemoryNodeOOMUtilChart(IEnumerable + /// Randomized loading messages displayed while data is being fetched. + /// + public static class LoadingMessages + { + private static readonly string[] Messages = + [ + "Reticulating splines...", + "Consulting the oracle...", + "Asking the database nicely...", + "Crunching numbers...", + "Thinking really hard...", + "Mulling it over...", + "Communing with SQL Server...", + "Summoning data spirits...", + "Decoding the matrix...", + "Interrogating indexes...", + "Parsing the void...", + "Calibrating flux capacitors...", + "Negotiating with stored procedures...", + "Convincing queries to run faster...", + "Herding cursors...", + "Massaging execution plans...", + "Whispering to wait stats...", + "Befriending buffer pools...", + "Coaxing data from disk...", + "Pondering performance...", + "Untangling spaghetti queries...", + "Poking the query optimizer...", + "Warming up the plan cache...", + "Dusting off statistics...", + "Reasoning with RESOURCE_SEMAPHORE...", + "Apologizing to tempdb...", + "Flattering the cardinality estimator...", + "Bribing the lock manager...", + "Decrypting wait stats hieroglyphics...", + "Teaching cursors to fly...", + ]; + + public static string GetRandom() => Messages[Random.Shared.Next(Messages.Length)]; + } +} diff --git a/Dashboard/Helpers/TabHelpers.cs b/Dashboard/Helpers/TabHelpers.cs index c7556a9c..a9cc90c9 100644 --- a/Dashboard/Helpers/TabHelpers.cs +++ b/Dashboard/Helpers/TabHelpers.cs @@ -132,7 +132,7 @@ public static void ApplyDarkModeToChart(WpfPlot chart) var darkBackground = ScottPlot.Color.FromHex("#22252b"); var darkerBackground = ScottPlot.Color.FromHex("#111217"); var textColor = ScottPlot.Color.FromHex("#9DA5B4"); - var gridColor = ScottPlot.Colors.White.WithAlpha(20); + var gridColor = ScottPlot.Colors.White.WithAlpha(40); chart.Plot.FigureBackground.Color = darkBackground; chart.Plot.DataBackground.Color = darkerBackground; diff --git a/Dashboard/MainWindow.xaml b/Dashboard/MainWindow.xaml index c285a568..f437f103 100644 --- a/Dashboard/MainWindow.xaml +++ b/Dashboard/MainWindow.xaml @@ -4,6 +4,7 @@ xmlns:controls="clr-namespace:PerformanceMonitorDashboard.Controls" Title="Performance Monitor Dashboard" Height="700" Width="1400" + MinHeight="600" MinWidth="900" WindowStartupLocation="CenterScreen" WindowState="Maximized" Background="{DynamicResource BackgroundBrush}"> @@ -171,14 +172,22 @@ Margin="0,0,0,12"/> diff --git a/Dashboard/ServerTab.xaml b/Dashboard/ServerTab.xaml index a0b1d7f7..f98c1980 100644 --- a/Dashboard/ServerTab.xaml +++ b/Dashboard/ServerTab.xaml @@ -91,13 +91,13 @@ -