//! Logging utilities for Veza Rust services //! //! This module provides centralized logging configuration and utilities. use tracing::{Level, Subscriber}; use tracing_subscriber::{ fmt::{self, format::FmtSpan}, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry, }; use crate::{VezaError, VezaResult}; /// Initialize logging for Veza services pub fn init() -> VezaResult<()> { // Set default log level if not specified if std::env::var("RUST_LOG").is_err() { std::env::set_var("RUST_LOG", "info"); } // Create environment filter let env_filter = EnvFilter::try_from_default_env() .unwrap_or_else(|_| EnvFilter::new("info")); // Create formatting layer let fmt_layer = fmt::layer() .with_target(true) .with_thread_ids(true) .with_thread_names(true) .with_span_events(FmtSpan::CLOSE) .with_file(true) .with_line_number(true) .with_ansi(true); // Initialize the subscriber Registry::default() .with(env_filter) .with(fmt_layer) .init(); tracing::info!("Logging initialized"); Ok(()) } /// Initialize logging with custom configuration pub fn init_with_config(config: LoggingConfig) -> VezaResult<()> { // Set log level std::env::set_var("RUST_LOG", &config.level); // Create environment filter let env_filter = EnvFilter::try_from_default_env() .unwrap_or_else(|_| EnvFilter::new(&config.level)); // Create formatting layer based on format let fmt_layer = match config.format.as_str() { "json" => fmt::layer() .json() .with_target(true) .with_thread_ids(true) .with_thread_names(true) .with_span_events(FmtSpan::CLOSE) .with_file(true) .with_line_number(true) .boxed(), "text" => fmt::layer() .with_target(true) .with_thread_ids(true) .with_thread_names(true) .with_span_events(FmtSpan::CLOSE) .with_file(true) .with_line_number(true) .with_ansi(true) .boxed(), _ => return Err(VezaError::Config(format!("Invalid log format: {}", config.format))), }; // Initialize the subscriber Registry::default() .with(env_filter) .with(fmt_layer) .init(); tracing::info!("Logging initialized with custom config"); Ok(()) } /// Logging configuration #[derive(Debug, Clone)] pub struct LoggingConfig { pub level: String, pub format: String, pub file: Option, pub max_size: u64, pub max_files: u32, pub compress: bool, } impl Default for LoggingConfig { fn default() -> Self { Self { level: "info".to_string(), format: "json".to_string(), file: None, max_size: 100 * 1024 * 1024, // 100MB max_files: 5, compress: true, } } } /// Structured logging macros #[macro_export] macro_rules! log_info { ($($arg:tt)*) => { tracing::info!($($arg)*) }; } #[macro_export] macro_rules! log_warn { ($($arg:tt)*) => { tracing::warn!($($arg)*) }; } #[macro_export] macro_rules! log_error { ($($arg:tt)*) => { tracing::error!($($arg)*) }; } #[macro_export] macro_rules! log_debug { ($($arg:tt)*) => { tracing::debug!($($arg)*) }; } #[macro_export] macro_rules! log_trace { ($($arg:tt)*) => { tracing::trace!($($arg)*) }; } /// Log performance metrics pub fn log_performance(operation: &str, duration: std::time::Duration, metadata: &[(&str, &dyn std::fmt::Display)]) { tracing::info!( operation = operation, duration_ms = duration.as_millis(), ?metadata, "Performance metric" ); } /// Log security events pub fn log_security_event(event: &str, user_id: Option, ip_address: Option<&str>, metadata: &[(&str, &dyn std::fmt::Display)]) { tracing::warn!( event = event, user_id = ?user_id, ip_address = ip_address, ?metadata, "Security event" ); } /// Log business events pub fn log_business_event(event: &str, user_id: Option, resource_id: Option, metadata: &[(&str, &dyn std::fmt::Display)]) { tracing::info!( event = event, user_id = ?user_id, resource_id = ?resource_id, ?metadata, "Business event" ); } /// Log system events pub fn log_system_event(event: &str, component: &str, metadata: &[(&str, &dyn std::fmt::Display)]) { tracing::info!( event = event, component = component, ?metadata, "System event" ); } /// Log error with context pub fn log_error_with_context(error: &VezaError, context: &[(&str, &dyn std::fmt::Display)]) { if error.should_log() { tracing::error!( error = %error, ?context, "Error occurred" ); } else { tracing::debug!( error = %error, ?context, "Error occurred" ); } } /// Log request/response pub fn log_request_response( method: &str, path: &str, status_code: u16, duration: std::time::Duration, user_id: Option, request_id: Option<&str>, ) { let level = if status_code >= 500 { Level::ERROR } else if status_code >= 400 { Level::WARN } else { Level::INFO }; tracing::event!( level, method = method, path = path, status_code = status_code, duration_ms = duration.as_millis(), user_id = ?user_id, request_id = request_id, "HTTP request" ); } /// Log database query pub fn log_db_query( query: &str, duration: std::time::Duration, rows_affected: Option, error: Option<&str>, ) { if let Some(error) = error { tracing::error!( query = query, duration_ms = duration.as_millis(), rows_affected = ?rows_affected, error = error, "Database query failed" ); } else { tracing::debug!( query = query, duration_ms = duration.as_millis(), rows_affected = ?rows_affected, "Database query" ); } } /// Log cache operations pub fn log_cache_operation( operation: &str, key: &str, hit: bool, duration: std::time::Duration, error: Option<&str>, ) { if let Some(error) = error { tracing::warn!( operation = operation, key = key, hit = hit, duration_ms = duration.as_millis(), error = error, "Cache operation failed" ); } else { tracing::debug!( operation = operation, key = key, hit = hit, duration_ms = duration.as_millis(), "Cache operation" ); } } /// Log WebSocket events pub fn log_websocket_event( event: &str, user_id: Option, connection_id: Option, metadata: &[(&str, &dyn std::fmt::Display)], ) { tracing::info!( event = event, user_id = ?user_id, connection_id = ?connection_id, ?metadata, "WebSocket event" ); } /// Log streaming events pub fn log_streaming_event( event: &str, track_id: Option, user_id: Option, metadata: &[(&str, &dyn std::fmt::Display)], ) { tracing::info!( event = event, track_id = ?track_id, user_id = ?user_id, ?metadata, "Streaming event" ); } /// Create a span for tracing pub fn create_span(name: &str, metadata: &[(&str, &dyn std::fmt::Display)]) -> tracing::Span { tracing::info_span!(name, ?metadata) } /// Log startup information pub fn log_startup(service_name: &str, version: &str, config: &[(&str, &dyn std::fmt::Display)]) { tracing::info!( service = service_name, version = version, ?config, "Service starting" ); } /// Log shutdown information pub fn log_shutdown(service_name: &str, uptime: std::time::Duration) { tracing::info!( service = service_name, uptime_seconds = uptime.as_secs(), "Service shutting down" ); }