1use tracing::{Level, Subscriber};
6use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
7
8pub 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
33fn 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
45pub 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
56pub struct LogFormatter;
59
60impl LogFormatter {
61 pub fn success(message: &str) -> String {
63 format!("✅ {}", message)
64 }
65
66 pub fn error(message: &str) -> String {
68 format!("❌ {}", message)
69 }
70
71 pub fn warning(message: &str) -> String {
73 format!("⚠️ {}", message)
74 }
75
76 pub fn info(message: &str) -> String {
78 format!("ℹ️ {}", message)
79 }
80
81 pub fn debug(message: &str) -> String {
83 format!("🔍 {}", message)
84 }
85
86 pub fn processing(message: &str) -> String {
88 format!("⏳ {}", message)
89 }
90
91 pub fn completed(message: &str) -> String {
93 format!("✅ {}", message)
94 }
95}
96
97
98pub fn set_log_level(level: &str) -> Result<(), Box<dyn std::error::Error>> {
100 let level = parse_log_level(level)?;
101
102 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
115pub 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}