Skip to main content

dm_database_sqllog2db/
config.rs

1use crate::constants::LOG_LEVELS;
2use crate::error::{ConfigError, Error, Result};
3pub use crate::features::FeaturesConfig;
4#[cfg(feature = "filters")]
5pub use crate::features::FiltersFeature;
6use serde::Deserialize;
7use std::path::{Path, PathBuf};
8
9/// 默认表名
10#[cfg(feature = "sqlite")]
11fn default_table_name() -> String {
12    "sqllog_records".to_string()
13}
14
15/// 默认 true 值
16fn default_true() -> bool {
17    true
18}
19
20#[cfg_attr(feature = "csv", derive(Default))]
21#[derive(Debug, Deserialize, Clone)]
22pub struct Config {
23    /// 新增:SQL 日志输入相关配置
24    #[serde(default)]
25    pub sqllog: SqllogConfig,
26    #[serde(default)]
27    pub error: ErrorConfig,
28    #[serde(default)]
29    pub logging: LoggingConfig,
30    #[serde(default)]
31    #[warn(dead_code)]
32    pub features: FeaturesConfig,
33    #[serde(default)]
34    pub exporter: ExporterConfig,
35}
36
37impl Config {
38    /// 从文件加载配置
39    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
40        let path = path.as_ref();
41        let content = std::fs::read_to_string(path)
42            .map_err(|_| Error::Config(ConfigError::NotFound(path.to_path_buf())))?;
43        Self::from_str(&content, path.to_path_buf())
44    }
45
46    /// 从字符串解析配置
47    pub fn from_str(content: &str, path: PathBuf) -> Result<Self> {
48        let config: Config = toml::from_str(content).map_err(|e| {
49            Error::Config(ConfigError::ParseFailed {
50                path,
51                reason: e.to_string(),
52            })
53        })?;
54
55        // 验证配置
56        config.validate()?;
57
58        Ok(config)
59    }
60
61    /// 验证配置的有效性
62    pub fn validate(&self) -> Result<()> {
63        // 验证日志级别
64        self.logging.validate()?;
65
66        // 验证导出器配置
67        self.exporter.validate()?;
68
69        // 验证 sqllog 配置
70        self.sqllog.validate()?;
71
72        // 验证功能配置
73        FeaturesConfig::validate();
74
75        Ok(())
76    }
77}
78
79/// SQL 日志输入配置
80#[derive(Debug, Deserialize, Clone)]
81pub struct SqllogConfig {
82    /// SQL 日志输入目录(可包含多个日志文件)
83    pub directory: String,
84}
85
86impl Default for SqllogConfig {
87    fn default() -> Self {
88        Self {
89            directory: "sqllogs".to_string(),
90        }
91    }
92}
93
94impl SqllogConfig {
95    /// 获取 SQL 日志输入目录
96    #[must_use]
97    pub fn directory(&self) -> &str {
98        &self.directory
99    }
100
101    /// 验证配置
102    pub fn validate(&self) -> Result<()> {
103        if self.directory.trim().is_empty() {
104            return Err(Error::Config(ConfigError::InvalidValue {
105                field: "sqllog.directory".to_string(),
106                value: self.directory.clone(),
107                reason: "Input directory cannot be empty".to_string(),
108            }));
109        }
110        Ok(())
111    }
112}
113
114#[derive(Debug, Deserialize, Clone)]
115pub struct ErrorConfig {
116    /// 错误日志输出文件路径
117    #[serde(default = "default_error_file")]
118    pub file: String,
119}
120
121fn default_error_file() -> String {
122    "export/errors.log".to_string()
123}
124
125impl ErrorConfig {
126    /// 获取错误日志输出文件路径
127    #[must_use]
128    pub fn file(&self) -> &str {
129        &self.file
130    }
131}
132
133impl Default for ErrorConfig {
134    fn default() -> Self {
135        Self {
136            file: "export/errors.log".to_string(),
137        }
138    }
139}
140
141#[derive(Debug, Deserialize, Clone)]
142pub struct LoggingConfig {
143    /// 应用日志输出文件路径
144    #[serde(default = "default_logging_file")]
145    pub file: String,
146    #[serde(default = "default_logging_level")]
147    pub level: String,
148    #[serde(default = "default_retention_days")]
149    pub retention_days: usize,
150}
151
152fn default_logging_file() -> String {
153    "logs/sqllog2db.log".to_string()
154}
155
156fn default_logging_level() -> String {
157    "info".to_string()
158}
159
160fn default_retention_days() -> usize {
161    7
162}
163
164impl LoggingConfig {
165    /// 获取日志输出文件路径
166    #[must_use]
167    pub fn file(&self) -> &str {
168        &self.file
169    }
170
171    /// 获取日志级别
172    #[must_use]
173    pub fn level(&self) -> &str {
174        &self.level
175    }
176
177    /// 获取日志保留天数
178    #[must_use]
179    pub fn retention_days(&self) -> usize {
180        self.retention_days
181    }
182
183    /// 验证日志级别是否有效
184    pub fn validate(&self) -> Result<()> {
185        if !LOG_LEVELS
186            .iter()
187            .any(|&l| l.eq_ignore_ascii_case(self.level.as_str()))
188        {
189            return Err(Error::Config(ConfigError::InvalidLogLevel {
190                level: self.level.clone(),
191                valid_levels: LOG_LEVELS.iter().map(|s| (*s).to_string()).collect(),
192            }));
193        }
194
195        // 验证保留天数(1-365天)
196        if self.retention_days == 0 || self.retention_days > 365 {
197            return Err(Error::Config(ConfigError::InvalidValue {
198                field: "logging.retention_days".to_string(),
199                value: self.retention_days.to_string(),
200                reason: "Retention days must be between 1 and 365".to_string(),
201            }));
202        }
203
204        Ok(())
205    }
206}
207
208impl Default for LoggingConfig {
209    fn default() -> Self {
210        Self {
211            file: "logs/sqllog2db.log".to_string(),
212            level: "info".to_string(),
213            retention_days: 7,
214        }
215    }
216}
217
218#[derive(Debug, Deserialize, Clone)]
219pub struct ExporterConfig {
220    #[cfg(feature = "csv")]
221    pub csv: Option<CsvExporter>,
222    #[cfg(feature = "jsonl")]
223    pub jsonl: Option<JsonlExporter>,
224    #[cfg(feature = "sqlite")]
225    pub sqlite: Option<SqliteExporter>,
226}
227
228impl ExporterConfig {
229    /// 获取 CSV 导出器配置
230    #[cfg(feature = "csv")]
231    #[must_use]
232    pub fn csv(&self) -> Option<&CsvExporter> {
233        self.csv.as_ref()
234    }
235
236    #[cfg(feature = "jsonl")]
237    /// 获取 JSONL 导出器配置
238    #[must_use]
239    pub fn jsonl(&self) -> Option<&JsonlExporter> {
240        self.jsonl.as_ref()
241    }
242
243    #[cfg(feature = "sqlite")]
244    /// 获取 `SQLite` 导出器配置
245    #[must_use]
246    pub fn sqlite(&self) -> Option<&SqliteExporter> {
247        self.sqlite.as_ref()
248    }
249
250    /// 检查是否有任何导出器配置
251    #[must_use]
252    pub fn has_exporters(&self) -> bool {
253        let mut found = false;
254        #[cfg(feature = "csv")]
255        {
256            found = found || self.csv.is_some();
257        }
258        #[cfg(feature = "jsonl")]
259        {
260            found = found || self.jsonl.is_some();
261        }
262        #[cfg(feature = "sqlite")]
263        {
264            found = found || self.sqlite.is_some();
265        }
266        found
267    }
268
269    /// 统计配置的导出器总数
270    #[must_use]
271    pub fn total_exporters(&self) -> usize {
272        let mut count = 0;
273        #[cfg(feature = "csv")]
274        {
275            if self.csv.is_some() {
276                count += 1;
277            }
278        }
279        #[cfg(feature = "jsonl")]
280        {
281            if self.jsonl.is_some() {
282                count += 1;
283            }
284        }
285        #[cfg(feature = "sqlite")]
286        {
287            if self.sqlite.is_some() {
288                count += 1;
289            }
290        }
291        count
292    }
293
294    /// 验证导出器配置(只支持单个导出器)
295    pub fn validate(&self) -> Result<()> {
296        if !self.has_exporters() {
297            return Err(Error::Config(ConfigError::NoExporters));
298        }
299
300        let total = self.total_exporters();
301        if total > 1 {
302            eprintln!("Warning: {total} exporters configured, but only one is supported.");
303            eprintln!("Will use the first exporter by priority: CSV > JSONL > SQLite");
304        }
305
306        Ok(())
307    }
308}
309
310impl Default for ExporterConfig {
311    fn default() -> Self {
312        Self {
313            #[cfg(feature = "csv")]
314            csv: Some(CsvExporter::default()),
315            #[cfg(feature = "jsonl")]
316            jsonl: None,
317            #[cfg(feature = "sqlite")]
318            sqlite: None,
319        }
320    }
321}
322
323#[cfg(feature = "jsonl")]
324#[derive(Debug, Deserialize, Clone)]
325pub struct JsonlExporter {
326    /// JSONL 输出文件路径
327    pub file: String,
328    /// 是否覆盖已存在的文件
329    #[serde(default = "default_true")]
330    pub overwrite: bool,
331    /// 是否追加模式
332    #[serde(default)]
333    pub append: bool,
334}
335
336#[cfg(feature = "jsonl")]
337impl Default for JsonlExporter {
338    fn default() -> Self {
339        Self {
340            file: "export/sqllog2db.jsonl".to_string(),
341            overwrite: true,
342            append: false,
343        }
344    }
345}
346
347#[cfg(feature = "sqlite")]
348#[derive(Debug, Deserialize, Clone)]
349pub struct SqliteExporter {
350    /// `SQLite` 数据库文件路径
351    pub database_url: String,
352    /// 表名
353    #[serde(default = "default_table_name")]
354    pub table_name: String,
355    /// 是否覆盖已存在的表
356    #[serde(default = "default_true")]
357    pub overwrite: bool,
358    /// 是否追加模式
359    #[serde(default)]
360    pub append: bool,
361}
362
363#[cfg(feature = "sqlite")]
364impl Default for SqliteExporter {
365    fn default() -> Self {
366        Self {
367            database_url: "export/sqllog2db.db".to_string(),
368            table_name: "sqllog_records".to_string(),
369            overwrite: true,
370            append: false,
371        }
372    }
373}
374
375#[cfg(feature = "csv")]
376#[derive(Debug, Deserialize, Clone)]
377pub struct CsvExporter {
378    /// CSV 输出文件路径
379    pub file: String,
380    /// 是否覆盖已存在的文件
381    #[serde(default = "default_true")]
382    pub overwrite: bool,
383    /// 是否追加模式(暂未实现)
384    #[serde(default)]
385    pub append: bool,
386}
387
388#[cfg(feature = "csv")]
389impl Default for CsvExporter {
390    fn default() -> Self {
391        Self {
392            file: "outputs/sqllog.csv".to_string(),
393            overwrite: true,
394            append: false,
395        }
396    }
397}