use anyhow::Result;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
pub fn get_session_log_file(session_name: &str) -> Result<PathBuf> {
let sessions_dir = crate::directories::get_sessions_dir()?;
let log_file = sessions_dir.join(format!("{}.jsonl", session_name));
Ok(log_file)
}
pub fn log_session_stats(
session_name: &str,
session_info: &crate::session::SessionInfo,
) -> Result<()> {
let log_file = get_session_log_file(session_name)?;
let log_entry = serde_json::json!({
"type": "STATS",
"timestamp": get_timestamp(),
"total_cost": session_info.total_cost,
"input_tokens": session_info.input_tokens,
"output_tokens": session_info.output_tokens,
"cached_tokens": session_info.cached_tokens,
"tool_calls": session_info.tool_calls,
"total_api_time_ms": session_info.total_api_time_ms,
"total_tool_time_ms": session_info.total_tool_time_ms,
"total_layer_time_ms": session_info.total_layer_time_ms,
"model": session_info.model,
"provider": session_info.provider
});
append_to_log(&log_file, &serde_json::to_string(&log_entry)?)?;
Ok(())
}
pub fn log_system_message(session_name: &str, content: &str) -> Result<()> {
let log_file = get_session_log_file(session_name)?;
let log_entry = serde_json::json!({
"type": "SYSTEM",
"timestamp": get_timestamp(),
"content": content
});
append_to_log(&log_file, &serde_json::to_string(&log_entry)?)?;
Ok(())
}
pub fn log_user_input(session_name: &str, content: &str) -> Result<()> {
let log_file = get_session_log_file(session_name)?;
let log_entry = serde_json::json!({
"type": "USER",
"timestamp": get_timestamp(),
"content": content
});
append_to_log(&log_file, &serde_json::to_string(&log_entry)?)?;
Ok(())
}
pub fn log_api_request(session_name: &str, request: &serde_json::Value) -> Result<()> {
let log_file = get_session_log_file(session_name)?;
let log_entry = serde_json::json!({
"type": "API_REQUEST",
"timestamp": get_timestamp(),
"data": request
});
append_to_log(&log_file, &serde_json::to_string(&log_entry)?)?;
Ok(())
}
pub fn log_api_response(
session_name: &str,
response: &serde_json::Value,
usage: Option<&crate::providers::TokenUsage>,
) -> Result<()> {
let log_file = get_session_log_file(session_name)?;
let log_entry = serde_json::json!({
"type": "API_RESPONSE",
"timestamp": get_timestamp(),
"data": response,
"usage": usage
});
append_to_log(&log_file, &serde_json::to_string(&log_entry)?)?;
Ok(())
}
pub fn log_tool_call(
session_name: &str,
tool_name: &str,
tool_id: &str,
parameters: &serde_json::Value,
) -> Result<()> {
let log_file = get_session_log_file(session_name)?;
let log_entry = serde_json::json!({
"type": "TOOL_CALL",
"timestamp": get_timestamp(),
"tool_name": tool_name,
"tool_id": tool_id,
"parameters": parameters
});
append_to_log(&log_file, &serde_json::to_string(&log_entry)?)?;
Ok(())
}
pub fn log_tool_result(
session_name: &str,
tool_id: &str,
result: &serde_json::Value,
execution_time_ms: u64,
) -> Result<()> {
let log_file = get_session_log_file(session_name)?;
let log_entry = serde_json::json!({
"type": "TOOL_RESULT",
"timestamp": get_timestamp(),
"tool_id": tool_id,
"result": result,
"execution_time_ms": execution_time_ms
});
append_to_log(&log_file, &serde_json::to_string(&log_entry)?)?;
Ok(())
}
pub fn log_assistant_response(session_name: &str, content: &str) -> Result<()> {
let log_file = get_session_log_file(session_name)?;
let log_entry = serde_json::json!({
"type": "ASSISTANT",
"timestamp": get_timestamp(),
"content": content
});
append_to_log(&log_file, &serde_json::to_string(&log_entry)?)?;
Ok(())
}
pub fn log_restoration_point(
session_name: &str,
user_message: &str,
assistant_response: &str,
) -> Result<()> {
let log_file = get_session_log_file(session_name)?;
let log_entry = serde_json::json!({
"type": "RESTORATION_POINT",
"timestamp": get_timestamp(),
"user_message": user_message,
"assistant_response": assistant_response
});
append_to_log(&log_file, &serde_json::to_string(&log_entry)?)?;
Ok(())
}
pub fn log_session_command(session_name: &str, command_line: &str) -> Result<()> {
let log_file = get_session_log_file(session_name)?;
let log_entry = serde_json::json!({
"type": "COMMAND",
"timestamp": get_timestamp(),
"command": command_line
});
append_to_log(&log_file, &serde_json::to_string(&log_entry)?)?;
Ok(())
}
pub fn log_cache_operation(session_name: &str, operation: &str, details: &str) -> Result<()> {
let log_file = get_session_log_file(session_name)?;
let log_entry = serde_json::json!({
"type": "CACHE",
"timestamp": get_timestamp(),
"operation": operation,
"details": details
});
append_to_log(&log_file, &serde_json::to_string(&log_entry)?)?;
Ok(())
}
pub fn log_error(session_name: &str, error: &str) -> Result<()> {
let log_file = get_session_log_file(session_name)?;
let log_entry = serde_json::json!({
"type": "ERROR",
"timestamp": get_timestamp(),
"error": error
});
append_to_log(&log_file, &serde_json::to_string(&log_entry)?)?;
Ok(())
}
fn get_timestamp() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
}
fn append_to_log(log_file: &PathBuf, content: &str) -> Result<()> {
let mut file = OpenOptions::new()
.create(true)
.append(true)
.open(log_file)?;
let single_line_content = content.replace(['\n', '\r'], " ");
writeln!(file, "{}", single_line_content)?;
Ok(())
}
pub fn log_user_request(content: &str) -> Result<()> {
log_user_input("default", content)
}
pub fn log_raw_exchange(exchange: &crate::session::ProviderExchange) -> Result<()> {
let session_name = "default";
log_api_request(session_name, &exchange.request)?;
log_api_response(session_name, &exchange.response, exchange.usage.as_ref())?;
Ok(())
}
pub fn get_session_log_path(session_name: &str) -> Result<PathBuf> {
get_session_log_file(session_name)
}
pub fn get_log_file() -> Result<PathBuf> {
let logs_dir = crate::directories::get_logs_dir()?;
let now = chrono::Local::now();
let log_file = logs_dir.join(format!("session_{}.jsonl", now.format("%Y-%m-%d")));
Ok(log_file)
}