Skip to content

Commit

Permalink
add commons/logging
Browse files Browse the repository at this point in the history
  • Loading branch information
naftulikay committed Mar 1, 2024
1 parent 513401b commit 2f80d09
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 0 deletions.
14 changes: 14 additions & 0 deletions examples_common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "examples_common"
version = "0.1.0"
edition = "2021"
publish = false

[[example]]
name = "example_logging"
path = "examples/example_logging.rs"

[dependencies]
parking_lot.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
21 changes: 21 additions & 0 deletions examples_common/examples/example_logging.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use tracing::Level;

use examples_common::logging::LogLevelFilter;

fn main() {
// setup logging
examples_common::logging::init_logging(LogLevelFilter::builder()
.global(Level::WARN)
// set the logging for this executable
.level(env!("CARGO_CRATE_NAME"), Level::TRACE)
// set the logging for the examples_common crate
.level(examples_common::CRATE_NAME, Level::TRACE)
.build()
);

// test the crate
examples_common::logging::self_log_test();

// test here
examples_common::logging::log_level_test!();
}
4 changes: 4 additions & 0 deletions examples_common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod logging;

/// The name of this crate, exported as a utility for logging.
pub const CRATE_NAME: &str = env!("CARGO_CRATE_NAME");
142 changes: 142 additions & 0 deletions examples_common/src/logging.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//! Logging utilities.
use std::collections::HashMap;
use std::io;
use std::iter::successors;
use std::sync::Arc;

use tracing::{Level, Metadata};
use tracing_subscriber::{filter, Layer, Registry};
use tracing_subscriber::layer::SubscriberExt;

use parking_lot::Once;

const DEFAULT_LEVEL: Level = Level::WARN;
const DEFAULT_CRATE_LEVEL: Level = Level::DEBUG;

static LOGGING_INIT: Once = Once::new();

/// Emit logs at all levels to test logging.
#[macro_export]
macro_rules! log_level_test {
() => {{
tracing::trace!("TRACE");
tracing::debug!("DEBUG");
tracing::info!("INFO");
tracing::warn!("WARN");
tracing::error!("ERROR");
}};
}

pub use log_level_test;

/// Initialize logging idempotently.
///
/// Calling this more than once will have no effect.
pub fn init_logging(filter: LogLevelFilter) {
let filter = Arc::new(filter);

LOGGING_INIT.call_once(move || init_logging_actual(filter));
}

/// Test logging in this crate by emitting events at all log levels.
#[allow(unused)]
pub fn self_log_test() {
log_level_test!();
}

fn init_logging_actual(filter: Arc<LogLevelFilter>) {
tracing::subscriber::set_global_default(
Registry::default().with(
tracing_subscriber::fmt::layer()
.with_writer(io::stderr)
.pretty()
.with_filter(filter::filter_fn(move |meta| filter.allow(meta))),
),
)
.unwrap();
}

pub struct LogLevelFilter {
global: Level,
modules: HashMap<String, Level>,
}

impl Default for LogLevelFilter {
fn default() -> Self {
Self::builder().build()
}
}

impl LogLevelFilter {
pub fn builder() -> LogLevelFilterBuilder {
let mut b = LogLevelFilterBuilder {
..Default::default()
};

b.global = Some(DEFAULT_LEVEL);
b.modules
.insert(env!("CARGO_CRATE_NAME").to_string(), DEFAULT_CRATE_LEVEL);

b
}

pub fn set_global(&mut self, level: Level) {
self.global = level;
}

pub fn filter<S>(&mut self, logger: S, level: Level)
where
S: Into<String>,
{
self.modules.insert(logger.into(), level);
}

pub fn allow(&self, meta: &Metadata) -> bool {
// NOTE on levels: trace has the _lowest_ possible value in sorting (i.e. 0), while error
// has the *highest* possible value in sorting (i.e. 4). thus, in order for us to
// determine whether a given level is allowed, we must check `log.level` is greater
// than or equal to `log_rule.level`.
if let Some(module) = meta.module_path() {
let level = successors(Some(module), |m| {
m.rsplit_once("::").map(|(head, _tail)| head)
})
.find_map(|m| self.modules.get(m))
.unwrap_or(&self.global);

return meta.level() <= level;
}

true
}
}

#[derive(Default)]
pub struct LogLevelFilterBuilder {
global: Option<Level>,
#[allow(unused)]
modules: HashMap<String, Level>,
}

impl LogLevelFilterBuilder {
#[allow(unused)]
pub fn global(mut self, level: Level) -> Self {
self.global = level.into();
self
}

pub fn level<S>(mut self, logger: S, level: Level) -> Self
where
S: Into<String>,
{
self.modules.insert(logger.into(), level);
self
}

pub fn build(self) -> LogLevelFilter {
LogLevelFilter {
global: self.global.unwrap_or(DEFAULT_LEVEL),
modules: self.modules,
}
}
}

0 comments on commit 2f80d09

Please sign in to comment.