chore: rename packages/ to crates/
Move all 29 workspace members from packages/<name>/ to crates/<name>/. Updates: workspace Cargo.toml (members + path deps), justfile, root CLAUDE.md, scripts/build/CARGO_INSTALL.md, docs/architecture/crates.md (renamed from packages.md), structural references in docs/architecture and docs/configuration, per-crate CLAUDE.md self-references. Historical plans, reports, and building/ docs are left untouched. No behavior change; just check-all stays green and fermata tests pass.
This commit is contained in:
+168
@@ -0,0 +1,168 @@
|
||||
//! Logging infrastructure for the ACP mocker.
|
||||
//!
|
||||
//! This module provides utilities for initializing and configuring structured logging
|
||||
//! using the `tracing` ecosystem. It supports multiple output formats and log levels
|
||||
//! configurable via environment variables.
|
||||
//!
|
||||
//! ## Important: stdio Mode
|
||||
//!
|
||||
//! When using stdio transport (the primary ACP communication method), ALL logs are
|
||||
//! automatically written to stderr to keep stdout clean for JSON-RPC messages.
|
||||
|
||||
use std::fs::OpenOptions;
|
||||
use std::sync::Arc;
|
||||
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
|
||||
|
||||
/// Output format for logs.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum LogFormat {
|
||||
/// Human-readable pretty format with colors (default for development).
|
||||
#[default]
|
||||
Pretty,
|
||||
/// JSON format for structured logging (recommended for production/CI).
|
||||
Json,
|
||||
/// Compact format for minimal output.
|
||||
Compact,
|
||||
}
|
||||
|
||||
/// Initialize the logging system with the specified format.
|
||||
///
|
||||
/// # Log Levels
|
||||
///
|
||||
/// Log levels can be configured via the `RUST_LOG` environment variable:
|
||||
/// - `RUST_LOG=trace` - Most verbose, includes all internal details
|
||||
/// - `RUST_LOG=debug` - Detailed debugging information
|
||||
/// - `RUST_LOG=info` - General informational messages (default)
|
||||
/// - `RUST_LOG=warn` - Warning messages only
|
||||
/// - `RUST_LOG=error` - Error messages only
|
||||
///
|
||||
/// You can also filter by module:
|
||||
/// ```bash
|
||||
/// RUST_LOG=dirigate=debug,axum=info
|
||||
/// ```
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use dirigate::logging::{init_logging, LogFormat};
|
||||
///
|
||||
/// // Initialize with pretty format (default)
|
||||
/// init_logging(LogFormat::Pretty);
|
||||
///
|
||||
/// // Initialize with JSON format for production
|
||||
/// init_logging(LogFormat::Json);
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if logging has already been initialized. This should be called
|
||||
/// exactly once at the start of the application.
|
||||
pub fn init_logging(format: LogFormat) {
|
||||
let env_filter = EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| EnvFilter::new("dirigate=info,axum=info"));
|
||||
|
||||
// Try to create log file in system temp directory
|
||||
// If this fails, we'll just log to stderr only
|
||||
let log_file_path = std::env::temp_dir().join("dirigate.log");
|
||||
let file_writer = OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(&log_file_path)
|
||||
.ok()
|
||||
.map(Arc::new);
|
||||
|
||||
// CRITICAL: All logs go to stderr (and optionally to file if available)
|
||||
// In stdio mode, stdout is reserved for JSON-RPC messages only
|
||||
match (format, file_writer) {
|
||||
(LogFormat::Pretty, Some(file_writer)) => {
|
||||
tracing_subscriber::registry()
|
||||
.with(env_filter)
|
||||
.with(fmt::layer().pretty().with_writer(std::io::stderr))
|
||||
.with(fmt::layer().with_ansi(false).with_writer(file_writer))
|
||||
.init();
|
||||
tracing::info!(
|
||||
"Logging initialized with Pretty format, writing to {:?}",
|
||||
log_file_path
|
||||
);
|
||||
}
|
||||
(LogFormat::Pretty, None) => {
|
||||
tracing_subscriber::registry()
|
||||
.with(env_filter)
|
||||
.with(fmt::layer().pretty().with_writer(std::io::stderr))
|
||||
.init();
|
||||
tracing::info!("Logging initialized with Pretty format (file logging unavailable)");
|
||||
}
|
||||
(LogFormat::Json, Some(file_writer)) => {
|
||||
tracing_subscriber::registry()
|
||||
.with(env_filter)
|
||||
.with(fmt::layer().json().with_writer(std::io::stderr))
|
||||
.with(
|
||||
fmt::layer()
|
||||
.json()
|
||||
.with_ansi(false)
|
||||
.with_writer(file_writer),
|
||||
)
|
||||
.init();
|
||||
tracing::info!(
|
||||
"Logging initialized with Json format, writing to {:?}",
|
||||
log_file_path
|
||||
);
|
||||
}
|
||||
(LogFormat::Json, None) => {
|
||||
tracing_subscriber::registry()
|
||||
.with(env_filter)
|
||||
.with(fmt::layer().json().with_writer(std::io::stderr))
|
||||
.init();
|
||||
tracing::info!("Logging initialized with Json format (file logging unavailable)");
|
||||
}
|
||||
(LogFormat::Compact, Some(file_writer)) => {
|
||||
tracing_subscriber::registry()
|
||||
.with(env_filter)
|
||||
.with(fmt::layer().compact().with_writer(std::io::stderr))
|
||||
.with(
|
||||
fmt::layer()
|
||||
.compact()
|
||||
.with_ansi(false)
|
||||
.with_writer(file_writer),
|
||||
)
|
||||
.init();
|
||||
tracing::info!(
|
||||
"Logging initialized with Compact format, writing to {:?}",
|
||||
log_file_path
|
||||
);
|
||||
}
|
||||
(LogFormat::Compact, None) => {
|
||||
tracing_subscriber::registry()
|
||||
.with(env_filter)
|
||||
.with(fmt::layer().compact().with_writer(std::io::stderr))
|
||||
.init();
|
||||
tracing::info!("Logging initialized with Compact format (file logging unavailable)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize logging with default settings (pretty format, info level).
|
||||
///
|
||||
/// This is a convenience function equivalent to `init_logging(LogFormat::Pretty)`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use dirigate::logging::init_default_logging;
|
||||
///
|
||||
/// init_default_logging();
|
||||
/// ```
|
||||
pub fn init_default_logging() {
|
||||
init_logging(LogFormat::default());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_log_format_default() {
|
||||
assert_eq!(LogFormat::default(), LogFormat::Pretty);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user