Пишем CLI-утилиту для мониторинга процессов — с нуля за один вечер

Go — идеальный язык для CLI-инструментов: быстрая компиляция, один статичный бинарник, никаких зависимостей при деплое. Показываю, как за пару часов сделать симпатичный мониторинг с bubbletea и gopsutil.

Зачем ещё один монитор процессов?

htop прекрасен. Но я хотела сделать инструмент, заточенный под конкретную задачу: следить за своими dev-серверами и ботами, которые крутятся в фоне. Плюс это отличный повод поиграть с bubbletea — TUI-фреймворком от Charm.

Структура проекта

terminal
catwatch/
├── main.go
├── model.go      # bubbletea Model
├── procs.go      # сбор данных через gopsutil
└── ui.go         # рендеринг таблицы

Инициализация модуля

terminal
go mod init github.com/molly/catwatch
go get github.com/charmbracelet/bubbletea
go get github.com/charmbracelet/lipgloss
go get github.com/shirou/gopsutil/v3

Сбор данных о процессах

procs.go
package main

import (
    "sort"
    "github.com/shirou/gopsutil/v3/process"
)

type ProcInfo struct {
    PID  int32
    Name string
    CPU  float64
    Mem  float32
}

func fetchProcs() []ProcInfo {
    procs, _ := process.Processes()
    var out []ProcInfo

    for _, p := range procs {
        name, _  := p.Name()
        cpu, _   := p.CPUPercent()
        mem, _   := p.MemoryPercent()
        out = append(out, ProcInfo{PID: p.Pid, Name: name, CPU: cpu, Mem: mem})
    }

    // сортируем по CPU убыванию
    sort.Slice(out, func(i, j int) bool {
        return out[i].CPU > out[j].CPU
    })

    if len(out) > 20 {
        return out[:20]   // показываем топ-20
    }
    return out
}

Модель bubbletea

model.go
package main

import (
    "time"
    tea "github.com/charmbracelet/bubbletea"
)

type tickMsg struct{}
type model struct { procs []ProcInfo }

func tick() tea.Cmd {
    return tea.Tick(2*time.Second, func(_ time.Time) tea.Msg {
        return tickMsg{}
    })
}

func (m model) Init() tea.Cmd        { return tick() }
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg.(type) {
    case tea.KeyMsg:
        return m, tea.Quit
    case tickMsg:
        m.procs = fetchProcs()
        return m, tick()
    }
    return m, nil
}
🐾 Совет
Bubbletea работает по паттерну Elm: Model → Update → View. Если вы раньше работали с React, это покажется знакомым — только без виртуального DOM и с настоящей скоростью.

Запуск и результат

terminal
go build -o catwatch .
./catwatch

В итоге получается интерактивная таблица, которая обновляется каждые 2 секунды, подсвечивает процессы с высокой нагрузкой и реагирует на нажатие любой клавиши для выхода. Весит бинарник около 8 МБ и не требует никакого рантайма.

Проект открытый, код лежит на GitHub. Фичи, которые планирую добавить: фильтрацию по имени, kill по PID и экспорт в CSV.

Была ли статья полезной?