Eventline
Causality-aware execution journal for systems-level programs.
Eventline records what happened, when it happened, and in what causal context — without assuming logging, tracing, or telemetry semantics. Built for daemons, CLI tools, and eventually Linux distributions.
Features
- Append-only journal — never mutates or removes records
- Scoped execution — track outcomes and durations
- Runtime log levels — filter events globally (Debug, Info, Warning, Error)
- Dual output mode — journal + optional real-time console printing
- Live logging — automatic, timestamped append to disk
- Unified color control — consistent ANSI colors across console and renderer
- Flexible filtering — by outcome, depth, duration, event kind, message content
- High-throughput batching —
JournalBufferfor batch writes - Async runtime support — fire-and-forget logging and async scopes (
scoped_async()) - Deterministic replay — safe concurrent reads, reliable audit trails
Why Eventline?
Eventline is not "better logging" - It's structured execution history.
- Events are append-only and never rewritten
- Work is grouped into scopes with outcomes and durations
- Event != results (warnings can happen in successful work)
- Journals can be replayed deterministically
- Console output is optional - Structure is always preserved
You get:
- human-readable output
- A complete execution record for post-mortem analysis
Quick Start
Runtime API (Fire-and-Forget Async)
use runtime;
use ;
async
Core Journal API (Explicit Control)
For libraries or embedded systems:
use Journal;
use Outcome;
use JournalWriter;
let mut journal = new;
journal.scoped;
// Use JournalWriter to output the journal
let writer = new;
// writer.write_to(&mut std::fs::File::create("events.log")?, &journal)?;
Console Output (Simple Format)
Clean, minimal output optimized for watching logs during development:
Starting server
Binding to 0.0.0.0:8080
Server started successfully
warning: cache at 95% capacity
Live Log File (Canonical Format)
Structured output with scope headers, timestamps, and aligned formatting:
[19:04:12.381] Scope startup (id=1) → Success (142ms)
• info Starting server
• info Binding to 0.0.0.0:8080
• info Server started successfully
• warning cache at 95% capacity
---
## Architecture
┌─────────────┐ │ Macros │ event_info!(), event_scope!() └──────┬──────┘ ↓ ┌─────────────┐ │ Runtime │ Global, thread-safe facade (optional) └──────┬──────┘ ↓ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Journal │ │ Console │ │ LiveLogFile │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ └────────────────┴────────────────┘ ↓ ┌─────────────────┐ │ Canonical Format│ Single rendering source └─────────────────┘
**Core Layer** (always available):
- **Journal** — Pure data structure
- **Scope** — Logical units of work
- **Record** — Individual events
- **Filter** — Composable criteria
**Runtime Layer** (optional):
- **runtime** — Global facade
- **console** — Dual output control
- **live_log** — Automatic, timestamped disk logging
- **Macros** — Zero-overhead convenience
- **Log levels** — Runtime event filtering
---
## Live Logging
Live logging is **enabled per file path** using
`runtime::enable_live_logging(PathBuf)`
```rust
use std::path::PathBuf;
runtime::enable_live_logging(PathBuf::from("/tmp/eventline.log")).await;
event_info!("This event will be written to the live log file automatically").await;
event_warn!("Timestamped and indented according to scope depth").await;
Dual Output Mode
Eventline supports two output modes:
Silent Journaling (Default)
Events are recorded but not printed:
init.await;
enable_console_output.await; // Default
event_info!.await; // Recorded, not printed
// Later, examine the journal
with_journal.await;
Dual Output (Traditional Logging Feel)
Events are both journaled AND printed to console:
init.await;
enable_console_output.await;
enable_console_color.await;
event_info!.await; // Journaled + printed
event_warn!.await; // Journaled + printed in yellow
event_error!.await; // Journaled + printed to stderr in red
Benefits:
- Get traditional logging behavior when you want it
- Always have structured journal for post-mortem
- Single flag to toggle:
enable_console_output(bool)
Key Concepts
Outcomes vs Events
Event kinds describe what happened:
Info— routine progressWarning— unexpected but recoverableError— something went wrongDebug— verbose diagnostics
Scope outcomes describe the result:
Success— completed normallyFailure— completed with errorsAborted— interrupted by panic
This separation enables:
- Warnings during successful operations
- Errors that don't cause failure
- Clear diagnostics vs results
Filtering at Render Time
Filtering happens when reading the journal, not when writing:
use *;
use Outcome;
let filter = scope;
render_journal_tree;
Benefits:
- Zero overhead when not filtering
- Complete journal always preserved
- Multiple views from same data
Advanced Usage
Batched Logging
use Journal;
use Outcome;
let mut journal = new;
let mut buffer = journal.create_buffer;
let scope = buffer.enter_scope;
for item in items
buffer.exit_scope;
journal.flush_buffer; // Atomic ID rebase
Custom Output
use JournalWriter;
use io;
let mut file = create?;
// Customize canonical format
new
.with_color // Disable colors
.with_timestamps // Include timestamps
.with_bullet // Custom bullet
.write_to_all?;
### Nested Scopes
```rust
event_scope!("Deployment", {
event_info!("Starting deployment").await;
event_scope!("BuildImage", {
event_info!("Building Docker image").await;
}).await;
event_scope!("PushRegistry", {
event_info!("Pushing to registry").await;
}).await;
event_info!("Deployment complete").await;
}).await;
Environment Variable Support
Respect common conventions:
use ;
async
CLI Integration
Typical pattern for command-line tools:
use Parser;
use ;
async
Design Principles
- Append-only be default - safe, auditable, deterministic
- Seperation of concerns - data != rendering != runtime
- Human-first output - readable without tooling
- Optional global state - usable in libraries
- Async-safe - fire-and-foget from any task
Test-Friendly
async
Installation
[]
= "0.3.2"
= { = "1", = ["full"] }
Optional features:
[]
= { = "0.3.2", = ["colour"] }
Roadmap
- Custom formatters (JSON, binary)
- Structured data (key-value pairs)
- Zero-copy query interface
- Tag-based filtering
- systemd journal integration
Version 0.3.x — Highlights
- Canonical rendering format — unified output across console, live log, and journal replay
- Simplified console output — clean format for development without visual noise
- Structured live logging — full canonical format with scope headers and timestamps
- Async runtime support —
init().await,scoped_async(), fire-and-forget logging from async tasks - Live logging — automatic timestamped file append
- Dual output mode — console + journal, fully async-safe
- Scoped event macros — single-line events with scope context (
event_info_scoped!, etc.) - Consistent formatting — arrow rule enforced (no duplication)
- Improved CLI integration — verbose, quiet, color flags respected
License
MIT