diff --git a/crates/obs/src/telemetry.rs b/crates/obs/src/telemetry.rs index a83dbac9..b2b0495b 100644 --- a/crates/obs/src/telemetry.rs +++ b/crates/obs/src/telemetry.rs @@ -1,19 +1,19 @@ use crate::OtelConfig; -use flexi_logger::{Age, Cleanup, Criterion, DeferredNow, FileSpec, LogSpecification, Naming, Record, WriteMode, style}; +use flexi_logger::{style, Age, Cleanup, Criterion, DeferredNow, FileSpec, LogSpecification, Naming, Record, WriteMode}; use nu_ansi_term::Color; use opentelemetry::trace::TracerProvider; -use opentelemetry::{KeyValue, global}; +use opentelemetry::{global, KeyValue}; use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge; use opentelemetry_otlp::WithExportConfig; use opentelemetry_sdk::logs::SdkLoggerProvider; use opentelemetry_sdk::{ - Resource, metrics::{MeterProviderBuilder, PeriodicReader, SdkMeterProvider}, trace::{RandomIdGenerator, Sampler, SdkTracerProvider}, + Resource, }; use opentelemetry_semantic_conventions::{ - SCHEMA_URL, attribute::{DEPLOYMENT_ENVIRONMENT_NAME, NETWORK_LOCAL_ADDRESS, SERVICE_VERSION as OTEL_SERVICE_VERSION}, + SCHEMA_URL, }; use rustfs_config::{ APP_NAME, DEFAULT_LOG_DIR, DEFAULT_LOG_KEEP_FILES, DEFAULT_LOG_LEVEL, ENVIRONMENT, METER_INTERVAL, SAMPLE_RATIO, @@ -27,7 +27,8 @@ use tracing::info; use tracing_error::ErrorLayer; use tracing_opentelemetry::{MetricsLayer, OpenTelemetryLayer}; use tracing_subscriber::fmt::format::FmtSpan; -use tracing_subscriber::{EnvFilter, Layer, layer::SubscriberExt, util::SubscriberInitExt}; +use tracing_subscriber::fmt::time::LocalTime; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer}; /// A guard object that manages the lifecycle of OpenTelemetry components. /// @@ -224,6 +225,7 @@ pub(crate) fn init_telemetry(config: &OtelConfig) -> OtelGuard { let fmt_layer = { let enable_color = std::io::stdout().is_terminal(); let mut layer = tracing_subscriber::fmt::layer() + .with_timer(LocalTime::rfc_3339()) .with_target(true) .with_ansi(enable_color) .with_thread_names(true) diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml index 8361b921..0ea41530 100644 --- a/crates/utils/Cargo.toml +++ b/crates/utils/Cargo.toml @@ -38,6 +38,7 @@ rand = { workspace = true, optional = true } futures = { workspace = true, optional = true } transform-stream = { workspace = true, optional = true } bytes = { workspace = true, optional = true } +sysinfo = { workspace = true, optional = true } [dev-dependencies] tempfile = { workspace = true } @@ -62,4 +63,5 @@ crypto = ["dep:base64-simd", "dep:hex-simd"] hash = ["dep:highway", "dep:md-5", "dep:sha2", "dep:blake3", "dep:serde", "dep:siphasher"] os = ["dep:nix", "dep:tempfile", "winapi"] # operating system utilities integration = [] # integration test features -full = ["ip", "tls", "net", "io", "hash", "os", "integration", "path", "crypto", "string", "compress"] # all features +sys = ["dep:sysinfo"] # system information features +full = ["ip", "tls", "net", "io", "hash", "os", "integration", "path", "crypto", "string", "compress", "sys"] # all features diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index 0f9433ba..0b05f712 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -33,10 +33,13 @@ pub mod dirs; #[cfg(feature = "tls")] pub use certs::*; + #[cfg(feature = "hash")] pub use hash::*; + #[cfg(feature = "io")] pub use io::*; + #[cfg(feature = "ip")] pub use ip::*; @@ -45,3 +48,6 @@ pub use crypto::*; #[cfg(feature = "compress")] pub use compress::*; + +#[cfg(feature = "sys")] +pub mod sys; diff --git a/crates/utils/src/sys/mod.rs b/crates/utils/src/sys/mod.rs new file mode 100644 index 00000000..5b5cd9b3 --- /dev/null +++ b/crates/utils/src/sys/mod.rs @@ -0,0 +1,4 @@ +mod user_agent; + +pub use user_agent::get_user_agent; +pub use user_agent::ServiceType; diff --git a/crates/notify/src/utils.rs b/crates/utils/src/sys/user_agent.rs similarity index 55% rename from crates/notify/src/utils.rs rename to crates/utils/src/sys/user_agent.rs index afb63c6f..31f42fd7 100644 --- a/crates/notify/src/utils.rs +++ b/crates/utils/src/sys/user_agent.rs @@ -1,14 +1,9 @@ +use rustfs_config::VERSION; +use std::env; use std::fmt; -#[cfg(unix)] -use std::os::unix::process::ExitStatusExt; -#[cfg(windows)] -use std::os::windows::process::ExitStatusExt; -use std::{env, process}; +use sysinfo::System; -// Define Rustfs version -const RUSTFS_VERSION: &str = "1.0.0"; - -// Business Type Enumeration +/// Business Type Enumeration #[derive(Debug, Clone, PartialEq)] pub enum ServiceType { Basis, @@ -39,11 +34,16 @@ struct UserAgent { } impl UserAgent { - // Create a new UserAgent instance and accept business type parameters + /// Create a new UserAgent instance and accept business type parameters + /// + /// # Arguments + /// * `service` - The type of service for which the User-Agent is being created. + /// # Returns + /// A new instance of `UserAgent` with the current OS platform, architecture, version, and service type. fn new(service: ServiceType) -> Self { let os_platform = Self::get_os_platform(); let arch = env::consts::ARCH.to_string(); - let version = RUSTFS_VERSION.to_string(); + let version = VERSION.to_string(); UserAgent { os_platform, @@ -53,64 +53,57 @@ impl UserAgent { } } - // Obtain operating system platform information + /// Obtain operating system platform information fn get_os_platform() -> String { + let sys = System::new_all(); if cfg!(target_os = "windows") { - Self::get_windows_platform() + Self::get_windows_platform(&sys) } else if cfg!(target_os = "macos") { - Self::get_macos_platform() + Self::get_macos_platform(&sys) } else if cfg!(target_os = "linux") { - Self::get_linux_platform() + Self::get_linux_platform(&sys) } else { "Unknown".to_string() } } - // Get Windows platform information + /// Get Windows platform information #[cfg(windows)] - fn get_windows_platform() -> String { - // Use cmd /c ver to get the version - let output = process::Command::new("cmd") - .args(&["/C", "ver"]) - .output() - .unwrap_or_else(|_| process::Output { - status: process::ExitStatus::from_raw(0), - stdout: Vec::new(), - stderr: Vec::new(), - }); - let version = String::from_utf8_lossy(&output.stdout); - let version = version - .lines() - .next() - .unwrap_or("Windows NT 10.0") - .replace("Microsoft Windows [Version ", "") - .replace("]", ""); - format!("Windows NT {}", version.trim()) + fn get_windows_platform(sys: &System) -> String { + // Priority to using sysinfo to get versions + if let Some(version) = sys.os_version() { + format!("Windows NT {}", version) + } else { + // Fallback to cmd /c ver + let output = std::process::Command::new("cmd") + .args(&["/C", "ver"]) + .output() + .unwrap_or_default(); + let version = String::from_utf8_lossy(&output.stdout); + let version = version + .lines() + .next() + .unwrap_or("Windows NT 10.0") + .replace("Microsoft Windows [Version ", "") + .replace("]", ""); + format!("Windows NT {}", version.trim()) + } } #[cfg(not(windows))] - fn get_windows_platform() -> String { + fn get_windows_platform(_sys: &System) -> String { "N/A".to_string() } - // Get macOS platform information + /// Get macOS platform information #[cfg(target_os = "macos")] - fn get_macos_platform() -> String { - let output = process::Command::new("sw_vers") - .args(&["-productVersion"]) - .output() - .unwrap_or_else(|_| process::Output { - status: process::ExitStatus::from_raw(0), - stdout: Vec::new(), - stderr: Vec::new(), - }); - let version = String::from_utf8_lossy(&output.stdout).trim().to_string(); - let parts: Vec<&str> = version.split('.').collect(); - let major = parts.get(0).unwrap_or(&"10").parse::().unwrap_or(10); - let minor = parts.get(1).map_or("15", |&m| m); - let patch = parts.get(2).map_or("0", |&p| p); + fn get_macos_platform(_sys: &System) -> String { + let binding = System::os_version().unwrap_or("14.5.0".to_string()); + let version = binding.split('.').collect::>(); + let major = version.get(0).unwrap_or(&"14").to_string(); + let minor = version.get(1).unwrap_or(&"5").to_string(); + let patch = version.get(2).unwrap_or(&"0").to_string(); - // Detect whether it is an Apple Silicon chip let arch = env::consts::ARCH; let cpu_info = if arch == "aarch64" { "Apple" } else { "Intel" }; @@ -119,36 +112,25 @@ impl UserAgent { } #[cfg(not(target_os = "macos"))] - fn get_macos_platform() -> String { + fn get_macos_platform(_sys: &System) -> String { "N/A".to_string() } - // Get Linux platform information + /// Get Linux platform information #[cfg(target_os = "linux")] - fn get_linux_platform() -> String { - let output = process::Command::new("uname") - .arg("-r") - .output() - .unwrap_or_else(|_| process::Output { - status: process::ExitStatus::from_raw(0), - stdout: Vec::new(), - stderr: Vec::new(), - }); - if output.status.success() { - let release = String::from_utf8_lossy(&output.stdout).trim().to_string(); - format!("X11; Linux {}", release) - } else { - "X11; Linux Unknown".to_string() - } + fn get_linux_platform(sys: &System) -> String { + let name = sys.name().unwrap_or("Linux".to_string()); + let version = sys.os_version().unwrap_or("Unknown".to_string()); + format!("X11; {} {}", name, version) } #[cfg(not(target_os = "linux"))] - fn get_linux_platform() -> String { + fn get_linux_platform(_sys: &System) -> String { "N/A".to_string() } } -// Implement Display trait to format User-Agent +/// Implement Display trait to format User-Agent impl fmt::Display for UserAgent { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.service == ServiceType::Basis { @@ -195,7 +177,6 @@ mod tests { let ua = get_user_agent(ServiceType::Event); assert!(ua.starts_with("Mozilla/5.0")); assert!(ua.contains("Rustfs/1.0.0 (event)")); - println!("User-Agent: {}", ua); } @@ -214,4 +195,18 @@ mod tests { assert!(ua.contains("Rustfs/1.0.0 (monitor)")); println!("User-Agent: {}", ua); } + + #[test] + fn test_all_service_type() { + // Example: Generate User-Agents of Different Business Types + let ua_core = get_user_agent(ServiceType::Core); + let ua_event = get_user_agent(ServiceType::Event); + let ua_logger = get_user_agent(ServiceType::Logger); + let ua_custom = get_user_agent(ServiceType::Custom("monitor".to_string())); + + println!("Core User-Agent: {}", ua_core); + println!("Event User-Agent: {}", ua_event); + println!("Logger User-Agent: {}", ua_logger); + println!("Custom User-Agent: {}", ua_custom); + } }