|
| 1 | +using System.Reactive.Linq; |
| 2 | +using Azure.Identity; |
| 3 | +using Azure.Messaging.ServiceBus; |
| 4 | +using ServiceBusToolset.Models; |
| 5 | +using ServiceBusToolset.Options; |
| 6 | +using ServiceBusToolset.Services; |
| 7 | +using Spectre.Console; |
| 8 | + |
| 9 | +namespace ServiceBusToolset.Commands; |
| 10 | + |
| 11 | +public class MonitorQueuesCommand(IQueueMonitorService monitorService, IConsoleOutput output) : ICommand<MonitorQueuesOptions> |
| 12 | +{ |
| 13 | + public async Task<int> ExecuteAsync(MonitorQueuesOptions options, CancellationToken cancellationToken = default) |
| 14 | + { |
| 15 | + var validationError = options.Validate(); |
| 16 | + if (validationError != null) |
| 17 | + { |
| 18 | + output.Error(validationError); |
| 19 | + return 1; |
| 20 | + } |
| 21 | + |
| 22 | + try |
| 23 | + { |
| 24 | + output.Info($"Connecting to Service Bus namespace: {options.Namespace}"); |
| 25 | + if (!string.IsNullOrEmpty(options.Filter)) |
| 26 | + { |
| 27 | + output.Info($"Filter: {options.Filter}"); |
| 28 | + } |
| 29 | + |
| 30 | + output.Info($"Refresh interval: {options.RefreshInterval} seconds"); |
| 31 | + output.Info("Press Ctrl+C to stop monitoring."); |
| 32 | + output.Info(""); |
| 33 | + |
| 34 | + var refreshInterval = TimeSpan.FromSeconds(options.RefreshInterval); |
| 35 | + |
| 36 | + await monitorService |
| 37 | + .ObserveQueues(options.Namespace, |
| 38 | + options.Filter, |
| 39 | + refreshInterval, |
| 40 | + cancellationToken) |
| 41 | + .ForEachAsync(stats => |
| 42 | + { |
| 43 | + Console.Clear(); |
| 44 | + var table = CreateTable(stats); |
| 45 | + AnsiConsole.Write(table); |
| 46 | + |
| 47 | + if (options.Verbose) |
| 48 | + { |
| 49 | + output.Verbose($"Updated at {DateTimeOffset.Now:HH:mm:ss} - {stats.Count} queues", options.Verbose); |
| 50 | + } |
| 51 | + }, |
| 52 | + cancellationToken); |
| 53 | + |
| 54 | + return 0; |
| 55 | + } |
| 56 | + catch (AuthenticationFailedException ex) |
| 57 | + { |
| 58 | + output.Error($"Authentication failed: {ex.Message}"); |
| 59 | + output.Error("Ensure you are logged in with 'az login' or have valid environment credentials."); |
| 60 | + return 1; |
| 61 | + } |
| 62 | + catch (ServiceBusException ex) |
| 63 | + { |
| 64 | + output.Error($"Service Bus error: {ex.Message}"); |
| 65 | + output.Verbose($"Reason: {ex.Reason}", options.Verbose); |
| 66 | + return 1; |
| 67 | + } |
| 68 | + catch (OperationCanceledException) |
| 69 | + { |
| 70 | + output.Info(""); |
| 71 | + output.Info("Monitoring stopped."); |
| 72 | + return 0; |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + private static Table CreateTable(IReadOnlyList<QueueStatistics> statistics) |
| 77 | + { |
| 78 | + var table = new Table() |
| 79 | + .Border(TableBorder.Rounded) |
| 80 | + .Title("[bold blue]Service Bus Queue Monitor[/]") |
| 81 | + .AddColumn(new TableColumn("[bold]Queue Name[/]").LeftAligned()) |
| 82 | + .AddColumn(new TableColumn("[bold]Active[/]").RightAligned()) |
| 83 | + .AddColumn(new TableColumn("[bold]DLQ[/]").RightAligned()) |
| 84 | + .AddColumn(new TableColumn("[bold]Scheduled[/]").RightAligned()); |
| 85 | + |
| 86 | + long totalActive = 0; |
| 87 | + long totalDlq = 0; |
| 88 | + long totalScheduled = 0; |
| 89 | + |
| 90 | + foreach (var stat in statistics) |
| 91 | + { |
| 92 | + var activeStyle = stat.ActiveMessageCount > 1000 ? "[yellow]" : "[white]"; |
| 93 | + var dlqStyle = stat.DeadLetterMessageCount > 0 ? "[red]" : "[white]"; |
| 94 | + |
| 95 | + table.AddRow(stat.Name, |
| 96 | + $"{activeStyle}{stat.ActiveMessageCount:N0}[/]", |
| 97 | + $"{dlqStyle}{stat.DeadLetterMessageCount:N0}[/]", |
| 98 | + $"[white]{stat.ScheduledMessageCount:N0}[/]"); |
| 99 | + |
| 100 | + totalActive += stat.ActiveMessageCount; |
| 101 | + totalDlq += stat.DeadLetterMessageCount; |
| 102 | + totalScheduled += stat.ScheduledMessageCount; |
| 103 | + } |
| 104 | + |
| 105 | + if (statistics.Count > 0) |
| 106 | + { |
| 107 | + table.AddEmptyRow(); |
| 108 | + |
| 109 | + var totalActiveStyle = totalActive > 1000 ? "[bold yellow]" : "[bold white]"; |
| 110 | + var totalDlqStyle = totalDlq > 0 ? "[bold red]" : "[bold white]"; |
| 111 | + |
| 112 | + table.AddRow("[bold]TOTAL[/]", |
| 113 | + $"{totalActiveStyle}{totalActive:N0}[/]", |
| 114 | + $"{totalDlqStyle}{totalDlq:N0}[/]", |
| 115 | + $"[bold white]{totalScheduled:N0}[/]"); |
| 116 | + } |
| 117 | + |
| 118 | + var timestamp = statistics.Count > 0 ? statistics[0].UpdatedAt : DateTimeOffset.Now; |
| 119 | + table.Caption($"Last updated: {timestamp:HH:mm:ss}"); |
| 120 | + |
| 121 | + return table; |
| 122 | + } |
| 123 | +} |
0 commit comments