feat(logging): tracing broadcast layer for real-time log streaming
This commit is contained in:
parent
dff155a93c
commit
bee11c7735
@ -23,6 +23,7 @@ prost = "0.14"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "json", "local-time"] }
|
||||
tracing-appender = "0.2"
|
||||
time = { version = "0.3", features = ["formatting", "local-offset"] }
|
||||
anyhow = "1.0"
|
||||
mime_guess = "2.0"
|
||||
base64 = "0.22"
|
||||
|
||||
@ -1,27 +1,93 @@
|
||||
use std::path::PathBuf;
|
||||
use std::sync::OnceLock;
|
||||
use tokio::sync::broadcast;
|
||||
use tracing::field::{Field, Visit};
|
||||
use tracing::{Event, Subscriber};
|
||||
use tracing_appender::rolling::{RollingFileAppender, Rotation};
|
||||
use tracing_subscriber::layer::Context;
|
||||
use tracing_subscriber::registry::LookupSpan;
|
||||
use tracing_subscriber::Layer;
|
||||
use tracing_subscriber::{
|
||||
EnvFilter, fmt, fmt::time::LocalTime, layer::SubscriberExt, util::SubscriberInitExt,
|
||||
};
|
||||
|
||||
/// Get the default log directory path: ~/.picobot/logs
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LogEvent {
|
||||
pub ts: String,
|
||||
pub level: String,
|
||||
pub target: String,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
static LOG_TX: OnceLock<broadcast::Sender<LogEvent>> = OnceLock::new();
|
||||
|
||||
pub fn log_sender() -> Option<broadcast::Sender<LogEvent>> {
|
||||
LOG_TX.get().cloned()
|
||||
}
|
||||
|
||||
struct MessageVisitor(String);
|
||||
|
||||
impl Visit for MessageVisitor {
|
||||
fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
|
||||
if field.name() == "message" {
|
||||
self.0 = format!("{value:?}");
|
||||
}
|
||||
}
|
||||
|
||||
fn record_str(&mut self, field: &Field, value: &str) {
|
||||
if field.name() == "message" {
|
||||
self.0 = value.to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct BroadcastLayer;
|
||||
|
||||
impl<S> Layer<S> for BroadcastLayer
|
||||
where
|
||||
S: Subscriber + for<'a> LookupSpan<'a>,
|
||||
{
|
||||
fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
|
||||
let Some(tx) = LOG_TX.get() else {
|
||||
return;
|
||||
};
|
||||
if tx.receiver_count() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut visitor = MessageVisitor(String::new());
|
||||
event.record(&mut visitor);
|
||||
|
||||
let ts = time::OffsetDateTime::now_local()
|
||||
.unwrap_or_else(|_| time::OffsetDateTime::now_utc())
|
||||
.format(&time::format_description::well_known::Rfc3339)
|
||||
.unwrap_or_default();
|
||||
|
||||
let _ = tx.send(LogEvent {
|
||||
ts,
|
||||
level: event.metadata().level().to_string(),
|
||||
target: event.metadata().target().to_string(),
|
||||
message: visitor.0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_default_log_dir() -> PathBuf {
|
||||
let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
||||
home.join(".picobot").join("logs")
|
||||
}
|
||||
|
||||
/// Get the default config file path: ~/.picobot/config.json
|
||||
pub fn get_default_config_path() -> PathBuf {
|
||||
let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
||||
home.join(".picobot").join("config.json")
|
||||
}
|
||||
|
||||
/// Initialize logging with file appender
|
||||
/// Logs are written to ~/.picobot/logs/ with daily rotation
|
||||
pub fn init_logging() {
|
||||
let (tx, _) = broadcast::channel::<LogEvent>(1024);
|
||||
let _ = LOG_TX.set(tx);
|
||||
|
||||
let log_dir = get_default_log_dir();
|
||||
|
||||
// Create log directory if it doesn't exist
|
||||
if !log_dir.exists()
|
||||
&& let Err(e) = std::fs::create_dir_all(&log_dir)
|
||||
{
|
||||
@ -32,10 +98,8 @@ pub fn init_logging() {
|
||||
);
|
||||
}
|
||||
|
||||
// Create file appender with daily rotation
|
||||
let file_appender = RollingFileAppender::new(Rotation::DAILY, &log_dir, "picobot.log");
|
||||
|
||||
// Build subscriber with both console and file output
|
||||
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
|
||||
|
||||
let file_layer = fmt::layer()
|
||||
@ -55,6 +119,7 @@ pub fn init_logging() {
|
||||
.with(env_filter)
|
||||
.with(console_layer)
|
||||
.with(file_layer)
|
||||
.with(BroadcastLayer)
|
||||
.init();
|
||||
|
||||
tracing::info!("Logging initialized. Log directory: {}", log_dir.display());
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user