ADR-0004: Logging System Architecture
Status: accepted | Date: 2026-02-14
References: RFC-0007, ADR-0002
Context
typub-ui currently provides both CLI output utilities (progress bars, spinners, styled messages) and logging functions (debug, info, warn, error). This coupling creates problematic dependencies: typub-storage must depend on the entire typub-ui crate just to use logging during upload operations, violating separation of concerns.
Problem Statement
- Coupling violation:
typub-storage(a service layer) depends ontypub-ui(a presentation layer) for logging - No standard logging: Current implementation uses custom
eprintln!-based output, incompatible with the Rust logging ecosystem - Limited debuggability: No structured logging, no log level filtering, no span tracing for complex operations
- Library use friction: If typub is used as a library, callers cannot integrate with their own logging infrastructure
Constraints
- RFC-0007 dependency rules require clean layering
- Must maintain backward-compatible CLI output (colors, icons, formatting)
- Async operations (S3 uploads) need progress reporting
Options Considered
- Split into
typub-log(simple) +typub-uiwith currenteprintln!approach - Introduce
tracingcrate as logging foundation with custom CLI formatter (recommended) - Use
logcrate facade withenv_logger - Keep current monolithic
typub-ui
Decision
We will introduce tracing as the logging foundation and create a new typub-log crate that:
- Provides
tracing-based logging with re-exported macros (debug!,info!,warn!,error!) - Implements a custom
tracing-subscriberlayer for CLI-formatted output (icons, colors) - Exposes a
ProgressReportertrait for decoupling storage from UI
Implementation Phases
Phase 1: Create typub-log crate
- Add
tracingandtracing-subscriberdependencies - Implement
CliLayerfor custom CLI formatting - Re-export tracing macros for crate-wide use
typub-loghas zero internal dependencies (Layer 0)
Phase 2: Define ProgressReporter trait in typub-log
- Create trait in
typub-log(semantically related to logging/reporting) - Provide
()(null reporter) default implementation FnReporterwrapper for simple closures
Phase 3: Refactor typub-storage
- Replace
typub-uidependency withtypub-log - Accept
&dyn ProgressReporterin upload functions - Use tracing macros for structured logging
Phase 4: Update typub-ui
- Implement
ProgressReportertrait - Re-export
typub-logfor convenience - Keep indicatif-based progress bars and spinners
New Dependency Graph
typub-log (Layer 0)
├── tracing
├── tracing-subscriber
├── ProgressReporter trait
└── (no internal deps)
typub-core (Layer 0)
└── (no internal deps)
typub-storage (Layer 2)
├── typub-core
├── typub-config
└── typub-log (NOT typub-ui)
typub-ui (Layer 2)
├── typub-log (re-export)
├── indicatif
├── owo-colors
└── implements ProgressReporter
RFC-0007 Amendment Required
This ADR requires amending RFC-0007 to add typub-log as a Layer 0 crate in the dependency table. The amendment should specify:
typub-logis Layer 0 (no internal dependencies)- All crates MAY depend on
typub-logfor logging typub-storageMUST NOT depend ontypub-ui(usetypub-loginstead)
Migration Notes
Macro syntax change: The tracing macros use structured fields instead of format strings:
#![allow(unused)]
fn main() {
// Before (typub-ui)
ui::debug(&format!("Processing file {}", path.display()));
// After (tracing)
tracing::debug!(file = %path.display(), "Processing file");
}
The % sigil formats the value with Display; ? uses Debug. This change enables structured logging but requires updating call sites.
RUST_LOG integration: The custom CliLayer works with standard EnvFilter:
#![allow(unused)]
fn main() {
// Enable debug logs for storage operations
RUST_LOG=typub_storage=debug typub publish
}
Consequences
Positive
- Clean layering:
typub-storageno longer depends ontypub-ui, respecting RFC-0007 dependency rules - Ecosystem compatibility: Standard
tracingallowsRUST_LOGfiltering (RUST_LOG=typub::storage=debug) - Structured logging: Enables
#[instrument]for automatic span creation and field extraction - Library-friendly: Callers can use their own
tracingsubscriber - Better debugging: Spans track async operation chains (e.g., upload → transform → publish)
- Testable:
ProgressReportertrait allows mock injection for unit tests
Negative
- Additional dependency:
tracing+tracing-subscriberadd ~50KB to compile time (mitigation: these are widely used, likely already in dependency tree via other crates) - Learning curve: Team needs familiarity with
tracingmacros and spans (mitigation: provide examples in crate docs) - Migration effort: Existing
ui::debug()calls need conversion totracing::debug!with syntax changes (mitigation: structured fields enable better filtering and analysis) - Two logging systems temporarily: During migration, old and new systems coexist (mitigation: Phase-by-phase migration)
Neutral
typub-uibecomes smaller, focused on indicatif-based progress indicators- CLI output appearance remains unchanged (custom formatter preserves icons/colors)
- May enable future features: JSON logging for CI, trace export for debugging