veza/veza-common/src/logging.rs
2025-12-12 21:34:34 -05:00

343 lines
8.3 KiB
Rust

//! 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<String>,
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<uuid::Uuid>, 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<uuid::Uuid>, resource_id: Option<uuid::Uuid>, 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<uuid::Uuid>,
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<u64>,
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<uuid::Uuid>,
connection_id: Option<uuid::Uuid>,
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<uuid::Uuid>,
user_id: Option<uuid::Uuid>,
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"
);
}