← All products

Find out what drained that charge.

A macOS command-line tool that records battery history and per-app energy, so you can look beyond the current battery percentage.

Product
batlog
Platform
macOS · CLI / TUI
Built with
Go, SQLite, Bubble Tea, launchd, GoReleaser
Made at
Sevtee, as an independent product
batlog logo

A background recorder keeps history on your Mac. Explore sessions, health trends, and app energy from the terminal, or export your records. Per-app battery cost is an estimate based on energy shares.

What it does

batlog is a macOS command-line tool that records battery level and per-app energy in the background, for days and weeks, so a vague complaint like “my battery only lasts five hours” can be answered with numbers. It started as a tool for my own MacBook and is aimed at people who are comfortable in a terminal.

batlog status shows the level, drain rate and a steady time-left estimate. history lists every charge and battery session. top ranks apps by energy share and estimated battery cost. health tracks real capacity against design capacity. report writes a daily or weekly digest, and ui opens a read-only terminal interface with a battery chart. Everything stays on the Mac: no telemetry, no network calls, no sudo.

What was built

  • A per-user LaunchAgent that samples once a minute. I built it third in the order, because every day it was not running was history I could never get back.
  • A battery reader that parses ioreg output as a property list. Dumps sent by other people become golden test fixtures.
  • Per-app energy read from the kernel’s coalition counters through raw syscalls, in pure Go.
  • Ninety days of raw samples, then a daily rollup; CSV and JSON export; --json on every command, with stable exit codes.
  • A four-view terminal UI built with Bubble Tea: battery chart with a cursor, apps, report and health.
  • Releases through GoReleaser and a Homebrew tap, with CI on macOS plus a Linux cross-compile check.

Engineering decisions

  • Pure Go with CGO_ENABLED=0 and a pure-Go SQLite. One Linux CI job builds both Mac architectures, tests run anywhere, and the slower database is invisible at about 1,500 rows a day.
  • Health shows the gauge’s raw measured capacity as the headline, with Apple’s smoothed figure beside it labelled “Apple reports”, so nobody thinks batlog disagrees with macOS.
  • Data lives in ~/Library/Application Support/batlog, the macOS convention, so Time Machine backs it up.
  • Per-app battery cost is shown as a share and an estimate, never as watts, because the counters give joules per app group, not a physical measurement per app.

What I learned

The plan named its riskiest assumption before any Go was written: that top -o power could rank apps fairly. A three-night spike against Activity Monitor said no. top only sees live processes, and one app had spawned 1.5 million short-lived children that held most of its energy, so the ranking was wrong, and the sampling loop itself used 3 to 5 percent of the energy it measured.

The replacement reads the kernel’s per-coalition energy counters directly. On my machine the top eight apps then matched Activity Monitor in order, at a measured cost of 0.004 percent. The first decision stayed in the record as a superseded ADR, three days after it was written, instead of disappearing.

That was also my first time reading a macOS source by syscall instead of shelling out, and I learned to guard non-public kernel layouts with a sanity check that refuses implausible readings and a fixture test on the struct offsets. Smaller lessons: treat gaps over 90 seconds as sleep and exclude them from rates, and walk dates with a local-day helper after a daylight-saving midnight sent the rollup loop spinning.

Next productSevjala

Project screenshots

Have something in mind?

Let’s make it work.