[pbs-devel] [RFC proxmox 2/2] proxmox-log: added tracing infra
Gabriel Goller
g.goller at proxmox.com
Mon Oct 23 10:56:27 CEST 2023
Thanks for the review (comments inline).
On 10/18/23 15:26, Dominik Csapak wrote:
> i just glanced over the code but a few comments inline
>
> On 10/11/23 16:01, Gabriel Goller wrote:
> [..snip..]
>> diff --git a/proxmox-log/src/file_layer.rs
>> b/proxmox-log/src/file_layer.rs
>> new file mode 100644
>> index 0000000..1a167f4
>> --- /dev/null
>> +++ b/proxmox-log/src/file_layer.rs
>> @@ -0,0 +1,89 @@
>> +use std::cell::RefCell;
>> +use tracing::error;
>> +use tracing::field::Field;
>> +use tracing::field::Visit;
>> +use tracing::Event;
>> +use tracing::Level;
>> +use tracing::Subscriber;
>> +use tracing_subscriber::layer::Context;
>> +use tracing_subscriber::Layer;
>> +
>> +use crate::FileLogger;
>> +
>> +tokio::task_local! {
>> + pub static LOGGER: RefCell<Option<FileLogger>>;
>> + pub static WARN_COUNTER: RefCell<Option<u64>>;
>
> why are these wrapped in options? with 'try_with' you get an error
> anyway when they don't exists and i did not see a place
> where you'd set them to None?
Right, this was a relic from when I used `thread_local`, now with
`task_local` its not used anymore. Thanks!
>
>> +}
>> +
>> +pub struct FilelogLayer {}
>> +
>> +impl FilelogLayer {
>> + pub fn new() -> Self {
>> + Self {}
>> + }
>> +}
>> +
>> +impl<S: Subscriber> Layer<S> for FilelogLayer {
>> + fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
>> + let mut buf = String::new();
>> +
>> + event.record(&mut EventVisitor::new(&mut buf));
>> +
>> + let logger_exists = LOGGER.try_with(|logger| {
>> + let mut l = logger.borrow_mut();
>> + let log = l.as_mut();
>> + if let Some(l) = log {
>> + log_to_file(l, event.metadata().level(), buf);
>> + } else {
>> + error!("FileLogger not found");
>> + }
>> + });
>> + if let Err(e) = logger_exists {
>> + error!(
>> + "Error getting the logger from the task-local
>> storage: {}",
>> + e
>> + )
>> + }
>
> this would be much shorter if it wasn't for the Option<>
>
>> + }
>> +}
>> +
>> +fn log_to_file(logger: &mut FileLogger, level: &Level, buf: String) {
>> + match level {
>> + &Level::ERROR | &Level::WARN => {
>> + let counter_exists = WARN_COUNTER.try_with(|counter| > +
>> counter.replace_with(|c| {
>> + if let Some(v) = c {
>> + Some(v.to_owned() + 1)
>> + } else {
>> + None
>> + }
>> + });
>
> two things here:
> * do we really want to increase the warn counter for error too?
> (i can't remember what we do now)
> normally we abort on an error anyway, so that does not make sense
> or if we now can log an error without aborting, we'd probably want
> to have a separate error count?
We always incremented the `warn_counter` on error and warning. The thing is
we don't really use it when there is an error (check `create_state()` in
worker_task.rs).
I don't have any strong feelings on this, we could also leave it as it
is, as one could
interpret it as 'counter of events of level warn and higher'.
>
> * clippy says this is a manually implemented map:
>
> counter.replace_with(|c| c.as_mut().map(|v| v.to_owned() + 1));
>
not necessary anymore, as I removed the Option<> wrap.
>
>
>> + logger.log(buf.clone());
>> + });
>> + if let Err(e) = counter_exists {
>> + error!("Error getting the warning_counter for the
>> logger: {}", e)
>> + }
>> + }
>> + &Level::INFO => logger.log(buf.clone()),
>
> this clone is redundant (thanks clippy ;) )
Fixed this and some other clippy lints :)
>
>> + &Level::DEBUG => logger.log(format!("DEBUG: {}", buf)),
>> + &Level::TRACE => logger.log(format!("TRACE: {}", buf)),
>> + };
>> +}
>> +
>> +struct EventVisitor<'a> {
>> + buf: &'a mut String,
>> +}
>> +
>> +impl<'a> EventVisitor<'a> {
>> + fn new(buf: &'a mut String) -> Self {
>> + Self { buf }
>> + }
>> +}
>> +
>> +impl Visit for EventVisitor<'_> {
>> + fn record_debug(&mut self, field: &Field, value: &dyn
>> std::fmt::Debug) {
>> + if field.name() == "message" {
>> + self.buf.push_str(&format!("{:?}", value));
>> + }
>> + }
>> +}
>> diff --git a/proxmox-rest-server/src/file_logger.rs
>> b/proxmox-log/src/file_logger.rs
>> similarity index 98%
>> rename from proxmox-rest-server/src/file_logger.rs
>> rename to proxmox-log/src/file_logger.rs
>> index 2bb1fac..c7e1d64 100644
>> --- a/proxmox-rest-server/src/file_logger.rs
>> +++ b/proxmox-log/src/file_logger.rs
>> @@ -30,7 +30,7 @@ pub struct FileLogOptions {
>> /// #### Example:
>> /// ```
>> /// # use anyhow::{bail, format_err, Error};
>> -/// use proxmox_rest_server::{flog, FileLogger, FileLogOptions};
>> +/// use proxmox_log::{flog, FileLogger, FileLogOptions};
>> ///
>> /// # std::fs::remove_file("test.log");
>> /// let options = FileLogOptions {
>> diff --git a/proxmox-log/src/lib.rs b/proxmox-log/src/lib.rs
>> new file mode 100644
>> index 0000000..0c101b7
>> --- /dev/null
>> +++ b/proxmox-log/src/lib.rs
>> @@ -0,0 +1,36 @@
>> +//! # Proxmox logging infrastructure
>> +use crate::syslog_layer::SyslogLayer;
>> +use file_layer::FilelogLayer;
>> +use std::env;
>> +use tracing_log::LogTracer;
>> +use tracing_subscriber::filter::{filter_fn, LevelFilter};
>> +use tracing_subscriber::prelude::*;
>> +
>> +pub mod file_layer;
>> +mod file_logger;
>> +pub use file_logger::{FileLogOptions, FileLogger};
>> +
>> +mod syslog_layer;
>> +
>> +pub fn init_logger(
>> + env_var_name: &str,
>> + default_log_level: LevelFilter,
>> + application_name: &str,
>> +) -> Result<(), anyhow::Error> {
>> + let mut log_level = default_log_level;
>> + if let Ok(v) = env::var(env_var_name) {
>> + if let Ok(l) = v.parse::<LevelFilter>() {
>> + log_level = l;
>> + }
>> + }
>> + let registry = tracing_subscriber::registry()
>> + .with(FilelogLayer::new().with_filter(filter_fn(|metadata| {
>> + metadata.fields().field("tasklog").is_some()
>
> you give 'tasklog = true' but only check here for is_some, wouldn't
> it be better to do
> metadata.fields().field("tasklog") == Some("true")
> ?
> that way a
> 'tasklog = false' wouldn't (confusingly) log to the task log
>
Sadly, we can't do that, because tracing only records the field names
before enabling/disabling a layer (because of performance reasons,
computing the value **could** be expensive).
> [..]
>
More information about the pbs-devel
mailing list