9 releases
Uses new Rust 2024
| 0.3.2 | Nov 15, 2025 |
|---|---|
| 0.3.1 | Nov 6, 2025 |
| 0.2.3 | Oct 17, 2025 |
| 0.2.1 | Aug 25, 2025 |
| 0.1.1 | Jul 27, 2025 |
#516 in Embedded development
Used in kramaframe
240KB
727 lines
๐ฏ 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?
| Feature | Benefit | Use Case |
|---|---|---|
| ๐จ 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
[dependencies]
cand = "0.3.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.3.2", default-feature=false, features=["colors"] }
Embedded/ESP32 with alloc but no_std
[dependencies]
cand = { version = "0.3.2", default-feature=false, features=["colors", "alloc"] }
use cand::{ULogger, UStorageProvider};
struct UartStorage {
// Serial can be from any MCU serial
serial: Serial
};
impl UStorageProvider for UartStorage {
fn write_data(&mut self, d: impl ufmt::uDebug) {
// Write to UART, RTT, or any embedded output
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(), ());
// Automatic error logging and graceful recovery
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!(); // Uses default logger for std environments
let custom_tp = TimeProvider1::new();
let custom_sp = StorageProvider1::new();
// Or with custom logger
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.3.2", default-feature=false, features=["colors", "ufmt"] }
struct UartStorage{
// Serial can be from any mcu serial
serial: Serial
};
impl UStorageProvider for UartStorage {
fn write_data(&mut self, d: impl ufmt::uDebug) {
// Write to UART, RTT, or any embedded output
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 | โ |
๐ Performance
- โก Zero allocations with
ufmtfeature - ๐ 1_000_000 logs in 4.2s (for alloc takes 5.1s) on example benchmark with decent release
๐๏ธ API Reference
Core Types
// Standard fmt-based logger
pub struct Logger<T: TimeProvider, S: StorageProvider>(pub T, pub S);
// Clonable version for multi-threaded use
pub struct MultiLogger<T: TimeProvider + Clone, S: StorageProvider + Clone>(pub T, pub S);
// ufmt-based logger (requires "ufmt" feature)
pub struct ULogger<T: TimeProvider, S: UStorageProvider>(pub T, pub S);
// Clonable ufmt-based logger
pub struct MultiULogger<T: TimeProvider + Clone, S: UStorageProvider + Clone>(pub T, pub S);
Traits
TimeProvider: For timestamping logs (e.g.,Instantor custom)StorageProvider: For fmt-based output destinationsUStorageProvider: For ufmt-based output destinations
๐งช Examples
Check out the examples directory:
basic_error_handling- Error recovery patternssample- Feature showcase and demobenchmark- Benchmark of 1_000_000 logs printerrorcand- Advanced error handling with try_get and try_runpaniccand- Panic handling demonstration with black_box_candcustom_panic- Same Panic handling but customcustom_panic_global- And this one is globally share
Run examples:
cargo run --example sample
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.