1#![warn(missing_docs)]
43#![warn(clippy::pedantic)]
44#![allow(clippy::module_name_repetitions)]
45#![allow(clippy::missing_errors_doc)]
46#![allow(clippy::missing_panics_doc)]
47
48pub mod cache;
49pub mod cli;
50pub mod config;
51pub mod config_reload;
52pub mod error;
53pub mod metrics;
54pub mod server;
55pub mod tools;
56pub mod utils;
57
58pub use crate::config::{AppConfig, ServerConfig};
59pub use crate::error::{Error, Result};
61pub use crate::server::CratesDocsServer;
63
64pub const VERSION: &str = env!("CARGO_PKG_VERSION");
68
69pub const NAME: &str = "crates-docs";
71
72#[deprecated(note = "Please use init_logging_with_config instead")]
77pub fn init_logging(debug: bool) -> Result<()> {
78 use tracing_subscriber::{fmt, prelude::*, EnvFilter};
79
80 let filter = if debug {
81 EnvFilter::new("debug")
82 } else {
83 EnvFilter::new("info")
84 };
85
86 let fmt_layer = fmt::layer()
87 .with_target(true)
88 .with_thread_ids(true)
89 .with_thread_names(true)
90 .compact();
91
92 tracing_subscriber::registry()
93 .with(filter)
94 .with(fmt_layer)
95 .try_init()
96 .map_err(|e| error::Error::initialization("logging", e.to_string()))?;
97
98 Ok(())
99}
100
101pub fn init_logging_with_config(config: &crate::config::LoggingConfig) -> Result<()> {
106 use tracing_subscriber::{fmt, prelude::*, EnvFilter};
107
108 macro_rules! fmt_layer {
110 () => {
111 fmt::layer()
112 .with_target(true)
113 .with_thread_ids(true)
114 .with_thread_names(true)
115 .compact()
116 };
117 ($writer:expr) => {
118 fmt::layer()
119 .with_writer($writer)
120 .with_target(true)
121 .with_thread_ids(true)
122 .with_thread_names(true)
123 .compact()
124 };
125 }
126
127 macro_rules! try_init {
129 ($subscriber:expr) => {
130 $subscriber
131 .try_init()
132 .map_err(|e| error::Error::initialization("logging", e.to_string()))?
133 };
134 }
135
136 let level = match config.level.to_lowercase().as_str() {
138 "trace" => "trace",
139 "debug" => "debug",
140 "warn" => "warn",
141 "error" => "error",
142 _ => "info",
143 };
144
145 let filter = EnvFilter::new(level);
146
147 match (config.enable_console, config.enable_file, &config.file_path) {
149 (true, true, Some(file_path)) => {
151 let (log_dir, log_file_name) = parse_log_path(file_path);
152 ensure_log_directory(&log_dir)?;
153 let file_appender = tracing_appender::rolling::daily(&log_dir, log_file_name);
154
155 try_init!(tracing_subscriber::registry()
156 .with(filter)
157 .with(fmt_layer!())
158 .with(fmt_layer!(file_appender)));
159 }
160
161 (true, _, _) | (false, false, _) => {
163 try_init!(tracing_subscriber::registry()
164 .with(filter)
165 .with(fmt_layer!()));
166 }
167
168 (false, true, Some(file_path)) => {
170 let (log_dir, log_file_name) = parse_log_path(file_path);
171 ensure_log_directory(&log_dir)?;
172 let file_appender = tracing_appender::rolling::daily(&log_dir, log_file_name);
173
174 try_init!(tracing_subscriber::registry()
175 .with(filter)
176 .with(fmt_layer!(file_appender)));
177 }
178
179 _ => {
181 try_init!(tracing_subscriber::registry()
182 .with(filter)
183 .with(fmt_layer!()));
184 }
185 }
186
187 Ok(())
188}
189
190fn parse_log_path(file_path: &str) -> (std::path::PathBuf, std::ffi::OsString) {
192 let path = std::path::Path::new(file_path);
193 let log_dir = path
194 .parent()
195 .filter(|p| !p.as_os_str().is_empty())
196 .map_or_else(|| std::path::PathBuf::from("."), std::path::PathBuf::from);
197 let log_file_name = path.file_name().map_or_else(
198 || std::ffi::OsString::from("crates-docs.log"),
199 std::ffi::OsString::from,
200 );
201 (log_dir, log_file_name)
202}
203
204fn ensure_log_directory(log_dir: &std::path::Path) -> Result<()> {
206 std::fs::create_dir_all(log_dir).map_err(|e| {
207 error::Error::initialization("log_directory", format!("Failed to create: {e}"))
208 })
209}