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
+10 -2
View File
@@ -5,7 +5,7 @@ use musicfs_metadata::artwork::{ArtSize, ArtType, Artwork};
use std::io::Cursor;
use std::path::Path;
use std::sync::Arc;
use tracing::debug;
use tracing::{debug, info, trace, warn};
const MAX_ARTWORK_INPUT_SIZE: usize = 10 * 1024 * 1024;
@@ -40,6 +40,7 @@ impl ArtworkCache {
[],
)?;
info!(path = ?db_path, "Artwork cache opened");
Ok(Self {
store,
db_path: db_path.to_path_buf(),
@@ -47,7 +48,9 @@ impl ArtworkCache {
}
pub async fn store(&self, file_id: i64, artwork: &Artwork) -> Result<ChunkHash, ArtworkError> {
trace!(file_id = file_id, size_bytes = artwork.data.len(), "Storing artwork");
if artwork.data.len() > MAX_ARTWORK_INPUT_SIZE {
warn!(file_id = file_id, size = artwork.data.len(), max = MAX_ARTWORK_INPUT_SIZE, "Artwork too large");
return Err(ArtworkError::ImageTooLarge(artwork.data.len()));
}
@@ -88,6 +91,7 @@ impl ArtworkCache {
art_type: &str,
size: ArtSize,
) -> Result<Option<Vec<u8>>, ArtworkError> {
trace!(file_id = file_id, art_type = %art_type, "Getting artwork");
let db_path = self.db_path.clone();
let art_type_clone = art_type.to_string();
@@ -107,6 +111,7 @@ impl ArtworkCache {
match hash_hex {
Some(hex) => {
trace!(file_id = file_id, "Artwork cache hit");
let hash = ChunkHash::from_hex(&hex).ok_or(ArtworkError::InvalidHash)?;
let data = self.store.get(&hash).await?;
@@ -118,7 +123,10 @@ impl ArtworkCache {
}
}
}
None => Ok(None),
None => {
trace!(file_id = file_id, "Artwork cache miss");
Ok(None)
}
}
}
+6 -3
View File
@@ -16,7 +16,7 @@ pub struct Database {
impl Database {
pub fn open(path: &Path) -> Result<Self> {
info!(?path, "Opening database");
debug!(?path, "Opening database");
let conn =
Connection::open(path).map_err(|e| Error::Database(format!("open failed: {}", e)))?;
@@ -24,9 +24,12 @@ impl Database {
conn.execute_batch(SCHEMA)
.map_err(|e| Error::Database(format!("schema init failed: {}", e)))?;
Ok(Self {
let db = Self {
conn: Arc::new(Mutex::new(conn)),
})
};
let count = db.file_count().unwrap_or(0);
info!(path = ?path, file_count = count, "Database opened");
Ok(db)
}
pub fn open_memory() -> Result<Self> {
+9 -2
View File
@@ -3,6 +3,7 @@ use musicfs_core::{AudioMeta, FileMeta, OriginId, Result, VirtualPath};
use std::path::Path;
use std::sync::Arc;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tracing::trace;
pub struct MetadataCache {
db: Arc<Database>,
@@ -34,7 +35,10 @@ impl MetadataCache {
}
pub fn lookup(&self, path: &VirtualPath) -> Result<Option<FileMeta>> {
self.db.get_file_by_virtual_path(path)
let result = self.db.get_file_by_virtual_path(path)?;
let hit = result.is_some();
trace!(path = path.as_str(), hit, "metadata cache lookup");
Ok(result)
}
pub fn is_fresh(
@@ -52,8 +56,11 @@ impl MetadataCache {
.duration_since(UNIX_EPOCH)
.unwrap_or(Duration::ZERO)
.as_secs();
Ok(current_secs == cached_secs)
let hit = current_secs == cached_secs;
trace!(path = ?real_path, hit, "metadata freshness check");
Ok(hit)
} else {
trace!(path = ?real_path, hit = false, "metadata freshness check");
Ok(false)
}
}
+11 -4
View File
@@ -3,6 +3,7 @@ use parking_lot::{Mutex, RwLock};
use std::collections::HashMap;
use std::path::Path;
use std::time::{SystemTime, UNIX_EPOCH};
use tracing::{debug, info, trace};
#[derive(Debug, Clone)]
pub struct AccessPattern {
@@ -79,15 +80,19 @@ impl PatternStore {
map
};
Ok(Self {
let store = Self {
db: Mutex::new(db),
sequence_counts: RwLock::new(sequence_counts),
time_patterns: RwLock::new(HashMap::new()),
max_history,
})
};
let sequence_count = store.sequence_counts.read().len();
info!(path = ?db_path, sequence_count = sequence_count, max_history = max_history, "Pattern store opened");
Ok(store)
}
pub fn record(&self, file_id: FileId, _context: AccessContext) -> Result<(), PatternError> {
trace!(file_id = file_id.0, "Recording access pattern");
let now = SystemTime::now();
let timestamp = now.duration_since(UNIX_EPOCH).unwrap().as_secs() as i64;
let hour = (timestamp / 3600 % 24) as u8;
@@ -144,11 +149,13 @@ impl PatternStore {
.collect();
predictions.sort_by(|a, b| b.1.cmp(&a.1));
predictions
let result: Vec<FileId> = predictions
.into_iter()
.take(limit)
.map(|(id, _)| id)
.collect()
.collect();
debug!(file_id = current.0, predictions = result.len(), "Predicted next files");
result
}
pub fn predict_for_time(&self, hour: u8, limit: usize) -> Vec<FileId> {
+8 -1
View File
@@ -4,6 +4,7 @@ use std::ffi::{OsStr, OsString};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::RwLock;
use std::time::{Duration, SystemTime};
use tracing::{debug, trace};
pub type Inode = u64;
pub const ROOT_INODE: Inode = 1;
@@ -123,8 +124,12 @@ impl VirtualTree {
pub fn lookup(&self, parent_inode: Inode, name: &OsStr) -> Option<Inode> {
if let Some(VirtualNode::Directory(dir)) = self.nodes.get(&parent_inode) {
dir.children.get(name).copied()
let result = dir.children.get(name).copied();
let hit = result.is_some();
trace!(inode = parent_inode, name = ?name, hit, "tree lookup");
result
} else {
trace!(inode = parent_inode, name = ?name, hit = false, "tree lookup");
None
}
}
@@ -194,6 +199,7 @@ impl VirtualTree {
dir.children.insert(name, inode);
}
debug!(inode, path = path.as_str(), file_id = ?meta.id, "add file to tree");
inode
}
@@ -263,6 +269,7 @@ impl VirtualTree {
}
}
debug!(inode, path = path.as_str(), file_id = ?file.file_id, "remove file from tree");
Some(file.file_id)
} else {
None