libindigo uses the tracing framework for structured logging throughout all crates. The logging system provides configurable output destinations and log levels with support for both stderr and file output.
use libindigo::logging::{LogConfig, LogLevel};
use libindigo::client::ClientBuilder;
// Initialize logging with default settings (INFO level, stderr output)
let log_config = LogConfig::default();
let client = ClientBuilder::new()
.with_strategy(strategy)
.with_logging(log_config)
.build()?;
use libindigo::logging::{LogConfig, LogLevel};
let log_config = LogConfig::default()
.with_level(LogLevel::Debug);
let client = ClientBuilder::new()
.with_strategy(strategy)
.with_logging(log_config)
.build()?;
use libindigo::logging::{LogConfig, LogLevel};
use std::path::PathBuf;
let log_config = LogConfig::default()
.with_level(LogLevel::Info)
.with_log_file(PathBuf::from("/var/log/indigo-client.log"));
let client = ClientBuilder::new()
.with_strategy(strategy)
.with_logging(log_config)
.build()?;
libindigo follows the standard tracing log level hierarchy:
| Level | Usage | Example |
|---|---|---|
| ERROR | Application integrity compromised | "Monitoring task panicked for 192.168.1.50:7624" |
| WARN | Graceful recovery possible | "ICMP unavailable, falling back to TCP connect" |
| INFO | Meaningful user information (default) | "Server 192.168.1.50:7624 status: Available -> Degraded" |
| DEBUG | Additional troubleshooting info | "Heartbeat check failed for 192.168.1.50: connection refused" |
| TRACE | Detailed application logic tracing | "ICMP ping sent to 192.168.1.50" |
Use for situations where application integrity is compromised:
Use for situations requiring graceful recovery:
Use for meaningful user-facing information:
Use for troubleshooting and diagnostics:
Use for detailed execution flow:
pub struct LogConfig {
/// The minimum log level to output (default: INFO)
pub level: LogLevel,
/// Optional file path for log output
/// If set, logs are written to both stderr and the file
pub log_file: Option<PathBuf>,
}
// Set log level
let config = LogConfig::default()
.with_level(LogLevel::Debug);
// Set log file
let config = LogConfig::default()
.with_log_file(PathBuf::from("app.log"));
// Chain multiple settings
let config = LogConfig::default()
.with_level(LogLevel::Trace)
.with_log_file(PathBuf::from("/var/log/indigo.log"));
The logging system respects the RUST_LOG environment variable, which takes precedence over programmatic configuration:
# Set log level for all libindigo crates
export RUST_LOG=libindigo=debug,libindigo_rs=debug,libindigo_ffi=debug
# Run your application
./my_indigo_app
# Or inline
RUST_LOG=trace ./my_indigo_app
# Set global level
RUST_LOG=debug
# Set per-crate level
RUST_LOG=libindigo=info,libindigo_rs=debug
# Set per-module level
RUST_LOG=libindigo_rs::monitoring=trace
# Combine multiple targets
RUST_LOG=libindigo=info,libindigo_rs::monitoring=trace,libindigo_rs::client=debug
By default, logs are written to stderr with ANSI color codes:
2026-03-12T06:45:23.123Z INFO libindigo_rs::client: Connected to 192.168.1.50:7624
2026-03-12T06:45:28.456Z WARN libindigo_rs::monitoring::heartbeat: ICMP unavailable, falling back to TCP connect
2026-03-12T06:45:33.789Z INFO libindigo_rs::monitoring::monitor: Status changed from Available to Degraded
When a log file is configured, logs are written to the file without ANSI codes:
let config = LogConfig::default()
.with_log_file(PathBuf::from("indigo.log"));
File output includes:
Both stderr and file output are written simultaneously when a log file is configured.
The ClientBuilder provides a fluent API for configuring logging:
use libindigo::client::ClientBuilder;
use libindigo::logging::{LogConfig, LogLevel};
let client = ClientBuilder::new()
.with_strategy(strategy)
.with_logging(
LogConfig::default()
.with_level(LogLevel::Debug)
.with_log_file(PathBuf::from("client.log"))
)
.build()?;
Logging is initialized when ClientBuilder::build() is called. If initialization fails (e.g., cannot create log file), build() returns an error.
The tracing subscriber can only be set once per process. If you create multiple clients:
Best Practice: Initialize logging once at application startup:
use libindigo::logging::{init_logging, LogConfig, LogLevel};
// At application startup
init_logging(&LogConfig::default().with_level(LogLevel::Info))?;
// Later, create clients without logging config
let client1 = ClientBuilder::new()
.with_strategy(strategy1)
.build()?;
let client2 = ClientBuilder::new()
.with_strategy(strategy2)
.build()?;
When using the monitoring feature, additional log levels are used:
| Component | Level | Example |
|---|---|---|
| HeartbeatChecker | TRACE | "ICMP ping sent to 192.168.1.50" |
| HeartbeatChecker | TRACE | "ICMP ping response from 192.168.1.50: rtt=12ms" |
| HeartbeatChecker | WARN | "ICMP unavailable, falling back to TCP connect" |
| HeartbeatChecker | DEBUG | "Heartbeat check failed for 192.168.1.50: connection refused" |
| ServerChecker | TRACE | "TCP handshake attempt to 192.168.1.50:7624" |
| ServerChecker | DEBUG | "Server handshake failed: timeout after 2s" |
| StatusTracker | TRACE | "Window state: 4/5 successes, current=Available" |
| ServerMonitor | INFO | "Server 192.168.1.50:7624 status: Available -> Degraded" |
# Production: Only status changes
RUST_LOG=libindigo_rs::monitoring=info
# Troubleshooting: Include check failures
RUST_LOG=libindigo_rs::monitoring=debug
# Deep debugging: All ping/handshake details
RUST_LOG=libindigo_rs::monitoring=trace
use libindigo::logging::{LogConfig, LogLevel};
use std::path::PathBuf;
let log_config = LogConfig::default()
.with_level(LogLevel::Info)
.with_log_file(PathBuf::from("/var/log/indigo-client.log"));
let client = ClientBuilder::new()
.with_strategy(strategy)
.with_logging(log_config)
.build()?;
use libindigo::logging::{LogConfig, LogLevel};
let log_config = LogConfig::default()
.with_level(LogLevel::Debug);
let client = ClientBuilder::new()
.with_strategy(strategy)
.with_logging(log_config)
.build()?;
# Debug only monitoring
RUST_LOG=libindigo=info,libindigo_rs::monitoring=debug ./app
# Trace monitoring, debug everything else
RUST_LOG=libindigo=debug,libindigo_rs::monitoring=trace ./app
use libindigo::logging::{init_logging, LogConfig, LogLevel};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize logging at application startup
init_logging(&LogConfig::default()
.with_level(LogLevel::Info)
.with_log_file(PathBuf::from("app.log")))?;
// Rest of application...
Ok(())
}
Problem: No logs appear when running the application.
Solutions:
ClientBuilder or init_logging)LogLevel::Trace)RUST_LOG environment variable isn’t overriding settingsProblem: Log file is not created or written.
Solutions:
build() or init_logging()Problem: Log messages appear multiple times.
Solutions:
init_logging() and ClientBuilder::with_logging()Problem: RUST_LOG environment variable has no effect.
Solutions:
RUST_LOG before running the applicationRUST_LOG=libindigo=debugRUST_LOG overrides programmatic configurationecho $RUST_LOGinit_logging() - Initialize logging systemLogConfig::default() - Create default configurationLogConfig::with_level() - Set log levelLogConfig::with_log_file() - Set log file pathLast Updated: 2026-03-12 Version: 0.2.0