Fix incorrect rendering of counters (#3816)
authorTom McDonald <tommcdon@microsoft.com>
Tue, 18 Apr 2023 03:07:57 +0000 (23:07 -0400)
committerGitHub <noreply@github.com>
Tue, 18 Apr 2023 03:07:57 +0000 (23:07 -0400)
* Fix incorrect rendering of counters

* Fixing IDE0008 Use explicit type instead of 'var' build error

* PR feedback and truncate output for multiple providers

* Simplify counter refresh console output

* Better drawing for very small console height screens

* Remove lambda to avoid unnecessary memory allocations

src/Tools/dotnet-counters/Exporters/ConsoleWriter.cs

index ded01c7d8f395691e6702939ccf3c77fed18ba82..76f06931f736475e6d01c3b6aadf79ab7c147ceb 100644 (file)
@@ -28,8 +28,13 @@ namespace Microsoft.Diagnostics.Tools.Counters.Exporters
             public readonly CounterProvider KnownProvider;
         }
 
+        private interface ICounterRow
+        {
+            int Row { get; set; }
+        }
+
         /// <summary>Information about an observed counter.</summary>
-        private class ObservedCounter
+        private class ObservedCounter : ICounterRow
         {
             public ObservedCounter(string displayName) => DisplayName = displayName;
             public string DisplayName { get; } // Display name for this counter.
@@ -41,7 +46,7 @@ namespace Microsoft.Diagnostics.Tools.Counters.Exporters
             public double LastValue { get; set; }
         }
 
-        private class ObservedTagSet
+        private class ObservedTagSet : ICounterRow
         {
             public ObservedTagSet(string tags)
             {
@@ -128,6 +133,19 @@ namespace Microsoft.Diagnostics.Tools.Counters.Exporters
         {
             Clear();
 
+            // clear row data on all counters
+            foreach (ObservedProvider provider in _providers.Values)
+            {
+                foreach (ObservedCounter counter in provider.Counters.Values)
+                {
+                    counter.Row = -1;
+                    foreach (ObservedTagSet tagSet in counter.TagSets.Values)
+                    {
+                        tagSet.Row = -1;
+                    }
+                }
+            }
+
             _consoleWidth = Console.WindowWidth;
             _consoleHeight = Console.WindowHeight;
             _maxNameLength = Math.Max(Math.Min(80, _consoleWidth) - (CounterValueLength + Indent + 1), 0); // Truncate the name to prevent line wrapping as long as the console width is >= CounterValueLength + Indent + 1 characters
@@ -144,38 +162,67 @@ namespace Microsoft.Diagnostics.Tools.Counters.Exporters
                 Console.WriteLine(_errorText);
                 row += GetLineWrappedLines(_errorText);
             }
-            Console.WriteLine(); row++; // Blank line.
 
-            foreach (ObservedProvider provider in _providers.Values.OrderBy(p => p.KnownProvider == null).ThenBy(p => p.Name)) // Known providers first.
+            bool RenderRow(ref int row, string lineOutput = null, ICounterRow counterRow = null)
             {
-                Console.WriteLine($"[{provider.Name}]"); row++;
+                if (row >= _consoleHeight + _topRow) // prevents from displaying more counters than vertical space available
+                {
+                    return false;
+                }
+
+                if (lineOutput != null)
+                {
+                    Console.Write(lineOutput);
+                }
 
-                foreach (ObservedCounter counter in provider.Counters.Values.OrderBy(c => c.DisplayName))
+                if (row < _consoleHeight + _topRow - 1) // prevents screen from scrolling due to newline on last line of console
                 {
+                    Console.WriteLine();
+                }
+
+                if (counterRow != null)
+                {
+                    counterRow.Row = row;
+                }
+
+                row++;
 
-                    string name = MakeFixedWidth($"{new string(' ', Indent)}{counter.DisplayName}", Indent + _maxNameLength);
-                    counter.Row = row++;
-                    if (counter.RenderValueInline)
+                return true;
+            }
+
+            if (RenderRow(ref row)) // Blank line.
+            {
+                foreach (ObservedProvider provider in _providers.Values.OrderBy(p => p.KnownProvider == null).ThenBy(p => p.Name)) // Known providers first.
+                {
+                    if (!RenderRow(ref row, $"[{provider.Name}]"))
                     {
-                        if (row >= _consoleHeight) // prevents from displaying more counters than vertical space available
-                        {
-                            break;
-                        }
-                        Console.WriteLine($"{name} {FormatValue(counter.LastValue)}");
+                        break;
                     }
-                    else
+
+                    foreach (ObservedCounter counter in provider.Counters.Values.OrderBy(c => c.DisplayName))
                     {
-                        Console.WriteLine(name);
-                        foreach (ObservedTagSet tagSet in counter.TagSets.Values.OrderBy(t => t.Tags))
+                        string name = MakeFixedWidth($"{new string(' ', Indent)}{counter.DisplayName}", Indent + _maxNameLength);
+                        if (counter.RenderValueInline)
                         {
-                            if (row >= _consoleHeight)
+                            if (!RenderRow(ref row, $"{name} {FormatValue(counter.LastValue)}", counter))
                             {
                                 break;
                             }
-
-                            string tagName = MakeFixedWidth($"{new string(' ', 2 * Indent)}{tagSet.Tags}", Indent + _maxNameLength);
-                            Console.WriteLine($"{tagName} {FormatValue(tagSet.LastValue)}");
-                            tagSet.Row = row++;
+                        }
+                        else
+                        {
+                            if (!RenderRow(ref row, name, counter))
+                            {
+                                break;
+                            }
+                            foreach (ObservedTagSet tagSet in counter.TagSets.Values.OrderBy(t => t.Tags))
+                            {
+                                string tagName = MakeFixedWidth($"{new string(' ', 2 * Indent)}{tagSet.Tags}", Indent + _maxNameLength);
+                                if (!RenderRow(ref row, $"{tagName} {FormatValue(tagSet.LastValue)}", tagSet))
+                                {
+                                    break;
+                                }
+                            }
                         }
                     }
                 }
@@ -253,6 +300,10 @@ namespace Microsoft.Diagnostics.Tools.Counters.Exporters
                 }
 
                 int row = counter.RenderValueInline ? counter.Row : tagSet.Row;
+                if (row < 0)
+                {
+                    return;
+                }
                 SetCursorPosition(Indent + _maxNameLength + 1, row);
                 Console.Write(FormatValue(payload.Value));
             }