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