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");

  • RoundSparrow@lemmy.mlOPM
    link
    fedilink
    arrow-up
    1
    ·
    1 year ago

    2 log files, “federation” and “misc” targets

    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 file_filter1 = Targets::new()
        .with_target("misc", Level::TRACE);
      
      let file_log1 = log_dir
          .map(|p| tracing_appender::non_blocking(tracing_appender::rolling::daily(p, "misc")))
          .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_filter1)
          });
    
    
      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)
        .with(file_log1)
        ;
    
      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");
      tracing::info!(target:"misc",       "zebracat file logging startup misc");
    
      Ok(guards)
    }