use claude_code_sdk::{query, ClaudeCodeOptions, Message};
use std::env;
use std::path::Path;
use tokio_stream::StreamExt;
use tracing::{debug, error, info, warn, Instrument};
use tracing_subscriber::{
fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter,
};
fn setup_json_logging() {
println!("🔧 Setting up JSON structured logging...");
tracing_subscriber::registry()
.with(EnvFilter::new("claude_code_sdk=debug"))
.with(
fmt::layer()
.json()
.with_target(true)
.with_current_span(false)
.with_span_list(true),
)
.init();
info!("JSON logging initialized");
}
fn setup_file_logging(log_file: &str) -> Result<(), Box<dyn std::error::Error>> {
println!("📁 Setting up file logging to: {}", log_file);
if let Some(parent) = Path::new(log_file).parent() {
std::fs::create_dir_all(parent)?;
}
let file = std::fs::OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(log_file)?;
tracing_subscriber::registry()
.with(EnvFilter::new("claude_code_sdk=trace"))
.with(
fmt::layer()
.with_writer(file)
.with_target(true)
.with_thread_ids(true)
.with_file(true)
.with_line_number(true)
.with_ansi(false), )
.with(
fmt::layer()
.with_writer(std::io::stdout)
.with_target(false)
.compact(),
)
.init();
info!("File logging initialized, writing to: {}", log_file);
Ok(())
}
fn setup_custom_logging() {
println!("🎨 Setting up custom formatted logging...");
tracing_subscriber::registry()
.with(EnvFilter::new("claude_code_sdk=debug"))
.with(
fmt::layer()
.with_target(true)
.with_thread_ids(true)
.with_file(true)
.with_line_number(true)
.with_level(true)
.with_timer(fmt::time::ChronoUtc::rfc_3339())
.pretty(),
)
.init();
info!("Custom logging initialized");
}
fn setup_performance_logging() {
println!("⚡ Setting up performance monitoring logging...");
tracing_subscriber::registry()
.with(EnvFilter::new("claude_code_sdk=trace"))
.with(
fmt::layer()
.with_target(true)
.with_thread_ids(true)
.with_timer(fmt::time::ChronoUtc::rfc_3339())
.json(),
)
.init();
info!("Performance logging initialized");
}
async fn performance_demo() -> Result<(), Box<dyn std::error::Error>> {
let span = tracing::info_span!("performance_demo", demo_type = "simple_query");
async move {
info!("Starting performance demonstration");
let start_time = std::time::Instant::now();
let options = ClaudeCodeOptions {
system_prompt: Some("You are a helpful assistant.".to_string()),
max_turns: Some(1),
..Default::default()
};
let mut stream = query("What is the capital of France?", Some(options)).await?;
let mut message_count = 0u64;
let mut total_text_length = 0usize;
while let Some(message) = stream.next().await {
message_count += 1;
match message {
Message::Assistant(msg) => {
for content in &msg.content {
if let claude_code_sdk::ContentBlock::Text(text_block) = content {
total_text_length += text_block.text.len();
debug!(
text_length = text_block.text.len(),
total_length = total_text_length,
"Assistant text received"
);
}
}
}
Message::Result(result) => {
info!(
duration_ms = result.duration_ms,
api_duration_ms = result.duration_api_ms,
turns = result.num_turns,
cost_usd = result.total_cost_usd,
is_error = result.is_error,
"Query result metrics"
);
}
_ => {}
}
}
let elapsed = start_time.elapsed();
info!(
total_duration_ms = elapsed.as_millis(),
message_count = message_count,
total_text_length = total_text_length,
avg_text_per_message = if message_count > 0 { total_text_length / message_count as usize } else { 0 },
"Performance demo completed"
);
Ok(())
}
.instrument(span)
.await
}
#[allow(dead_code)]
async fn error_demo() -> Result<(), Box<dyn std::error::Error>> {
info!("Starting error demonstration");
let options = ClaudeCodeOptions {
..Default::default()
};
match query("This should fail", Some(options)).await {
Ok(mut stream) => {
warn!("Expected error but got success");
while let Some(message) = stream.next().await {
debug!(?message, "Unexpected message received");
}
}
Err(e) => {
error!(error = %e, "Expected error occurred (this is good for demo)");
println!("✓ Error logging demonstrated successfully");
}
}
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<String> = env::args().collect();
match args.get(1).map(|s| s.as_str()) {
Some("--json") => {
setup_json_logging();
println!("📊 Running with JSON logging");
}
Some("--file") => {
let default_log_file = "claude_sdk.log".to_string();
let log_file = args.get(2).unwrap_or(&default_log_file);
setup_file_logging(log_file)?;
println!("📁 Running with file logging");
}
Some("--custom") => {
setup_custom_logging();
println!("🎨 Running with custom formatting");
}
Some("--perf") => {
setup_performance_logging();
println!("⚡ Running with performance monitoring");
performance_demo().await?;
return Ok(());
}
_ => {
claude_code_sdk::init_tracing();
println!("🔍 Running with default logging");
println!("💡 Try: --json, --file <path>, --custom, or --perf");
}
}
println!("\n=== Claude Code SDK Logging Demo ===");
info!("Starting basic query demonstration");
let mut stream = query("Hello! Can you tell me what 5 + 3 equals?", None).await?;
while let Some(message) = stream.next().await {
match message {
Message::Assistant(msg) => {
info!("Assistant response received with {} content blocks", msg.content.len());
for content in &msg.content {
if let claude_code_sdk::ContentBlock::Text(text_block) = content {
println!("Assistant: {}", text_block.text);
debug!(response_length = text_block.text.len(), "Response text metrics");
}
}
}
Message::System(msg) => {
debug!(subtype = %msg.subtype, "System message: {}", msg.subtype);
}
Message::Result(msg) => {
info!(
duration_ms = msg.duration_ms,
turns = msg.num_turns,
cost = msg.total_cost_usd,
is_error = msg.is_error,
"Query completed"
);
if msg.is_error {
error!("Query failed: {:?}", msg.result);
} else {
info!("Query succeeded");
}
}
_ => {
debug!("Other message type received");
}
}
}
info!("Logging demonstration completed");
println!("\n✅ Logging demo completed successfully!");
println!("Check the logs above to see the different log levels and formats.");
Ok(())
}