## The Problem Reading raw log files is tedious: important tokens (timestamps, IPs, UUIDs, etc.) blend into noise, and ad‑hoc grep pipelines quickly become unreadable. Teams need a lightweight viewer that highlights common patterns without requiring per‑project configuration.
## What This Does tailspin ships a single binary (tspin) that streams a log file (or stdin) line‑by‑line, applies a suite of regular‑expression highlighters, and writes the coloured output to either stdout or a pager (less). All highlight groups—dates, URLs, numbers, IPs, key/value pairs, etc.—are built‑in, and users can override styles via a theme.toml placed in ~/.config/tailspin. The crate is also publishable on crates.io, allowing integration into other Rust programs.
## How It Is Wired
Execution starts in src/main.rs at fn main() (line 23). main builds the configuration, spawns a temporary pager if needed, and then calls process_stream.
process_stream (line 52) opens the input source (file, stdin, or --exec command) and iterates over lines via read_lines from src/io/reader/buffer_line_counter.rs. This reader tracks line numbers and normalises newline handling.
For each line, the core pipeline is:
- Highlighter construction –
src/core/highlighter.rs::builder()creates aHighlighterGroupthat aggregates the concrete highlighters located undersrc/core/highlighters/(e.g.,url.rs,ip_v6.rs,json.rs). - Apply –
HighlighterGroup::apply(reached fromapplyinsrc/core/highlighter.rs, called 21 times across the codebase) walks the line, invoking each highlighter’spaintmethod. - Style rendering –
src/core/style.rssupplies colour helpers (fg,bold,italic, etc.) used by everypaintimplementation. The most‑used style functions aredefault -> fg(29 calls) anddefault -> faint(12 calls).
The final coloured fragments are emitted by write_line in src/main.rs, which forwards to the selected presenter (src/io/presenter/pager.rs for pager mode or src/io/presenter/stdout.rs for direct output). The presenter may spawn an external pager via present → command.status() (the only external command invoked).
Key responsibility map (most‑connected files):
| File | Core duties | Reach |
|---|---|---|
src/core/highlighter.rs | Builder, apply, highlighter aggregation | 22 functions, 17 callers |
src/core/style.rs | Colour helpers (fg, bold, italic) | 9 functions, 17 callers |
src/core/config.rs | Default configuration parsing | 12 functions, high branching (61 branches/185 loc) |
src/theme/mod.rs | Theme struct hierarchy, TOML deserialization | 15 types |
src/io/reader/buffer_line_counter.rs | Buffered line reading, EOF handling | 16 functions, 1 caller |
No circular import cycles were detected; the internal call graph contains 194 resolved edges, with apply, fg, and try_add_highlighter being the most widely referenced functions.
## How To Use It
# Clone the repo
git clone https://github.com/moses-y/tailspin
cd tailspin
# Build and install the binary
cargo install --path .
# Basic usage
tspin /var/log/myapp.log # view with built‑in pager
cat /var/log/myapp.log | tspin # pipe through stdout
tspin --exec='kubectl logs -f pod' # run a command and pager the output
To customise colours, create ~/.config/tailspin/theme.toml following the format described in README.md. No additional runtime flags are required for the default behaviour.
## Real‑World Use A CI/CD pipeline can pipe build logs into tspin before archiving them, making timestamps and error codes instantly visible. Example in a Bash script:
#!/usr/bin/env bash
./build.sh 2>&1 | tspin > build.colored.log
The coloured log can be opened locally with less -R build.colored.log for quick post‑mortem analysis.
## Code Health & Issues
- HIGH – Pin GitHub Actions –
.github/workflows/*.ymluse mutable tags (ncipollo/release-action@v1,taiki-e/upload-rust-binary-action@v1). Replace each@vNwith the full 40‑character SHA and keep the version comment. - HIGH – Push to default branch –
.github/workflows/Publish.ymlperforms a directgit push. Change the workflow to push to a bot branch and open a PR, or restrict pushes to tag refs only. - MEDIUM – Least‑privilege GITHUB_TOKEN –
BuildAndTest.ymldeclares no permissions while accessing secrets. Addpermissions: contents: readat the workflow top and widen only where needed. - MEDIUM – No dependency vulnerability scan – No
dependency-review-actionor similar step. Insert a scan onpull_requestand a scheduled run. - MEDIUM – Checkout persists credentials –
checkoutstep keeps the token, exposing it to later install scripts. Addwith: persist-credentials: falseand supply an explicit token only to steps that need it. - LOW – Missing job timeouts – Jobs in
BuildAndTest.ymllacktimeout-minutes. Set a reasonable bound (e.g.,30) to avoid runaway builds.
Additional findings from static analysis: deep nesting (max depth 8) in several highlighters (url.rs, ip_v6.rs, json.rs) and duplicated 6‑line blocks across 15 files, suggesting an opportunity to extract shared helpers.
## The Bottom Line tailspin delivers a functional, zero‑config log highlighter with a clean Rust codebase and a useful CLI. The core logic is well‑encapsulated, but the highlighter implementations suffer from deep nesting and repeated code, and the CI workflow needs hardening (pinned actions, proper token scopes, and timeouts). It is suitable for teams that need quick visual parsing of logs and are comfortable managing a small Rust build environment.