# Hero Logger A hierarchical logging system for the Hero project that provides system-level and per-job logging with complete isolation using the `tracing` ecosystem. ## Features - **Hierarchical Organization**: Physical separation of logs by component and job - **System Logger**: Global logging for all non-job-specific events - **Per-Job Logger**: Isolated logging for individual job execution - **Hourly Rotation**: Automatic log file rotation every hour - **Rhai Integration**: Capture Rhai script `print()` and `debug()` calls - **High Performance**: Async logging with efficient filtering - **Structured Logging**: Rich context and metadata support ## Architecture The logging system uses a hybrid approach with two main components: ### System Logger (Global) - Long-lived logger initialized at application startup - Routes logs to different files based on tracing targets - Supports multiple components simultaneously ### Per-Job Logger (Dynamic) - Created on-demand for each job execution - Provides complete isolation for job-specific logs - Automatically disposed after job completion ## Directory Structure ``` logs/ ├── supervisor/ # System logs for supervisor │ └── 2025-08-06-11.log └── actor/ ├── osis/ │ ├── 2025-08-06-11.log # General OSIS actor logs │ ├── job-a1b2c3d4/ # Job-specific logs │ │ └── 2025-08-06-11.log │ └── job-9a8b7c6d/ │ └── 2025-08-06-12.log └── sal/ ├── 2025-08-06-13.log # General SAL actor logs └── job-f1e2d3c4/ └── 2025-08-06-13.log ``` ## Quick Start ### 1. Initialize System Logger ```rust use hero_logger; #[tokio::main] async fn main() -> Result<(), Box> { // Define your system components let components = vec![ "supervisor".to_string(), "osis_actor".to_string(), "sal_actor".to_string(), ]; // Initialize the system logger let _guards = hero_logger::init_system_logger("logs", &components)?; // Now you can use tracing macros with targets tracing::info!(target: "supervisor", "System started"); tracing::info!(target: "osis_actor", "Actor ready"); Ok(()) } ``` ### 2. Per-Job Logging ```rust use hero_logger::create_job_logger; use tracing::subscriber::with_default; async fn process_job(job_id: &str, actor_type: &str) { // Create job-specific logger let job_logger = create_job_logger("logs", actor_type, job_id)?; // Execute job within logging context with_default(job_logger, || { tracing::info!(target: "osis_actor", "Job {} started", job_id); // All tracing calls here go to the job-specific log tracing::debug!(target: "osis_actor", "Processing data..."); tracing::info!(target: "osis_actor", "Job {} completed", job_id); }); } ``` ### 3. Rhai Script Integration ```rust use hero_logger::rhai_integration::configure_rhai_logging; use rhai::Engine; fn setup_rhai_engine() -> Engine { let mut engine = Engine::new(); // Configure Rhai to capture print/debug calls configure_rhai_logging(&mut engine, "osis_actor"); engine } // Now Rhai scripts can use print() and debug() let script = r#" print("Hello from Rhai!"); debug("Debug information"); 42 "#; let result = engine.eval::(script)?; ``` ## API Reference ### Core Functions #### `init_system_logger(logs_root, components)` Initialize the global system logger with component-based filtering. **Parameters:** - `logs_root`: Root directory for log files - `components`: List of component names for dedicated logging **Returns:** Vector of `WorkerGuard`s that must be kept alive #### `create_job_logger(logs_root, actor_type, job_id)` Create a per-job logger for isolated logging. **Parameters:** - `logs_root`: Root directory for log files - `actor_type`: Type of actor (e.g., "osis", "sal") - `job_id`: Unique job identifier **Returns:** Boxed subscriber for use with `with_default()` ### Rhai Integration #### `configure_rhai_logging(engine, target)` Configure a Rhai engine to capture print/debug output. #### `add_custom_logging_functions(engine, target)` Add custom logging functions (`log_info`, `log_debug`, etc.) to Rhai. #### `create_logging_enabled_engine(target, include_custom)` Create a new Rhai engine with full logging integration. ### Utilities #### `ensure_log_directories(logs_root, components)` Ensure the log directory structure exists. #### `extract_actor_type(component)` Extract actor type from component name. #### `cleanup_old_logs(directory, pattern, max_age_days)` Clean up old log files based on age. ## Configuration ### Log Levels The system supports standard tracing log levels: - `ERROR`: Critical errors - `WARN`: Warning messages - `INFO`: Informational messages - `DEBUG`: Debug information - `TRACE`: Detailed trace information ### Environment Variables - `RUST_LOG`: Set log level filtering (e.g., `RUST_LOG=debug`) ### File Rotation - **Hourly**: Default rotation every hour - **Daily**: Optional daily rotation - **Never**: Single file (no rotation) ## Examples ### Basic Usage ```bash cargo run --example logging_demo ``` ### Integration with Actor System ```rust // In your actor implementation async fn process_job(&self, job: &Job) { let job_logger = hero_logger::create_job_logger( "logs", &self.actor_type, &job.id ).unwrap(); let job_task = async move { tracing::info!(target: &self.actor_type, "Job processing started"); // Configure Rhai engine for this job let mut engine = Engine::new(); hero_logger::rhai_integration::configure_rhai_logging( &mut engine, &self.actor_type ); // Execute Rhai script - print/debug calls captured let result = engine.eval::(&job.script)?; tracing::info!(target: &self.actor_type, "Job finished: {}", result); Ok(result) }; // Execute with job-specific logging tracing::subscriber::with_default(job_logger, job_task).await; } ``` ## Performance Considerations - **Async Logging**: All file I/O is asynchronous - **Efficient Filtering**: Target-based filtering minimizes overhead - **Memory Usage**: Per-job loggers are short-lived and automatically cleaned up - **File Handles**: Automatic rotation prevents excessive file handle usage ## Troubleshooting ### Common Issues 1. **Logs not appearing**: Ensure `WorkerGuard`s are kept alive 2. **Permission errors**: Check write permissions on log directory 3. **Missing directories**: Use `ensure_log_directories()` before logging 4. **Rhai output not captured**: Verify `configure_rhai_logging()` is called ### Debug Mode Enable debug logging to see internal logger operations: ```bash RUST_LOG=hero_logger=debug cargo run ``` ## Testing Run the test suite: ```bash cargo test ``` Run the demo example: ```bash cargo run --example logging_demo ``` ## License This project is part of the Hero ecosystem and follows the same licensing terms.