Skip to content
ConsoleMonster

Process monitor

Track CPU and memory counters in a bounded live dashboard with process rows and a compact summary.

Demos Dashboards

Use it when a long-running task needs a compact view of CPU and memory pressure.

  • Live
  • 100×26
  • PowerShell 7.4+
  • .NET 8.0+
Process monitor 100×2624-bitFull

How it's built

The whole program in one file, with its sample data at the top. It drew the recording above; both languages draw the same cells.

PowerShellprocess-monitor.ps1View on GitHub
# Follow CPU and memory pressure in a bounded top-like dashboard.
param([switch] $StaticSnapshot, [ValidateRange(0, 2)][int] $Step = 0)
Import-Module ConsoleMonster

# Data: synthetic counters, not the machine's real process list.
$processes = @(
    [pscustomobject] @{ Name = 'build-worker'; Pid = 4102; Cpu = 42; Memory = 860 }
    [pscustomobject] @{ Name = 'test-runner'; Pid = 4107; Cpu = 28; Memory = 512 }
    [pscustomobject] @{ Name = 'api-host'; Pid = 4120; Cpu = 12; Memory = 220 }
    [pscustomobject] @{ Name = 'file-watcher'; Pid = 4131; Cpu = 3; Memory = 48 }
)
$cpuHistory = @(38, 45, 51, 48, 56)

# Layout: build a complete frame from one set of counters.
function New-ProcessFrame($Tick) {
    $rows = foreach ($process in $processes) {
        [pscustomobject] @{ Name = $process.Name; Pid = $process.Pid; 'CPU %' = $process.Cpu + $Tick * 3; 'Memory MiB' = $process.Memory + $Tick * 16 }
    }
    New-ConsoleRows -Gap 1 {
        New-ConsoleRule '[bold]Process monitor[/] [muted]· refresh 1s[/]'
        New-ConsoleText -Markup "[accent]CPU $((56 + $Tick * 5))%[/]   Memory $((1640 + $Tick * 64)) MiB   [muted]4 processes[/]"
        New-ConsoleTable -InputObject $rows -Property Name, Pid, 'CPU %', 'Memory MiB' -Expand
        New-ConsolePanel -Title 'CPU history' {
            New-ConsoleSparkline -Label 'CPU %' -Value @($cpuHistory + (56 + $Tick * 5))
        }
        '[muted]Ctrl+C stops monitoring. Final counters stay in scrollback.[/]'
    }
}

# Behaviour: one deterministic snapshot in CI; three refreshes in a terminal.
if ($StaticSnapshot -or [Console]::IsInputRedirected) {
    New-ProcessFrame $Step | Write-ConsoleWidget
    return
}
$frames = @(0..2 | ForEach-Object { New-ProcessFrame $_ })
$screen = New-ConsoleRegion -Name processes -Widget $frames[0]
Start-ConsoleDashboard $screen -ArgumentList (, $frames) {
    param($Frames)
    foreach ($frame in $Frames) {
        if ($ConsoleSession.Cancellation.IsCancellationRequested) { break }
        Set-ConsoleRegion -Name processes -Widget $frame
        Start-Sleep -Milliseconds 1000
    }
}
C#Program.csView on GitHub
// Follow CPU and memory pressure in a bounded top-like dashboard.
using ConsoleMonster;
using ConsoleMonster.Layout;
using ConsoleMonster.Live;
using ConsoleMonster.Presenters;
using ConsoleMonster.Widgets;

// Data: synthetic counters, not the machine's real process list.
var processes = new[] {
    new { Name = "build-worker", Pid = 4102, Cpu = 42, Memory = 860 },
    new { Name = "test-runner", Pid = 4107, Cpu = 28, Memory = 512 },
    new { Name = "api-host", Pid = 4120, Cpu = 12, Memory = 220 },
    new { Name = "file-watcher", Pid = 4131, Cpu = 3, Memory = 48 }
};
double[] cpuHistory = { 38, 45, 51, 48, 56 };

// Layout: build a complete frame from one set of counters.
IWidget Frame(int tick) {
    Table table = new Table { Expand = true };
    table.AddColumn("Name").AddColumn("Pid", column => column.Presenter = CellPresenters.Identifier)
        .AddColumn("CPU %").AddColumn("Memory MiB");
    foreach (var process in processes) {
        table.AddRow(process.Name, TableCell.FromValue(process.Pid), TableCell.FromValue(process.Cpu + tick * 3),
            TableCell.FromValue(process.Memory + tick * 16));
    }
    Sparkline history = new Sparkline { Label = "CPU %" };
    history.AddRange(cpuHistory.Concat(new[] { 56.0 + tick * 5 }));
    return new Rows(new Rule("[bold]Process monitor[/] [muted]· refresh 1s[/]"),
        new TextBlock($"[accent]CPU {56 + tick * 5}%[/]   Memory {1640 + tick * 64} MiB   [muted]4 processes[/]"), table,
        new Panel(history, "CPU history"),
        new TextBlock("[muted]Ctrl+C stops monitoring. Final counters stay in scrollback.[/]")) { Gap = 1 };
}

// Behaviour: one deterministic snapshot in CI; three refreshes in a terminal.
int at = Array.IndexOf(args, "--step");
int step = at < 0 ? 0 : int.Parse(args[at + 1], System.Globalization.CultureInfo.InvariantCulture);
if (step < 0 || step > 2) throw new ArgumentOutOfRangeException(nameof(step));
if (args.Contains("--snapshot") || (!args.Contains("--interactive") && Console.IsInputRedirected)) {
    Terminal.Write(Frame(step));
    return;
}
Slot screen = new Slot("processes", Frame(0));
using LiveSession session = new LiveSession(screen);
session.Run(live => {
    for (int tick = 0; tick <= 2 && !live.Cancellation.IsCancellationRequested; tick++) {
        screen.Handle(live).Set(Frame(tick));
        if (live.Cancellation.WaitHandle.WaitOne(1000)) break;
    }
});

Run it yourself

Three steps in PowerShell, four in C#. Every command copies with one click.

  1. Install the module

    Once. Windows PowerShell 5.1 or PowerShell 7 on Windows, macOS or Linux.

    Install-Module ConsoleMonster -Scope CurrentUser
  2. Save the program

    Download it, or copy it from above into process-monitor.ps1.

  3. Run it

    From the folder you saved it in.

    ./process-monitor.ps1
  1. Create a project

    A plain console app; .NET 8 or later.

    dotnet new console -n Processmonitor; cd Processmonitor
  2. Add ConsoleMonster

    The only package the program needs.

    dotnet add package ConsoleMonster
  3. Replace Program.cs

    Download it, or copy the program from above.

  4. Run it

    dotnet run

Or skip the copying. Every demo ships with the module and the project template.

Show-ConsoleDemo process-monitor
Get-ConsoleDemo process-monitor -Path ./process-monitor.ps1
dotnet new install ConsoleMonster.Templates
dotnet new consolemonster --demo process-monitor -n Processmonitor

Make it yours

Start with these, then read the rest of the file.

  1. Collect process counters before each refresh and retain stable process identifiers.

  2. Choose a refresh interval that keeps collection inexpensive.

  3. Add a bounded history or a status threshold for the resource you investigate most.

More dashboards

All demos