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_writer(std::io::stderr)
87 .with_target(true)
88 .with_thread_ids(true)
89 .with_thread_names(true)
90 .compact()
91 };
92 ($writer:expr) => {
93 fmt::layer()
94 .with_writer($writer)
95 .with_target(true)
96 .with_thread_ids(true)
97 .with_thread_names(true)
98 .compact()
99 };
100 }
101
102 macro_rules! try_init {
104 ($subscriber:expr) => {
105 $subscriber
106 .try_init()
107 .map_err(|e| error::Error::initialization("logging", e.to_string()))?
108 };
109 }
110
111 let level = config.level.to_lowercase();
113 let level = match level.as_str() {
114 "trace" | "debug" | "warn" | "error" => level.clone(),
115 _ => "info".to_string(),
116 };
117
118 let filter = EnvFilter::new(level);
119
120 match (config.enable_console, config.enable_file, &config.file_path) {
122 (true, true, Some(file_path)) => {
123 let (log_dir, log_file_name) = parse_log_path(file_path);
125 ensure_log_directory(&log_dir)?;
126 let file_appender = tracing_appender::rolling::daily(&log_dir, log_file_name);
127
128 try_init!(tracing_subscriber::registry()
129 .with(filter)
130 .with(fmt_layer!())
131 .with(fmt_layer!(file_appender)));
132 }
133
134 (false, true, Some(file_path)) => {
135 let (log_dir, log_file_name) = parse_log_path(file_path);
137 ensure_log_directory(&log_dir)?;
138 let file_appender = tracing_appender::rolling::daily(&log_dir, log_file_name);
139
140 try_init!(tracing_subscriber::registry()
141 .with(filter)
142 .with(fmt_layer!(file_appender)));
143 }
144
145 _ => {
147 try_init!(tracing_subscriber::registry()
148 .with(filter)
149 .with(fmt_layer!()));
150 }
151 }
152
153 Ok(())
154}
155
156fn parse_log_path(file_path: &str) -> (std::path::PathBuf, std::ffi::OsString) {
158 let path = std::path::Path::new(file_path);
159 let log_dir = path
160 .parent()
161 .filter(|p| !p.as_os_str().is_empty())
162 .map_or_else(|| std::path::PathBuf::from("."), std::path::PathBuf::from);
163 let log_file_name = path.file_name().map_or_else(
164 || std::ffi::OsString::from("crates-docs.log"),
165 std::ffi::OsString::from,
166 );
167 (log_dir, log_file_name)
168}
169
170fn ensure_log_directory(log_dir: &std::path::Path) -> Result<()> {
172 std::fs::create_dir_all(log_dir).map_err(|e| {
173 error::Error::initialization("log_directory", format!("Failed to create: {e}"))
174 })
175}