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::{
59 AppConfig, EnvAppConfig, EnvLoggingConfig, EnvServerConfig, LoggingConfig, PerformanceConfig,
60 ServerConfig,
61};
62pub use crate::error::{Error, Result};
64pub use crate::server::CratesDocsServer;
66
67pub const VERSION: &str = env!("CARGO_PKG_VERSION");
71
72pub const NAME: &str = "crates-docs";
74
75pub fn init_logging_with_config(config: &crate::config::LoggingConfig) -> Result<()> {
80 use tracing_subscriber::{fmt, prelude::*, EnvFilter};
81
82 macro_rules! fmt_layer {
84 () => {
85 fmt::layer()
86 .with_target(true)
87 .with_thread_ids(true)
88 .with_thread_names(true)
89 .compact()
90 };
91 ($writer:expr) => {
92 fmt::layer()
93 .with_writer($writer)
94 .with_target(true)
95 .with_thread_ids(true)
96 .with_thread_names(true)
97 .compact()
98 };
99 }
100
101 macro_rules! try_init {
103 ($subscriber:expr) => {
104 $subscriber
105 .try_init()
106 .map_err(|e| error::Error::initialization("logging", e.to_string()))?
107 };
108 }
109
110 let level = config.level.to_lowercase();
112 let level = match level.as_str() {
113 "trace" | "debug" | "warn" | "error" => level.clone(),
114 _ => "info".to_string(),
115 };
116
117 let filter = EnvFilter::new(level);
118
119 match (config.enable_console, config.enable_file, &config.file_path) {
121 (true, true, Some(file_path)) => {
122 let (log_dir, log_file_name) = parse_log_path(file_path);
124 ensure_log_directory(&log_dir)?;
125 let file_appender = tracing_appender::rolling::daily(&log_dir, log_file_name);
126
127 try_init!(tracing_subscriber::registry()
128 .with(filter)
129 .with(fmt_layer!())
130 .with(fmt_layer!(file_appender)));
131 }
132
133 (false, true, Some(file_path)) => {
134 let (log_dir, log_file_name) = parse_log_path(file_path);
136 ensure_log_directory(&log_dir)?;
137 let file_appender = tracing_appender::rolling::daily(&log_dir, log_file_name);
138
139 try_init!(tracing_subscriber::registry()
140 .with(filter)
141 .with(fmt_layer!(file_appender)));
142 }
143
144 _ => {
146 try_init!(tracing_subscriber::registry()
147 .with(filter)
148 .with(fmt_layer!()));
149 }
150 }
151
152 Ok(())
153}
154
155fn parse_log_path(file_path: &str) -> (std::path::PathBuf, std::ffi::OsString) {
157 let path = std::path::Path::new(file_path);
158 let log_dir = path
159 .parent()
160 .filter(|p| !p.as_os_str().is_empty())
161 .map_or_else(|| std::path::PathBuf::from("."), std::path::PathBuf::from);
162 let log_file_name = path.file_name().map_or_else(
163 || std::ffi::OsString::from("crates-docs.log"),
164 std::ffi::OsString::from,
165 );
166 (log_dir, log_file_name)
167}
168
169fn ensure_log_directory(log_dir: &std::path::Path) -> Result<()> {
171 std::fs::create_dir_all(log_dir).map_err(|e| {
172 error::Error::initialization("log_directory", format!("Failed to create: {e}"))
173 })
174}