1#![doc = "ロギングの実装"]
2use crate::env::ENV_KEY_LOG;
3use crate::util::app_global::global_dir;
4use std::path::{Path, PathBuf};
5use tracing::Level;
6use tracing_appender::rolling;
7use tracing_subscriber::filter::Directive;
8use tracing_subscriber::fmt::writer::MakeWriterExt;
9use tracing_subscriber::EnvFilter;
10
11pub use rolling::InitError;
12
13const DEFAULT_LOG_LEVEL: &str = "info";
14
15#[cfg(debug_assertions)]
16const DEFAULT_LOG_FILTER: &[&str] = &[
17 "sqlx::query=trace",
18 "iced_wgpu::window::compositor=warn",
19 "iced_winit=warn",
20];
21#[cfg(not(debug_assertions))]
22const DEFAULT_LOG_FILTER: &[&str] = &["iced_wgpu::window::compositor=warn", "iced_winit=warn"];
23
24const ERROR_LOG_FILE_LEVEL: Level = Level::ERROR;
25const INFO_LOG_FILE_LEVEL: Level = Level::INFO;
26const STDOUT_LOG_LEVEL: Level = Level::TRACE;
27const STDERR_LOG_LEVEL: Level = Level::WARN;
28const DEBUG_LOG_FILE_LEVEL: Level = Level::TRACE;
29
30pub const ERROR_LOG_FILENAME: &str = "error.log";
31pub const INFO_LOG_FILENAME: &str = "info.log";
32#[cfg(debug_assertions)]
33pub const DEBUG_LOG_FILENAME: &str = "debug.log";
34
35#[cfg(debug_assertions)]
36const LOG_DIR_NAME: &str = "";
37#[cfg(not(debug_assertions))]
38const LOG_DIR_NAME: &str = "log";
39
40pub fn log_dir() -> PathBuf {
42 let mut dir = global_dir();
43 dir.push(LOG_DIR_NAME);
44 dir
45}
46
47const MAX_LOG_FILES: usize = 5;
48
49#[cfg(debug_assertions)]
50const ROTATION_CYCLE: rolling::Rotation = rolling::Rotation::NEVER;
51#[cfg(not(debug_assertions))]
52const ROTATION_CYCLE: rolling::Rotation = rolling::Rotation::WEEKLY;
53
54fn appender(
55 rotation: rolling::Rotation,
56 directory: impl AsRef<Path>,
57 filename_prefix: impl Into<String>,
58) -> Result<rolling::RollingFileAppender, InitError> {
59 Ok(rolling::Builder::new()
60 .rotation(rotation)
61 .filename_prefix(filename_prefix)
62 .max_log_files(MAX_LOG_FILES)
63 .build(directory)?)
64}
65
66fn default_env_filter() -> EnvFilter {
67 EnvFilter::try_from_env(ENV_KEY_LOG).unwrap_or(EnvFilter::new(DEFAULT_LOG_LEVEL))
68}
69
70fn add_directive(env_filter: EnvFilter, directive: Directive) -> EnvFilter {
71 env_filter.add_directive(directive)
72}
73
74pub fn initialize_logger() -> Result<(), InitError> {
76 let default_log_filter: EnvFilter = DEFAULT_LOG_FILTER
77 .iter()
78 .map(|v| v.parse().unwrap())
79 .fold(default_env_filter(), add_directive);
80
81 let stdout_logger = std::io::stdout.with_max_level(STDOUT_LOG_LEVEL);
82 let stderr_logger = std::io::stderr.with_max_level(STDERR_LOG_LEVEL);
83 let error_appender = appender(ROTATION_CYCLE, log_dir(), ERROR_LOG_FILENAME)?
84 .with_max_level(ERROR_LOG_FILE_LEVEL);
85 let info_appender =
86 appender(ROTATION_CYCLE, log_dir(), INFO_LOG_FILENAME)?.with_max_level(INFO_LOG_FILE_LEVEL);
87
88 let info_debug_appender = info_appender;
89 #[cfg(debug_assertions)]
90 let info_debug_appender = info_debug_appender.or_else(
91 rolling::never(log_dir(), DEBUG_LOG_FILENAME).with_max_level(DEBUG_LOG_FILE_LEVEL),
92 );
93
94 let writer = stderr_logger
95 .or_else(stdout_logger)
96 .and(error_appender.or_else(info_debug_appender));
97
98 Ok(tracing_subscriber::fmt::fmt()
99 .with_env_filter(default_log_filter)
100 .with_ansi(false)
101 .with_file(cfg!(debug_assertions))
102 .with_line_number(cfg!(debug_assertions))
103 .with_writer(writer)
104 .init())
105}