use crate::{EventId, SimTime};
use std::collections::HashMap;
use tracing::{debug, error, info, trace, warn, Span};
use tracing_subscriber::{filter::EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt};
pub fn init_simulation_logging() {
init_simulation_logging_with_level("info")
}
pub fn init_simulation_logging_with_level(level: &str) {
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
format!(
"{}={},descartes_core::scheduler=debug,descartes_core::async_runtime=debug,descartes_components=info",
env!("CARGO_PKG_NAME"),
level
)
.into()
});
let result = tracing_subscriber::registry()
.with(
fmt::layer()
.with_target(true)
.with_thread_ids(true)
.with_level(true)
.with_file(true)
.with_line_number(true),
)
.with(filter)
.try_init();
match result {
Ok(_) => info!("Simulation logging initialized at level: {}", level),
Err(_) => {
eprintln!("Note: Global tracing subscriber already initialized");
}
}
}
pub fn init_detailed_simulation_logging() {
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
"trace,descartes_core=trace,descartes_components=debug,descartes_metrics=debug".into()
});
let result = tracing_subscriber::registry()
.with(
fmt::layer()
.with_target(true)
.with_thread_ids(true)
.with_level(true)
.with_file(true)
.with_line_number(true)
.pretty(), )
.with(filter)
.try_init();
match result {
Ok(_) => info!("Detailed simulation logging initialized"),
Err(_) => {
eprintln!("Note: Global tracing subscriber already initialized");
}
}
}
pub fn simulation_span(name: &str) -> Span {
tracing::info_span!("simulation", name = name)
}
pub fn component_span(component_name: &str, component_id: &str) -> Span {
tracing::debug_span!("component", name = component_name, id = component_id)
}
pub fn event_span(event_id: EventId, event_type: &str, time: SimTime) -> Span {
tracing::trace_span!("event",
id = ?event_id,
event_type = event_type,
time = ?time
)
}
pub fn task_span(task_name: &str, task_id: &str) -> Span {
tracing::debug_span!("task", name = task_name, id = task_id)
}
pub mod events {
use super::*;
pub fn simulation_started(name: &str, end_time: Option<SimTime>) {
match end_time {
Some(end) => info!(
simulation = name,
end_time = ?end,
"Simulation started"
),
None => info!(simulation = name, "Simulation started (unbounded)"),
}
}
pub fn simulation_completed(name: &str, final_time: SimTime, events_processed: u64) {
info!(
simulation = name,
final_time = ?final_time,
events_processed = events_processed,
"Simulation completed"
);
}
pub fn event_scheduled(event_id: EventId, event_type: &str, time: SimTime, component: &str) {
trace!(
event_id = ?event_id,
event_type = event_type,
time = ?time,
component = component,
"Event scheduled"
);
}
pub fn event_processing_started(event_id: EventId, event_type: &str, time: SimTime) {
trace!(
event_id = ?event_id,
event_type = event_type,
time = ?time,
"Processing event"
);
}
pub fn event_processing_completed(event_id: EventId, duration_ns: u64) {
trace!(
event_id = ?event_id,
duration_ns = duration_ns,
"Event processing completed"
);
}
pub fn component_state_changed(component: &str, old_state: &str, new_state: &str) {
debug!(
component = component,
old_state = old_state,
new_state = new_state,
"Component state changed"
);
}
pub fn task_spawned(task_name: &str, task_id: &str) {
debug!(task_name = task_name, task_id = task_id, "Task spawned");
}
pub fn task_completed(task_name: &str, task_id: &str, success: bool) {
debug!(
task_name = task_name,
task_id = task_id,
success = success,
"Task completed"
);
}
pub fn scheduler_state(
current_time: SimTime,
pending_events: usize,
next_event_time: Option<SimTime>,
) {
trace!(
current_time = ?current_time,
pending_events = pending_events,
next_event_time = ?next_event_time,
"Scheduler state"
);
}
pub fn performance_metrics(
events_per_second: f64,
memory_usage_mb: f64,
simulation_time_ratio: f64,
) {
info!(
events_per_second = events_per_second,
memory_usage_mb = memory_usage_mb,
simulation_time_ratio = simulation_time_ratio,
"Performance metrics"
);
}
}
pub mod diagnostics {
use super::*;
pub fn potential_deadlock_detected(component: &str, details: &str) {
warn!(
component = component,
details = details,
"Potential deadlock detected"
);
}
pub fn excessive_queue_growth(queue_size: usize, threshold: usize) {
warn!(
queue_size = queue_size,
threshold = threshold,
"Event queue growing excessively"
);
}
pub fn slow_event_processing(event_type: &str, duration_ms: f64, threshold_ms: f64) {
warn!(
event_type = event_type,
duration_ms = duration_ms,
threshold_ms = threshold_ms,
"Slow event processing detected"
);
}
pub fn component_error(component: &str, error: &str, context: &HashMap<String, String>) {
error!(
component = component,
error = error,
context = ?context,
"Component error occurred"
);
}
pub fn simulation_inconsistency(description: &str, expected: &str, actual: &str) {
error!(
description = description,
expected = expected,
actual = actual,
"Simulation inconsistency detected"
);
}
pub fn resource_exhaustion(resource_type: &str, current: u64, limit: u64) {
error!(
resource_type = resource_type,
current = current,
limit = limit,
"Resource exhaustion detected"
);
}
}
#[macro_export]
macro_rules! sim_trace {
($($arg:tt)*) => {
tracing::trace!($($arg)*)
};
}
#[macro_export]
macro_rules! sim_debug {
($($arg:tt)*) => {
tracing::debug!($($arg)*)
};
}
#[macro_export]
macro_rules! sim_info {
($($arg:tt)*) => {
tracing::info!($($arg)*)
};
}
#[macro_export]
macro_rules! sim_warn {
($($arg:tt)*) => {
tracing::warn!($($arg)*)
};
}
#[macro_export]
macro_rules! sim_error {
($($arg:tt)*) => {
tracing::error!($($arg)*)
};
}
#[macro_export]
macro_rules! sim_log_with_time {
($level:ident, $time:expr, $($arg:tt)*) => {
tracing::$level!(time = ?$time, $($arg)*)
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_logging_initialization() {
let _ = std::panic::catch_unwind(|| {
init_simulation_logging_with_level("debug");
});
info!("Test info message");
debug!("Test debug message");
trace!("Test trace message");
}
#[test]
fn test_span_creation() {
let _sim_span = simulation_span("test_simulation");
let _comp_span = component_span("test_component", "comp_1");
let _event_span = event_span(EventId(1), "TestEvent", SimTime::from_millis(100));
let _task_span = task_span("test_task", "task_1");
}
#[test]
fn test_event_logging() {
events::simulation_started("test_sim", Some(SimTime::from_secs(10)));
events::event_scheduled(
EventId(1),
"TestEvent",
SimTime::from_millis(100),
"test_component",
);
events::event_processing_started(EventId(1), "TestEvent", SimTime::from_millis(100));
events::event_processing_completed(EventId(1), 1000);
events::simulation_completed("test_sim", SimTime::from_secs(5), 100);
}
#[test]
fn test_diagnostic_logging() {
diagnostics::potential_deadlock_detected("test_component", "No events scheduled");
diagnostics::excessive_queue_growth(10000, 1000);
diagnostics::slow_event_processing("SlowEvent", 500.0, 100.0);
let mut context = HashMap::new();
context.insert("request_id".to_string(), "123".to_string());
diagnostics::component_error("test_component", "Processing failed", &context);
}
#[test]
fn test_logging_macros() {
sim_info!("Test info macro");
sim_debug!("Test debug macro");
sim_warn!("Test warning macro");
sim_error!("Test error macro");
let time = SimTime::from_millis(100);
sim_log_with_time!(info, time, "Test with time context");
}
}