Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ jobs:
run: |
set -x
cargo run --example simple_stdout
cargo run --example log_with_logger
cargo run --example multiple_dispatches
cargo run --example custom_layout_filter
cargo run --no-default-features --example simple_stdout
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ All notable changes to this project will be documented in this file.
* `Append::flush` is now fallible.
* `Diagnostic`'s and `Visitor`'s `visit` methods are fallible.
* `NonBlocking` related types and the feature flag are now private.
* `logforth::Builder` is renamed to `logforth::LoggerBuilder`.
* `LoggerBuilder` has no longer an option to configure the global `max_level`. Check its documentation for more details.
* Constructing `RollingFile` and `Syslog` appender is heavily simplified.

Before:
Expand Down Expand Up @@ -75,6 +77,7 @@ fn construct_syslog() {

* Add `LogfmtLayout` to support logfmt format.
* Add `GoogleStructuredLogLayout` to support Google structured log format.
* `LoggerBuilder` now has a `build` method to construct the `Logger` for use.

## [0.23.1] 2025-03-23

Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ internal-serde = ["dep:serde", "log/kv_serde", "jiff/serde"]
anyhow = { version = "1.0" }
env_filter = { version = "0.1.1" }
jiff = { version = "0.2" }
log = { version = "0.4", features = ["std", "kv"] }
log = { version = "0.4.27", features = ["std", "kv"] }

# Optional dependencies
colored = { version = "3.0", optional = true }
Expand Down Expand Up @@ -91,6 +91,11 @@ doc-scrape-examples = true
name = "simple_stdout"
path = "examples/simple_stdout.rs"

[[example]]
doc-scrape-examples = true
name = "log_with_logger"
path = "examples/log_with_logger.rs"

[[example]]
doc-scrape-examples = true
name = "json_stdout"
Expand Down
26 changes: 26 additions & 0 deletions examples/log_with_logger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2024 FastLabs Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use log::LevelFilter;

fn main() {
log::set_max_level(LevelFilter::Trace);
let l = logforth::stdout().build();

log::error!(logger: l, "Hello error!");
log::warn!(logger: l, "Hello warn!");
log::info!(logger: l, "Hello info!");
log::debug!(logger: l, "Hello debug!");
log::trace!(logger: l, "Hello trace!");
}
2 changes: 1 addition & 1 deletion src/append/journald/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Rolling File Appender
# Journald Appender

This appender is a remix of [tracing-journald](https://crates.io/crates/tracing-journald) and [systemd-journal-logger](https://crates.io/crates/systemd-journal-logger), with several modifications to fit this crate's needs.
2 changes: 2 additions & 0 deletions src/append/rolling_file/rotation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub enum Rotation {
}

impl Rotation {
/// Get the next date timestamp based on the current date and rotation policy.
pub fn next_date_timestamp(&self, current_date: &Zoned) -> Option<usize> {
let timestamp_round = ZonedRound::new().mode(RoundMode::Trunc);

Expand All @@ -50,6 +51,7 @@ impl Rotation {
Some(next_date.timestamp().as_millisecond() as usize)
}

/// Get the date format string for the rotation policy.
pub fn date_format(&self) -> &'static str {
match *self {
Rotation::Minutely => "%F-%H-%M",
Expand Down
1 change: 1 addition & 0 deletions src/diagnostic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub trait Visitor {

/// A trait representing a Mapped Diagnostic Context (MDC) that provides diagnostic key-values.
pub trait Diagnostic: fmt::Debug + Send + Sync + 'static {
/// Visits the MDC key-values with the provided visitor.
fn visit(&self, visitor: &mut dyn Visitor) -> anyhow::Result<()>;
}

Expand Down
3 changes: 3 additions & 0 deletions src/diagnostic/static_global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ pub struct StaticDiagnostic {
}

impl StaticDiagnostic {
/// Creates a new [`StaticDiagnostic`] instance with a prebuilt key-value store.
pub fn new(kvs: BTreeMap<String, String>) -> Self {
Self { kvs }
}

/// Inserts a key-value pair into the static diagnostic .
pub fn insert<K, V>(&mut self, key: K, value: V)
where
K: Into<String>,
Expand All @@ -46,6 +48,7 @@ impl StaticDiagnostic {
self.kvs.insert(key.into(), value.into());
}

/// Remove a key-value pair from the static diagnostic.
pub fn remove(&mut self, key: &str) {
self.kvs.remove(key);
}
Expand Down
2 changes: 2 additions & 0 deletions src/diagnostic/thread_local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ thread_local! {
pub struct ThreadLocalDiagnostic {}

impl ThreadLocalDiagnostic {
/// Inserts a key-value pair into the thread local diagnostic .
pub fn insert<K, V>(key: K, value: V)
where
K: Into<String>,
Expand All @@ -46,6 +47,7 @@ impl ThreadLocalDiagnostic {
});
}

/// Removes a key-value pair from the thread local diagnostic.
pub fn remove(key: &str) {
CONTEXT.with(|map| {
map.borrow_mut().remove(key);
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#![deny(missing_docs)]

//! Logforth is a flexible logging framework for Rust applications, providing easy log dispatching
//! and configuration.
//!
Expand Down
58 changes: 29 additions & 29 deletions src/logger/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::Append;
use crate::Diagnostic;
use crate::Filter;

/// Creates a new empty [`Builder`] instance for configuring log dispatching.
/// Creates a new empty [`LoggerBuilder`] instance for configuring log dispatching.
///
/// # Examples
///
Expand All @@ -33,11 +33,11 @@ use crate::Filter;
/// .dispatch(|d| d.append(append::Stderr::default()))
/// .apply();
/// ```
pub fn builder() -> Builder {
Builder::new()
pub fn builder() -> LoggerBuilder {
LoggerBuilder::new()
}

/// Creates a [`Builder`] with a default [`append::Stdout`] appender and an [`env_filter`](https://crates.io/crates/env_filter)
/// Creates a [`LoggerBuilder`] with a default [`append::Stdout`] appender and an [`env_filter`](https://crates.io/crates/env_filter)
/// respecting `RUST_LOG`.
///
/// # Examples
Expand All @@ -46,14 +46,14 @@ pub fn builder() -> Builder {
/// logforth::stdout().apply();
/// log::error!("This error will be logged to stdout.");
/// ```
pub fn stdout() -> Builder {
pub fn stdout() -> LoggerBuilder {
builder().dispatch(|d| {
d.filter(EnvFilter::from_default_env())
.append(append::Stdout::default())
})
}

/// Creates a [`Builder`] with a default [`append::Stderr`] appender and an [`env_filter`](https://crates.io/crates/env_filter)
/// Creates a [`LoggerBuilder`] with a default [`append::Stderr`] appender and an [`env_filter`](https://crates.io/crates/env_filter)
/// respecting `RUST_LOG`.
///
/// # Examples
Expand All @@ -62,7 +62,7 @@ pub fn stdout() -> Builder {
/// logforth::stderr().apply();
/// log::info!("This info will be logged to stderr.");
/// ```
pub fn stderr() -> Builder {
pub fn stderr() -> LoggerBuilder {
builder().dispatch(|d| {
d.filter(EnvFilter::from_default_env())
.append(append::Stderr::default())
Expand All @@ -80,25 +80,19 @@ pub fn stderr() -> Builder {
/// .dispatch(|d| d.append(append::Stdout::default()))
/// .apply();
/// ```
#[must_use = "call `apply` to set the global logger"]
#[must_use = "call `apply` to set the global logger or `build` to construct a logger instance"]
#[derive(Debug)]
pub struct Builder {
pub struct LoggerBuilder {
// stashed dispatches
dispatches: Vec<Dispatch>,

// default to trace - we need this because the global default is OFF
max_level: LevelFilter,
}

impl Builder {
impl LoggerBuilder {
fn new() -> Self {
Builder {
dispatches: vec![],
max_level: LevelFilter::Trace,
}
LoggerBuilder { dispatches: vec![] }
}

/// Registers a new dispatch with the [`Builder`].
/// Registers a new dispatch with the [`LoggerBuilder`].
///
/// # Examples
///
Expand All @@ -117,27 +111,28 @@ impl Builder {
self
}

/// Sets the global maximum log level. Default to [`LevelFilter::Trace`].
///
/// This will be passed to `log::set_max_level()`.
/// Build the [`Logger`].
///
/// # Examples
///
/// ```
/// logforth::builder()
/// .max_level(log::LevelFilter::Warn)
/// .apply();
/// let l = logforth::builder().build();
/// log::error!(logger: l, "Hello error!");
/// ```
pub fn max_level(mut self, max_level: LevelFilter) -> Self {
self.max_level = max_level;
self
pub fn build(self) -> Logger {
Logger::new(self.dispatches)
}

/// Sets up the global logger with all the configured dispatches.
///
/// This should be called early in the execution of a Rust program. Any log events that occur
/// before initialization will be ignored.
///
/// This will set the global maximum log level to [`LevelFilter::Trace`]. To override this,
/// call [`log::set_max_level`] after this function. Alternatively, you can obtain a [`Logger`]
/// instance by calling [`LoggerBuilder::build`], and then call [`log::set_boxed_logger`]
/// manually.
///
/// # Errors
///
/// Returns an error if a global logger has already been set.
Expand All @@ -151,9 +146,9 @@ impl Builder {
/// }
/// ```
pub fn try_apply(self) -> Result<(), log::SetLoggerError> {
let logger = Logger::new(self.dispatches);
let logger = self.build();
log::set_boxed_logger(Box::new(logger))?;
log::set_max_level(self.max_level);
log::set_max_level(LevelFilter::Trace);
Ok(())
}

Expand All @@ -162,6 +157,11 @@ impl Builder {
/// This function will panic if it is called more than once, or if another library has already
/// initialized a global logger.
///
/// This function will set the global maximum log level to [`LevelFilter::Trace`]. To override
/// this, call [`log::set_max_level`] after this function. Alternatively, you can obtain a
/// [`Logger`] instance by calling [`LoggerBuilder::build`], and then call
/// [`log::set_boxed_logger`] manually.
///
/// # Panics
///
/// Panics if the global logger has already been set.
Expand Down
4 changes: 2 additions & 2 deletions src/logger/log_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ use crate::Append;
use crate::Diagnostic;
use crate::Filter;

/// A logger facade that dispatches log records to one or more [`Dispatch`] instances.
/// A logger facade that dispatches log records to one or more dispatcher.
///
/// This struct implements [`log::Log`] to bridge Logforth's logging implementations
/// with the [`log`] crate.
#[derive(Debug)]
pub(super) struct Logger {
pub struct Logger {
dispatches: Vec<Dispatch>,
}

Expand Down
1 change: 1 addition & 0 deletions src/logger/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ mod builder;
pub use builder::*;

mod log_impl;
pub use log_impl::Logger;
Loading