#logging #esp32

no-std cand

Beautiful embedded-first Rust logging library for ESP32 to servers with colorful output and zero-panic design

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

MIT license

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)

sample of output

๐Ÿ›ก๏ธ 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 ufmt feature
  • ๐Ÿš€ 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., 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.

Dependencies