You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
All logging today is `println!` / `eprintln!` (~120 call sites across the codebase). This works for terminal humans but blocks several capabilities:
No log levels. Verbose mode is a boolean; can't say "DEBUG level for monorepo orchestrator only, INFO for git ops".
No JSON output for CI ingestion. Users running ferrflow in a release workflow want to ship the run to Datadog / Loki / CloudWatch. Today they get raw text with ANSI color codes mixed in.
No structured fields. `println!("Pushed and verified on {remote}/{branch}")` is unparseable downstream; users want to filter on `remote=origin branch=main`.
OpenTelemetry integration impossible. Customers asking for OTLP export of release activity can't be served.
Test-time stdout assertions are fragile (already burned us with the bench output parsing).
Proposal
Migrate to `tracing` + `tracing-subscriber`.
```rust
// src/main.rs
fn init_logging(verbose: bool, format: LogFormat) {
let filter = if verbose { "ferrflow=debug" } else { "ferrflow=info" };
let registry = tracing_subscriber::registry()
.with(tracing_subscriber::EnvFilter::new(filter));
match format {
LogFormat::Human => registry.with(tracing_subscriber::fmt::layer().compact()).init(),
LogFormat::Json => registry.with(tracing_subscriber::fmt::layer().json()).init(),
}
}
```
Replace the existing prints incrementally:
`eprintln!("warning: …")` → `tracing::warn!(…)`
`println!("✓ Pushed …")` → `tracing::info!(remote, branch, "pushed and verified")`
The pretty colored output (✓ / ✗ / →) stays in the human `fmt` layer's prefix.
```bash
ferrflow check # human, colors, current behavior
ferrflow check --log-format json # one JSON line per event
RUST_LOG=ferrflow::git=trace ferrflow release # debug just the git layer
```
Problem
All logging today is `println!` / `eprintln!` (~120 call sites across the codebase). This works for terminal humans but blocks several capabilities:
Proposal
Migrate to `tracing` + `tracing-subscriber`.
```rust
// src/main.rs
fn init_logging(verbose: bool, format: LogFormat) {
let filter = if verbose { "ferrflow=debug" } else { "ferrflow=info" };
let registry = tracing_subscriber::registry()
.with(tracing_subscriber::EnvFilter::new(filter));
match format {
LogFormat::Human => registry.with(tracing_subscriber::fmt::layer().compact()).init(),
LogFormat::Json => registry.with(tracing_subscriber::fmt::layer().json()).init(),
}
}
```
Replace the existing prints incrementally:
```bash
ferrflow check # human, colors, current behavior
ferrflow check --log-format json # one JSON line per event
RUST_LOG=ferrflow::git=trace ferrflow release # debug just the git layer
```
Bonus once it lands
Scope / migration plan
Do this in stages, not one giant PR:
Acceptance
Related #478 (--timing flag — gets free from `#[instrument]`).