Skip to content

feat(cli): add 'blocky stats' command - #2138

Merged
0xERR0R merged 6 commits into
mainfrom
feat/stats-cli-command
Jun 24, 2026
Merged

0xERR0R merged 6 commits into
mainfrom
feat/stats-cli-command

Conversation

@0xERR0R

@0xERR0R 0xERR0R commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a new blocky stats CLI command that queries the GET /api/stats REST endpoint and renders the in-memory DNS statistics as a readable terminal dashboard.

The dashboard shows:

  • Summary — queries, cached, forwarded, blocked, local, dropped, errors, avg response time, and cache hit rate (with % of queries where meaningful)
  • Top Domains / Top Blocked / Top Clients tables
  • Breakdowns by query type, response code, and response type (sorted by count, then name)
  • Cache entry count and a per-group Lists table (denylist / allowlist)

Rendering uses github.com/jedib0t/go-pretty/v6, promoted from an indirect to a direct dependency — it was already in the module graph, so this adds no new transitive dependencies. Tabular output is written to stdout rather than through the logger, so table alignment is preserved.

Pretty-only by design (YAGNI): no flags, no JSON mode, no per-hour series — the raw response stays available via curl .../api/stats.

Behaviour

Response Outcome
200 renders the dashboard
503 (statistics disabled) friendly error: statistics are disabled (enable 'statistics.enable' in config)
transport error can't execute … (matches sibling commands)
other non-200 response NOK, <status> <body>

Tests

ginkgo/gomega specs in cmd/stats_test.go:

  • Pure renderer — populated snapshot (all sections, thousands separators, cache-hit %, deterministic count-desc ordering, footer group-key union with a denylist-only group), empty/zero-query snapshot ((none) sections, divide-by-zero guard), and the formatInt/formatPercent helpers.
  • Command (HTTP-mocked via testHTTPAPIServer) — 200 renders the dashboard, 503 → disabled error, 500 → error, unreachable → connection error.

go test ./cmd/ passes; golangci-lint run ./cmd/... reports 0 issues.

Docs

docs/interfaces.md CLI command list updated with ./blocky stats.

Copilot AI review requested due to automatic review settings June 24, 2026 08:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new blocky stats CLI subcommand that calls the GET /api/stats endpoint and renders the returned in-memory DNS statistics as a terminal dashboard (tables + breakdowns) using go-pretty.

Changes:

  • Promote github.com/jedib0t/go-pretty/v6 to a direct dependency to support table/dashboard rendering.
  • Introduce cmd/stats.go implementing the stats command plus a pure renderer for formatted output.
  • Add Ginkgo/Gomega tests for both rendering and HTTP-mocked command behavior, and document the new command in CLI docs.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
go.mod Promotes go-pretty/v6 to a direct dependency for stats dashboard rendering.
docs/interfaces.md Documents the new ./blocky stats command and its config prerequisite.
cmd/stats.go Implements blocky stats: API call handling + dashboard rendering (summary, top tables, breakdowns, footer).
cmd/stats_test.go Adds renderer snapshot-style checks and command behavior tests with an HTTP mock server.
cmd/root.go Wires NewStatsCommand() into the root CLI command set.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov

codecov Bot commented Jun 24, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.26866% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.79%. Comparing base (fc353a0) to head (a875380).
⚠️ Report is 9 commits behind head on main.

Files with missing lines Patch % Lines
cmd/stats.go 96.24% 3 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2138      +/-   ##
==========================================
+ Coverage   87.67%   87.79%   +0.12%     
==========================================
  Files         122      123       +1     
  Lines        9455     9589     +134     
==========================================
+ Hits         8290     8419     +129     
- Misses        923      926       +3     
- Partials      242      244       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@0xERR0R 0xERR0R added the 🔨 enhancement New feature or request label Jun 24, 2026
@0xERR0R 0xERR0R added this to the v0.33.0 milestone Jun 24, 2026
@0xERR0R
0xERR0R enabled auto-merge (squash) June 24, 2026 09:08
@0xERR0R
0xERR0R merged commit 552eda5 into main Jun 24, 2026
20 checks passed
@0xERR0R
0xERR0R deleted the feat/stats-cli-command branch June 24, 2026 09:12
dadezzz pushed a commit to dadezzz/infra_docker-compose that referenced this pull request Jul 2, 2026
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/0xerr0r/blocky](https://github.com/0xERR0R/blocky) | minor | `v0.32.1` → `v0.33.0` |

---

### Release Notes

<details>
<summary>0xERR0R/blocky (ghcr.io/0xerr0r/blocky)</summary>

### [`v0.33.0`](https://github.com/0xERR0R/blocky/releases/tag/v0.33.0)

[Compare Source](0xERR0R/blocky@v0.32.1...v0.33.0)

#### Changelog

##### Features

- [`552eda5`](0xERR0R/blocky@552eda5): feat(cli): add 'blocky stats' command ([#&#8203;2138](0xERR0R/blocky#2138)) ([@&#8203;0xERR0R](https://github.com/0xERR0R))
- [`e9deb53`](0xERR0R/blocky@e9deb53): feat(queryLog): add dnstap as a query log destination ([#&#8203;2144](0xERR0R/blocky#2144)) ([@&#8203;rtmongold](https://github.com/rtmongold))

##### Bug fixes

- [`a191ad2`](0xERR0R/blocky@a191ad2): fix(dnssec): propagate Indeterminate (not Bogus) for unreachable chain of trust ([#&#8203;2120](0xERR0R/blocky#2120)) ([#&#8203;2139](0xERR0R/blocky#2139)) ([@&#8203;0xERR0R](https://github.com/0xERR0R))
- [`4b524e8`](0xERR0R/blocky@4b524e8): fix(ecs): apply useAsClient above cache and client-name lookup ([#&#8203;2140](0xERR0R/blocky#2140)) ([#&#8203;2141](0xERR0R/blocky#2141)) ([@&#8203;0xERR0R](https://github.com/0xERR0R))

##### Build and dependencies

- [`afea452`](0xERR0R/blocky@afea452): build(deps): bump github.com/breml/rootcerts from 0.3.5 to 0.3.6 ([#&#8203;2133](0xERR0R/blocky#2133)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`247a3c1`](0xERR0R/blocky@247a3c1): build(deps): bump github.com/jedib0t/go-pretty/v6 from 6.6.7 to 6.8.1 ([#&#8203;2145](0xERR0R/blocky#2145)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`2aa728d`](0xERR0R/blocky@2aa728d): build(deps): bump github.com/oapi-codegen/runtime from 1.4.1 to 1.4.2 ([#&#8203;2134](0xERR0R/blocky#2134)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`3f3f7d9`](0xERR0R/blocky@3f3f7d9): build(deps): bump github.com/onsi/ginkgo/v2 from 2.31.0 to 2.32.0 ([#&#8203;2135](0xERR0R/blocky#2135)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`2d0e6f2`](0xERR0R/blocky@2d0e6f2): build(deps): bump github.com/onsi/gomega from 1.42.0 to 1.42.1 ([#&#8203;2137](0xERR0R/blocky#2137)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`61d9a98`](0xERR0R/blocky@61d9a98): build(deps): bump github.com/testcontainers/testcontainers-go/modules/mariadb from 0.42.0 to 0.43.0 ([#&#8203;2131](0xERR0R/blocky#2131)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`cf865a6`](0xERR0R/blocky@cf865a6): build(deps): bump github.com/testcontainers/testcontainers-go/modules/postgres from 0.42.0 to 0.43.0 ([#&#8203;2130](0xERR0R/blocky#2130)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`e3c30dc`](0xERR0R/blocky@e3c30dc): build(deps): bump github.com/testcontainers/testcontainers-go/modules/redis from 0.42.0 to 0.43.0 ([#&#8203;2129](0xERR0R/blocky#2129)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`a58f1c8`](0xERR0R/blocky@a58f1c8): build(deps): bump google.golang.org/protobuf from 1.36.10 to 1.36.11 ([#&#8203;2148](0xERR0R/blocky#2148)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])
- [`6df83cd`](0xERR0R/blocky@6df83cd): build(deps): bump gorm.io/gorm from 1.31.1 to 1.31.2 ([#&#8203;2147](0xERR0R/blocky#2147)) ([@&#8203;dependabot](https://github.com/dependabot)\[bot])

##### Misc

- [`6da7ce1`](0xERR0R/blocky@6da7ce1): perf(blocking): Tier 2 — lock-free grouped cache, cheapest-first lookup, client-name indexing ([#&#8203;2146](0xERR0R/blocky#2146)) ([@&#8203;0xERR0R](https://github.com/0xERR0R))
- [`302ca65`](0xERR0R/blocky@302ca65): perf(log): cut per-request logger allocations on the resolver hot path ([#&#8203;2142](0xERR0R/blocky#2142)) ([@&#8203;0xERR0R](https://github.com/0xERR0R))
- [`223df0c`](0xERR0R/blocky@223df0c): perf(queryLog): skip per-request LogEntry build when off or ignored ([#&#8203;2149](0xERR0R/blocky#2149)) ([@&#8203;0xERR0R](https://github.com/0xERR0R))
- [`b73422e`](0xERR0R/blocky@b73422e): perf(resolver): allocation-free resolver selection in ParallelBestResolver ([#&#8203;2150](0xERR0R/blocky#2150)) ([@&#8203;0xERR0R](https://github.com/0xERR0R))

***

#### ❤️ Support Blocky

Blocky is free and open source, developed in my spare time — no telemetry, no ads, no hidden
filtering. If it's useful to you, please consider supporting its continued development:

[GitHub Sponsors](https://github.com/sponsors/0xERR0R) ·
[thanks.dev](https://thanks.dev/u/gh/0xERR0R) ·
[Liberapay](https://liberapay.com/spx01) ·
[Ko-fi](https://ko-fi.com/0xerr0r) ·
[PayPal](https://paypal.me/spx01)

Thank you to everyone supporting Blocky! 🙏

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNDYuMSIsInVwZGF0ZWRJblZlciI6IjQzLjI0Ni4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔨 enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants