Lemmy server, hacking application-specific log files into the Rust code
For weeks I've been trying to figure out how to get more organized logging out of the Rust code, and I have never worked with Rust before. The default behavior of lemmy_server is to put all Rust logging into the Linux syslog and you typically filter by the Linux user account 'lemmy'.
Where to write Files ==========
I did "Lemmy from Scratch" and run without Docker. So my lemmy_server lemmy I throw logs into /home/lemmy/logs folder. I don't think Docker even creates a /home/lemmy directory, not sure. I'm inclined to make this an environment variable to set the output folder - suggestions welcome.
Rust Library =========
crate add tracing-appender
Code surgery to /src/lib.rs ==========
use tracing::Level;
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::{
fmt::{self, writer::MakeWriterExt}
};
pub fn init_logging(opentelemetry_url: &Option<Url>) -> Result<Vec<Option<WorkerGuard>>, LemmyError> {
LogTracer::init()?;
let log_dir = Some("/home/lemmy/logs");
// Code may have multiple log files (target tags) in the future, so return an array.
let mut guards = Vec::new();
let file_filter = Targets::new()
.with_target("federation", Level::TRACE);
let file_log = log_dir
.map(|p| tracing_appender::non_blocking(tracing_appender::rolling::daily(p, "federation")))
.map(|(appender_type, g)| {
let guard = Some(g);
guards.push(guard);
fmt::Layer::new()
.with_writer(appender_type.with_max_level(Level::TRACE))
.with_ansi(false)
.with_filter(file_filter)
});
let log_description = std::env::var("RUST_LOG").unwrap_or_else(|_| "info".into());
let targets = log_description
.trim()
.trim_matches('"')
.parse::<Targets>()?;
let format_layer = {
#[cfg(feature = "json-log")]
let layer = tracing_subscriber::fmt::layer().json();
#[cfg(not(feature = "json-log"))]
let layer = tracing_subscriber::fmt::layer();
layer.with_filter(targets.clone())
};
let subscriber = Registry::default()
.with(format_layer)
.with(ErrorLayer::default())
.with(file_log)
;
if let Some(_url) = opentelemetry_url {
#[cfg(feature = "console")]
telemetry::init_tracing(_url.as_ref(), subscriber, targets)?;
#[cfg(not(feature = "console"))]
tracing::error!("Feature `console` must be enabled for opentelemetry tracing");
} else {
set_global_default(subscriber)?;
}
tracing::warn!("zebracat logging checkpoint A");
tracing::info!(target:"federation", "zebracat file logging startup");
Ok(guards)
}
Code surgery to /src/main.rs ==========
let _guards = init_logging(&SETTINGS.opentelemetry_url)?;
How to use ================
Anywhere you want in Lemmy code, you can now log to your file:
tracing::info!(target:"federation", "this will go to the file");