baobab/core/logger/README.md
Maxime Van Hees 9c4fa1a78b logger
2025-08-06 14:34:56 +02:00

259 lines
7.0 KiB
Markdown

# 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<dyn std::error::Error>> {
// 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::<i64>(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::<String>(&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.