The Problem
Operators often need a quick visual for a shell‑generated metric (e.g., DB row count, MQ depth) without standing up a full monitoring stack. Building a custom dashboard for a one‑off script is time‑consuming and introduces unnecessary services.
What This Does
sampler runs user‑defined shell commands at a configurable interval, parses the numeric output, and draws terminal UI components (barchart, gauge, sparkline, etc.). The YAML file example.yml (or any user‑supplied file) describes the commands, sampling rate, and which UI component to bind. Core code lives in Go (*.go files) with the entry point main.go. UI logic is in component/ (23 files) and configuration handling in config/ (6 files). The binary can also be built into a container via the provided Dockerfile.
How It Is Wired
Execution starts in main.go at main (line 67). main builds a Starter that calls startAll (line 29) → start (line 58) → the UI layout (component/layout/layout.go). The layout creates the root Block (component/block.go) and registers components such as RunChart (component/runchart/runchart.go), Gauge (component/gauge/gauge.go), and Menu (component/menu.go).
User‑defined commands are turned into Consumer objects (data/consumer.go). NewConsumer is invoked from ten locations (the most‑used hub) and yields a Consumer that runs the command, captures stdout, and forwards the numeric sample to the component via HandleConsumeSuccess / HandleConsumeFailure.
Rendering follows the call chain: HandleKeyboardEvent (event/handler.go) → draw functions in each component. For example, Draw in component/gauge/gauge.go calls FormatValue (component/util/format.go) eight times, and Draw in component/runchart/runchart.go calls RenderAlert six times.
The central formatting helper FormatValue is called from six distinct callers, making it a low‑coupling utility. The most‑connected functions—NewBlock, NewConsumer, FormatValue, GetMiddlePoint—have the widest blast radius (10‑6 distinct call sites) and should be changed cautiously. No circular imports were detected, and the internal call graph contains 233 edges across 206 functions and 66 types.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/sampler
cd sampler
# Build the binary (requires Go 1.22+)
go build -o sampler .
# Or build the container image
docker build -t sampler .
Create a configuration file (see example.yml). A minimal config.yml might contain:
components:
- type: gauge
command: "cat /proc/loadavg | awk '{print $1}'"
interval: 5s
Run the tool:
./sampler -c config.yml
# or, using Docker
docker run --rm -v $(pwd)/config.yml:/root/config.yml sampler -c /root/config.yml
The UI appears in the terminal; use arrow keys to select components, Enter to edit, and Esc to exit.
Real‑World Use
A DevOps engineer can monitor a nightly backup size without deploying Prometheus. The YAML defines a barchart that runs du -sh /backups/* every minute, and a trigger (in data/trigger.go) that plays a tone when the size exceeds a threshold, leveraging the bundled audio assets (asset/player.go).
Code Health & Issues
- High – Build gate missing –
Dockerfileproduces an image but no CI step validates the build. - Medium – Dependabot not configured – Only one manifest (
go.mod) present; no automated dependency updates. - Medium – Base image not pinned –
Dockerfileusesubuntu:focal; mutable tag can introduce untracked CVEs. - Medium – No non‑root user – Container runs as root; add a dedicated user and
USERdirective.
Additional findings from static analysis:
- Medium – Deep nesting –
data/sampler.goreaches six indentation levels, making control flow hard to follow. - Medium – Duplicated code – Repeated six‑line blocks across five component files (
barchart,gauge,runchart,sparkline, etc.) could be extracted to shared helpers.
The repo includes tests (*_test.go), a Travis CI config, a license, and a lockfile; no secrets were found.
The Bottom Line
sampler delivers a lightweight, terminal‑based visualization layer for arbitrary shell commands, with a clear Go codebase and container support. It is production‑ready for ad‑hoc monitoring but needs a few hygiene improvements (build gate, pinned base image, non‑root user) and modest refactoring to reduce nesting and duplication. Engineers looking for a quick, no‑service monitoring UI will find it practical; teams requiring long‑term maintainability should address the identified health issues.