consola 0.0.0-alpha.0

🐨 Elegant Console Logger for Rust and Browser
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
# Architecture

System design and implementation details for consola-rs

## Overview

consola-rs is structured as a modular logging library with several key components:

```
┌─────────────┐
│   Macros    │  (info!, warn!, error!, etc.)
└──────┬──────┘
┌─────────────┐
│   Logger    │  (LoggerBuilder, filtering, pause/resume)
└──────┬──────┘
┌─────────────┐
│ Throttling  │  (Deduplication, repetition counting)
└──────┬──────┘
┌─────────────┐
│  Formatter  │  (Segment pipeline, styling)
└──────┬──────┘
┌─────────────┐
│  Reporter   │  (Basic, Fancy, JSON)
└──────┬──────┘
┌─────────────┐
│   Output    │  (stdout, stderr, custom sinks)
└─────────────┘
```

## Core Modules

### 1. Levels (`levels.rs`)

**Purpose**: Log level management and type registration

**Key Types**:
- `LogLevel(i16)`: Newtype wrapper for log levels
- `LogTypeSpec`: Specification for custom log types
- Type registry: Global `RwLock<HashMap<String, LogTypeSpec>>`

**Constants**:
```rust
pub const SILENT: LogLevel = LogLevel(-99);
pub const FATAL: LogLevel = LogLevel(0);
pub const ERROR: LogLevel = LogLevel(1);
pub const WARN: LogLevel = LogLevel(2);
pub const LOG: LogLevel = LogLevel(3);
pub const INFO: LogLevel = LogLevel(4);
pub const SUCCESS: LogLevel = LogLevel(5);
pub const DEBUG: LogLevel = LogLevel(6);
pub const TRACE: LogLevel = LogLevel(7);
pub const VERBOSE: LogLevel = LogLevel(99);
```

**Thread Safety**: Uses `parking_lot::RwLock` for lock-free reads in the common case.

### 2. Record (`record.rs`)

**Purpose**: Log record data structure and argument handling

**Key Types**:
```rust
pub struct LogRecord {
    pub timestamp: Instant,
    pub level: LogLevel,
    pub type_name: String,
    pub tag: Option<String>,
    pub args: Vec<ArgValue>,
    pub repetition_count: u32,
}

pub enum ArgValue {
    String(String),
    Number(f64),
    Bool(bool),
    Error(String),
    OtherDebug(String),
}
```

**Features**:
- Flexible argument handling (primitives, errors, debug values)
- JSON serialization support (feature: `json`)
- Efficient storage using `SmallVec` for common cases

### 3. Throttling (`throttling.rs`)

**Purpose**: Message deduplication and repetition counting

**Algorithm**:
1. Generate fingerprint: `blake3(type_name + args + tag + level)`
2. Check if fingerprint matches previous log
3. If within throttle window and count >= min_count: suppress and increment count
4. On window expiry or different fingerprint: flush with repetition count

**Configuration**:
- `throttle_window_ms`: Time window for deduplication (default: 500ms)
- `throttle_min_count`: Minimum occurrences before suppression (default: 2)

**Clock Abstraction**:
- `RealClock`: Uses system time
- `MockClock`: Deterministic time for testing

### 4. Formatter (`format.rs`)

**Purpose**: Transform LogRecord into styled segments

**Pipeline**:
```
LogRecord → FormatOptions → Segments → Styled Output
```

**Segments**:
- Time (optional)
- Type/Level indicator
- Tag (optional)
- Message
- Additional data (key-value pairs)
- Metadata
- Stack traces
- Repetition count

**FormatOptions**:
```rust
pub struct FormatOptions {
    pub date: bool,
    pub colors: bool,
    pub compact: bool,
    pub columns: Option<usize>,
    pub error_level: usize,
    pub unicode_mode: bool,
}
```

**Raw Path**: Bypass formatting for performance (`log_raw` methods)

### 5. Reporter (`reporter.rs`)

**Purpose**: Output formatted logs to sinks

**Implementations**:

#### BasicReporter
- Simple `[type] message` format
- stderr for levels < 2, stdout otherwise
- Error chain formatting with depth limiting

#### FancyReporter (feature: `fancy`)
- Icons for each log type (✔, ✖, ⚠, ℹ, etc.)
- ASCII fallback for non-unicode terminals
- Colored badges and type names
- Enhanced stack trace formatting

#### JsonReporter (feature: `json`)
- Single-line JSON per log
- Structured error chains
- Deterministic key ordering
- Schema version: `consola-rs/v1`

**Custom Reporters**: Implement the `Reporter` trait

### 6. Error Chain (`error_chain.rs`)

**Purpose**: Extract and format Rust error source chains

**Features**:
- Recursive source extraction via `Error::source()`
- Cycle detection (prevents infinite loops)
- Depth limiting (via `FormatOptions.error_level`)
- Multi-line message normalization

**Format**:
```
Error: Main error message
Caused by:
  - First cause
  - Second cause
  - Third cause
```

### 7. Utilities (`utils.rs`)

**Purpose**: Helper functions for formatting and output

**Components**:
- `strip_ansi`: Remove ANSI escape codes
- Box drawing: Unicode and ASCII box characters
- Tree formatting: Hierarchical output with proper indentation
- Alignment: Left/right/center text alignment
- Sinks: `StdoutSink`, `StderrSink`, `TestSink`

### 8. Clock (`clock.rs`)

**Purpose**: Time abstraction for testability

**Implementations**:
- `RealClock`: `Instant::now()` wrapper
- `MockClock`: Controllable time for deterministic tests

### 9. Prompt (`prompt.rs`)

**Purpose**: Interactive user input (feature: `prompt-demand`)

**Key Types**:
```rust
pub enum PromptCancelStrategy {
    Reject,     // Return error
    Default,    // Use default value
    Undefined,  // Return Undefined
    Null,       // Return NullValue
    Symbol,     // Return SymbolCancel
}

pub enum PromptOutcome<T> {
    Value(T),
    Undefined,
    NullValue,
    SymbolCancel,
    Cancelled,
}

pub trait PromptProvider {
    fn text(&self, prompt: &str, default: Option<&str>) -> Result<PromptOutcome<String>>;
    fn confirm(&self, prompt: &str, default: Option<bool>) -> Result<PromptOutcome<bool>>;
    fn select(&self, prompt: &str, options: &[&str]) -> Result<PromptOutcome<usize>>;
    fn multiselect(&self, prompt: &str, options: &[&str]) -> Result<PromptOutcome<Vec<usize>>>;
}
```

**WASM Handling**: `WasmPromptStub` returns `PromptError::NotSupported`

### 10. Macros (`macros.rs`)

**Purpose**: Ergonomic logging API

**Macro Types**:
- Standard: `info!`, `warn!`, `error!`, `success!`, `debug!`, `trace!`, `fatal!`, `ready!`, `start!`, `fail!`
- Custom: `log_type!(type_name, format, args)`
- Raw: `info_raw!`, `warn_raw!`, `error_raw!`, etc.

**Implementation**: Thin wrappers around helper functions

## Data Flow

### 1. Normal Logging Flow

```
User calls info!("message")
Macro expands to log_message("info", "message")
Create LogRecord with timestamp, level, type, args
Level filtering (check if level >= configured level)
Throttling check (fingerprint, window, count)
Mock interception (if set)
Pause check (buffer if paused)
Format pipeline (LogRecord → Segments)
Reporter (segments → styled output)
Sink (stdout/stderr/custom)
```

### 2. Raw Logging Flow

```
User calls info_raw!("message")
Macro expands to log_message_raw("info", "message")
Create LogRecord (minimal)
Level filtering
Throttling check (same as normal)
Direct output (bypass formatting)
Sink
```

### 3. Throttled Flow

```
Same message repeated multiple times
First: Normal flow
Subsequent (within window): Suppress, increment count
On flush/window expiry/different message:
    Emit "[type] message  (xN)"
```

### 4. Paused Flow

```
logger.pause() called
New logs added to pause queue (VecDeque)
logger.resume() called
Flush suppressed throttled message (if any)
Drain queue, process each log sequentially
```

## Thread Safety

### Global State

**Type Registry**:
```rust
static TYPE_REGISTRY: Lazy<RwLock<HashMap<String, LogTypeSpec>>> = ...;
```
- Thread-safe via `parking_lot::RwLock`
- Optimized for concurrent reads

**Logger Instances**: Not yet implemented (currently using global helpers)

### Concurrency Model

- **Read-heavy operations**: Type lookups use RwLock for minimal contention
- **Write operations**: Type registration locks for write
- **Independent logs**: No shared state between log calls (stateless formatters)

## Performance Considerations

### 1. Hot Path Optimizations

- **SmallVec**: Avoid heap allocations for typical argument counts
- **Raw logging**: Bypass entire formatting pipeline
- **Level guards**: Early return before expensive operations
- **Fingerprint caching**: Use fast blake3 hashing

### 2. Memory Management

- **String interning**: Considered for common log types
- **Arena allocation**: Potential for segment allocation
- **Zero-copy**: Where possible, use string references

### 3. Lock-Free Operations

- **RwLock**: parking_lot implementation for better performance
- **Atomic operations**: For simple counters and flags
- **Lock-free structures**: Considered for future async reporters

## Feature Gates

### Why Feature Gates?

- **Minimize dependencies**: Users only pay for what they use
- **Platform support**: Some features (like prompts) don't work in WASM
- **Build time**: Reduce compilation time for minimal builds

### Feature Matrix

| Feature | Dependencies | Adds |
|---------|-------------|------|
| `color` | anstream, anstyle | Color support |
| `fancy` | unicode-width | Fancy reporter, icons |
| `json` | serde, serde_json | JSON reporter |
| `prompt-demand` | demand | Interactive prompts |
| `wasm` | wasm-bindgen | WASM exports |
| `bridge-log` | log | log crate integration |
| `bridge-tracing` | tracing, tracing-subscriber | tracing integration |

### Default Features

```toml
default = ["color", "fancy"]
```

Provides colored output with fancy formatting out of the box.

## Testing Strategy

### Unit Tests

- Embedded in each module
- Test individual components in isolation
- Use `MockClock` for deterministic timing

### Integration Tests

- `tests/` directory
- Test component interactions
- Snapshot testing with `insta` crate

### Property Tests

- Use `proptest` for randomized testing
- Verify panic-free operation
- Test invariants (e.g., repetition counts)

### Benchmark Tests

- `benches/` directory (when created)
- Measure hot path performance
- Compare raw vs formatted overhead

## Future Architecture

### Planned Improvements

1. **Async Reporters**: Non-blocking output via async channels
2. **Plugin System**: Dynamic reporter and formatter plugins
3. **Middleware**: Pre/post processing hooks
4. **Multi-sink Routing**: Different outputs for different log levels
5. **Structured Logging**: First-class support for structured data
6. **Span Support**: Integration with tracing spans

### Potential Optimizations

1. **String Interning**: Reduce allocations for repeated strings
2. **Custom Allocators**: Arena allocation for log records
3. **SIMD**: Vectorized operations for ANSI stripping
4. **Lock-Free Queue**: For pause/resume buffering

---

For implementation details, see the source code in `src/`. For usage examples, see the [README](README.md) and other documentation files.