mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-13 08:36:54 +00:00
improve signal watch
This commit is contained in:
+101
-25
@@ -1,5 +1,5 @@
|
||||
use crate::global::{ENVIRONMENT, LOGGER_LEVEL, METER_INTERVAL, SAMPLE_RATIO, SERVICE_NAME, SERVICE_VERSION};
|
||||
use config::{Config, File, FileFormat};
|
||||
use crate::global::{ENVIRONMENT, LOGGER_LEVEL, METER_INTERVAL, SAMPLE_RATIO, SERVICE_NAME, SERVICE_VERSION, USE_STDOUT};
|
||||
use config::{Config, Environment, File, FileFormat};
|
||||
use serde::Deserialize;
|
||||
use std::env;
|
||||
|
||||
@@ -22,18 +22,44 @@ pub struct OtelConfig {
|
||||
pub logger_level: Option<String>,
|
||||
}
|
||||
|
||||
// 辅助函数:从环境变量中提取可观测性配置
|
||||
fn extract_otel_config_from_env() -> OtelConfig {
|
||||
OtelConfig {
|
||||
endpoint: env::var("RUSTFS_OBSERVABILITY_ENDPOINT").unwrap_or_else(|_| "".to_string()),
|
||||
use_stdout: env::var("RUSTFS_OBSERVABILITY_USE_STDOUT")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.or(Some(USE_STDOUT)),
|
||||
sample_ratio: env::var("RUSTFS_OBSERVABILITY_SAMPLE_RATIO")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.or(Some(SAMPLE_RATIO)),
|
||||
meter_interval: env::var("RUSTFS_OBSERVABILITY_METER_INTERVAL")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.or(Some(METER_INTERVAL)),
|
||||
service_name: env::var("RUSTFS_OBSERVABILITY_SERVICE_NAME")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.or(Some(SERVICE_NAME.to_string())),
|
||||
service_version: env::var("RUSTFS_OBSERVABILITY_SERVICE_VERSION")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.or(Some(SERVICE_VERSION.to_string())),
|
||||
environment: env::var("RUSTFS_OBSERVABILITY_ENVIRONMENT")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.or(Some(ENVIRONMENT.to_string())),
|
||||
logger_level: env::var("RUSTFS_OBSERVABILITY_LOGGER_LEVEL")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.or(Some(LOGGER_LEVEL.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for OtelConfig {
|
||||
fn default() -> Self {
|
||||
OtelConfig {
|
||||
endpoint: "".to_string(),
|
||||
use_stdout: Some(true),
|
||||
sample_ratio: Some(SAMPLE_RATIO),
|
||||
meter_interval: Some(METER_INTERVAL),
|
||||
service_name: Some(SERVICE_NAME.to_string()),
|
||||
service_version: Some(SERVICE_VERSION.to_string()),
|
||||
environment: Some(ENVIRONMENT.to_string()),
|
||||
logger_level: Some(LOGGER_LEVEL.to_string()),
|
||||
}
|
||||
extract_otel_config_from_env()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,14 +95,18 @@ pub struct FileSinkConfig {
|
||||
|
||||
impl FileSinkConfig {
|
||||
pub fn get_default_log_path() -> String {
|
||||
let temp_dir = env::temp_dir().join("rustfs").join("logs");
|
||||
let temp_dir = env::temp_dir().join("rustfs");
|
||||
|
||||
if let Err(e) = std::fs::create_dir_all(&temp_dir) {
|
||||
eprintln!("Failed to create log directory: {}", e);
|
||||
return "logs/app.log".to_string();
|
||||
return "rustfs/rustfs.log".to_string();
|
||||
}
|
||||
|
||||
temp_dir.join("app.log").to_str().unwrap_or("logs/app.log").to_string()
|
||||
temp_dir
|
||||
.join("rustfs.log")
|
||||
.to_str()
|
||||
.unwrap_or("rustfs/rustfs.log")
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +114,10 @@ impl Default for FileSinkConfig {
|
||||
fn default() -> Self {
|
||||
FileSinkConfig {
|
||||
enabled: true,
|
||||
path: Self::get_default_log_path(),
|
||||
path: env::var("RUSTFS_SINKS_FILE_PATH")
|
||||
.ok()
|
||||
.filter(|s| !s.trim().is_empty())
|
||||
.unwrap_or_else(|| Self::get_default_log_path()),
|
||||
buffer_size: Some(8192),
|
||||
flush_interval_ms: Some(1000),
|
||||
flush_threshold: Some(100),
|
||||
@@ -93,11 +126,21 @@ impl Default for FileSinkConfig {
|
||||
}
|
||||
|
||||
/// Sink configuration collection
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct SinkConfig {
|
||||
pub kafka: KafkaSinkConfig,
|
||||
pub webhook: WebhookSinkConfig,
|
||||
pub file: FileSinkConfig,
|
||||
pub kafka: Option<KafkaSinkConfig>,
|
||||
pub webhook: Option<WebhookSinkConfig>,
|
||||
pub file: Option<FileSinkConfig>,
|
||||
}
|
||||
|
||||
impl Default for SinkConfig {
|
||||
fn default() -> Self {
|
||||
SinkConfig {
|
||||
kafka: None,
|
||||
webhook: None,
|
||||
file: Some(FileSinkConfig::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///Logger Configuration
|
||||
@@ -109,7 +152,7 @@ pub struct LoggerConfig {
|
||||
impl Default for LoggerConfig {
|
||||
fn default() -> Self {
|
||||
LoggerConfig {
|
||||
queue_capacity: Some(1000),
|
||||
queue_capacity: Some(10000),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,11 +171,28 @@ impl Default for LoggerConfig {
|
||||
///
|
||||
/// let config = load_config(None);
|
||||
/// ```
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct AppConfig {
|
||||
pub observability: OtelConfig,
|
||||
pub sinks: SinkConfig,
|
||||
pub logger: LoggerConfig,
|
||||
pub logger: Option<LoggerConfig>,
|
||||
}
|
||||
|
||||
// 为 AppConfig 实现 Default
|
||||
impl AppConfig {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
observability: OtelConfig::default(),
|
||||
sinks: SinkConfig::default(),
|
||||
logger: Some(LoggerConfig::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AppConfig {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG_FILE: &str = "obs";
|
||||
@@ -187,9 +247,25 @@ pub fn load_config(config_dir: Option<String>) -> AppConfig {
|
||||
let config = Config::builder()
|
||||
.add_source(File::with_name(config_dir.as_str()).format(FileFormat::Toml).required(false))
|
||||
.add_source(File::with_name(config_dir.as_str()).format(FileFormat::Yaml).required(false))
|
||||
.add_source(config::Environment::with_prefix(""))
|
||||
.add_source(
|
||||
Environment::default()
|
||||
.prefix("RUSTFS")
|
||||
.prefix_separator("__")
|
||||
.separator("__")
|
||||
.with_list_parse_key("volumes")
|
||||
.try_parsing(true),
|
||||
)
|
||||
.build()
|
||||
.unwrap_or_default();
|
||||
|
||||
config.try_deserialize().unwrap_or_default()
|
||||
match config.try_deserialize::<AppConfig>() {
|
||||
Ok(app_config) => {
|
||||
println!("Parsed AppConfig: {:?}", app_config);
|
||||
app_config
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Failed to deserialize config: {}", e);
|
||||
AppConfig::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ impl Logger {
|
||||
/// Returns Logger and corresponding Receiver
|
||||
pub fn new(config: &AppConfig) -> (Self, Receiver<UnifiedLogEntry>) {
|
||||
// Get queue capacity from configuration, or use default values 10000
|
||||
let queue_capacity = config.logger.queue_capacity.unwrap_or(10000);
|
||||
let queue_capacity = config.logger.as_ref().and_then(|l| l.queue_capacity).unwrap_or(10000);
|
||||
let (sender, receiver) = mpsc::channel(queue_capacity);
|
||||
(Logger { sender, queue_capacity }, receiver)
|
||||
}
|
||||
|
||||
+73
-42
@@ -4,7 +4,6 @@ use std::sync::Arc;
|
||||
use tokio::fs::OpenOptions;
|
||||
use tokio::io;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tracing::debug;
|
||||
|
||||
/// Sink Trait definition, asynchronously write logs
|
||||
#[async_trait]
|
||||
@@ -274,15 +273,15 @@ impl FileSink {
|
||||
// if the file not exists, create it
|
||||
if !file_exists {
|
||||
tokio::fs::create_dir_all(std::path::Path::new(&path).parent().unwrap()).await?;
|
||||
debug!("the file not exists,create if. path: {:?}", path)
|
||||
tracing::debug!("the file not exists,create if. path: {:?}", path)
|
||||
}
|
||||
let file = if file_exists {
|
||||
// If the file exists, open it in append mode
|
||||
debug!("FileSink: File exists, opening in append mode.");
|
||||
tracing::debug!("FileSink: File exists, opening in append mode.");
|
||||
OpenOptions::new().append(true).create(true).open(&path).await?
|
||||
} else {
|
||||
// If the file does not exist, create it
|
||||
debug!("FileSink: File does not exist, creating a new file.");
|
||||
tracing::debug!("FileSink: File does not exist, creating a new file.");
|
||||
// Create the file and write a header or initial content if needed
|
||||
OpenOptions::new().create(true).truncate(true).write(true).open(&path).await?
|
||||
};
|
||||
@@ -414,52 +413,84 @@ pub async fn create_sinks(config: &AppConfig) -> Vec<Arc<dyn Sink>> {
|
||||
let mut sinks: Vec<Arc<dyn Sink>> = Vec::new();
|
||||
|
||||
#[cfg(feature = "kafka")]
|
||||
if config.sinks.kafka.enabled {
|
||||
match rdkafka::config::ClientConfig::new()
|
||||
.set("bootstrap.servers", &config.sinks.kafka.bootstrap_servers)
|
||||
.set("message.timeout.ms", "5000")
|
||||
.create()
|
||||
{
|
||||
Ok(producer) => {
|
||||
sinks.push(Arc::new(KafkaSink::new(
|
||||
producer,
|
||||
config.sinks.kafka.topic.clone(),
|
||||
config.sinks.kafka.batch_size.unwrap_or(100),
|
||||
config.sinks.kafka.batch_timeout_ms.unwrap_or(1000),
|
||||
)));
|
||||
{
|
||||
match &config.sinks.kafka {
|
||||
Some(sink_kafka) => {
|
||||
if sink_kafka.enabled {
|
||||
match rdkafka::config::ClientConfig::new()
|
||||
.set("bootstrap.servers", &sink_kafka.bootstrap_servers)
|
||||
.set("message.timeout.ms", "5000")
|
||||
.create()
|
||||
{
|
||||
Ok(producer) => {
|
||||
sinks.push(Arc::new(KafkaSink::new(
|
||||
producer,
|
||||
sink_kafka.topic.clone(),
|
||||
sink_kafka.batch_size.unwrap_or(100),
|
||||
sink_kafka.batch_timeout_ms.unwrap_or(1000),
|
||||
)));
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to create Kafka producer: {}", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tracing::info!("Kafka sink is disabled in the configuration");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
tracing::info!("Kafka sink is not configured or disabled");
|
||||
}
|
||||
Err(e) => eprintln!("Failed to create Kafka producer: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "webhook")]
|
||||
if config.sinks.webhook.enabled {
|
||||
sinks.push(Arc::new(WebhookSink::new(
|
||||
config.sinks.webhook.endpoint.clone(),
|
||||
config.sinks.webhook.auth_token.clone(),
|
||||
config.sinks.webhook.max_retries.unwrap_or(3),
|
||||
config.sinks.webhook.retry_delay_ms.unwrap_or(100),
|
||||
)));
|
||||
{
|
||||
match &config.sinks.webhook {
|
||||
Some(sink_webhook) => {
|
||||
if sink_webhook.enabled {
|
||||
sinks.push(Arc::new(WebhookSink::new(
|
||||
sink_webhook.endpoint.clone(),
|
||||
sink_webhook.auth_token.clone(),
|
||||
sink_webhook.max_retries.unwrap_or(3),
|
||||
sink_webhook.retry_delay_ms.unwrap_or(100),
|
||||
)));
|
||||
} else {
|
||||
tracing::info!("Webhook sink is disabled in the configuration");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
tracing::info!("Webhook sink is not configured or disabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "file")]
|
||||
{
|
||||
let path = if config.sinks.file.enabled {
|
||||
config.sinks.file.path.clone()
|
||||
} else {
|
||||
"default.log".to_string()
|
||||
};
|
||||
debug!("FileSink: Using path: {}", path);
|
||||
sinks.push(Arc::new(
|
||||
FileSink::new(
|
||||
path.clone(),
|
||||
config.sinks.file.buffer_size.unwrap_or(8192),
|
||||
config.sinks.file.flush_interval_ms.unwrap_or(1000),
|
||||
config.sinks.file.flush_threshold.unwrap_or(100),
|
||||
)
|
||||
.await
|
||||
.unwrap(),
|
||||
));
|
||||
// let config = config.clone();
|
||||
match &config.sinks.file {
|
||||
Some(sink_file) => {
|
||||
tracing::info!("File sink is enabled in the configuration");
|
||||
let path = if sink_file.enabled {
|
||||
sink_file.path.clone()
|
||||
} else {
|
||||
"rustfs.log".to_string()
|
||||
};
|
||||
tracing::debug!("FileSink: Using path: {}", path);
|
||||
sinks.push(Arc::new(
|
||||
FileSink::new(
|
||||
path.clone(),
|
||||
sink_file.buffer_size.unwrap_or(8192),
|
||||
sink_file.flush_interval_ms.unwrap_or(1000),
|
||||
sink_file.flush_threshold.unwrap_or(100),
|
||||
)
|
||||
.await
|
||||
.unwrap(),
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
tracing::info!("File sink is not configured or disabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sinks
|
||||
|
||||
@@ -293,7 +293,10 @@ pub fn init_telemetry(config: &OtelConfig) -> OtelGuard {
|
||||
|
||||
registry.with(ErrorLayer::default()).with(fmt_layer).init();
|
||||
if !config.endpoint.is_empty() {
|
||||
info!("OpenTelemetry telemetry initialized with OTLP endpoint: {}", config.endpoint);
|
||||
info!(
|
||||
"OpenTelemetry telemetry initialized with OTLP endpoint: {}, logger_level: {}",
|
||||
config.endpoint, logger_level
|
||||
);
|
||||
}
|
||||
|
||||
OtelGuard {
|
||||
|
||||
Reference in New Issue
Block a user