RepoPilotOpen in app →

rcoh/angle-grinder

Slice and dice logs on the command line

Healthy

Healthy across all four use cases

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 3mo ago
  • 13 active contributors
  • MIT licensed
Show all 7 evidence items →
  • CI configured
  • Tests present
  • Slowing — last commit 3mo ago
  • Concentrated ownership — top contributor handles 60% of recent commits

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/rcoh/angle-grinder)](https://repopilot.app/r/rcoh/angle-grinder)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/rcoh/angle-grinder on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: rcoh/angle-grinder

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/rcoh/angle-grinder shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across all four use cases

  • Last commit 3mo ago
  • 13 active contributors
  • MIT licensed
  • CI configured
  • Tests present
  • ⚠ Slowing — last commit 3mo ago
  • ⚠ Concentrated ownership — top contributor handles 60% of recent commits

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live rcoh/angle-grinder repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/rcoh/angle-grinder.

What it runs against: a local clone of rcoh/angle-grinder — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in rcoh/angle-grinder | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch main exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 121 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>rcoh/angle-grinder</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of rcoh/angle-grinder. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/rcoh/angle-grinder.git
#   cd angle-grinder
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of rcoh/angle-grinder and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "rcoh/angle-grinder(\\.git)?\\b" \\
  && ok "origin remote is rcoh/angle-grinder" \\
  || miss "origin remote is not rcoh/angle-grinder (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
  && ok "default branch main exists" \\
  || miss "default branch main no longer exists"

# 4. Critical files exist
test -f "src/lib.rs" \\
  && ok "src/lib.rs" \\
  || miss "missing critical file: src/lib.rs"
test -f "src/bin/agrind.rs" \\
  && ok "src/bin/agrind.rs" \\
  || miss "missing critical file: src/bin/agrind.rs"
test -f "src/lang.rs" \\
  && ok "src/lang.rs" \\
  || miss "missing critical file: src/lang.rs"
test -f "src/operator.rs" \\
  && ok "src/operator.rs" \\
  || miss "missing critical file: src/operator.rs"
test -f "src/filter.rs" \\
  && ok "src/filter.rs" \\
  || miss "missing critical file: src/filter.rs"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 121 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~91d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/rcoh/angle-grinder"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Angle-grinder is a command-line log processing tool written in Rust that enables real-time parsing, filtering, and aggregation of log files with a live-updating terminal UI. It implements a functional query language (similar to SQL but for logs) supporting operations like JSON/logfmt parsing, filtering, grouping, and statistical aggregation (sum, average, percentile, min/max, count) directly in the terminal, processing >1M rows/second on simple pipelines. Single-binary Rust project: src/bin/agrind.rs is the CLI entry point; core parsing and evaluation lives in src/lang.rs, src/filter.rs, src/operator.rs (with submodules for each operator: src/operator/sum.rs, count.rs, parse.rs, etc.). Data structures in src/data.rs, error handling in src/errors.rs. Aliases (log format templates) stored in toml files (aliases/*.toml) for reuse across domains (Nginx, K8s, Apache).

👥Who it's for

DevOps engineers, SREs, and system administrators who need to analyze large log files locally without shipping data to external observability platforms (Splunk, Honeycomb, Kibana). Users who want sophisticated analytics on logs but lack graphite/Datadog access or need a lightweight command-line alternative.

🌱Maturity & risk

Production-ready and actively maintained. The project has CI/CD pipelines (.github/workflows/ci.yaml, release.yml), published binaries for macOS/Linux/FreeBSD via multiple package managers (Homebrew, Macports, Cargo), and a comprehensive test suite (benches/ with 10k/1k/1m.inp test files, src/operator/* with individual modules). Version v0.19.6 with self-update capability indicates stable release practices.

Low risk overall—single maintainer (rcoh) but strong ecosystem support via package managers. Dependencies are well-established (serde, nom, regex, chrono) with no obvious bloat. The 317KB Rust codebase is substantial but focused. Main risk: breaking changes to query syntax would affect users' scripts; however, the documented query syntax in README.md suggests it's stabilized. No visible open issue backlog or stale pull requests in the file list.

Active areas of work

The project is in maintenance/stabilization phase. Version pinning in Cargo.lock (nom 7.1.1, clap 4.0.18, chrono 0.4) and recent dependency updates (itertools 0.14, ordered-float 5) suggest ongoing updates. CI runs on push/PR (ci.yaml exists). Self-update feature (Cargo.toml feature flag) was added to enable users to stay current without package managers.

🚀Get running

git clone https://github.com/rcoh/angle-grinder.git
cd angle-grinder
cargo build --release
./target/release/ag --help

Or via Homebrew (macOS): brew install angle-grinder. Binary is called agrind or ag.

Daily commands: Local development: cargo build (debug) or cargo build --release (optimized). Run locally: ./target/debug/ag '<query>' < logfile.txt. Example: cat benches/10k.inp | ag 'json | sum(response_time)'.

🗺️Map of the codebase

  • src/lib.rs — Core library entry point that defines the public API and orchestrates the parsing pipeline, filter evaluation, and operator execution.
  • src/bin/agrind.rs — CLI binary entry point that parses command-line arguments and drives the main application flow for processing log streams.
  • src/lang.rs — Query language parser that transforms user input into an AST, fundamental to understanding how angle-grinder interprets pipelines.
  • src/operator.rs — Operator trait definition and orchestration layer that processes data through the pipeline; every aggregation and transformation depends on this.
  • src/filter.rs — Filter expression evaluation engine that determines which records pass through the pipeline based on boolean logic.
  • src/data.rs — Core data structures (Value, Row, Record) that represent log entries and field values throughout the processing pipeline.
  • Cargo.toml — Project manifest defining dependencies and compilation configuration; critical for understanding external tools and build targets.

🛠️How to make changes

Add a new aggregation operator

  1. Create a new operator struct in src/operator/{operator_name}.rs implementing the Operator trait (src/operator/{operator_name}.rs)
  2. Implement process() method to handle row processing and aggregate state (src/operator/{operator_name}.rs)
  3. Register operator in the lang.rs parser within the operator_from_args function (src/lang.rs)
  4. Add type-checking rules in typecheck.rs for the new operator's input/output types (src/typecheck.rs)
  5. Create test files in tests/structured_tests/{operator_name}.toml with input/expected output (tests/structured_tests/{operator_name}.toml)

Add a new built-in function

  1. Define the function logic in src/funcs.rs within the apply_function function (src/funcs.rs)
  2. Add type signature and argument validation rules for the function (src/funcs.rs)
  3. Update lang.rs tokenizer if the function uses special syntax (src/lang.rs)
  4. Create unit tests in src/funcs.rs or integration tests in tests/integration.rs (tests/integration.rs)

Add a new input parser (e.g., CSV, protobuf)

  1. Implement custom parsing logic in a new file or extend src/operator/parse.rs (src/operator/parse.rs)
  2. Update the parser detection logic in src/data.rs to recognize the new format (src/data.rs)
  3. Add format flag or alias in src/bin/agrind.rs for user invocation (src/bin/agrind.rs)
  4. Create test files in test_files/ and integration tests in tests/integration.rs (tests/integration.rs)

Add a new query alias

  1. Create a new TOML file in aliases/ directory with the alias name (aliases/{domain}.toml)
  2. Define the query template and field mapping following the TOML schema (aliases/{domain}.toml)
  3. The alias loader in src/alias.rs will automatically discover and load it (src/alias.rs)
  4. Add test case in tests/structured_tests/aliases/ to verify the alias works (tests/structured_tests/aliases/{domain}.toml)

🔧Why these technologies

  • Rust — High performance (~1M+ rows/sec), memory safety, and compiled binary distribution without runtime overhead; critical for log processing at scale.
  • Custom hand-written parser (lang.rs) — Provides precise control over query syntax and error messages; avoids large parser generator dependencies for a domain-specific language.
  • Trait-based operator architecture — Allows flexible composition of stateful transformations (count, sum, sort, filter) without tight coupling; easy to add new operators.
  • Value enum for heterogeneous types — Logs are semi-structured; represents strings, numbers, booleans, arrays, and objects uniformly for filtering and aggregation.
  • Terminal UI rendering (render.rs) — Provides live-updating display with streaming results; improves UX for long-running log analysis queries.

⚖️Trade-offs already made

  • Hand-written parser vs. parser combinator library

    • Why: Custom parser avoids heavyweight dependency and gives full control over error messages and syntax extension.
    • Consequence: More maintenance burden; parser bugs require manual fixes, but codebase remains small and fast to compile.
  • Eager aggregation in-memory vs. streaming window functions

    • Why: Simpler logic for GROUP BY and statistical operators; matches SQL semantics that users expect.
    • Consequence: Memory usage scales with cardinality of grouping keys; not suitable for infinite/unbounded log streams with high cardinality.
  • Field-level JSON/logfmt parsing vs. regex-only

    • Why: Structured parsing enables type-aware filtering and aggregation; generic regex fallback for unstructured logs.
    • Consequence: Mixed-format logs require manual parsing rules; JSON/logfmt auto-detection adds complexity but improves UX.
  • Single-pass execution vs. multiple-pass optimization

    • Why: One-pass executor is simple and predictable; reduces latency for streaming results.
    • Consequence: Cannot optimize away redundant computations or reorder operators; user must write efficient queries.

🚫Non-goals (don't propose these)

  • Real-time distributed log aggregation (no Kafka/Splunk-style clustering)
  • Authentication or multi-user access control (designed as CLI tool, not service)
  • Persistent storage of results (output is terminal or file redirect only)
  • Full SQL compatibility (subset of SQL semantics adapted for log processing)
  • Non-Unix platforms (primary target is Linux/macOS; Windows support may be limited)
  • Schema validation or schema enforcement (semi-structured data model)

🪤Traps & gotchas

Parser version constraint: nom 7.1.1 is pinned in Cargo.lock; nom 8+ has breaking changes in combinator APIs, so upgrading requires parser rewrites in src/lang.rs and src/operator/parse.rs. Terminal size detection: terminal_size crate used for live UI rendering; may behave unexpectedly in piped environments or inside tmux—check ci/test.bash for how tests handle this. Floating-point aggregation: ordered-float crate used to make f64 hashable; NaN handling differs from standard Rust, important for percentile and sum operators. No explicit async I/O: crossbeam-channel is used for the UI thread, but stdin reading is blocking—large files may feel sluggish if CPU can't keep up with parsing. Memory usage with grouping: Large cardinality dimensions (e.g., grouping by user_id on billion-row logs) can exhaust memory; im crate provides structural sharing but is not magic.

🏗️Architecture

💡Concepts to learn

  • Parser Combinators (nom library) — Angle-grinder uses nom to build the query language parser in src/lang.rs; understanding how combinators compose is essential to extending query syntax
  • Functional Programming Pipeline Pattern — The entire query model (filters → operators → output) is a functional pipeline; new operators must implement the Operator trait to compose into this chain
  • Structural Sharing with Immutable Data Structures (im crate) — The im crate enables efficient grouping/aggregation by sharing structure across intermediate states; important for memory efficiency when handling millions of rows
  • Percentile Calculation (Quantile Approximation) — The percentile operator in src/operator/percentile.rs uses the quantiles crate to approximate percentiles without storing all values; critical for memory-bounded aggregation
  • Regex-based Field Extraction — The parse(<pattern>) operator in src/operator/parse.rs uses regex capture groups to turn unstructured logs into named fields; foundational for all filtering/aggregation
  • Time Slicing / Bucketing (Histogram Binning) — The timeslice operator groups events into time windows (e.g., per-minute aggregates); essential for time-series analysis of logs
  • Custom Allocator (jemalloc) — Linux builds use tikv-jemallocator instead of the default allocator for better performance at scale (Cargo.toml target condition); critical for hitting 1M+ rows/sec throughput
  • BurntSushi/xsv — CSV querying tool in Rust; similar CLI-first data-wrangling philosophy but for tabular CSV instead of logs
  • jqlang/jq — JSON query language (jq) that inspired angle-grinder's functional approach; angle-grinder extends this idea to unstructured logs + real-time UI
  • stedolan/jq — Original canonical jq implementation in C; angle-grinder's query language design draws from jq's functional paradigm
  • elastic/elasticsearch — Elasticsearch is the heavy-duty counterpart for large-scale log analytics; angle-grinder is the lightweight local alternative when you can't/won't use a remote stack

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive integration tests for operator pipelines in test_files/

The repo has test_files/ with various log formats (JSON, logfmt, multiline, etc.) but no visible integration tests that exercise complete operator chains. The src/operator/ directory has 12+ operators (parse, filter, sum, average, percentile, etc.) that should be tested together. This would catch regressions when operators interact and improve confidence in data processing correctness.

  • [ ] Create tests/integration_tests.rs that reads from test_files/ (basic, filter_test.log, test_json.log, etc.)
  • [ ] Add test cases for common operator chains: 'parse ... | where ... | count' and 'parse ... | sum' patterns
  • [ ] Add edge case tests using test_files/binary_data.bin, test_files/long_lines.log, and test_files/multiline
  • [ ] Run tests in CI by adding a test target to .github/workflows/ci.yaml

Add structured logging and metrics collection to src/bin/agrind.rs

The agrind binary is the entry point but lacks observability. Given that angle-grinder is a high-performance log processor (1M+ rows/sec claimed), adding internal metrics (rows processed, throughput, operator timing) would help users understand performance and debug slow pipelines. This could leverage the existing logfmt dependency.

  • [ ] Add a metrics module to src/metrics.rs with counters for: rows_processed, operators_executed, processing_duration
  • [ ] Instrument src/bin/agrind.rs and src/operator.rs to collect timing data per operator
  • [ ] Add a --stats or --verbose flag to src/bin/agrind.rs to output performance metrics in logfmt format
  • [ ] Document the new flag in README.md with examples of output

Add type checking validation tests in src/typecheck.rs

src/typecheck.rs exists but there are no visible dedicated tests for type validation. Angle-grinder is a functional language where type errors should be caught early. Adding comprehensive typecheck tests would ensure that invalid operator combinations (e.g., sum on string fields, percentile on non-numeric) are properly rejected before execution.

  • [ ] Create tests/typecheck_tests.rs with test cases for invalid type operations (e.g., sum(string_field), average(non_numeric))
  • [ ] Test that typecheck properly validates filter expressions with type mismatches
  • [ ] Test that parse operator type inference works correctly for different field patterns
  • [ ] Add tests for operator composition validation (e.g., incompatible field references across pipes)

🌿Good first issues

  • Add unit tests for src/operator/timeslice.rs and src/operator/percentile.rs (files exist but have minimal test coverage visible; most operators test in benches/ not unit tests). Start by adding a test module at the bottom of timeslice.rs following the pattern in sum.rs.
  • Create a new alias file aliases/cloudflare.toml for Cloudflare Workers logs (similar structure to aliases/nginx.toml); document it in README.md's aliases section. This adds value for a common log format with no code changes.
  • Improve error messages in src/lang.rs parser by integrating the existing annotate-snippets dependency (already imported) to show context around parse failures. Current errors are terse; show the failing query with a caret under the error position.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 9c2fc88 — Bump bytes from 1.10.1 to 1.11.1 (#227) (dependabot[bot])
  • 2f84d30 — Fix case-sensitive search for quoted strings (#226) (rcoh)
  • f1f5619 — Aliases (#224) (rcoh)
  • 3587663 — Bump crossbeam-channel from 0.5.14 to 0.5.15 (#223) (dependabot[bot])
  • 7ba87ae — Bump tokio from 1.44.1 to 1.44.2 (#222) (dependabot[bot])
  • 7b9f33b — Update cargo.toml to match release tag (rcoh)
  • e98ad5c — add noconvert test (#221) (rcoh)
  • 16c4d62 — Update release.yml (#220) (rcoh)
  • ddbaed5 — Bump version (rcoh)
  • 0481ed0 — Cleanup atty dep (#218) (rcoh)

🔒Security observations

The angle-grinder codebase is a Rust CLI tool with a reasonable security posture. The primary concerns are: (1) outdated versions of regex and nom dependencies that may have unpatched vulnerabilities, (2) potential ReDoS attacks through user-controlled regex patterns in query filters, and (3) lack of explicit input validation on user-supplied parsing logic. The use of memory-safe Rust significantly reduces common vulnerability

  • Medium · Outdated Dependency: nom 7.1.1 — Cargo.toml - nom = "7.1.1". The nom parser combinator library version 7.1.1 is used, but this version series has known issues. Current stable versions are in the 8.x and 9.x range. While 7.1.1 is relatively recent, updating to the latest version would provide security patches and performance improvements. Fix: Update nom to the latest stable version (e.g., nom = "9") and test for compatibility with the parsing logic.
  • Medium · Outdated Dependency: regex 1.5.5 — Cargo.toml - regex = "1.5.5". The regex crate version 1.5.5 is specified, but this version is from 2021. Current versions are 1.10+. Regex engines can have ReDoS (Regular Expression Denial of Service) vulnerabilities. Using an outdated version may leave the application vulnerable if regex patterns are user-controlled. Fix: Update regex to the latest stable version (e.g., regex = "1") to receive security patches for ReDoS vulnerabilities.
  • Medium · User-Controlled Regular Expressions — src/filter.rs, src/operator/parse.rs, src/lang.rs. The angle-grinder tool processes user-supplied log parsing rules and filter expressions. Given the presence of regex dependency and filter.rs/parse.rs modules, there is potential for Regular Expression Denial of Service (ReDoS) attacks if users can craft malicious regex patterns in their queries. Fix: Implement regex pattern validation, set timeouts on regex execution, or use bounded backtracking mechanisms. Document regex complexity limitations for users.
  • Low · Optional Self-Update Feature Without Integrity Verification — Cargo.toml - self_update = { version = "0.32.0", features = ["rustls"] }. The 'self-update' feature is conditionally compiled and uses the self_update crate. While this feature is optional, if enabled, it could potentially download and execute updates without cryptographic verification if not properly implemented. Fix: Ensure self-update feature is disabled by default in production. If enabled, verify that updates are signed and validated using proper cryptographic mechanisms before execution.
  • Low · Unsafe JSON Parsing — Cargo.toml - serde_json = "1.0.33", src/data.rs. The application uses serde_json to parse arbitrary JSON from log files. While serde_json itself is safe, parsing untrusted JSON without size limits could lead to denial of service through deeply nested structures or excessively large payloads. Fix: Implement limits on JSON depth and payload size during parsing. Consider using streaming JSON parsers for very large files.
  • Low · No Input Validation Documentation — src/lang.rs, src/filter.rs, src/alias.rs. The angle-grinder language parser (src/lang.rs) processes user-supplied query syntax. While injection attacks are mitigated by the fact this is not SQL-like, there is no evident input validation or sanitization in the file structure. Fix: Document security constraints of the angle-grinder query language. Validate alias files loaded from disk against path traversal attacks. Ensure TOML parsing is sandboxed.
  • Low · Incomplete Dependency Version Specification — Cargo.toml - multiple dependencies with loose versions. Some dependencies use loose version specifications (e.g., itertools = "0.14", chrono = "0.4") which could pull in minor/patch versions with breaking changes or vulnerabilities. Fix: Use more restrictive version specifications (e.g., itertools = "0.14.0") and regularly audit dependencies with 'cargo audit'. Consider using cargo-deny for supply chain checks.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · rcoh/angle-grinder — RepoPilot