1use rustfs_config::{
16 APP_NAME, DEFAULT_LOG_DIR, DEFAULT_LOG_FILENAME, DEFAULT_LOG_KEEP_FILES, DEFAULT_LOG_LEVEL, DEFAULT_LOG_ROTATION_SIZE_MB,
17 DEFAULT_LOG_ROTATION_TIME, ENVIRONMENT, METER_INTERVAL, SAMPLE_RATIO, SERVICE_VERSION, USE_STDOUT,
18};
19use serde::{Deserialize, Serialize};
20use std::env;
21
22#[derive(Debug, Deserialize, Serialize, Clone)]
31pub struct OtelConfig {
32 pub endpoint: String, pub use_stdout: Option<bool>, pub sample_ratio: Option<f64>, pub meter_interval: Option<u64>, pub service_name: Option<String>, pub service_version: Option<String>, pub environment: Option<String>, pub logger_level: Option<String>, pub local_logging_enabled: Option<bool>, pub log_directory: Option<String>, pub log_filename: Option<String>, pub log_rotation_size_mb: Option<u64>, pub log_rotation_time: Option<String>, pub log_keep_files: Option<u16>, }
48
49impl OtelConfig {
50 pub fn extract_otel_config_from_env(endpoint: Option<String>) -> OtelConfig {
52 let endpoint = if let Some(endpoint) = endpoint {
53 if endpoint.is_empty() {
54 env::var("RUSTFS_OBS_ENDPOINT").unwrap_or_else(|_| "".to_string())
55 } else {
56 endpoint
57 }
58 } else {
59 env::var("RUSTFS_OBS_ENDPOINT").unwrap_or_else(|_| "".to_string())
60 };
61 let mut use_stdout = env::var("RUSTFS_OBS_USE_STDOUT")
62 .ok()
63 .and_then(|v| v.parse().ok())
64 .or(Some(USE_STDOUT));
65 if endpoint.is_empty() {
66 use_stdout = Some(true);
67 }
68
69 OtelConfig {
70 endpoint,
71 use_stdout,
72 sample_ratio: env::var("RUSTFS_OBS_SAMPLE_RATIO")
73 .ok()
74 .and_then(|v| v.parse().ok())
75 .or(Some(SAMPLE_RATIO)),
76 meter_interval: env::var("RUSTFS_OBS_METER_INTERVAL")
77 .ok()
78 .and_then(|v| v.parse().ok())
79 .or(Some(METER_INTERVAL)),
80 service_name: env::var("RUSTFS_OBS_SERVICE_NAME")
81 .ok()
82 .and_then(|v| v.parse().ok())
83 .or(Some(APP_NAME.to_string())),
84 service_version: env::var("RUSTFS_OBS_SERVICE_VERSION")
85 .ok()
86 .and_then(|v| v.parse().ok())
87 .or(Some(SERVICE_VERSION.to_string())),
88 environment: env::var("RUSTFS_OBS_ENVIRONMENT")
89 .ok()
90 .and_then(|v| v.parse().ok())
91 .or(Some(ENVIRONMENT.to_string())),
92 logger_level: env::var("RUSTFS_OBS_LOGGER_LEVEL")
93 .ok()
94 .and_then(|v| v.parse().ok())
95 .or(Some(DEFAULT_LOG_LEVEL.to_string())),
96 local_logging_enabled: env::var("RUSTFS_OBS_LOCAL_LOGGING_ENABLED")
97 .ok()
98 .and_then(|v| v.parse().ok())
99 .or(Some(false)),
100 log_directory: env::var("RUSTFS_OBS_LOG_DIRECTORY")
101 .ok()
102 .and_then(|v| v.parse().ok())
103 .or(Some(DEFAULT_LOG_DIR.to_string())),
104 log_filename: env::var("RUSTFS_OBS_LOG_FILENAME")
105 .ok()
106 .and_then(|v| v.parse().ok())
107 .or(Some(DEFAULT_LOG_FILENAME.to_string())),
108 log_rotation_size_mb: env::var("RUSTFS_OBS_LOG_ROTATION_SIZE_MB")
109 .ok()
110 .and_then(|v| v.parse().ok())
111 .or(Some(DEFAULT_LOG_ROTATION_SIZE_MB)), log_rotation_time: env::var("RUSTFS_OBS_LOG_ROTATION_TIME")
113 .ok()
114 .and_then(|v| v.parse().ok())
115 .or(Some(DEFAULT_LOG_ROTATION_TIME.to_string())), log_keep_files: env::var("RUSTFS_OBS_LOG_KEEP_FILES")
117 .ok()
118 .and_then(|v| v.parse().ok())
119 .or(Some(DEFAULT_LOG_KEEP_FILES)), }
121 }
122
123 pub fn new() -> Self {
128 Self::extract_otel_config_from_env(None)
129 }
130}
131
132impl Default for OtelConfig {
133 fn default() -> Self {
134 Self::new()
135 }
136}
137
138#[derive(Debug, Deserialize, Serialize, Clone)]
140pub struct KafkaSinkConfig {
141 pub brokers: String,
142 pub topic: String,
143 pub batch_size: Option<usize>, pub batch_timeout_ms: Option<u64>, }
146
147impl KafkaSinkConfig {
148 pub fn new() -> Self {
149 Self::default()
150 }
151}
152
153impl Default for KafkaSinkConfig {
154 fn default() -> Self {
155 Self {
156 brokers: env::var("RUSTFS_SINKS_KAFKA_BROKERS")
157 .ok()
158 .filter(|s| !s.trim().is_empty())
159 .unwrap_or_else(|| "localhost:9092".to_string()),
160 topic: env::var("RUSTFS_SINKS_KAFKA_TOPIC")
161 .ok()
162 .filter(|s| !s.trim().is_empty())
163 .unwrap_or_else(|| "rustfs_sink".to_string()),
164 batch_size: Some(100),
165 batch_timeout_ms: Some(1000),
166 }
167 }
168}
169
170#[derive(Debug, Deserialize, Serialize, Clone)]
172pub struct WebhookSinkConfig {
173 pub endpoint: String,
174 pub auth_token: String,
175 pub max_retries: Option<usize>, pub retry_delay_ms: Option<u64>, }
178
179impl WebhookSinkConfig {
180 pub fn new() -> Self {
181 Self::default()
182 }
183}
184
185impl Default for WebhookSinkConfig {
186 fn default() -> Self {
187 Self {
188 endpoint: env::var("RUSTFS_SINKS_WEBHOOK_ENDPOINT")
189 .ok()
190 .filter(|s| !s.trim().is_empty())
191 .unwrap_or_else(|| "http://localhost:8080".to_string()),
192 auth_token: env::var("RUSTFS_SINKS_WEBHOOK_AUTH_TOKEN")
193 .ok()
194 .filter(|s| !s.trim().is_empty())
195 .unwrap_or_else(|| "rustfs_webhook_token".to_string()),
196 max_retries: Some(3),
197 retry_delay_ms: Some(100),
198 }
199 }
200}
201
202#[derive(Debug, Deserialize, Serialize, Clone)]
204pub struct FileSinkConfig {
205 pub path: String,
206 pub buffer_size: Option<usize>, pub flush_interval_ms: Option<u64>, pub flush_threshold: Option<usize>, }
210
211impl FileSinkConfig {
212 pub fn get_default_log_path() -> String {
213 let temp_dir = env::temp_dir().join("rustfs");
214
215 if let Err(e) = std::fs::create_dir_all(&temp_dir) {
216 eprintln!("Failed to create log directory: {e}");
217 return "rustfs/rustfs.log".to_string();
218 }
219 temp_dir
220 .join("rustfs.log")
221 .to_str()
222 .unwrap_or("rustfs/rustfs.log")
223 .to_string()
224 }
225 pub fn new() -> Self {
226 Self::default()
227 }
228}
229
230impl Default for FileSinkConfig {
231 fn default() -> Self {
232 Self {
233 path: env::var("RUSTFS_SINKS_FILE_PATH")
234 .ok()
235 .filter(|s| !s.trim().is_empty())
236 .unwrap_or_else(Self::get_default_log_path),
237 buffer_size: env::var("RUSTFS_SINKS_FILE_BUFFER_SIZE")
238 .ok()
239 .and_then(|v| v.parse().ok())
240 .or(Some(8192)),
241 flush_interval_ms: env::var("RUSTFS_SINKS_FILE_FLUSH_INTERVAL_MS")
242 .ok()
243 .and_then(|v| v.parse().ok())
244 .or(Some(1000)),
245 flush_threshold: env::var("RUSTFS_SINKS_FILE_FLUSH_THRESHOLD")
246 .ok()
247 .and_then(|v| v.parse().ok())
248 .or(Some(100)),
249 }
250 }
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize)]
255#[serde(tag = "type")]
256pub enum SinkConfig {
257 File(FileSinkConfig),
258 Kafka(KafkaSinkConfig),
259 Webhook(WebhookSinkConfig),
260}
261
262impl SinkConfig {
263 pub fn new() -> Self {
264 Self::File(FileSinkConfig::new())
265 }
266}
267
268impl Default for SinkConfig {
269 fn default() -> Self {
270 Self::new()
271 }
272}
273
274#[derive(Debug, Deserialize, Serialize, Clone)]
276pub struct LoggerConfig {
277 pub queue_capacity: Option<usize>,
278}
279
280impl LoggerConfig {
281 pub fn new() -> Self {
282 Self {
283 queue_capacity: Some(10000),
284 }
285 }
286}
287
288impl Default for LoggerConfig {
289 fn default() -> Self {
290 Self::new()
291 }
292}
293
294#[derive(Debug, Deserialize, Clone)]
308pub struct AppConfig {
309 pub observability: OtelConfig,
310 pub sinks: Vec<SinkConfig>,
311 pub logger: Option<LoggerConfig>,
312}
313
314impl AppConfig {
315 pub fn new() -> Self {
320 Self {
321 observability: OtelConfig::default(),
322 sinks: vec![SinkConfig::default()],
323 logger: Some(LoggerConfig::default()),
324 }
325 }
326
327 pub fn new_with_endpoint(endpoint: Option<String>) -> Self {
328 Self {
329 observability: OtelConfig::extract_otel_config_from_env(endpoint),
330 sinks: vec![SinkConfig::new()],
331 logger: Some(LoggerConfig::new()),
332 }
333 }
334}
335
336impl Default for AppConfig {
338 fn default() -> Self {
339 Self::new()
340 }
341}