# ๐ฏ CAND - Colorful And Nice Debugging
> **Beautiful embedded-first Rust logging library for ESP32 to servers with colorful output and zero-panic design.**
## โจ **Why Choose CAND?**
| ๐จ **Smart Colors** | Spot issues instantly | `logger.log_err("โ Clear visibility")` |
| โก **no_std Ready** | Runs everywhere | ESP32, STM32, WASM, bare metal |
| ๐ก๏ธ **Never Panic** | Production safe | `try_run()` and `try_get()` handles all errors gracefully |
| ๐ **Pluggable** | Your infrastructure | Files, UART, RTT, databases, networks |
| ๐ฆ **Tiny Binary** | <1KB overhead | Perfect for memory-constrained devices |
| ๐ฏ **2-Line Setup** | Start in seconds | Works out of the box |
## ๐ **Quick Start**
### **Desktop/Server**
```toml
[dependencies]
cand = "0.2.2"
use cand::Logger;
use std::time::Instant;
fn main() {
let mut logger = Logger(Instant::now(), ());
logger.log_ok("๐ Server started successfully!");
logger.log_info("๐ก Listening on port 8080");
logger.log_warn("โ ๏ธ High memory usage: 87%");
logger.log_err("โ Database connection failed");
}
Embedded/ESP32 with no_std
[dependencies]
cand = { version = "0.2.2", default-feature=false, features=["colors"] }
Embedded/ESP32 with alloc but no_std
[dependencies]
cand = { version = "0.2.2", default-feature=false, features=["colors", "alloc"] }
use cand::{ULogger, UStorageProvider};
struct UartStorage {
serial: Serial
};
impl UStorageProvider for UartStorage {
fn write_data(&mut self, d: impl ufmt::uDebug) {
ufmt::uwrite!(&mut self.serial, "{:?}", d).ok();
}
}
fn main() {
let mut logger = ULogger((), UartStorage { serial: ... });
logger.log_ok("๐ Device initialized!");
logger.log_info("๐ก Connected to network");
}
๐จ Beautiful Output
CAND automatically color-codes your logs for instant visual feedback:
- ๐ข
log_ok() - Success operations (green)
- ๐ต
log_info() - Informational messages (blue)
- ๐ก
log_warn() - Warnings that need attention (yellow)
- ๐ด
log_err() - Critical errors (red)

๐ก๏ธ Error Handling That Never Panics
use cand::Logger;
fn risky_operation() -> Result<String, &'static str> {
Err("network timeout")
}
fn fallback_handler(mut logger: Logger<std::time::Instant, ()>) {
logger.log_warn("๐ Entering fallback mode");
logger.log_info("๐พ Switching to cached data");
}
fn main() {
let logger = Logger(std::time::Instant::now(), ());
let (data, recovered_logger) = logger.try_get(
risky_operation(),
fallback_handler
);
}
๐ก๏ธ Panic Handling with black_box_cand
CAND provides a macro to set up a panic handler that logs panics using the logger, ensuring even panics are captured gracefully.
use cand::{black_box_cand, Logger};
fn main() {
black_box_cand!();
let custom_tp = TimeProvider1::new();
let custom_sp = StorageProvider1::new();
let logger = Logger(custom_tp, custom_sp);
black_box_cand!(logger);
panic!("This will be logged at critical level");
}
For no_std environments, the macro sets a panic_handler that logs to the provided logger.
๐ Custom Storage Providers
Embedded UART with ufmt
[dependencies]
cand = { version = "0.2.2", default-feature=false, features=["colors", "ufmt"] }
struct UartStorage{
serial: Serial
};
impl UStorageProvider for UartStorage {
fn write_data(&mut self, d: impl ufmt::uDebug) {
ufmt::uwrite!(self.serial,"{:?}", d);
}
}
Standard Output with fmt
impl StorageProvider for () {
fn write_data(&mut self, args: Arguments<'_>, _debuglevel: &StatusLevel) {
print!("{args}")
}
}
๐๏ธ Feature Flags
| Feature |
Description |
Default |
std |
Standard library support, enables Instant time provider |
โ
|
colors |
ANSI color output for beautiful terminal logs |
โ
|
ufmt |
Embedded-friendly formatting with zero allocations, supports both no_std and std |
No |
alloc |
Enables Box for dynamic error handling |
No |
๐ Performance
- โก Zero allocations with
ufmt feature
- ๐ 1_000_000 logs in 4.2s (for alloc takes 5.1s) on example benchmark with decent release
๐๏ธ API Reference
Core Types
pub struct Logger<T: TimeProvider, S: StorageProvider>(pub T, pub S);
pub struct MultiLogger<T: TimeProvider + Clone, S: StorageProvider + Clone>(pub T, pub S);
pub struct ULogger<T: TimeProvider, S: UStorageProvider>(pub T, pub S);
pub struct MultiULogger<T: TimeProvider + Clone, S: UStorageProvider + Clone>(pub T, pub S);
Traits
TimeProvider: For timestamping logs (e.g., Instant or custom)
StorageProvider: For fmt-based output destinations
UStorageProvider: For ufmt-based output destinations
๐งช Examples
Check out the examples directory:
Run examples:
cargo run --example sample
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.