mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-18 10:43:15 +00:00
refactor(logging): standardize protocol and observability events (#3419)
* refactor(logging): standardize object capacity events * refactor(logging): standardize protocol server events * refactor(logging): standardize swift protocol events * refactor(logging): standardize observability events * refactor(logging): move masking helper and extend guardrails
This commit is contained in:
@@ -31,6 +31,10 @@ use std::io::{BufReader, BufWriter, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
const LOG_COMPONENT_OBS: &str = "obs";
|
||||
const LOG_SUBSYSTEM_LOG_CLEANER: &str = "log_cleaner";
|
||||
const EVENT_LOG_CLEANER_COMPRESSION_STATE: &str = "log_cleaner_compression_state";
|
||||
|
||||
/// Compression options shared by serial and parallel cleaner paths.
|
||||
///
|
||||
/// The core cleaner prepares this immutable bundle once per cleanup pass and
|
||||
@@ -78,9 +82,13 @@ pub(super) fn compress_file(path: &Path, options: &CompressionOptions) -> Result
|
||||
Ok(output) => Ok(output),
|
||||
Err(err) if options.zstd_fallback_to_gzip => {
|
||||
warn!(
|
||||
event = EVENT_LOG_CLEANER_COMPRESSION_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_LOG_CLEANER,
|
||||
file = ?path,
|
||||
error = %err,
|
||||
"zstd compression failed, fallback to gzip"
|
||||
result = "zstd_failed_fallback_gzip",
|
||||
"log cleaner compression state changed"
|
||||
);
|
||||
compress_gzip(path, options.gzip_level, options.dry_run)
|
||||
}
|
||||
@@ -127,7 +135,7 @@ where
|
||||
// Keep idempotent behavior: existing archive means this file has already
|
||||
// been handled in a previous cleanup pass.
|
||||
if archive_path.exists() {
|
||||
debug!(file = ?archive_path, "compressed archive already exists, skipping");
|
||||
debug!(event = EVENT_LOG_CLEANER_COMPRESSION_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_LOG_CLEANER, file = ?archive_path, state = "archive_exists", "log cleaner compression state changed");
|
||||
let input_bytes = std::fs::metadata(path).map(|m| m.len()).unwrap_or(0);
|
||||
let output_bytes = std::fs::metadata(archive_path).map(|m| m.len()).unwrap_or(0);
|
||||
return Ok(CompressionOutput {
|
||||
@@ -140,7 +148,7 @@ where
|
||||
|
||||
let input_bytes = std::fs::metadata(path).map(|m| m.len()).unwrap_or(0);
|
||||
if dry_run {
|
||||
info!("[DRY RUN] Would compress file: {:?} -> {:?}", path, archive_path);
|
||||
info!(event = EVENT_LOG_CLEANER_COMPRESSION_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_LOG_CLEANER, state = "dry_run_compress", file = ?path, archive = ?archive_path, input_bytes, "log cleaner compression state changed");
|
||||
return Ok(CompressionOutput {
|
||||
archive_path: archive_path.to_path_buf(),
|
||||
algorithm_used,
|
||||
@@ -185,12 +193,16 @@ where
|
||||
|
||||
let output_bytes = std::fs::metadata(archive_path).map(|m| m.len()).unwrap_or(0);
|
||||
debug!(
|
||||
event = EVENT_LOG_CLEANER_COMPRESSION_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_LOG_CLEANER,
|
||||
file = ?path,
|
||||
archive = ?archive_path,
|
||||
input_bytes,
|
||||
output_bytes,
|
||||
algorithm = %algorithm_used,
|
||||
"compression finished"
|
||||
state = "compression_finished",
|
||||
"log cleaner compression state changed"
|
||||
);
|
||||
|
||||
Ok(CompressionOutput {
|
||||
|
||||
@@ -37,6 +37,10 @@ use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::time::{Duration, Instant, SystemTime};
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
const LOG_COMPONENT_OBS: &str = "obs";
|
||||
const LOG_SUBSYSTEM_LOG_CLEANER: &str = "log_cleaner";
|
||||
const EVENT_LOG_CLEANER_STATE: &str = "log_cleaner_state";
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CompressionTaskResult {
|
||||
/// Original file metadata so successful workers can be deleted later.
|
||||
@@ -108,7 +112,14 @@ impl LogCleaner {
|
||||
/// Perform one full cleanup pass.
|
||||
pub fn cleanup(&self) -> Result<(usize, u64), std::io::Error> {
|
||||
if !self.log_dir.exists() {
|
||||
debug!("Log directory does not exist: {:?}", self.log_dir);
|
||||
debug!(
|
||||
event = EVENT_LOG_CLEANER_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_LOG_CLEANER,
|
||||
state = "log_dir_missing",
|
||||
log_dir = ?self.log_dir,
|
||||
"log cleaner state changed"
|
||||
);
|
||||
return Ok((0, 0));
|
||||
}
|
||||
|
||||
@@ -134,10 +145,14 @@ impl LogCleaner {
|
||||
let total_size: u64 = logs.iter().map(|f| f.size).sum();
|
||||
|
||||
info!(
|
||||
"Found {} regular log files, total size: {} bytes ({:.2} MB)",
|
||||
logs.len(),
|
||||
total_size,
|
||||
total_size as f64 / 1024.0 / 1024.0
|
||||
event = EVENT_LOG_CLEANER_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_LOG_CLEANER,
|
||||
state = "scan_completed",
|
||||
regular_log_files = logs.len(),
|
||||
total_bytes = total_size,
|
||||
total_megabytes = total_size as f64 / 1024.0 / 1024.0,
|
||||
"log cleaner state changed"
|
||||
);
|
||||
|
||||
// Select the oldest files first, then additionally trim any files
|
||||
@@ -167,10 +182,14 @@ impl LogCleaner {
|
||||
counter!(METRIC_LOG_CLEANER_DELETED_FILES_TOTAL).increment(total_deleted as u64);
|
||||
counter!(METRIC_LOG_CLEANER_FREED_BYTES_TOTAL).increment(total_freed);
|
||||
info!(
|
||||
"Cleanup completed: deleted {} files, freed {} bytes ({:.2} MB)",
|
||||
total_deleted,
|
||||
total_freed,
|
||||
total_freed as f64 / 1024.0 / 1024.0
|
||||
event = EVENT_LOG_CLEANER_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_LOG_CLEANER,
|
||||
state = "cleanup_completed",
|
||||
deleted_files = total_deleted,
|
||||
freed_bytes = total_freed,
|
||||
freed_megabytes = total_freed as f64 / 1024.0 / 1024.0,
|
||||
"log cleaner state changed"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -324,17 +343,21 @@ impl LogCleaner {
|
||||
let compressed = match compress_file(&file.path, &options) {
|
||||
Ok(output) => {
|
||||
debug!(
|
||||
event = EVENT_LOG_CLEANER_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_LOG_CLEANER,
|
||||
file = ?file.path,
|
||||
archive = ?output.archive_path,
|
||||
algorithm = %output.algorithm_used,
|
||||
input_bytes = output.input_bytes,
|
||||
output_bytes = output.output_bytes,
|
||||
"parallel compression done"
|
||||
state = "parallel_compression_done",
|
||||
"log cleaner state changed"
|
||||
);
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(file = ?file.path, error = %err, "parallel compression failed");
|
||||
warn!(event = EVENT_LOG_CLEANER_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_LOG_CLEANER, file = ?file.path, error = %err, result = "parallel_compression_failed", "log cleaner state changed");
|
||||
false
|
||||
}
|
||||
};
|
||||
@@ -350,7 +373,14 @@ impl LogCleaner {
|
||||
|
||||
// Any worker panic triggers deterministic fallback behavior.
|
||||
if scope_result.is_err() {
|
||||
warn!("parallel compression worker panicked, falling back to serial path");
|
||||
warn!(
|
||||
event = EVENT_LOG_CLEANER_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_LOG_CLEANER,
|
||||
result = "parallel_worker_panicked",
|
||||
fallback = "serial",
|
||||
"log cleaner state changed"
|
||||
);
|
||||
return self.serial_compress_and_delete(files);
|
||||
}
|
||||
|
||||
@@ -376,6 +406,9 @@ impl LogCleaner {
|
||||
gauge!(METRIC_LOG_CLEANER_STEAL_SUCCESS_RATE).set(success_rate);
|
||||
|
||||
info!(
|
||||
event = EVENT_LOG_CLEANER_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_LOG_CLEANER,
|
||||
workers = worker_count,
|
||||
algorithm = %self.compression_algorithm,
|
||||
deleted,
|
||||
@@ -384,7 +417,8 @@ impl LogCleaner {
|
||||
steal_attempts = attempts,
|
||||
steal_successes = successes,
|
||||
steal_success_rate = success_rate,
|
||||
"parallel cleanup finished"
|
||||
state = "parallel_cleanup_finished",
|
||||
"log cleaner state changed"
|
||||
);
|
||||
|
||||
Ok((deleted, freed))
|
||||
@@ -441,17 +475,21 @@ impl LogCleaner {
|
||||
match compress_file(&file.path, &options) {
|
||||
Ok(output) => {
|
||||
debug!(
|
||||
event = EVENT_LOG_CLEANER_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_LOG_CLEANER,
|
||||
file = ?file.path,
|
||||
archive = ?output.archive_path,
|
||||
algorithm = %output.algorithm_used,
|
||||
input_bytes = output.input_bytes,
|
||||
output_bytes = output.output_bytes,
|
||||
"serial compression done"
|
||||
state = "serial_compression_done",
|
||||
"log cleaner state changed"
|
||||
);
|
||||
deletable.push(file.clone());
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(file = ?file.path, error = %err, "serial compression failed, source kept");
|
||||
warn!(event = EVENT_LOG_CLEANER_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_LOG_CLEANER, file = ?file.path, error = %err, result = "serial_compression_failed", "log cleaner state changed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -523,7 +561,7 @@ impl LogCleaner {
|
||||
|
||||
for f in files {
|
||||
if self.dry_run {
|
||||
info!("[DRY RUN] Would delete: {:?} ({} bytes)", f.path, f.size);
|
||||
info!(event = EVENT_LOG_CLEANER_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_LOG_CLEANER, state = "dry_run_delete", file = ?f.path, bytes = f.size, "log cleaner state changed");
|
||||
deleted += 1;
|
||||
freed += f.size;
|
||||
continue;
|
||||
@@ -533,10 +571,10 @@ impl LogCleaner {
|
||||
Ok(()) => {
|
||||
deleted += 1;
|
||||
freed += f.size;
|
||||
debug!("Deleted: {:?}", f.path);
|
||||
debug!(event = EVENT_LOG_CLEANER_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_LOG_CLEANER, state = "deleted", file = ?f.path, bytes = f.size, "log cleaner state changed");
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to delete {:?}: {}", f.path, e);
|
||||
error!(event = EVENT_LOG_CLEANER_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_LOG_CLEANER, result = "delete_failed", file = ?f.path, error = %e, "log cleaner state changed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,10 @@ use std::path::Path;
|
||||
use std::time::SystemTime;
|
||||
use tracing::debug;
|
||||
|
||||
const LOG_COMPONENT_OBS: &str = "obs";
|
||||
const LOG_SUBSYSTEM_LOG_CLEANER: &str = "log_cleaner";
|
||||
const EVENT_LOG_CLEANER_SCAN_STATE: &str = "log_cleaner_scan_state";
|
||||
|
||||
/// Result of a single pass directory scan.
|
||||
///
|
||||
/// Separating regular logs from compressed archives keeps the selection logic
|
||||
@@ -125,7 +129,7 @@ pub(super) fn scan_log_directory(
|
||||
|
||||
// 2. Check exclusion patterns early.
|
||||
if is_excluded(filename, exclude_patterns) {
|
||||
debug!("Excluding file from cleanup: {:?}", filename);
|
||||
debug!(event = EVENT_LOG_CLEANER_SCAN_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_LOG_CLEANER, state = "excluded", filename = %filename, "log cleaner scan state changed");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -171,12 +175,12 @@ pub(super) fn scan_log_directory(
|
||||
if !is_compressed && file_size == 0 && delete_empty_files {
|
||||
if !dry_run {
|
||||
if let Err(e) = fs::remove_file(&path) {
|
||||
tracing::warn!("Failed to delete empty file {:?}: {}", path, e);
|
||||
tracing::warn!(event = EVENT_LOG_CLEANER_SCAN_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_LOG_CLEANER, result = "empty_file_delete_failed", path = ?path, error = %e, "log cleaner scan state changed");
|
||||
} else {
|
||||
debug!("Deleted empty file: {:?}", path);
|
||||
debug!(event = EVENT_LOG_CLEANER_SCAN_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_LOG_CLEANER, state = "empty_file_deleted", path = ?path, "log cleaner scan state changed");
|
||||
}
|
||||
} else {
|
||||
tracing::info!("[DRY RUN] Would delete empty file: {:?}", path);
|
||||
tracing::info!(event = EVENT_LOG_CLEANER_SCAN_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_LOG_CLEANER, state = "dry_run_empty_file_delete", path = ?path, "log cleaner scan state changed");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ use std::sync::{Arc, Mutex};
|
||||
use tokio::sync::OnceCell;
|
||||
use tracing::{info, warn};
|
||||
|
||||
const LOG_COMPONENT_OBS: &str = "obs";
|
||||
const LOG_SUBSYSTEM_GLOBAL: &str = "global";
|
||||
const EVENT_OBS_GLOBAL_STATE: &str = "obs_global_state";
|
||||
|
||||
/// Global guard for OpenTelemetry tracing
|
||||
static GLOBAL_GUARD: OnceCell<Arc<Mutex<OtelGuard>>> = OnceCell::const_new();
|
||||
|
||||
@@ -50,9 +54,13 @@ pub(crate) fn set_observability_metric_enabled(enabled: bool) {
|
||||
&& *current != enabled
|
||||
{
|
||||
warn!(
|
||||
event = EVENT_OBS_GLOBAL_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_GLOBAL,
|
||||
current = *current,
|
||||
requested = enabled,
|
||||
"OBSERVABILITY_METRIC_ENABLED was already initialized; keeping original value"
|
||||
result = "metrics_flag_already_initialized",
|
||||
"obs global state changed"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -143,7 +151,13 @@ pub async fn init_obs_with_config(config: &OtelConfig) -> Result<OtelGuard, Glob
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn set_global_guard(guard: OtelGuard) -> Result<(), GlobalError> {
|
||||
info!("Initializing global guard");
|
||||
info!(
|
||||
event = EVENT_OBS_GLOBAL_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_GLOBAL,
|
||||
state = "guard_initializing",
|
||||
"obs global state changed"
|
||||
);
|
||||
GLOBAL_GUARD.set(Arc::new(Mutex::new(guard))).map_err(GlobalError::SetError)
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
use rustfs_security_governance::{RedactionLevel, RedactionPolicyError, RedactionRule, validate_redaction_rules};
|
||||
use std::fmt;
|
||||
pub use rustfs_utils::MaskedAccessKey;
|
||||
|
||||
pub const REDACTED_LOG_VALUE: &str = "***redacted***";
|
||||
|
||||
@@ -56,41 +56,6 @@ pub fn redacted_optional_log_value(value: Option<&str>) -> Option<&'static str>
|
||||
value.map(redacted_log_value)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct MaskedAccessKey<'a>(pub &'a str);
|
||||
|
||||
impl fmt::Display for MaskedAccessKey<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let value = self.0;
|
||||
if value.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let chars: Vec<char> = value.chars().collect();
|
||||
match chars.len() {
|
||||
0 => Ok(()),
|
||||
1..=4 => f.write_str("***"),
|
||||
5..=8 => write!(f, "{}***{}", chars[0], chars[chars.len() - 1]),
|
||||
len => {
|
||||
for ch in &chars[..4] {
|
||||
write!(f, "{ch}")?;
|
||||
}
|
||||
f.write_str("***")?;
|
||||
for ch in &chars[len - 4..] {
|
||||
write!(f, "{ch}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for MaskedAccessKey<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -47,6 +47,10 @@ use thiserror::Error;
|
||||
|
||||
use tracing::warn;
|
||||
|
||||
const LOG_COMPONENT_OBS: &str = "obs";
|
||||
const LOG_SUBSYSTEM_GPU_METRICS: &str = "gpu_metrics";
|
||||
const EVENT_GPU_METRICS_STATE: &str = "gpu_metrics_state";
|
||||
|
||||
/// GPU statistics.
|
||||
///
|
||||
/// Contains GPU memory usage metrics for the monitored process.
|
||||
@@ -138,7 +142,14 @@ impl GpuCollector {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
warn!("Could not get GPU stats, recording 0 for GPU memory usage");
|
||||
warn!(
|
||||
event = EVENT_GPU_METRICS_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_GPU_METRICS,
|
||||
result = "process_stats_unavailable",
|
||||
fallback_memory_usage = 0,
|
||||
"gpu metrics state changed"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return Err(GpuError::DeviceError("No GPU device found".to_string()));
|
||||
|
||||
@@ -94,6 +94,10 @@ use tokio::time::Instant;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::warn;
|
||||
|
||||
const LOG_COMPONENT_OBS: &str = "obs";
|
||||
const LOG_SUBSYSTEM_METRICS_RUNTIME: &str = "metrics_runtime";
|
||||
const EVENT_METRICS_RUNTIME_STATE: &str = "metrics_runtime_state";
|
||||
|
||||
/// Default interval for system monitoring metrics (15 seconds)
|
||||
const DEFAULT_SYSTEM_METRICS_INTERVAL: Duration = Duration::from_secs(15);
|
||||
/// Environment variable for system monitoring interval
|
||||
@@ -497,7 +501,7 @@ pub fn init_metrics_runtime(token: CancellationToken) {
|
||||
report_metrics(&metrics);
|
||||
}
|
||||
_ = token_clone.cancelled() => {
|
||||
warn!("Metrics collection for cluster stats cancelled.");
|
||||
warn!(event = EVENT_METRICS_RUNTIME_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_RUNTIME, collector = "cluster_stats", state = "cancelled", "metrics runtime state changed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -537,7 +541,7 @@ pub fn init_metrics_runtime(token: CancellationToken) {
|
||||
}
|
||||
}
|
||||
_ = token_clone.cancelled() => {
|
||||
warn!("Metrics collection for supplementary cluster stats cancelled.");
|
||||
warn!(event = EVENT_METRICS_RUNTIME_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_RUNTIME, collector = "supplementary_cluster_stats", state = "cancelled", "metrics runtime state changed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -556,7 +560,7 @@ pub fn init_metrics_runtime(token: CancellationToken) {
|
||||
report_metrics(&metrics);
|
||||
}
|
||||
_ = token_clone.cancelled() => {
|
||||
warn!("Metrics collection for bucket stats cancelled.");
|
||||
warn!(event = EVENT_METRICS_RUNTIME_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_RUNTIME, collector = "bucket_stats", state = "cancelled", "metrics runtime state changed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -577,7 +581,7 @@ pub fn init_metrics_runtime(token: CancellationToken) {
|
||||
report_metrics(&metrics);
|
||||
}
|
||||
_ = token_clone.cancelled() => {
|
||||
warn!("Metrics collection for node/disk stats cancelled.");
|
||||
warn!(event = EVENT_METRICS_RUNTIME_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_RUNTIME, collector = "node_disk_stats", state = "cancelled", "metrics runtime state changed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -601,7 +605,7 @@ pub fn init_metrics_runtime(token: CancellationToken) {
|
||||
let current_live_keys = repl_bw_live_keys(&stats);
|
||||
|
||||
if !monitor_available {
|
||||
warn!("Bucket monitor unavailable; skip replication bandwidth key-state transition this cycle.");
|
||||
warn!(event = EVENT_METRICS_RUNTIME_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_RUNTIME, collector = "bucket_replication_bandwidth", result = "bucket_monitor_unavailable", "metrics runtime state changed");
|
||||
}
|
||||
update_repl_bw_zero_tombstones(
|
||||
monitor_available,
|
||||
@@ -626,7 +630,7 @@ pub fn init_metrics_runtime(token: CancellationToken) {
|
||||
expire_repl_bw_zero_tombstones(monitor_available, &mut zero_tombstones);
|
||||
}
|
||||
_ = token_clone.cancelled() => {
|
||||
warn!("Metrics collection for bucket replication bandwidth stats cancelled.");
|
||||
warn!(event = EVENT_METRICS_RUNTIME_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_RUNTIME, collector = "bucket_replication_bandwidth", state = "cancelled", "metrics runtime state changed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -653,7 +657,7 @@ pub fn init_metrics_runtime(token: CancellationToken) {
|
||||
report_metrics(&metrics);
|
||||
}
|
||||
_ = token_clone.cancelled() => {
|
||||
warn!("Metrics collection for audit target stats cancelled.");
|
||||
warn!(event = EVENT_METRICS_RUNTIME_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_RUNTIME, collector = "audit_target_stats", state = "cancelled", "metrics runtime state changed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -689,7 +693,7 @@ pub fn init_metrics_runtime(token: CancellationToken) {
|
||||
report_metrics(&metrics);
|
||||
}
|
||||
_ = token_clone.cancelled() => {
|
||||
warn!("Metrics collection for notification stats cancelled.");
|
||||
warn!(event = EVENT_METRICS_RUNTIME_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_RUNTIME, collector = "notification_stats", state = "cancelled", "metrics runtime state changed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -718,7 +722,7 @@ pub fn init_metrics_runtime(token: CancellationToken) {
|
||||
}
|
||||
}
|
||||
_ = token_clone.cancelled() => {
|
||||
warn!("Metrics collection for background workflow stats cancelled.");
|
||||
warn!(event = EVENT_METRICS_RUNTIME_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_RUNTIME, collector = "background_workflow_stats", state = "cancelled", "metrics runtime state changed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -742,7 +746,7 @@ pub fn init_metrics_runtime(token: CancellationToken) {
|
||||
let current_pid = match sysinfo::get_current_pid() {
|
||||
Ok(pid) => Some(pid),
|
||||
Err(e) => {
|
||||
warn!("Failed to get current PID for system monitoring: {}", e);
|
||||
warn!(event = EVENT_METRICS_RUNTIME_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_RUNTIME, collector = "system_monitoring", result = "current_pid_unavailable", error = %e, "metrics runtime state changed");
|
||||
None
|
||||
}
|
||||
};
|
||||
@@ -776,11 +780,11 @@ pub fn init_metrics_runtime(token: CancellationToken) {
|
||||
metrics.extend(collect_gpu_metrics(&gpu_stats, &labels));
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("GPU metrics collection failed: {}", e);
|
||||
warn!(event = EVENT_METRICS_RUNTIME_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_RUNTIME, collector = "gpu_metrics", result = "collect_failed", error = %e, "metrics runtime state changed");
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
warn!("GPU collector initialization failed: {}", e);
|
||||
warn!(event = EVENT_METRICS_RUNTIME_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_RUNTIME, collector = "gpu_metrics", result = "collector_init_failed", error = %e, "metrics runtime state changed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -790,7 +794,7 @@ pub fn init_metrics_runtime(token: CancellationToken) {
|
||||
}
|
||||
}
|
||||
_ = token_clone.cancelled() => {
|
||||
warn!("Process metrics collection cancelled.");
|
||||
warn!(event = EVENT_METRICS_RUNTIME_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_RUNTIME, collector = "process_metrics", state = "cancelled", "metrics runtime state changed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -812,7 +816,7 @@ pub fn init_metrics_runtime(token: CancellationToken) {
|
||||
}
|
||||
}
|
||||
_ = token_clone.cancelled() => {
|
||||
warn!("Metrics collection for internode network stats cancelled.");
|
||||
warn!(event = EVENT_METRICS_RUNTIME_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_RUNTIME, collector = "internode_network_stats", state = "cancelled", "metrics runtime state changed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -865,7 +869,7 @@ fn current_process_metric_labels() -> Vec<(&'static str, Cow<'static, str>)> {
|
||||
}
|
||||
|
||||
fn fallback_process_metric_labels(err: ProcessAttributeError) -> Vec<(&'static str, Cow<'static, str>)> {
|
||||
warn!("Failed to collect process attributes for metrics labels: {}", err);
|
||||
warn!(event = EVENT_METRICS_RUNTIME_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_RUNTIME, collector = "process_metric_labels", result = "collect_failed", error = %err, "metrics runtime state changed");
|
||||
vec![
|
||||
("process_pid", Cow::Owned(std::process::id().to_string())),
|
||||
("process_executable_name", Cow::Borrowed("unknown")),
|
||||
|
||||
@@ -46,6 +46,10 @@ use std::time::Duration;
|
||||
use sysinfo::{Networks, System};
|
||||
use tracing::{instrument, warn};
|
||||
|
||||
const LOG_COMPONENT_OBS: &str = "obs";
|
||||
const LOG_SUBSYSTEM_METRICS_COLLECTOR: &str = "metrics_collector";
|
||||
const EVENT_METRICS_COLLECTOR_STATE: &str = "metrics_collector_state";
|
||||
|
||||
fn current_scanner_cycle_age_seconds(
|
||||
current_cycle: u64,
|
||||
current_started: chrono::DateTime<Utc>,
|
||||
@@ -178,7 +182,7 @@ pub async fn collect_cluster_and_health_stats() -> (ClusterStats, ClusterHealthS
|
||||
let (buckets_count, objects_count) = match load_data_usage_from_backend(store.clone()).await {
|
||||
Ok(data_usage) => (data_usage.buckets_count, data_usage.objects_total_count),
|
||||
Err(e) => {
|
||||
warn!("Failed to load data usage from backend: {}", e);
|
||||
warn!(event = EVENT_METRICS_COLLECTOR_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_COLLECTOR, collector = "cluster_stats", result = "data_usage_load_failed", error = %e, "metrics collector state changed");
|
||||
// Fall back to bucket list for buckets_count, objects_count stays 0.
|
||||
let buckets = store
|
||||
.list_bucket(&BucketOptions {
|
||||
@@ -187,7 +191,7 @@ pub async fn collect_cluster_and_health_stats() -> (ClusterStats, ClusterHealthS
|
||||
})
|
||||
.await
|
||||
.unwrap_or_else(|err| {
|
||||
warn!("Failed to list buckets for cluster metrics: {}", err);
|
||||
warn!(event = EVENT_METRICS_COLLECTOR_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_COLLECTOR, collector = "cluster_stats", result = "bucket_list_failed", error = %err, "metrics collector state changed");
|
||||
Vec::new()
|
||||
});
|
||||
(buckets.len() as u64, 0)
|
||||
@@ -246,7 +250,7 @@ pub async fn collect_bucket_stats() -> Vec<BucketStats> {
|
||||
let data_usage = match load_data_usage_from_backend(store.clone()).await {
|
||||
Ok(info) => Some(info),
|
||||
Err(e) => {
|
||||
warn!("Failed to load data usage for bucket metrics: {}", e);
|
||||
warn!(event = EVENT_METRICS_COLLECTOR_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_COLLECTOR, collector = "bucket_stats", result = "data_usage_load_failed", error = %e, "metrics collector state changed");
|
||||
None
|
||||
}
|
||||
};
|
||||
@@ -261,7 +265,7 @@ pub async fn collect_bucket_stats() -> Vec<BucketStats> {
|
||||
{
|
||||
Ok(buckets) => buckets,
|
||||
Err(e) => {
|
||||
warn!("Failed to list buckets for bucket metrics: {}", e);
|
||||
warn!(event = EVENT_METRICS_COLLECTOR_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_COLLECTOR, collector = "bucket_stats", result = "bucket_list_failed", error = %e, "metrics collector state changed");
|
||||
return Vec::new();
|
||||
}
|
||||
};
|
||||
@@ -310,10 +314,7 @@ pub fn collect_bucket_replication_bandwidth_stats() -> Vec<BucketReplicationBand
|
||||
.map(|(opts, details)| {
|
||||
let target_arn = opts.replication_arn;
|
||||
let limit_bytes_per_sec = u64::try_from(details.limit_bytes_per_sec).unwrap_or_else(|_| {
|
||||
warn!(
|
||||
"Invalid bandwidth limit value for target {:?}: {}",
|
||||
target_arn, details.limit_bytes_per_sec
|
||||
);
|
||||
warn!(event = EVENT_METRICS_COLLECTOR_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_COLLECTOR, collector = "bucket_replication_bandwidth", result = "invalid_limit_value", target_arn = ?target_arn, limit_value = details.limit_bytes_per_sec, "metrics collector state changed");
|
||||
0
|
||||
});
|
||||
|
||||
|
||||
@@ -39,6 +39,10 @@ use rustfs_utils::get_env_usize;
|
||||
use std::path::PathBuf;
|
||||
use tracing::{info, warn};
|
||||
|
||||
const LOG_COMPONENT_OBS: &str = "obs";
|
||||
const LOG_SUBSYSTEM_DIAL9: &str = "dial9";
|
||||
const EVENT_DIAL9_STATE: &str = "dial9_state";
|
||||
|
||||
/// Configuration for dial9 Tokio telemetry.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Dial9Config {
|
||||
@@ -131,25 +135,51 @@ impl Dial9SessionGuard {
|
||||
/// Returns `Ok(None)` if dial9 is disabled.
|
||||
pub async fn new(config: Dial9Config) -> Result<Option<Self>, TelemetryError> {
|
||||
if !config.enabled {
|
||||
info!("Dial9 telemetry disabled");
|
||||
info!(
|
||||
event = EVENT_DIAL9_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_DIAL9,
|
||||
state = "disabled",
|
||||
"dial9 state changed"
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
info!(
|
||||
event = EVENT_DIAL9_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_DIAL9,
|
||||
state = "validating",
|
||||
output_dir = %config.output_dir,
|
||||
file_prefix = %config.file_prefix,
|
||||
sampling_rate = config.sampling_rate,
|
||||
"Validating dial9 telemetry configuration"
|
||||
"dial9 state changed"
|
||||
);
|
||||
|
||||
// Only create directory; writer will be created in build_traced_runtime
|
||||
if let Err(e) = tokio::fs::create_dir_all(&config.output_dir).await {
|
||||
warn!("Failed to create dial9 output directory '{}': {}", config.output_dir, e);
|
||||
warn!("Continuing without dial9 telemetry");
|
||||
warn!(
|
||||
event = EVENT_DIAL9_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_DIAL9,
|
||||
result = "output_dir_create_failed",
|
||||
output_dir = %config.output_dir,
|
||||
error = %e,
|
||||
fallback = "disabled",
|
||||
"dial9 state changed"
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
info!("Dial9 telemetry configuration validated successfully");
|
||||
info!(
|
||||
event = EVENT_DIAL9_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_DIAL9,
|
||||
state = "validated",
|
||||
output_dir = %config.output_dir,
|
||||
file_prefix = %config.file_prefix,
|
||||
"dial9 state changed"
|
||||
);
|
||||
|
||||
Ok(Some(Self { _guard: None, config }))
|
||||
}
|
||||
@@ -168,7 +198,13 @@ impl Dial9SessionGuard {
|
||||
/// Flush any pending telemetry data.
|
||||
pub async fn shutdown(&self) {
|
||||
if let Some(_guard) = &self._guard {
|
||||
info!("Dial9 telemetry data will be flushed on drop");
|
||||
info!(
|
||||
event = EVENT_DIAL9_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_DIAL9,
|
||||
state = "shutdown_requested",
|
||||
"dial9 state changed"
|
||||
);
|
||||
// TelemetryGuard handles flushing automatically when dropped
|
||||
}
|
||||
}
|
||||
@@ -178,7 +214,13 @@ impl Drop for Dial9SessionGuard {
|
||||
fn drop(&mut self) {
|
||||
if let Some(_guard) = &self._guard {
|
||||
// TelemetryGuard flushes automatically when dropped
|
||||
info!("Dial9 telemetry guard dropped, data flushed");
|
||||
info!(
|
||||
event = EVENT_DIAL9_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_DIAL9,
|
||||
state = "flushed",
|
||||
"dial9 state changed"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,11 @@ use tracing_subscriber::{
|
||||
util::SubscriberInitExt,
|
||||
};
|
||||
|
||||
const LOG_COMPONENT_OBS: &str = "obs";
|
||||
const LOG_SUBSYSTEM_LOCAL_LOGGING: &str = "local_logging";
|
||||
const EVENT_LOCAL_LOGGING_STATE: &str = "local_logging_state";
|
||||
const EVENT_LOG_CLEANER_STATE: &str = "log_cleaner_state";
|
||||
|
||||
pub(super) fn build_json_log_layer<S, W>(writer: W, enable_ansi: bool, span_events: FmtSpan) -> impl tracing_subscriber::Layer<S>
|
||||
where
|
||||
S: Subscriber + for<'span> LookupSpan<'span>,
|
||||
@@ -161,12 +166,16 @@ fn init_stdout_only(_config: &OtelConfig, logger_level: &str, is_production: boo
|
||||
set_observability_metric_enabled(false);
|
||||
counter!("rustfs_start_total").increment(1);
|
||||
info!(
|
||||
event = EVENT_LOCAL_LOGGING_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_LOCAL_LOGGING,
|
||||
state = "initialized",
|
||||
backend = "local",
|
||||
sink = "stdout",
|
||||
output_format = "json",
|
||||
logger_level,
|
||||
is_production,
|
||||
"Initialized local logging"
|
||||
"local logging state changed"
|
||||
);
|
||||
|
||||
OtelGuard {
|
||||
@@ -271,6 +280,10 @@ fn init_file_logging_internal(
|
||||
let cleanup_handle = spawn_cleanup_task(config, log_directory, log_filename, keep_files);
|
||||
|
||||
info!(
|
||||
event = EVENT_LOCAL_LOGGING_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_LOCAL_LOGGING,
|
||||
state = "initialized",
|
||||
backend = "local",
|
||||
sink = "file",
|
||||
output_format = "json",
|
||||
@@ -280,7 +293,7 @@ fn init_file_logging_internal(
|
||||
stdout_mirror_enabled = stdout_guard.is_some(),
|
||||
is_production,
|
||||
logger_level,
|
||||
"Initialized local logging"
|
||||
"local logging state changed"
|
||||
);
|
||||
|
||||
Ok(OtelGuard {
|
||||
@@ -459,13 +472,17 @@ pub fn spawn_cleanup_task(
|
||||
);
|
||||
|
||||
info!(
|
||||
event = EVENT_LOG_CLEANER_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_LOCAL_LOGGING,
|
||||
state = "configured",
|
||||
compression_algorithm = %compression_algorithm,
|
||||
parallel_compress,
|
||||
parallel_workers,
|
||||
zstd_level,
|
||||
zstd_fallback_to_gzip,
|
||||
zstd_workers,
|
||||
"log cleaner compression profile configured"
|
||||
"log cleaner state changed"
|
||||
);
|
||||
|
||||
tokio::spawn(async move {
|
||||
@@ -483,11 +500,25 @@ pub fn spawn_cleanup_task(
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
counter!(METRIC_LOG_CLEANER_RUN_FAILURES_TOTAL).increment(1);
|
||||
tracing::warn!("Log cleanup failed: {}", e);
|
||||
tracing::warn!(
|
||||
event = EVENT_LOG_CLEANER_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_LOCAL_LOGGING,
|
||||
result = "cleanup_failed",
|
||||
error = %e,
|
||||
"log cleaner state changed"
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
counter!(METRIC_LOG_CLEANER_RUN_FAILURES_TOTAL).increment(1);
|
||||
tracing::warn!("Log cleanup task panicked: {}", e);
|
||||
tracing::warn!(
|
||||
event = EVENT_LOG_CLEANER_STATE,
|
||||
component = LOG_COMPONENT_OBS,
|
||||
subsystem = LOG_SUBSYSTEM_LOCAL_LOGGING,
|
||||
result = "cleanup_task_panicked",
|
||||
error = %e,
|
||||
"log cleaner state changed"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,10 @@ use std::{
|
||||
};
|
||||
use tracing::error;
|
||||
|
||||
const LOG_COMPONENT_OBS: &str = "obs";
|
||||
const LOG_SUBSYSTEM_RECORDER: &str = "recorder";
|
||||
const EVENT_RECORDER_STATE: &str = "recorder_state";
|
||||
|
||||
macro_rules! configure_builder {
|
||||
($builder:expr, $metadata:expr) => {{
|
||||
let mut builder = $builder;
|
||||
@@ -151,7 +155,7 @@ impl Recorder {
|
||||
let cache = match lock.read() {
|
||||
Ok(g) => g,
|
||||
Err(e) => {
|
||||
error!("{} cache read lock poisoned: {}", metric_type, e);
|
||||
error!(event = EVENT_RECORDER_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_RECORDER, metric_type = %metric_type, result = "cache_read_lock_poisoned", error = %e, "recorder state changed");
|
||||
e.into_inner()
|
||||
}
|
||||
};
|
||||
@@ -162,7 +166,7 @@ impl Recorder {
|
||||
let mut cache = match lock.write() {
|
||||
Ok(g) => g,
|
||||
Err(e) => {
|
||||
error!("{} cache write lock poisoned: {}", metric_type, e);
|
||||
error!(event = EVENT_RECORDER_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_RECORDER, metric_type = %metric_type, result = "cache_write_lock_poisoned", error = %e, "recorder state changed");
|
||||
e.into_inner()
|
||||
}
|
||||
};
|
||||
@@ -179,7 +183,7 @@ impl Recorder {
|
||||
F: FnOnce(&mut HashMap<KeyName, MetricMetadata>) -> R,
|
||||
{
|
||||
let mut guard = self.metrics_metadata.lock().unwrap_or_else(|e| {
|
||||
error!("metrics_metadata lock poisoned: {}", e);
|
||||
error!(event = EVENT_RECORDER_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_RECORDER, result = "metrics_metadata_lock_poisoned", error = %e, "recorder state changed");
|
||||
e.into_inner()
|
||||
});
|
||||
f(&mut guard)
|
||||
|
||||
Reference in New Issue
Block a user