Skip to content

Structured Logging with Tracing

Note for Python users: This page documents the Rust-internal tracing_config module, which is not exposed to the Python package. There is no evlib.tracing_config in Python. To control Rust log verbosity from Python, set the RUST_LOG environment variable before importing evlib (for example os.environ['RUST_LOG'] = 'evlib=debug').

evlib uses the tracing crate for structured, configurable logging. This provides better control over log verbosity, structured output, and integration with external logging systems.

Quick Start

Basic Setup

use evlib::tracing_config;

// Initialize with sensible defaults
tracing_config::init();

// Now load events - logging will be structured and configurable
let events = evlib::ev_formats::load_events_with_config("tests/data/eTram/h5/val_2/val_night_011_td.h5", &config)?;

Environment Variable Control

Control logging via the RUST_LOG environment variable:

# Default: INFO level for evlib, WARN for everything else
cargo run

# Enable debug logging for evlib
RUST_LOG=evlib=debug cargo run

# Only show warnings and errors
RUST_LOG=evlib=warn cargo run

# Very detailed tracing for specific module
RUST_LOG=evlib::ev_formats::prophesee_ecf_codec=trace cargo run

# Multiple module control
RUST_LOG="evlib::ev_formats=debug,evlib::ev_representations=info" cargo run

Log Levels

evlib uses the following log levels:

ERROR - Critical Failures

  • Model loading failures
  • File parsing errors
  • System-level problems
  • Fallback mechanism failures

Example output:

2024-01-15T10:30:45.123456Z ERROR evlib::ev_formats::mod: Python fallback failed error="File not found"

WARN - Warnings and Fallbacks

  • Fallback behaviors activated
  • Unsupported file formats
  • Missing optional dependencies
  • Performance degradation warnings

Example output:

2024-01-15T10:30:45.123456Z  WARN evlib::ev_formats::prophesee_ecf_codec: Unknown delta_bits value in ECF decoder delta_bits=7

INFO - Status and Progress

  • File loading progress
  • Model downloading status
  • Successful operations
  • Stream status updates

Example output:

2024-01-15T10:30:45.123456Z  INFO evlib::ev_formats::mod: Native Rust ECF decoder loaded events events=1048576

DEBUG - Development Information

  • Detailed ECF decoding steps
  • WebSocket connection details
  • Model verification results
  • Internal state information

Example output:

2024-01-15T10:30:45.123456Z DEBUG evlib::ev_formats::prophesee_ecf_codec: ECF base timestamp base_timestamp=1627849200000000

TRACE - Very Detailed Debug

  • Low-level operations
  • Performance timing
  • Memory allocation details

Note: Currently no TRACE level logs in evlib, reserved for future use.

Configuration Options

Default Configuration

use evlib::tracing_config;

// Standard configuration - good for most use cases
tracing_config::init();

Debug Configuration

use evlib::tracing_config;

// Enables debug logging with file/line information
tracing_config::init_debug();

Custom Configuration

use evlib::tracing_config;

// Fine-grained control over specific modules
tracing_config::init_with_filter("evlib::ev_formats=trace,evlib=info");

Production Configuration

use evlib::tracing_config::examples;

// JSON output suitable for log aggregation
examples::init_production();

Development Configuration

use evlib::tracing_config::examples;

// Pretty-printed output with colors and extra context
examples::init_development();

Integration Examples

With env_logger style

use tracing_subscriber::{EnvFilter, fmt};

// Similar to env_logger but with structured output
fmt()
    .with_env_filter(EnvFilter::from_default_env())
    .init();

With syslog

use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

tracing_subscriber::registry()
    .with(tracing_syslog::layer())
    .with(EnvFilter::from_default_env())
    .init();

With File Output

use tracing_subscriber::{fmt, EnvFilter};
use std::fs::File;

let file = File::create("evlib.log")?;
fmt()
    .with_writer(file)
    .with_env_filter(EnvFilter::from_default_env())
    .init();

With JSON for Kubernetes/Docker

use tracing_subscriber::{fmt, EnvFilter};

fmt()
    .json()
    .with_env_filter(EnvFilter::from_default_env())
    .init();

Testing Integration

#[cfg(test)]
mod tests {
    use evlib::tracing_config;

    #[test]
    fn test_event_loading() {
        // Initialize tracing for tests
        tracing_config::init_test();

        // Test code here - logs will be captured in test output
        let events = evlib::ev_formats::load_events_from_text("tests/data/test.txt")?;
    }
}

Python Integration

When using evlib from Python, you can still control Rust logging:

import os
import evlib

# Set logging level before importing/using evlib
os.environ['RUST_LOG'] = 'evlib=debug'

# Initialize tracing from Python (if exposed)
# Note: This requires exposing tracing_config functions to Python
# evlib.tracing_config.init()

# Load events - Rust logs will be controlled by RUST_LOG
events = evlib.load_events('tests/data/eTram/h5/val_2/val_night_011_td.h5')
print(f"Loaded events with shape: {events.collect().shape}")

Performance Considerations

Runtime Overhead

  • trace! and debug! macros compile to no-ops when disabled
  • info!, warn!, error! have minimal overhead when disabled
  • Structured logging is more efficient than string formatting
  • Filtering happens at compile time when possible

Memory Usage

  • Structured logs use less memory than formatted strings
  • Span context tracking has minimal overhead
  • Field values are lazily formatted only when logged

Common Patterns

Module-Specific Debugging

# Debug only ECF decoding
RUST_LOG=evlib::ev_formats::prophesee_ecf_codec=debug cargo run

# Debug all format readers
RUST_LOG=evlib::ev_formats=debug cargo run

# Debug HDF5 reading specifically
RUST_LOG=evlib::ev_formats::hdf5_reader=debug cargo run

The deep-learning models (E2VID, RVT) are implemented in Python under python/evlib/models/, not in the Rust crate, so they are not controlled by RUST_LOG. To debug model loading, configure Python logging instead:

import logging

logging.basicConfig(level=logging.DEBUG)

Production Logging

# Minimal logging for production
RUST_LOG=evlib=warn cargo run

# Structured JSON for log aggregation
RUST_LOG=evlib=info cargo run > logs.json

Development Debugging

# Comprehensive debugging
RUST_LOG=evlib=debug cargo run

# Very detailed tracing
RUST_LOG=evlib=trace cargo run

Migration from println!/eprintln!

Old code using print statements:

eprintln!("Loading {} events from file", events.len());
println!("ECF decoder failed: {}", error);

New structured logging:

use tracing::{info, error};

info!(events = events.len(), "Loading events from file");
error!(error = %error, "ECF decoder failed");

Benefits

  1. Configurable Verbosity: Control logging at runtime without recompilation
  2. Structured Output: Key-value pairs for better parsing and filtering
  3. Performance: Debug logs compile to no-ops when disabled
  4. Integration: Works with existing Rust logging ecosystem
  5. Consistency: Uniform logging patterns across the entire library
  6. Observability: Better debugging and monitoring capabilities

Troubleshooting

No Logs Appearing

  1. Check that tracing is initialized: tracing_config::init()
  2. Verify RUST_LOG environment variable: RUST_LOG=evlib=debug
  3. Ensure log level is appropriate for desired messages

Too Many Logs

  1. Increase filter level: RUST_LOG=evlib=warn
  2. Use module-specific filtering: RUST_LOG=evlib::ev_formats=info
  3. Switch to production configuration for cleaner output

JSON Parsing Issues

  1. Use fmt().json() for consistent JSON output
  2. Ensure no mixed output formats in configuration
  3. Consider using dedicated JSON logging libraries for complex parsing

For more information, see the tracing documentation and tracing-subscriber documentation.