kotoba_cli/
logging.rs

1//! ログ管理
2//!
3//! Merkle DAG: cli_interface -> LogFormatter component
4
5use tracing::{Level, Subscriber};
6use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
7
8/// ログシステムを初期化
9pub fn init_logging(level: &str) -> Result<(), Box<dyn std::error::Error>> {
10    let level = parse_log_level(level)?;
11
12    let filter = EnvFilter::try_from_default_env()
13        .unwrap_or_else(|_| {
14            EnvFilter::from_default_env()
15                .add_directive(format!("kotoba={}", level).parse().unwrap())
16        });
17
18    let subscriber = tracing_subscriber::registry()
19        .with(filter)
20        .with(
21            tracing_subscriber::fmt::layer()
22                .with_target(false)
23                .with_thread_ids(false)
24                .with_thread_names(false)
25                .compact()
26        );
27
28    subscriber.init();
29
30    Ok(())
31}
32
33/// ログレベルをパース
34fn parse_log_level(level: &str) -> Result<Level, Box<dyn std::error::Error>> {
35    match level.to_lowercase().as_str() {
36        "error" => Ok(Level::ERROR),
37        "warn" => Ok(Level::WARN),
38        "info" => Ok(Level::INFO),
39        "debug" => Ok(Level::DEBUG),
40        "trace" => Ok(Level::TRACE),
41        _ => Err(format!("Invalid log level: {}", level).into()),
42    }
43}
44
45/// ログレベルを文字列に変換
46pub fn level_to_string(level: Level) -> &'static str {
47    match level {
48        Level::ERROR => "error",
49        Level::WARN => "warn",
50        Level::INFO => "info",
51        Level::DEBUG => "debug",
52        Level::TRACE => "trace",
53    }
54}
55
56/// ログメッセージのフォーマッタ
57/// Merkle DAG: cli_interface -> LogFormatter component
58pub struct LogFormatter;
59
60impl LogFormatter {
61    /// 成功メッセージをフォーマット
62    pub fn success(message: &str) -> String {
63        format!("✅ {}", message)
64    }
65
66    /// エラーメッセージをフォーマット
67    pub fn error(message: &str) -> String {
68        format!("❌ {}", message)
69    }
70
71    /// 警告メッセージをフォーマット
72    pub fn warning(message: &str) -> String {
73        format!("⚠️  {}", message)
74    }
75
76    /// 情報メッセージをフォーマット
77    pub fn info(message: &str) -> String {
78        format!("ℹ️  {}", message)
79    }
80
81    /// デバッグメッセージをフォーマット
82    pub fn debug(message: &str) -> String {
83        format!("🔍 {}", message)
84    }
85
86    /// 処理中メッセージをフォーマット
87    pub fn processing(message: &str) -> String {
88        format!("⏳ {}", message)
89    }
90
91    /// 完了メッセージをフォーマット
92    pub fn completed(message: &str) -> String {
93        format!("✅ {}", message)
94    }
95}
96
97
98/// ログレベルの設定を変更
99pub fn set_log_level(level: &str) -> Result<(), Box<dyn std::error::Error>> {
100    let level = parse_log_level(level)?;
101
102    // 既存のフィルタを更新
103    let filter = EnvFilter::from_default_env()
104        .add_directive(format!("kotoba={}", level).parse().unwrap());
105
106    tracing::subscriber::set_global_default(
107        tracing_subscriber::registry()
108            .with(filter)
109            .with(tracing_subscriber::fmt::layer())
110    )?;
111
112    Ok(())
113}
114
115/// ログをファイルに出力する設定
116pub fn setup_file_logging(log_file: &std::path::Path) -> Result<(), Box<dyn std::error::Error>> {
117    use std::fs::OpenOptions;
118    use tracing_subscriber::fmt::format::Writer;
119
120    let file = OpenOptions::new()
121        .create(true)
122        .append(true)
123        .open(log_file)?;
124
125    let file_layer = tracing_subscriber::fmt::layer()
126        .with_writer(move || file.try_clone().unwrap())
127        .with_target(false)
128        .with_thread_ids(true)
129        .with_thread_names(true);
130
131    tracing::subscriber::set_global_default(
132        tracing_subscriber::registry()
133            .with(EnvFilter::from_default_env())
134            .with(file_layer)
135    )?;
136
137    Ok(())
138}