CpuInspectCore
A Ruby gem that monitors CPU usage per core in the background and displays results as ASCII progress bars — designed for profiling Heroku dynos.
Runs a lightweight background thread, writes a rotating 1 MB JSON log, and exposes a single method you can call from a Rails console or bash at any time. On multi-dyno Heroku deployments the optional Redis backend aggregates all dynos into one view.
Contents
- How it works
- Installation
- Quick start
- Rails integration
- Multi-dyno on Heroku (Redis backend)
- CLI
- Configuration reference
- Development
How it works
A background thread samples /proc/stat on Linux (Heroku) or top on macOS at a configurable interval (default 2 s). Each sample is:
- stored in an in-process ring buffer for instant
statusreads - appended as a JSON line to a rotating local log (1 MB cap, keeps newest 50%)
- optionally written to Redis tagged with the Heroku
$DYNOidentifier
CpuInspectCore.status reads the ring buffer and prints one progress bar per core:
=== web.1 @ 2026-04-15T10:00:02.341Z ===
cpu0 |||||||||||||||||||||||| 48.2%
cpu1 idle
cpu2 |||||||||||||||||||||||||||||||||||||||||||||||| 95.4%
cpu3 ||| 6.1%
On a scaled Heroku deployment with the Redis backend enabled, calling status shows every live dyno in one shot — no SSH needed.
Installation
Add to your Gemfile:
gem "cpu-inspect-core"
For multi-dyno Redis support also add:
gem "redis", "~> 5.0"
Then:
bundle install
Or install globally:
gem install cpu-inspect-core
Requires Ruby 3.1 or higher. No other runtime dependencies.
Quick start
require "cpu_inspect_core"
CpuInspectCore.start # launches background thread
sleep 5 # let it collect a few samples
CpuInspectCore.status # print progress bars
CpuInspectCore.tail(lines: 10) # print last 10 raw JSON log lines
CpuInspectCore.stop
From a Rails console:
CpuInspectCore.status
The gem auto-starts via its Railtie when Rails boots, so status is ready as soon as your app is up.
Rails integration
Drop a file at config/initializers/cpu_inspect_core.rb:
CpuInspectCore.configure do |c|
c.interval = 2.0 # seconds between samples
c.idle_threshold = 2.0 # cores below this % are shown as "idle"
c. = 50 # character width of the progress bar
end
The Railtie calls CpuInspectCore.start automatically after the Rails logger is ready. Set CPU_INSPECT_CORE_DISABLED=1 to opt out (useful in test environments or during asset precompilation).
Puma with preload_app!
Threads do not survive fork. If you use preload_app! in config/puma.rb, add:
on_worker_boot do
CpuInspectCore.start
end
Multi-dyno on Heroku (Redis backend)
By default each dyno writes only to its local /tmp file. To see all dynos in one status call, enable the Redis backend:
# config/initializers/cpu_inspect_core.rb
CpuInspectCore.configure do |c|
c.backend = :redis
# c.redis_url defaults to ENV["REDIS_URL"] — set automatically by Heroku Redis add-on
end
Each dyno writes its latest sample to a Redis key named cpu_inspect_core:<dyno> (e.g. cpu_inspect_core:web.1) with a 30-second TTL. When a dyno scales down or restarts its key expires automatically.
Viewing all dynos
From a one-off dyno or any Rails console session:
CpuInspectCore.status
Output on a 5-dyno deployment:
=== web.1 @ 2026-04-15T10:00:02.341Z ===
cpu0 |||||||||||||||||||||||| 48.2%
cpu1 idle
=== web.2 @ 2026-04-15T10:00:02.198Z ===
cpu0 |||||||||||||||||||||||||||||||||||||||||||||||| 95.4%
cpu1 ||||||||||||||||||||||||||||||| 61.8%
=== web.3 @ 2026-04-15T10:00:02.512Z ===
cpu0 idle
cpu1 idle
=== web.4 @ 2026-04-15T10:00:02.089Z ===
cpu0 ||||||||||||||||||| 38.7%
cpu1 |||||||||||| 24.1%
=== web.5 @ 2026-04-15T10:00:02.763Z ===
cpu0 ||||||||||||||||||||||||||||||||||||| 73.0%
cpu1 ||||||||||||||||||||||||||||||||||||||||||||||| 93.2%
This tells you how many physical vCPU cores each dyno has and how hard they are working — useful for choosing the right dyno type and size.
Redis configuration options
| Option | Default | Description |
|---|---|---|
redis_url |
ENV["REDIS_URL"] |
Redis connection URL |
redis_key_prefix |
"cpu_inspect_core" |
Namespace for all Redis keys |
redis_ttl |
30 |
Seconds before a dead dyno's key expires |
CLI
The cpu-inspect-core executable is available after installation.
bundle exec cpu-inspect-core [options]
| Flag | Description |
|---|---|
--once |
Print status once and exit |
--watch |
Refresh status continuously (clears screen each cycle) |
--tail [N] |
Print last N lines from the local log file (default 20) |
--interval SECS |
Override the sample interval |
--log PATH |
Override the log file path |
Examples:
# Print current CPU snapshot
bundle exec cpu-inspect-core --once
# Live dashboard that refreshes every 2 seconds
bundle exec cpu-inspect-core --watch
# Inspect the raw JSON log
bundle exec cpu-inspect-core --tail 30
# Run at a faster rate
bundle exec cpu-inspect-core --watch --interval 0.5
Configuration reference
All options are set via CpuInspectCore.configure:
CpuInspectCore.configure do |c|
c.interval = 2.0 # sample interval in seconds
c.log_path = "/tmp/cpu_inspect_core.log" # local log file (per dyno)
c.log_max_bytes = 1_048_576 # rotate when log exceeds this (1 MB)
c.idle_threshold = 2.0 # % below which a core shows as "idle"
c. = 50 # progress bar width in characters
c.backend = :file # :file or :redis
c.redis_url = ENV["REDIS_URL"] # Redis connection URL
c.redis_key_prefix = "cpu_inspect_core" # Redis key namespace
c.redis_ttl = 30 # seconds before dyno key expires
end
The log path can also be set via environment variable: CPU_INSPECT_CORE_LOG=/path/to/log.
Development
git clone https://github.com/mykbren/cpu-inspect-core
cd cpu-inspect-core
bundle install
bundle exec rake test
Run a single test file:
bundle exec ruby -Ilib -Itest test/test_renderer.rb
Run a single test by name:
bundle exec ruby -Ilib -Itest test/test_renderer.rb -n test_cores_are_sorted_numerically_not_lexicographically
The test suite targets 100% line and branch coverage (verified by SimpleCov on every run).
License
MIT — Copyright (c) 2026 Mykyta Bren