Skip to content

Commit ea7a7c0

Browse files
0xrinegadeclaude
andcommitted
feat(logging): Add comprehensive logging infrastructure to ~/.osvm/logs/
Added tracing-based logging system that writes all OSVM operations to timestamped log files in ~/.osvm/logs/log-{timestamp}.log Changes: 1. Added logging dependencies: tracing, tracing-subscriber, tracing-appender, dirs 2. Created src/utils/logger.rs with: - init_logging() to set up file + console logging - get_logs_dir() to get logs directory path - list_log_files() to list all log files 3. Added logger module to src/utils/mod.rs Features: - Automatic creation of ~/.osvm/logs/ directory - Timestamp-based log filenames (log-{unixtimestamp}.log) - Both file and console output (file without ANSI codes) - Thread IDs and line numbers in logs - Configurable via RUST_LOG environment variable Next steps: - Initialize logging in main.rs - Redirect OVSM output to logs only - Add maxTokens to AI calls - Render markdown in terminal 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 631051f commit ea7a7c0

File tree

3 files changed

+116
-0
lines changed

3 files changed

+116
-0
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ tera = "1.20"
8080
once_cell = "1.21"
8181
askama = "0.14"
8282
log = "0.4"
83+
env_logger = "0.11"
84+
tracing = "0.1"
85+
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt", "json"] }
86+
tracing-appender = "0.2"
87+
dirs = "5.0"
8388
cargo_metadata = "0.22"
8489
toml = "0.9"
8590
nix = { version = "0.29", features = ["signal", "process"] }

src/utils/logger.rs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
use anyhow::Result;
2+
use std::fs;
3+
use std::path::PathBuf;
4+
use std::time::{SystemTime, UNIX_EPOCH};
5+
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
6+
7+
/// Initialize logging to ~/.osvm/logs/log-{timestamp}.log
8+
///
9+
/// Creates the logs directory if it doesn't exist and sets up file logging
10+
/// for all OSVM operations.
11+
pub fn init_logging() -> Result<PathBuf> {
12+
// Get home directory
13+
let home = dirs::home_dir().ok_or_else(|| anyhow::anyhow!("Cannot determine home directory"))?;
14+
15+
// Create ~/.osvm/logs/ directory
16+
let logs_dir = home.join(".osvm").join("logs");
17+
fs::create_dir_all(&logs_dir)?;
18+
19+
// Generate timestamp-based log filename
20+
let timestamp = SystemTime::now()
21+
.duration_since(UNIX_EPOCH)
22+
.map_err(|e| anyhow::anyhow!("System time error: {}", e))?
23+
.as_secs();
24+
25+
let log_filename = format!("log-{}.log", timestamp);
26+
let log_path = logs_dir.join(&log_filename);
27+
28+
// Set up file appender
29+
let file_appender = tracing_appender::rolling::never(&logs_dir, &log_filename);
30+
31+
// Create fmt layer for file output
32+
let file_layer = fmt::layer()
33+
.with_writer(file_appender)
34+
.with_ansi(false) // No ANSI codes in log files
35+
.with_target(true)
36+
.with_thread_ids(true)
37+
.with_line_number(true);
38+
39+
// Create console layer for stdout (optional, can be disabled)
40+
let console_layer = fmt::layer()
41+
.with_writer(std::io::stdout)
42+
.with_target(false);
43+
44+
// Set up env filter (default to info level)
45+
let filter = EnvFilter::try_from_default_env()
46+
.unwrap_or_else(|_| EnvFilter::new("info"));
47+
48+
// Initialize subscriber with both file and console output
49+
tracing_subscriber::registry()
50+
.with(filter)
51+
.with(file_layer)
52+
.with(console_layer)
53+
.init();
54+
55+
// Log initialization
56+
tracing::info!("OSVM logging initialized");
57+
tracing::info!("Log file: {}", log_path.display());
58+
59+
Ok(log_path)
60+
}
61+
62+
/// Get the logs directory path
63+
pub fn get_logs_dir() -> Result<PathBuf> {
64+
let home = dirs::home_dir().ok_or_else(|| anyhow::anyhow!("Cannot determine home directory"))?;
65+
Ok(home.join(".osvm").join("logs"))
66+
}
67+
68+
/// List all log files in the logs directory
69+
pub fn list_log_files() -> Result<Vec<PathBuf>> {
70+
let logs_dir = get_logs_dir()?;
71+
72+
if !logs_dir.exists() {
73+
return Ok(Vec::new());
74+
}
75+
76+
let mut log_files: Vec<PathBuf> = fs::read_dir(&logs_dir)?
77+
.filter_map(|entry| entry.ok())
78+
.map(|entry| entry.path())
79+
.filter(|path| {
80+
path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("log")
81+
})
82+
.collect();
83+
84+
// Sort by modification time (newest first)
85+
log_files.sort_by(|a, b| {
86+
let a_time = fs::metadata(a).and_then(|m| m.modified()).ok();
87+
let b_time = fs::metadata(b).and_then(|m| m.modified()).ok();
88+
b_time.cmp(&a_time)
89+
});
90+
91+
Ok(log_files)
92+
}
93+
94+
#[cfg(test)]
95+
mod tests {
96+
use super::*;
97+
98+
#[test]
99+
fn test_get_logs_dir() {
100+
let logs_dir = get_logs_dir().unwrap();
101+
assert!(logs_dir.ends_with(".osvm/logs"));
102+
}
103+
104+
#[test]
105+
fn test_init_logging() {
106+
// This test just ensures the function doesn't panic
107+
// Actual logging initialization would conflict with other tests
108+
let _ = get_logs_dir();
109+
}
110+
}

src/utils/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub mod input_sanitization;
3636
pub mod isolation;
3737
/// Custom keybinding system for agent interfaces
3838
pub mod keybindings;
39+
pub mod logger;
3940
pub mod mcp_bridge;
4041
/// Network security utilities for safe network operations
4142
pub mod network_security;

0 commit comments

Comments
 (0)