Add comprehensive logging with tracing, file rotation, and systemd integration

- Add tracing-appender and tracing-journald for production logging
- Add LoggingConfig with trace_sample_rate, json_output, journald options
- Expand init_logging() with file rotation, journald, and stderr layers
- Add sanitize_path() helper for PII protection in logs
- Instrument FUSE operations with #[instrument] and trace decision points
- Instrument gRPC handlers (10 methods) with span correlation
- Add spawn instrumentation for health monitor, indexer, watcher tasks
- Add broadcast lag handling (RecvError::Lagged) in event subscribers
- Fix webhook.rs expect() calls with proper error handling
- Add logging to patterns.rs, collections.rs, artwork.rs database ops
- Add Drop impl logging for PluginManager and WatchHandle
- Update systemd service with rate limiting and journal output
- Add logrotate config and example config.toml with logging section
This commit is contained in:
Alexander
2026-05-13 11:21:51 +02:00
parent bc9fa36646
commit 5ac33987c0
32 changed files with 1646 additions and 177 deletions
+84 -15
View File
@@ -2,7 +2,7 @@ use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
use musicfs_cache::TreeBuilder;
use musicfs_cas::{CasConfig, CasStore, ContentFetcher, FileReader};
use musicfs_core::{FileId, FileMeta, OriginId, RealPath, VirtualPath};
use musicfs_core::{FileId, FileMeta, LoggingConfig, OriginId, RealPath, VirtualPath};
use musicfs_fuse::MusicFs;
use musicfs_metadata::MetadataParser;
use musicfs_origins::{LocalOrigin, Origin};
@@ -10,6 +10,8 @@ use std::path::{Path, PathBuf};
use std::sync::{Arc, RwLock};
use std::time::SystemTime;
use tracing::{debug, info};
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::{fmt, prelude::*, EnvFilter, Layer};
#[derive(Parser)]
#[command(name = "musicfs")]
@@ -86,7 +88,6 @@ enum OriginCommands {
fn main() -> Result<()> {
let cli = Cli::parse();
init_logging(&cli.log_level);
match cli.command {
Commands::Mount {
@@ -94,13 +95,38 @@ fn main() -> Result<()> {
mountpoint,
origin,
cache_dir,
} => run_mount(mountpoint, origin, cache_dir),
Commands::Status => run_status(),
Commands::Cache { command } => run_cache(command),
Commands::Search { query, limit } => run_search(&query, limit),
Commands::Origin { command } => run_origin(command),
Commands::Events { r#type } => run_events(r#type),
Commands::Shutdown { graceful, timeout } => run_shutdown(graceful, timeout),
} => {
let log_config = LoggingConfig {
level: cli.log_level,
..Default::default()
};
let _guard = init_logging(&log_config)?;
run_mount(mountpoint, origin, cache_dir)
}
Commands::Status => {
init_basic_logging(&cli.log_level);
run_status()
}
Commands::Cache { command } => {
init_basic_logging(&cli.log_level);
run_cache(command)
}
Commands::Search { query, limit } => {
init_basic_logging(&cli.log_level);
run_search(&query, limit)
}
Commands::Origin { command } => {
init_basic_logging(&cli.log_level);
run_origin(command)
}
Commands::Events { r#type } => {
init_basic_logging(&cli.log_level);
run_events(r#type)
}
Commands::Shutdown { graceful, timeout } => {
init_basic_logging(&cli.log_level);
run_shutdown(graceful, timeout)
}
}
}
@@ -115,9 +141,7 @@ fn run_mount(
let handle = runtime.handle().clone();
let (tree, reader) = runtime.block_on(async {
info!("MusicFS starting...");
info!("Origin: {:?}", origin_path);
info!("Mountpoint: {:?}", mountpoint);
info!(origin = ?origin_path, mountpoint = ?mountpoint, "Mount configuration");
let cache_dir = cache_dir.unwrap_or_else(|| {
dirs::cache_dir()
@@ -240,13 +264,58 @@ fn run_shutdown(graceful: bool, timeout: u32) -> Result<()> {
Ok(())
}
fn init_logging(level: &str) {
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
fn init_logging(config: &LoggingConfig) -> Result<WorkerGuard> {
std::fs::create_dir_all(&config.log_dir)?;
let file_appender = tracing_appender::rolling::daily(&config.log_dir, "musicfs.log");
let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
let file_layer = if config.json_output {
fmt::layer()
.json()
.with_writer(non_blocking)
.with_ansi(false)
.boxed()
} else {
fmt::layer()
.with_writer(non_blocking)
.with_ansi(false)
.boxed()
};
let stderr_layer = fmt::layer().with_writer(std::io::stderr).compact();
let filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new(&config.level));
let subscriber = tracing_subscriber::registry()
.with(filter)
.with(file_layer)
.with(stderr_layer);
#[cfg(target_os = "linux")]
let subscriber = {
let journald_layer = if config.journald {
tracing_journald::layer()
.ok()
.map(|l| l.with_syslog_identifier("musicfs".to_string()))
} else {
None
};
subscriber.with(journald_layer)
};
subscriber.init();
info!(version = env!("CARGO_PKG_VERSION"), "MusicFS starting");
Ok(guard)
}
fn init_basic_logging(level: &str) {
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(level));
tracing_subscriber::registry()
.with(fmt::layer())
.with(fmt::layer().compact())
.with(filter)
.init();
}