From c4b276bc38331a6936321ffdc7d6914e69c29282 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Mon, 30 Mar 2026 20:51:30 -0400 Subject: [PATCH] Release v2.5.0: version bumps, changelog, bug fixes found during release testing - Bump all project versions to 2.5.0 - Add v2.5.0 changelog entry and README sync (Overview tab, query heatmap, webhooks, chart drill-down) - Fix PERCENTILE_CONT missing OVER() in FinOps VM right-sizing query - Fix database_id column reference on AWS RDS sys.dm_db_persisted_sku_features - Fix FinOps right-click copy/export broken on all Dashboard grids (PlacementTarget was row, not grid) - Add server name to FinOps recommendation error logs - Add null guard for _dbSizesFilterMgr race condition Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 86 ++++++++++++++++++++ Dashboard/Controls/FinOpsContent.xaml.cs | 25 ++++-- Dashboard/Dashboard.csproj | 8 +- Dashboard/Services/DatabaseService.FinOps.cs | 41 ++++++---- Dashboard/Services/DatabaseService.cs | 6 ++ Installer.Core/Installer.Core.csproj | 8 +- Installer/PerformanceMonitorInstaller.csproj | 8 +- Lite/PerformanceMonitorLite.csproj | 8 +- README.md | 16 ++-- 9 files changed, 161 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f8b28fe..36b6afdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,92 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.5.0] - 2026-03-30 + +### Important + +- **InstallerGui retired**: The standalone GUI installer has been removed. Installation, upgrade, and uninstall are now handled directly from the Dashboard's Add Server dialog, powered by the new Installer.Core shared library. The CLI installer continues to work as before. ([#755]) + +### Added + +- **Dashboard integrated installer** — Add Server dialog now installs, upgrades, and uninstalls PerformanceMonitor directly, replacing the standalone InstallerGui ([#755]) +- **Installer.Core shared library** — shared installation logic used by both the CLI installer and Dashboard ([#755]) +- **Overview tab** for Lite with 2x2 resource chart grid (CPU, Memory, Wait Stats, TempDB) ([#689]) +- **Chart drill-down** on CPU, Memory, TempDB, Blocking, and Deadlock charts in both Dashboard and Lite — right-click any chart point to jump to Active Queries for that time window ([#682]) +- **Grid-to-slicer overlay** for Query Stats, Procedure Stats, and Query Store tabs — click a row to overlay its trend on the slicer chart ([#683]) +- **Query heatmap** tab in both Dashboard and Lite — visual heat map of query activity over time ([#739], [#743]) +- **Webhook notifications** for alerts — configurable webhook endpoint for alert delivery ([#725]) +- **Per-server collector schedule intervals** — customize collection frequency per server ([#703]) +- **Investigate button** in Critical Issues grid — jump directly to relevant tab from an alert ([#684]) +- **Dismiss Selected** context menu and View Log sidebar button for alert management ([#718], [#740]) +- **Alert archival awareness** — dismissed_archive_alerts sidecar table, source column for live vs archived alerts, stale-data indicator, structured telemetry ([#718]) +- **Dashboard read-only connection intent** — connections use `ApplicationIntent=ReadOnly` where supported ([#728]) +- FUNDING.yml for GitHub Sponsors ([#752]) + +### Changed + +- **Installer architecture** refactored: CLI installer is now a thin wrapper over Installer.Core ([#755]) +- **DuckDB memory capped** at 2 GB during parquet compaction to prevent out-of-memory on large archives ([#758]) +- **Text rendering** improved with `TextOptions.TextFormattingMode="Display"` for sharper text ([#710]) +- **installation_history version columns** widened from nvarchar(255) to nvarchar(512) to handle long @@VERSION strings ([#712]) + +### Fixed + +- **Memory leaks in Lite** — delta cache, event handlers, and chart helpers properly disposed ([#758]) +- **Doomed transaction errors** in delta framework and ensure_collection_table — ROLLBACK now occurs before error logging ([#756]) +- **XACT_STATE check** added after third-party stored procedure calls (sp_HumanEventsBlockViewer, sp_BlitzLock) to prevent doomed transaction errors ([#695]) +- **CREATE DATABASE failure** when model database has large default file sizes ([#676]) +- **CPU metrics mixed** for different Azure SQL databases on the same logical server ([#680]) +- **Azure SQL DB vCore** FinOps calculations incorrect for serverless/vCore tiers ([#736]) +- **Webhook alert recording** not persisting correctly ([#726]) +- **Drill-down timezone** misalignment between chart and detail view ([#747], [#750]) +- **Drill-down refresh** losing context on auto-refresh ([#744]) +- **Drill-down target** incorrectly routing Memory to Memory Grants instead of Active Queries ([#706]) +- **Heatmap colorbar stacking** when switching between servers ([#746]) +- **Display mode pickers** not reflecting current state on tab switch ([#751]) +- **Slicer custom range** handling and sub-hour display issues ([#704]) +- **Overlay selection** lost on Dashboard auto-refresh ([#683]) +- **Numeric values** in alert details treated as strings instead of numbers ([#732]) +- **FinOps VM right-sizing** query error — `PERCENTILE_CONT` missing required `OVER()` clause +- **FinOps Enterprise features** query error on AWS RDS — `database_id` column not present in `sys.dm_db_persisted_sku_features` on RDS +- **FinOps right-click copy** broken on all Dashboard FinOps grids — context menu walked to row instead of grid +- **FinOps recommendation error logs** now include server name for easier troubleshooting + +### Deprecated + +- **InstallerGui** — removed from the solution and build pipeline. Use the Dashboard or CLI installer instead. ([#755]) + +[#676]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/676 +[#680]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/680 +[#682]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/682 +[#683]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/683 +[#684]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/684 +[#689]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/689 +[#695]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/695 +[#703]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/703 +[#704]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/704 +[#706]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/706 +[#710]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/710 +[#712]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/712 +[#718]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/718 +[#725]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/725 +[#726]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/726 +[#728]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/728 +[#732]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/732 +[#736]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/736 +[#739]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/739 +[#740]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/740 +[#743]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/743 +[#744]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/744 +[#746]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/746 +[#747]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/747 +[#750]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/750 +[#751]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/751 +[#752]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/752 +[#755]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/755 +[#756]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/756 +[#758]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/758 + ## [2.4.0] - 2026-03-23 ### Important diff --git a/Dashboard/Controls/FinOpsContent.xaml.cs b/Dashboard/Controls/FinOpsContent.xaml.cs index e2a3a6c8..0ef75be1 100644 --- a/Dashboard/Controls/FinOpsContent.xaml.cs +++ b/Dashboard/Controls/FinOpsContent.xaml.cs @@ -630,7 +630,8 @@ private async Task LoadDatabaseSizesAsync() } } - _dbSizesFilterMgr!.UpdateData(data); + if (_dbSizesFilterMgr == null) return; + _dbSizesFilterMgr.UpdateData(data); UpdateDbSizeCountUI(); } catch (Exception ex) @@ -901,11 +902,22 @@ private async void ServerInventoryRefresh_Click(object sender, RoutedEventArgs e // Copy / Export Context Menu Handlers // ============================================ + private static DataGrid? FindParentDataGrid(DependencyObject? element) + { + while (element != null) + { + if (element is DataGrid dg) return dg; + element = VisualTreeHelper.GetParent(element); + } + return null; + } + private void CopyCell_Click(object sender, RoutedEventArgs e) { if (sender is MenuItem menuItem && menuItem.Parent is ContextMenu contextMenu) { - if (contextMenu.PlacementTarget is DataGrid grid && grid.CurrentCell.Column != null) + var grid = FindParentDataGrid(contextMenu.PlacementTarget); + if (grid != null && grid.CurrentCell.Column != null) { var cellContent = TabHelpers.GetCellContent(grid, grid.CurrentCell); if (!string.IsNullOrEmpty(cellContent)) @@ -921,7 +933,8 @@ private void CopyRow_Click(object sender, RoutedEventArgs e) { if (sender is MenuItem menuItem && menuItem.Parent is ContextMenu contextMenu) { - if (contextMenu.PlacementTarget is DataGrid grid && grid.SelectedItem != null) + var grid = FindParentDataGrid(contextMenu.PlacementTarget); + if (grid != null && grid.SelectedItem != null) { var rowText = TabHelpers.GetRowAsText(grid, grid.SelectedItem); if (!string.IsNullOrEmpty(rowText)) @@ -937,7 +950,8 @@ private void CopyAllRows_Click(object sender, RoutedEventArgs e) { if (sender is MenuItem menuItem && menuItem.Parent is ContextMenu contextMenu) { - if (contextMenu.PlacementTarget is DataGrid grid) + var grid = FindParentDataGrid(contextMenu.PlacementTarget); + if (grid != null) { var sb = new StringBuilder(); @@ -975,7 +989,8 @@ private void ExportToCsv_Click(object sender, RoutedEventArgs e) { if (sender is MenuItem menuItem && menuItem.Parent is ContextMenu contextMenu) { - if (contextMenu.PlacementTarget is DataGrid grid) + var grid = FindParentDataGrid(contextMenu.PlacementTarget); + if (grid != null) { var dialog = new SaveFileDialog { diff --git a/Dashboard/Dashboard.csproj b/Dashboard/Dashboard.csproj index fe6f7ff6..a3585aa5 100644 --- a/Dashboard/Dashboard.csproj +++ b/Dashboard/Dashboard.csproj @@ -7,10 +7,10 @@ PerformanceMonitorDashboard.Program PerformanceMonitorDashboard SQL Server Performance Monitor Dashboard - 2.4.1 - 2.4.1.0 - 2.4.1.0 - 2.4.1 + 2.5.0 + 2.5.0.0 + 2.5.0.0 + 2.5.0 Darling Data, LLC Copyright © 2026 Darling Data, LLC EDD.ico diff --git a/Dashboard/Services/DatabaseService.FinOps.cs b/Dashboard/Services/DatabaseService.FinOps.cs index 19901380..5f0db5d6 100644 --- a/Dashboard/Services/DatabaseService.FinOps.cs +++ b/Dashboard/Services/DatabaseService.FinOps.cs @@ -1841,11 +1841,18 @@ public async Task> GetFinOpsRecommendationsAsync(deci if (edition.Contains("Enterprise", StringComparison.OrdinalIgnoreCase)) { - using var featCmd = new SqlCommand(@" -SELECT - DB_NAME(database_id) AS database_name, - feature_name -FROM sys.dm_db_persisted_sku_features", connection); + var hasDatabaseId = false; + using (var colCheck = new SqlCommand( + "SELECT COL_LENGTH('sys.dm_db_persisted_sku_features', 'database_id')", connection)) + { + colCheck.CommandTimeout = 10; + hasDatabaseId = await colCheck.ExecuteScalarAsync() is not null and not DBNull; + } + + var featSql = hasDatabaseId + ? "SELECT DB_NAME(database_id) AS database_name, feature_name FROM sys.dm_db_persisted_sku_features" + : "SELECT N'(unknown)' AS database_name, feature_name FROM sys.dm_db_persisted_sku_features"; + using var featCmd = new SqlCommand(featSql, connection); featCmd.CommandTimeout = 30; var features = new List(); @@ -1909,7 +1916,7 @@ public async Task> GetFinOpsRecommendationsAsync(deci } catch (Exception ex) { - Logger.Error($"Recommendation check failed (Enterprise features): {ex.Message}", ex); + Logger.Error($"[{ServerLabel}] Recommendation check failed (Enterprise features): {ex.Message}", ex); } // 2. CPU right-sizing score @@ -1953,7 +1960,7 @@ FROM report.finops_utilization_efficiency AS v } catch (Exception ex) { - Logger.Error($"Recommendation check failed (CPU right-sizing): {ex.Message}", ex); + Logger.Error($"[{ServerLabel}] Recommendation check failed (CPU right-sizing): {ex.Message}", ex); } // 3. Memory right-sizing score @@ -1997,7 +2004,7 @@ ORDER BY ms.collection_time DESC } catch (Exception ex) { - Logger.Error($"Recommendation check failed (Memory right-sizing): {ex.Message}", ex); + Logger.Error($"[{ServerLabel}] Recommendation check failed (Memory right-sizing): {ex.Message}", ex); } // 4. Unused index cost quantification @@ -2019,7 +2026,7 @@ ORDER BY ms.collection_time DESC } catch (Exception ex) { - Logger.Error($"Recommendation check failed (Index analysis): {ex.Message}", ex); + Logger.Error($"[{ServerLabel}] Recommendation check failed (Index analysis): {ex.Message}", ex); } // 5. Compression savings estimator @@ -2086,7 +2093,7 @@ size_mb DESC } catch (Exception ex) { - Logger.Error($"Recommendation check failed (Compression): {ex.Message}", ex); + Logger.Error($"[{ServerLabel}] Recommendation check failed (Compression): {ex.Message}", ex); } // 6. Dormant database detection with cost impact @@ -2122,7 +2129,7 @@ size_mb DESC } catch (Exception ex) { - Logger.Error($"Recommendation check failed (Dormant databases): {ex.Message}", ex); + Logger.Error($"[{ServerLabel}] Recommendation check failed (Dormant databases): {ex.Message}", ex); } // 7. Dev/test workload detection @@ -2159,7 +2166,7 @@ FROM sys.databases } catch (Exception ex) { - Logger.Error($"Recommendation check failed (Dev/test detection): {ex.Message}", ex); + Logger.Error($"[{ServerLabel}] Recommendation check failed (Dev/test detection): {ex.Message}", ex); } // 11. Maintenance window efficiency — jobs running long @@ -2206,7 +2213,7 @@ HAVING SUM(CAST(is_running_long AS int)) >= 3 } catch (Exception ex) { - Logger.Error($"Recommendation check failed (Maintenance window): {ex.Message}", ex); + Logger.Error($"[{ServerLabel}] Recommendation check failed (Maintenance window): {ex.Message}", ex); } // 12. VM right-sizing — prescriptive core/memory targets @@ -2215,7 +2222,7 @@ HAVING SUM(CAST(is_running_long AS int)) >= 3 using var vmCmd = new SqlCommand(@" SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SELECT - p95_cpu = (SELECT PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY cus.sqlserver_cpu_utilization) + p95_cpu = (SELECT TOP (1) PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY cus.sqlserver_cpu_utilization) OVER () FROM collect.cpu_utilization_stats AS cus WHERE cus.collection_time >= DATEADD(DAY, -7, SYSDATETIME())), cpu_count = (SELECT si.cpu_count FROM sys.dm_os_sys_info AS si), @@ -2291,7 +2298,7 @@ FROM sys.dm_os_performance_counters AS pc } catch (Exception ex) { - Logger.Error($"Recommendation check failed (VM right-sizing): {ex.Message}", ex); + Logger.Error($"[{ServerLabel}] Recommendation check failed (VM right-sizing): {ex.Message}", ex); } // 13. Storage tier optimization — flag databases with low IO latency @@ -2353,7 +2360,7 @@ ORDER BY } catch (Exception ex) { - Logger.Error($"Recommendation check failed (Storage tier): {ex.Message}", ex); + Logger.Error($"[{ServerLabel}] Recommendation check failed (Storage tier): {ex.Message}", ex); } // 14. Reserved capacity candidates — stable CPU utilization @@ -2398,7 +2405,7 @@ HAVING COUNT(*) >= 24 } catch (Exception ex) { - Logger.Error($"Recommendation check failed (Reserved capacity): {ex.Message}", ex); + Logger.Error($"[{ServerLabel}] Recommendation check failed (Reserved capacity): {ex.Message}", ex); } return recommendations; diff --git a/Dashboard/Services/DatabaseService.cs b/Dashboard/Services/DatabaseService.cs index ef2e35d8..afdd2e8d 100644 --- a/Dashboard/Services/DatabaseService.cs +++ b/Dashboard/Services/DatabaseService.cs @@ -37,6 +37,12 @@ public DatabaseService(string connectionString) /// public string ConnectionString => _connectionString; + /// + /// Server name extracted from the connection string, for use in log messages. + /// + private string ServerLabel => + new SqlConnectionStringBuilder(_connectionString).DataSource ?? "(unknown)"; + /// /// Opens a throttled database connection. The semaphore is released when the connection is disposed. /// diff --git a/Installer.Core/Installer.Core.csproj b/Installer.Core/Installer.Core.csproj index d203bfe7..6c4ca000 100644 --- a/Installer.Core/Installer.Core.csproj +++ b/Installer.Core/Installer.Core.csproj @@ -7,10 +7,10 @@ Installer.Core Installer.Core SQL Server Performance Monitor Installer Core - 2.4.1 - 2.4.1.0 - 2.4.1.0 - 2.4.1 + 2.5.0 + 2.5.0.0 + 2.5.0.0 + 2.5.0 Darling Data, LLC Copyright (c) 2026 Darling Data, LLC true diff --git a/Installer/PerformanceMonitorInstaller.csproj b/Installer/PerformanceMonitorInstaller.csproj index 2986f975..2f58b546 100644 --- a/Installer/PerformanceMonitorInstaller.csproj +++ b/Installer/PerformanceMonitorInstaller.csproj @@ -20,10 +20,10 @@ PerformanceMonitorInstaller SQL Server Performance Monitor Installer - 2.4.1 - 2.4.1.0 - 2.4.1.0 - 2.4.1 + 2.5.0 + 2.5.0.0 + 2.5.0.0 + 2.5.0 Darling Data, LLC Copyright © 2026 Darling Data, LLC Installation utility for SQL Server Performance Monitor - Supports SQL Server 2016-2025 diff --git a/Lite/PerformanceMonitorLite.csproj b/Lite/PerformanceMonitorLite.csproj index aa35371b..5ccd3c34 100644 --- a/Lite/PerformanceMonitorLite.csproj +++ b/Lite/PerformanceMonitorLite.csproj @@ -8,10 +8,10 @@ PerformanceMonitorLite PerformanceMonitorLite SQL Server Performance Monitor Lite - 2.4.1 - 2.4.1.0 - 2.4.1.0 - 2.4.1 + 2.5.0 + 2.5.0.0 + 2.5.0.0 + 2.5.0 Darling Data, LLC Copyright © 2026 Darling Data, LLC Lightweight SQL Server performance monitoring - no installation required on target servers diff --git a/README.md b/README.md index b9d40d86..bdfd3b23 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ | **Requires** | SQL Agent running ([see permissions](#permissions)) | `VIEW SERVER STATE` ([see permissions](#permissions)) | | **Get started** | Run the installer, open the dashboard | Download, run, add a server, done | -Both editions include real-time alerts (system tray + email), charts and graphs, dark and light themes, CSV export, and a built-in MCP server for AI-powered analysis with tools like Claude. +Both editions include real-time alerts (system tray + email + webhooks), charts and graphs, dark and light themes, CSV export, and a built-in MCP server for AI-powered analysis with tools like Claude. All release binaries are digitally signed via [SignPath](https://signpath.io) — no more Windows SmartScreen warnings. @@ -42,7 +42,7 @@ All release binaries are digitally signed via [SignPath](https://signpath.io) 🔍 **32 specialized T-SQL collectors** running on configurable schedules with named presets (Aggressive, Balanced, Low-Impact) — wait stats, query performance, blocking chains, deadlock graphs, memory grants, file I/O, tempdb, perfmon counters, FinOps/capacity, and more. Query text and execution plan collection can be disabled per-collector for sensitive environments. -🚨 **Real-time alerts** for blocking, deadlocks, and high CPU — system tray notifications plus styled HTML emails with full XML attachments for offline analysis +🚨 **Real-time alerts** for blocking, deadlocks, and high CPU — system tray notifications, styled HTML emails with full XML attachments, and webhook notifications for external integrations 📊 **NOC-style dashboard** with green/yellow/red health cards, auto-refresh, configurable time ranges, and dark/light themes @@ -301,7 +301,7 @@ The Full Edition supports Azure SQL Managed Instance and AWS RDS for SQL Server | Graphical plan viewer | Built-in with 30-rule PlanAnalyzer | Built-in with 30-rule PlanAnalyzer | | Standalone plan viewer | Open/paste/drag `.sqlplan` files | Open/paste/drag `.sqlplan` files | | Community tools (sp_WhoIsActive, sp_BlitzLock) | Installed automatically | Not needed | -| Alerts (tray + email) | Blocking, deadlocks, CPU | Blocking, deadlocks, CPU | +| Alerts (tray + email + webhooks) | Blocking, deadlocks, CPU | Blocking, deadlocks, CPU | | Dashboard | Separate app | Built-in | | Themes | Dark and light | Dark and light | | Portability | Server-bound | Single executable | @@ -316,7 +316,7 @@ The Full Edition supports Azure SQL Managed Instance and AWS RDS for SQL Server | Tab | Contents | |---|---| | **Overview** | Resource overview, daily summary, critical issues, server config changes, database config changes, trace flag changes, collection health | -| **Performance** | Performance trends, expensive queries, active queries, query stats, procedure stats, Query Store, Query Store regressions, query trace patterns | +| **Performance** | Performance trends, expensive queries, active queries, query stats, procedure stats, Query Store, Query Store regressions, query trace patterns, query heatmap | | **Resource Metrics** | Server trends, wait stats, TempDB, file I/O latency, perfmon counters, default trace events, trace analysis, session stats, latch stats, spinlock stats | | **Memory** | Memory overview, grants, clerks, plan cache, memory pressure events | | **Locking** | Blocking chains, deadlocks, blocking/deadlock trends | @@ -328,11 +328,12 @@ Plus a NOC-style landing page with server health cards (green/yellow/red severit | Tab | Contents | |---|---| +| **Overview** | 2x2 resource chart grid (CPU, Memory, Wait Stats, TempDB) with drill-down | | **Active Queries** | Running queries with session details, wait types, blocking, DOP, memory grants | | **Wait Stats** | Filterable wait statistics chart with delta calculations | | **CPU** | SQL Server CPU vs Other Processes over time | | **Memory** | Physical memory overview, SQL Server memory trend, memory clerk breakdown | -| **Queries** | Performance trends, top queries and procedures by duration, Query Store integration | +| **Queries** | Performance trends, top queries and procedures by duration, Query Store integration, query heatmap | | **File I/O** | Read/write I/O trends per database file | | **TempDB** | Space usage breakdown and TempDB file I/O | | **Blocking** | Blocking/deadlock trends, blocked process reports, deadlock history | @@ -340,7 +341,7 @@ Plus a NOC-style landing page with server health cards (green/yellow/red severit | **Configuration** | Server configuration, database configuration, scoped configuration, trace flags | | **FinOps** | Utilization & provisioning analysis, database resource breakdown, storage growth (7d/30d), idle database detection, index analysis via sp_IndexCleanup, application connections, server inventory, cost optimization recommendations (enterprise feature audit, CPU/memory right-sizing, compression savings, dormant databases, dev/test detection), column-level filtering on all grids | -Both editions feature auto-refresh, configurable time ranges, right-click CSV export, system tray integration, dark and light themes, and timezone display options (server time, local time, or UTC). +Both editions feature auto-refresh, configurable time ranges, chart drill-down to Active Queries, right-click CSV export, system tray integration, dark and light themes, and timezone display options (server time, local time, or UTC). --- @@ -369,8 +370,9 @@ All thresholds are configurable in Settings. - **System tray** — balloon notifications with a configurable per-metric cooldown (default: 5 minutes) - **Email (SMTP)** — styled HTML emails with a configurable per-metric cooldown (default: 15 minutes), plus configurable SMTP settings (server, port, SSL, authentication, recipients) +- **Webhook** — HTTP POST to a configurable endpoint for integration with external alerting systems (Slack, Teams, PagerDuty, etc.) -Both cooldown periods are independently configurable in Settings under the Performance Alerts section. +All cooldown periods are independently configurable in Settings under the Performance Alerts section. ### Email Alerts