Skip to content

Commit 8f44811

Browse files
HannahVernonCopilot
andcommitted
Fix mute dialog showing truncated query text for blocking alerts
- Collapse newlines in Truncate/TruncateText so detail_text fields stay single-line in the label: value format - Handle multi-line query values in PopulateFromDetailText by accumulating continuation lines until the next indented field - Recognize variant query labels (Blocked Query, Blocking Query, Victim SQL) in addition to Query Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 130438a commit 8f44811

4 files changed

Lines changed: 114 additions & 8 deletions

File tree

Dashboard/MainWindow.xaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1655,7 +1655,7 @@ await _emailAlertService.TrySendAlertEmailAsync(
16551655
private static string Truncate(string text, int maxLength = 300)
16561656
{
16571657
if (string.IsNullOrEmpty(text)) return "";
1658-
text = text.Trim();
1658+
text = text.Replace('\r', ' ').Replace('\n', ' ').Trim();
16591659
return text.Length <= maxLength ? text : text.Substring(0, maxLength) + "...";
16601660
}
16611661

Dashboard/Models/MuteRule.cs

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,23 +102,76 @@ public class AlertMuteContext
102102
/// Extracts context fields (Database, Query, Wait Type, Job Name) from the
103103
/// structured detail_text stored with each alert. The format is label/value
104104
/// pairs indented with two spaces, e.g. " Database: MyDB".
105+
/// Query values may span multiple lines and use variant labels
106+
/// (Blocked Query, Blocking Query, Victim SQL).
105107
/// </summary>
106108
public void PopulateFromDetailText(string? detailText)
107109
{
108110
if (string.IsNullOrEmpty(detailText)) return;
109111

110-
foreach (var line in detailText.Split('\n'))
112+
System.Text.StringBuilder? queryBuilder = null;
113+
var lines = detailText.Split('\n');
114+
115+
foreach (var line in lines)
111116
{
112117
var trimmed = line.TrimStart();
118+
113119
if (DatabaseName == null && trimmed.StartsWith("Database: ", StringComparison.Ordinal))
120+
{
121+
FlushQuery(ref queryBuilder);
114122
DatabaseName = trimmed.Substring("Database: ".Length).Trim();
115-
else if (QueryText == null && trimmed.StartsWith("Query: ", StringComparison.Ordinal))
116-
QueryText = trimmed.Substring("Query: ".Length).Trim();
123+
}
117124
else if (WaitType == null && trimmed.StartsWith("Wait Type: ", StringComparison.Ordinal))
125+
{
126+
FlushQuery(ref queryBuilder);
118127
WaitType = trimmed.Substring("Wait Type: ".Length).Trim();
128+
}
119129
else if (JobName == null && trimmed.StartsWith("Job Name: ", StringComparison.Ordinal))
130+
{
131+
FlushQuery(ref queryBuilder);
120132
JobName = trimmed.Substring("Job Name: ".Length).Trim();
133+
}
134+
else if (QueryText == null && queryBuilder == null && TryExtractQueryValue(trimmed, out var qv))
135+
{
136+
queryBuilder = new System.Text.StringBuilder(qv);
137+
}
138+
else if (queryBuilder != null)
139+
{
140+
// Continuation lines from multi-line query values don't start
141+
// with the two-space indent used by ContextToDetailText fields.
142+
if (string.IsNullOrWhiteSpace(trimmed) || line.StartsWith(" ", StringComparison.Ordinal))
143+
{
144+
FlushQuery(ref queryBuilder);
145+
}
146+
else
147+
{
148+
queryBuilder.Append(' ').Append(trimmed.Trim());
149+
}
150+
}
151+
}
152+
153+
FlushQuery(ref queryBuilder);
154+
}
155+
156+
private void FlushQuery(ref System.Text.StringBuilder? builder)
157+
{
158+
if (builder != null && QueryText == null)
159+
QueryText = builder.ToString();
160+
builder = null;
161+
}
162+
163+
private static bool TryExtractQueryValue(string trimmed, out string value)
164+
{
165+
foreach (var prefix in new[] { "Query: ", "Blocked Query: ", "Blocking Query: ", "Victim SQL: " })
166+
{
167+
if (trimmed.StartsWith(prefix, StringComparison.Ordinal))
168+
{
169+
value = trimmed.Substring(prefix.Length).Trim();
170+
return true;
171+
}
121172
}
173+
value = "";
174+
return false;
122175
}
123176
}
124177
}

Lite/MainWindow.xaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1635,7 +1635,7 @@ await _emailAlertService.TrySendAlertEmailAsync(
16351635
private static string TruncateText(string text, int maxLength = 300)
16361636
{
16371637
if (string.IsNullOrEmpty(text)) return "";
1638-
text = text.Trim();
1638+
text = text.Replace('\r', ' ').Replace('\n', ' ').Trim();
16391639
return text.Length <= maxLength ? text : text.Substring(0, maxLength) + "...";
16401640
}
16411641

Lite/Models/MuteRule.cs

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,22 +102,75 @@ public class AlertMuteContext
102102
/// Extracts context fields (Database, Query, Wait Type, Job Name) from the
103103
/// structured detail_text stored with each alert. The format is label/value
104104
/// pairs indented with two spaces, e.g. " Database: MyDB".
105+
/// Query values may span multiple lines and use variant labels
106+
/// (Blocked Query, Blocking Query, Victim SQL).
105107
/// </summary>
106108
public void PopulateFromDetailText(string? detailText)
107109
{
108110
if (string.IsNullOrEmpty(detailText)) return;
109111

110-
foreach (var line in detailText.Split('\n'))
112+
System.Text.StringBuilder? queryBuilder = null;
113+
var lines = detailText.Split('\n');
114+
115+
foreach (var line in lines)
111116
{
112117
var trimmed = line.TrimStart();
118+
113119
if (DatabaseName == null && trimmed.StartsWith("Database: ", StringComparison.Ordinal))
120+
{
121+
FlushQuery(ref queryBuilder);
114122
DatabaseName = trimmed.Substring("Database: ".Length).Trim();
115-
else if (QueryText == null && trimmed.StartsWith("Query: ", StringComparison.Ordinal))
116-
QueryText = trimmed.Substring("Query: ".Length).Trim();
123+
}
117124
else if (WaitType == null && trimmed.StartsWith("Wait Type: ", StringComparison.Ordinal))
125+
{
126+
FlushQuery(ref queryBuilder);
118127
WaitType = trimmed.Substring("Wait Type: ".Length).Trim();
128+
}
119129
else if (JobName == null && trimmed.StartsWith("Job Name: ", StringComparison.Ordinal))
130+
{
131+
FlushQuery(ref queryBuilder);
120132
JobName = trimmed.Substring("Job Name: ".Length).Trim();
133+
}
134+
else if (QueryText == null && queryBuilder == null && TryExtractQueryValue(trimmed, out var qv))
135+
{
136+
queryBuilder = new System.Text.StringBuilder(qv);
137+
}
138+
else if (queryBuilder != null)
139+
{
140+
// Continuation lines from multi-line query values don't start
141+
// with the two-space indent used by ContextToDetailText fields.
142+
if (string.IsNullOrWhiteSpace(trimmed) || line.StartsWith(" ", StringComparison.Ordinal))
143+
{
144+
FlushQuery(ref queryBuilder);
145+
}
146+
else
147+
{
148+
queryBuilder.Append(' ').Append(trimmed.Trim());
149+
}
150+
}
151+
}
152+
153+
FlushQuery(ref queryBuilder);
154+
}
155+
156+
private void FlushQuery(ref System.Text.StringBuilder? builder)
157+
{
158+
if (builder != null && QueryText == null)
159+
QueryText = builder.ToString();
160+
builder = null;
161+
}
162+
163+
private static bool TryExtractQueryValue(string trimmed, out string value)
164+
{
165+
foreach (var prefix in new[] { "Query: ", "Blocked Query: ", "Blocking Query: ", "Victim SQL: " })
166+
{
167+
if (trimmed.StartsWith(prefix, StringComparison.Ordinal))
168+
{
169+
value = trimmed.Substring(prefix.Length).Trim();
170+
return true;
171+
}
121172
}
173+
value = "";
174+
return false;
122175
}
123176
}

0 commit comments

Comments
 (0)